971 lines · 19.7 KB · Shell
RawDownload
#!/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. # Kselftest framework requirement - SKIP code is 4.ksft_skip=4 TEST_NAME="sysctl"TEST_DRIVER="test_${TEST_NAME}"TEST_DIR=$(dirname $0)TEST_FILE=$(mktemp) # This represents## TEST_ID:TEST_COUNT:ENABLED:TARGET## TEST_ID: is the test id number# TEST_COUNT: number of times we should run the test# ENABLED: 1 if enabled, 0 otherwise# TARGET: test target file required on the test_sysctl module## Once these are enabled please leave them as-is. Write your own test,# we have tons of space.ALL_TESTS="0001:1:1:int_0001"ALL_TESTS="$ALL_TESTS 0002:1:1:string_0001"ALL_TESTS="$ALL_TESTS 0003:1:1:int_0002"ALL_TESTS="$ALL_TESTS 0004:1:1:uint_0001"ALL_TESTS="$ALL_TESTS 0005:3:1:int_0003"ALL_TESTS="$ALL_TESTS 0006:50:1:bitmap_0001"ALL_TESTS="$ALL_TESTS 0007:1:1:boot_int" 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 	if [ -z $PAGE_SIZE ]; then		PAGE_SIZE=$(getconf PAGESIZE)	fi	if [ -z $MAX_DIGITS ]; then		MAX_DIGITS=$(($PAGE_SIZE/8))	fi	if [ -z $INT_MAX ]; then		INT_MAX=$(getconf INT_MAX)	fi	if [ -z $UINT_MAX ]; then		UINT_MAX=$(getconf UINT_MAX)	fi} test_reqs(){	uid=$(id -u)	if [ $uid -ne 0 ]; then		echo $msg must be run as root >&2		exit $ksft_skip	fi 	if ! which perl 2> /dev/null > /dev/null; then		echo "$0: You need perl installed"		exit $ksft_skip	fi	if ! which getconf 2> /dev/null > /dev/null; then		echo "$0: You need getconf installed"		exit $ksft_skip	fi	if ! which diff 2> /dev/null > /dev/null; then		echo "$0: You need diff installed"		exit $ksft_skip	fi} function load_req_mod(){	if [ ! -d $SYSCTL ]; then		if ! modprobe -q -n $TEST_DRIVER; then			echo "$0: module $TEST_DRIVER not found [SKIP]"			echo "You must set CONFIG_TEST_SYSCTL=m in your kernel" >&2			exit $ksft_skip		fi		modprobe $TEST_DRIVER		if [ $? -ne 0 ]; then			echo "$0: modprobe $TEST_DRIVER failed."			exit		fi	fi} reset_vals(){	VAL=""	TRIGGER=$(basename ${TARGET})	case "$TRIGGER" in		int_0001)			VAL="60"			;;		int_0002)			VAL="1"			;;		uint_0001)			VAL="314"			;;		string_0001)			VAL="(none)"			;;		bitmap_0001)			VAL=""			;;		*)			;;	esac	echo -n $VAL > $TARGET} set_orig(){	if [ ! -z $TARGET ] && [ ! -z $ORIG ]; then		if [ -f ${TARGET} ]; then			echo "${ORIG}" > "${TARGET}"		fi	fi} set_test(){	echo "${TEST_STR}" > "${TARGET}"} verify(){	local seen	seen=$(cat "$1")	if [ "${seen}" != "${TEST_STR}" ]; then		return 1	fi	return 0} # proc files get read a page at a time, which can confuse diff,# and get you incorrect results on proc files with long data. To use# diff against them you must first extract the output to a file, and# then compare against that file.verify_diff_proc_file(){	TMP_DUMP_FILE=$(mktemp)	cat $1 > $TMP_DUMP_FILE 	if ! diff -w -q $TMP_DUMP_FILE $2; then		return 1	else		return 0	fi} verify_diff_w(){	echo "$TEST_STR" | diff -q -w -u - $1 > /dev/null	return $?} 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	test_rc} check_failure(){	echo -n "Testing that $1 fails as expected..."	reset_vals	TEST_STR="$1"	orig="$(cat $TARGET)"	echo -n "$TEST_STR" > $TARGET 2> /dev/null 	# write should fail and $TARGET should retain its original value	if [ $? = 0 ] || [ "$(cat $TARGET)" != "$orig" ]; then		echo "FAIL" >&2		rc=1	else		echo "ok"	fi	test_rc} run_wideint_tests(){	# sysctl conversion functions receive a boolean sign and ulong	# magnitude; here we list the magnitudes we want to test (each of	# which will be tested in both positive and negative forms).  Since	# none of these values fit in 32 bits, writing them to an int- or	# uint-typed sysctl should fail.	local magnitudes=(		# common boundary-condition values (zero, +1, -1, INT_MIN,		# and INT_MAX respectively) if truncated to lower 32 bits		# (potential for being falsely deemed in range)		0x0000000100000000		0x0000000100000001		0x00000001ffffffff		0x0000000180000000		0x000000017fffffff 		# these look like negatives, but without a leading '-' are		# actually large positives (should be rejected as above		# despite being zero/+1/-1/INT_MIN/INT_MAX in the lower 32)		0xffffffff00000000		0xffffffff00000001		0xffffffffffffffff		0xffffffff80000000		0xffffffff7fffffff	) 	for sign in '' '-'; do		for mag in "${magnitudes[@]}"; do			check_failure "${sign}${mag}"		done	done} # Your test must accept digits 3 and 4 to use thisrun_limit_digit(){	echo -n "Checking ignoring spaces up to PAGE_SIZE works on write ..."	reset_vals 	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	test_rc} # You are using an intrun_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} # You used an int arrayrun_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

Lines 1–500 of 971.