xref: /linux/tools/testing/selftests/sysctl/sysctl.sh (revision 7b08e5851884e4ad207a6401b1dcffdb03d43c28)
1#!/usr/bin/env 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# Reduce noise by disabling "unreachable" check.
6# shellcheck disable=SC2317
7
8# This performs a series of tests against the proc sysctl interface.
9
10DIR="$(dirname "$(readlink -f "$0")")"
11source "${DIR}"/../kselftest/ktap_helpers.sh
12
13TEST_DRIVER="test_sysctl"
14
15assert_write_rejected() # <file> <value>
16{
17	local file=$1 val=$2
18	if printf '%s' "$val" > "$file" 2>/dev/null; then
19		ktap_print_msg "$file: write of '$val' succeeded, expected rejection"
20		return 1
21	fi
22	return 0
23}
24
25assert_content() # <file> <want-file>
26{
27	local file=$1 want_file=$2 got
28	got=$(cat "$file")
29	if [ "$got" == "$want_file" ]; then
30		return 0
31	fi
32	ktap_print_msg "$file: got '$got', want '$want_file'"
33	return 1
34}
35
36# compare ignoring whitespaces
37assert_content_loose() # <file> <want-file>
38{
39	local file=$1 want_file=$2
40	if echo "$want_file" | diff -q -w -u - "$file" > /dev/null; then
41		return 0
42	fi
43	ktap_print_msg "$file: content does not match '$want_file' (whitespace-insensitive)"
44	return 1
45}
46
47# For checks where writes must NOT take effect
48assert_content_rejected() # <file> <bad>
49{
50	local file=$1 bad=$2
51	if echo "$bad" | diff -q -w -u - "$file" > /dev/null; then
52		ktap_print_msg "$file: unexpectedly now holds rejected value '$bad'"
53		return 1
54	fi
55	return 0
56}
57
58# proc files get read a page at a time, which can confuse diff resulting in
59# incorrect results. Use a temp file to diff.
60assert_diff_proc_file() # <file> <want-file>
61{
62	local file=$1 want_file=$2 tmp
63	tmp=$(mktemp)
64	cat "$file" > "$tmp"
65
66	if diff -w -q "$tmp" "$want_file" > /dev/null; then
67		rm -f "$tmp"
68		return 0
69	fi
70	ktap_print_msg "$file: content does not match expected file $want_file"
71	rm -f "$tmp"
72	return 1
73}
74
75# Verify that an erroneous update fails and does not change the baseline.
76assert_no_partial_update() # <file> <baseline> <bad-value>
77{
78	local file=$1 baseline=$2 bad=$3
79
80	printf '%s' "$baseline" > "$file"
81	assert_write_rejected "$file" "$bad" || return 1
82	assert_content_loose "$file" "$baseline"
83}
84
85assert_grep() # <file> <pattern> - <pattern> must appear in <file>
86{
87	local file=$1 pattern=$2
88	if grep -q "$pattern" "$file"; then
89		return 0
90	fi
91	ktap_print_msg "$file: pattern '$pattern' not found"
92	return 1
93}
94
95assert_grep_absent() # <file> <pattern> - <pattern> must NOT appear
96{
97	local file=$1 pattern=$2
98	if ! grep -q "$pattern" "$file"; then
99		return 0
100	fi
101	ktap_print_msg "$file: pattern '$pattern' unexpectedly found"
102	return 1
103}
104
105assert_dmesg_count() # <pattern> <want-count>
106{
107	local pattern=$1 want=$2 got
108	got=$(dmesg | grep -c "$pattern")
109	if [ "$got" -eq "$want" ]; then
110		return 0
111	fi
112	ktap_print_msg "dmesg: pattern '$pattern' seen $got times, want $want"
113	return 1
114}
115
116# write <str> through dd in <bs>-sized chunks
117write_in_chunks()  # <file> <str> <bs>
118{
119	local file=$1 str=$2 bs=$3
120	printf '%s' "$str" | dd of="$file" bs="$bs" 2>/dev/null
121}
122
123# write <str> through dd in <bs>-sized chunks. seeking <seek> blocks of size
124# <bs> into the output. skipping <skip> blocks of size <bs> from the input.
125# Unsyncrhonized when skip is not passed.
126write_at_offset()  # <file> <str> <bs> <seek> [skip]
127{
128	local file=$1 str=$2 bs=$3 seek=$4 skip=${5:-}
129	if [ -n "$skip" ]; then
130		printf '%s' "$str" | dd of="$file" bs="$bs" seek="$seek" skip="$skip" 2>/dev/null
131	else
132		printf '%s' "$str" | dd of="$file" bs="$bs" seek="$seek" 2>/dev/null
133	fi
134}
135
136# pad (prefix) <str> with <pad> 'A' characters, then write it all to <file>
137# with dd block size <bs> (defaults to <pad>).
138write_overlong()  # <file> <pad> <str> [bs]
139{
140	local file=$1 pad=$2 str=$3 bs=$4 p
141	: "${bs:=$pad}"
142
143	# save a string with $pad spaces in $p
144	printf -v p '%*s' "$pad" ''
145	# replace " " with "A"
146	printf '%s%s' "${p// /A}" "$str" | dd of="$file" bs="$bs" 2>/dev/null
147}
148
149# pad (prefix) <str> with <pad> ASCII spaces, then write with default block size.
150write_padded_digits()  # <file> <pad> <val>
151{
152	local file=$1 pad=$2 val=$3 p
153	printf -v p '%*s' "$pad" ''
154	printf '%s%s' "$p" "$val" | dd of="$file" 2>/dev/null
155}
156
157test_int_array_writes()
158{
159	local t="${SYSCTL}/int_0003"
160	local limit
161
162	[[ -f "$t" ]] || { RET=$KSFT_SKIP; return; }
163
164	printf '%s' "4 3 2 1" > "$t"
165	assert_content_loose "$t" "4 3 2 1" || RET=$KSFT_FAIL
166
167	# Skipping trailing elements leaves them intact
168	printf '%s' "100 101" > "$t"
169	assert_content_loose "$t" "100 101 2 1" || RET=$KSFT_FAIL
170
171	# Even for an int array, a single write is still capped at MAX_DIGITS
172	# (PAGE_SIZE/8) bytes; check right at, and just past, that boundary.
173	# Carries on the state from the writes above.
174	limit=$((MAX_DIGITS - 1))
175	write_padded_digits "$t" "$limit" "9"
176	assert_content_loose "$t" "9 101 2 1" || RET=$KSFT_FAIL
177
178	limit=$((MAX_DIGITS))
179	write_padded_digits "$t" "$limit" "7"
180	assert_content_rejected "$t" "7 101 2 1" || RET=$KSFT_FAIL
181}
182
183test_int_array_no_partial_update()
184{
185	local t="${SYSCTL}/int_0003"
186
187	[[ -f "$t" ]] || { RET=$KSFT_SKIP; return; }
188
189	# bad 3rd element (not a number)
190	assert_no_partial_update "$t" "1 2 3 4" "10 20 abc 40" || RET=$KSFT_FAIL
191
192	# bad 3rd element (overflows int)
193	assert_no_partial_update "$t" "1 2 3 4" "10 20 $((INT_MAX + 1)) 40" || RET=$KSFT_FAIL
194
195	# bad 1st element
196	assert_no_partial_update "$t" "1 2 3 4" "abc 20 30 40" || RET=$KSFT_FAIL
197}
198
199# Exercise the write patterns
200assert_write_patterns()  # <file> <baseline> <val>
201{
202	local file=$1 baseline=$2 val=$3 status=0
203
204	printf '%s' "$val" > "$file"
205	assert_content "$file" "$val" || status=1
206	printf '%s' "$baseline" > "$file"
207	assert_content "$file" "$baseline" || status=1
208
209	printf '%s' "$baseline" > "$file"
210	write_in_chunks "$file" "$val" 4096
211	assert_content "$file" "$val" || status=1
212
213	printf '%s' "$val" > "$file"
214	write_at_offset "$file" "$val" 1 1 1
215	assert_content "$file" "$val" || status=1
216
217	printf '%s' "$baseline" > "$file"
218	write_at_offset "$file" "$val" 20 2
219	assert_content_rejected "$file" "$val" || status=1
220
221	printf '%s' "$baseline" > "$file"
222	write_overlong "$file" 50 "$val"
223	assert_content_rejected "$file" "$val" || status=1
224
225	return "$status"
226}
227
228# None of these fit in 32 bits, so writing them to an int/uint sysctl must fail
229assert_wideint_writes_rejected()  # <file> <baseline>
230{
231	local file=$1 baseline=$2 sign mag status=0
232	local magnitudes=(
233		0x0000000100000000
234		0x0000000100000001
235		0x00000001ffffffff
236		0x0000000180000000
237		0x000000017fffffff
238		0xffffffff00000000
239		0xffffffff00000001
240		0xffffffffffffffff
241		0xffffffff80000000
242		0xffffffff7fffffff
243	)
244
245	for sign in '' '-'; do
246		for mag in "${magnitudes[@]}"; do
247			assert_no_partial_update "$file" "$baseline" "${sign}${mag}" || status=1
248		done
249	done
250	return "$status"
251}
252
253# A single write is capped at MAX_DIGITS (PAGE_SIZE/8) bytes; check that
254# leading whitespace up to that limit is ignored, and that PAGE_SIZE of
255# leading whitespace pushes the value past the limit and fails.
256assert_digit_limit()  # <file> <baseline>
257{
258	local file=$1 baseline=$2 status=0
259
260	printf '%s' "$baseline" > "$file"
261	write_padded_digits "$file" "$((MAX_DIGITS - 1))" "3"
262	assert_content "$file" "3" || status=1
263
264	printf '%s' "$baseline" > "$file"
265	write_padded_digits "$file" "$MAX_DIGITS" "4"
266	assert_content_rejected "$file" "4" || status=1
267
268	return "$status"
269}
270
271# proc_dointvec()-specific range checks: INT_MAX must be accepted, INT_MAX+1
272# must be rejected, and negative values must be accepted.
273assert_int_range()  # <file> <baseline>
274{
275	local file=$1 baseline=$2 status=0
276
277	printf '%s' "$baseline" > "$file"
278	printf '%s' "$INT_MAX" > "$file"
279	assert_content "$file" "$INT_MAX" || status=1
280
281	assert_no_partial_update "$file" "$baseline" "$((INT_MAX + 1))" || status=1
282
283	printf '%s' "$baseline" > "$file"
284	printf '%s' "-3" > "$file" 2>/dev/null
285	assert_content "$file" "-3" || status=1
286
287	return "$status"
288}
289
290# proc_douintvec()-specific range checks: UINT_MAX must be accepted,
291# UINT_MAX+1 must be rejected, and negative values must be rejected too.
292assert_uint_range()  # <file> <baseline>
293{
294	local file=$1 baseline=$2 status=0
295
296	printf '%s' "$baseline" > "$file"
297	printf '%s' "$UINT_MAX" > "$file"
298	assert_content "$file" "$UINT_MAX" || status=1
299
300	assert_no_partial_update "$file" "$baseline" "$((UINT_MAX + 1))" || status=1
301	assert_no_partial_update "$file" "$baseline" "-3" || status=1
302
303	return "$status"
304}
305
306test_int_minmax()
307{
308	local t="${SYSCTL}/int_0001"
309	local baseline="60" val="61"
310
311	[[ -f "$t" ]] || { RET=$KSFT_SKIP; return; }
312
313	assert_write_patterns "$t" "$baseline" "$val" || RET=$KSFT_FAIL
314	assert_wideint_writes_rejected "$t" "$baseline" || RET=$KSFT_FAIL
315	assert_digit_limit "$t" "$baseline" || RET=$KSFT_FAIL
316}
317
318test_int_plain()
319{
320	local t="${SYSCTL}/int_0002"
321	local baseline="1" val="2"
322
323	[[ -f "$t" ]] || { RET=$KSFT_SKIP; return; }
324
325	assert_write_patterns "$t" "$baseline" "$val" || RET=$KSFT_FAIL
326	assert_wideint_writes_rejected "$t" "$baseline" || RET=$KSFT_FAIL
327	assert_digit_limit "$t" "$baseline" || RET=$KSFT_FAIL
328	assert_int_range "$t" "$baseline" || RET=$KSFT_FAIL
329}
330
331# The jiffies converters report the sign separately from the magnitude, so a
332# negative value must survive the round trip through the converter.  Each
333# value below is chosen to convert exactly at every CONFIG_HZ.
334test_int_jiffies_negative()
335{
336	local t="${SYSCTL}/int_jiffies"
337	local m="${SYSCTL}/int_ms_jiffies"
338	local u="${SYSCTL}/int_userhz_jiffies"
339
340	[[ -f "$t" && -f "$m" && -f "$u" ]] || { RET=$KSFT_SKIP; return; }
341
342	printf '%s' "-1" > "$t"
343	assert_content "$t" "-1" || RET=$KSFT_FAIL
344	printf '%s' "-1000" > "$m"
345	assert_content "$m" "-1000" || RET=$KSFT_FAIL
346	printf '%s' "-100" > "$u"
347	assert_content "$u" "-100" || RET=$KSFT_FAIL
348
349	# The positive path was never broken; keep it covered.
350	printf '%s' "1" > "$t"
351	assert_content "$t" "1" || RET=$KSFT_FAIL
352}
353
354test_uint_plain()
355{
356	local t="${SYSCTL}/uint_0001"
357	local baseline="314" val="315"
358
359	[[ -f "$t" ]] || { RET=$KSFT_SKIP; return; }
360
361	assert_write_patterns "$t" "$baseline" "$val" || RET=$KSFT_FAIL
362	assert_wideint_writes_rejected "$t" "$baseline" || RET=$KSFT_FAIL
363	assert_digit_limit "$t" "$baseline" || RET=$KSFT_FAIL
364	assert_uint_range "$t" "$baseline" || RET=$KSFT_FAIL
365}
366
367# proc_dostring()-specific write patterns
368assert_string_write_patterns()  # <file> <baseline> <val> <maxlen>
369{
370	local file=$1 baseline=$2 val=$3 maxlen=$4 status=0
371
372	printf '%s' "$baseline" > "$file"
373	write_in_chunks "$file" "$val" 1
374	assert_content "$file" "$val" || status=1
375
376	printf '%s' "$val" > "$file"
377	write_at_offset "$file" "$val" 1 1
378	assert_content_rejected "$file" "$val" || status=1
379
380	# sysctl maxlen is at least $maxlen
381	printf '%s' "$baseline" > "$file"
382	write_overlong "$file" "$((maxlen - 2))" "B" "$maxlen"
383	assert_grep "$file" "B" || status=1
384
385	# keep original string on overflow append
386	printf '%s' "$baseline" > "$file"
387	write_overlong "$file" "$((maxlen - 1))" "B" "$((maxlen - 1))"
388	assert_grep_absent "$file" "B" || status=1
389
390	# sysctl stays NULL terminated on write
391	printf '%s' "$baseline" > "$file"
392	write_overlong "$file" "$((maxlen - 1))" "B" "$maxlen"
393	assert_grep_absent "$file" "B" || status=1
394
395	# sysctl stays NULL terminated on overwrite
396	printf '%s' "$baseline" > "$file"
397	write_overlong "$file" "$((maxlen - 1))" "BB" "$((maxlen + 1))"
398	assert_grep_absent "$file" "B" || status=1
399
400	return "$status"
401}
402
403test_string_dostring()
404{
405	local t="${SYSCTL}/string_0001"
406	local baseline="(none)" val="Testing sysctl" maxlen=65
407
408	[[ -f "$t" ]] || { RET=$KSFT_SKIP; return; }
409
410	assert_write_patterns "$t" "$baseline" "$val" || RET=$KSFT_FAIL
411	assert_string_write_patterns "$t" "$baseline" "$val" "$maxlen" || RET=$KSFT_FAIL
412}
413
414# Emit a random sysctl bit map
415random_bitmap_spec()
416{
417	local length=$((RANDOM % 65000))
418	local bit=$((RANDOM % 1024))
419	local spec=$bit
420	local range_end
421
422	while [ "${#spec}" -le "$length" ]; do
423		# Keep entries discontiguous, skip ahead by at least 2.
424		bit=$((bit + 2 + RANDOM % 10))
425		spec="${spec},${bit}"
426
427		if [ "$((RANDOM % 2))" -eq 1 ]; then
428			range_end=$((bit + 1 + RANDOM % 10))
429			spec="${spec}-${range_end}"
430			bit=$range_end
431		fi
432	done
433
434	echo -n "$spec"
435}
436
437# Do *not* prefix with "test_". All autogenerate bitmap tests call this helper
438bitmap_range_check_once()  # <iter>
439{
440	local t="${SYSCTL}/bitmap_0001"
441	local spec_file seed
442
443	[[ -f "$t" ]] || { RET=$KSFT_SKIP; return; }
444
445	seed=$((BITMAP_SEED + $1))
446	RANDOM=$seed
447
448	spec_file=$(mktemp)
449	random_bitmap_spec > "$spec_file"
450
451	if ! cat "$spec_file" > "$t" 2>/dev/null; then
452		ktap_print_msg "$t: write of random bitmap spec rejected (seed $seed)"
453		RET=$KSFT_FAIL
454	elif ! assert_diff_proc_file "$t" "$spec_file"; then
455		ktap_print_msg "$t: readback mismatch (seed $seed)"
456		RET=$KSFT_FAIL
457	fi
458
459	rm -f "$spec_file"
460}
461
462test_unregister_removes_dir()
463{
464	local t="${SYSCTL}/unregister_error"
465
466	if [[ -d "$t" ]]; then
467		ktap_print_msg "$t: directory still exists, expected it to be unregistered"
468		RET=$KSFT_FAIL
469	fi
470}
471
472test_mount_point_error()
473{
474	local t="${SYSCTL}/mnt/mnt_error"
475
476	if [[ -d "$t" ]]; then
477		ktap_print_msg "$t: directory unexpectedly created"
478		RET=$KSFT_FAIL
479	fi
480}
481
482test_empty_dir_registration()
483{
484	local t="${SYSCTL}/empty_add"
485
486	if [[ ! -d "$t" ]]; then
487		ktap_print_msg "$t: directory was not created"
488		RET=$KSFT_FAIL
489		return
490	fi
491	if [[ ! -d "$t/empty" ]]; then
492		ktap_print_msg "$t/empty: directory was not created"
493		RET=$KSFT_FAIL
494	fi
495}
496
497test_u8_range_check()
498{
499	local t="${SYSCTL}/u8_valid"
500
501	if [[ ! -f "$t" ]]; then
502		ktap_print_msg "$t: file was not created"
503		RET=$KSFT_FAIL
504		return
505	fi
506
507	assert_dmesg_count "u8_over range value" 1 || RET=$KSFT_FAIL
508	assert_dmesg_count "u8_under range value" 1 || RET=$KSFT_FAIL
509}
510
511test_boot_param_int()
512{
513	local t="${SYSCTL}/boot_int"
514	local orig found
515
516	[[ -f "$t" ]] || { RET=$KSFT_SKIP; return; }
517
518	# Boot cmdline params only apply when test_sysctl is built in, not
519	# loaded as a module.
520	if [[ -d "$MODULE_DIR" ]]; then
521		ktap_print_msg "$t: test only possible if test_sysctl is built-in, not a module"
522		RET=$KSFT_SKIP
523		return
524	fi
525
526	orig=$(cat "$t")
527	if [[ "$orig" == "1" ]]; then
528		return
529	fi
530
531	if [[ ! -f /proc/cmdline ]]; then
532		ktap_print_msg "$t: no /proc/cmdline to check for the boot parameter"
533		RET=$KSFT_SKIP
534		return
535	fi
536
537	found=$(grep -c "sysctl[./]debug[./]test_sysctl[./]boot_int=1" /proc/cmdline)
538	if [[ "$found" == "1" ]]; then
539		ktap_print_msg "$t: kernel param found on cmdline but value is not 1"
540		RET=$KSFT_FAIL
541		return
542	fi
543
544	ktap_print_msg "$t: kernel not booted with sysctl.debug.test_sysctl.boot_int=1"
545	RET=$KSFT_SKIP
546}
547
548test_sysctl_macro_match()
549{
550	local t="${SYSCTL}/match_int"
551
552	[[ -f "$t" ]] || { RET=$KSFT_SKIP; return; }
553
554	assert_content "$t" "1" || RET=$KSFT_FAIL
555}
556
557run_one()
558{
559	local name=$1
560
561	# Subshell: a test cannot leak vars/cwd/traps into the next one, and an
562	# accidental `exit` inside a test kills only its own subshell.
563	( RET=$KSFT_PASS; "$name"; exit $RET )
564
565	case $? in
566	"$KSFT_PASS") ktap_test_pass "$name" ;;
567	"$KSFT_SKIP") ktap_test_skip "$name" ;;
568	*)          ktap_test_fail "$name" ;;
569	esac
570}
571
572usage()
573{
574	cat <<-EOF
575	Usage: $0 [-r N] [-l] [-t T] [-s N] [-h|--help]
576
577	Runs every auto-discovered test_* function. Setting SYSCTL_TESTS will
578	override the default and passing -t will override both env var and the
579	default.
580
581	    -r N          repeat selected tests N times (default 1)
582	    -l            list discovered tests
583	    -t T          space separated tests list. For example "test_1 test_2"
584	    -s N          base seed for the randomized bitmap iterations.
585	    -h, --help    help
586
587	    SYSCTL_TESTS='test_1 test_2' $0  run only the named tests
588	EOF
589}
590
591list_tests()
592{
593	echo "Discovered tests:"
594	local t
595	for t in $ALL_TESTS; do
596		echo "  $t"
597	done
598}
599
600check_reqs()
601{
602	local uid
603	uid=$(id -u)
604
605	if [ "$uid" -ne 0 ]; then
606		ktap_skip_all "must be run as root"
607		exit "$KSFT_SKIP"
608	fi
609
610	if ! which getconf > /dev/null 2>&1; then
611		ktap_skip_all "$0: You need getconf installed"
612		exit "$KSFT_SKIP"
613	fi
614
615	if ! which diff > /dev/null 2>&1; then
616		ktap_skip_all "$0: You need diff installed"
617		exit "$KSFT_SKIP"
618	fi
619}
620
621check_args()  # <repeat> <selected> <seed>
622{
623	local repeat_arg="$1"
624	local selected_arg="$2"
625	local seed_arg="$3"
626	local t
627
628	if [ -z "$selected_arg" ]; then
629		ktap_exit_fail_msg "no test selected"
630	fi
631
632	if ! [[ "$repeat_arg" =~ ^[0-9]+$ ]]; then
633		ktap_exit_fail_msg "repeat argument '$repeat_arg' is not a number"
634	fi
635
636	if ! [[ "$seed_arg" =~ ^[0-9]+$ ]]; then
637		ktap_exit_fail_msg "seed argument '$seed_arg' is not a number"
638	fi
639
640	for t in $selected_arg; do
641		if ! declare -F "$t" > /dev/null; then
642			ktap_exit_fail_msg "unknown selected test '$t'"
643		fi
644	done
645}
646
647allow_user_defaults()
648{
649	if [ -z "${MODULE_DIR}" ]; then
650		MODULE_DIR="/sys/module/test_sysctl/"
651	fi
652	if [ -z "${SYSCTL}" ]; then
653		SYSCTL="/proc/sys/debug/test_sysctl"
654	fi
655	if [ -z "${PROD_SYSCTL}" ]; then
656		PROD_SYSCTL="/proc/sys"
657	fi
658	if [ -z "${WRITES_STRICT}" ]; then
659		WRITES_STRICT="${PROD_SYSCTL}/kernel/sysctl_writes_strict"
660	fi
661	if [ -z "${BITMAP_ITERATIONS}" ]; then
662		BITMAP_ITERATIONS=50
663	fi
664	# Set seed for bitmap random tests
665	if [ -z "${BITMAP_SEED}" ]; then
666		BITMAP_SEED=$RANDOM
667	fi
668}
669
670check_production_sysctl_writes_strict()
671{
672	if [ ! -e "${WRITES_STRICT}" ]; then
673		ktap_print_msg "${WRITES_STRICT} missing, skipping strict write check (old kernel?)"
674	else
675		old_strict=$(cat "${WRITES_STRICT}")
676		if [ "$old_strict" != "1" ]; then
677			ktap_print_msg "forcing ${WRITES_STRICT} to 1 (was ${old_strict})"
678			echo "1" > "${WRITES_STRICT}"
679		fi
680	fi
681
682	if [ -z "${PAGE_SIZE}" ]; then
683		PAGE_SIZE=$(getconf PAGESIZE)
684	fi
685	if [ -z "${MAX_DIGITS}" ]; then
686		MAX_DIGITS=$((PAGE_SIZE / 8))
687	fi
688	if [ -z "${INT_MAX}" ]; then
689		INT_MAX=$(getconf INT_MAX)
690	fi
691	if [ -z "${UINT_MAX}" ]; then
692		UINT_MAX=$(getconf UINT_MAX)
693	fi
694}
695
696load_req_mod()
697{
698	if [ ! -d "${SYSCTL}" ]; then
699		if ! modprobe -q -n "${TEST_DRIVER}"; then
700			ktap_skip_all "module ${TEST_DRIVER} not found. \
701				You must set CONFIG_TEST_SYSCTL=m in your kernel"
702			exit "$KSFT_SKIP"
703		fi
704		if ! modprobe "${TEST_DRIVER}"; then
705			ktap_exit_fail_msg "modprobe ${TEST_DRIVER} failed"
706		fi
707	fi
708}
709
710# Cleanup only! Do not call exit here or it will silently override KTAP exit status
711restore_sysctl_writes_strict()
712{
713	if [ -n "${old_strict}" ]; then
714		echo "${old_strict}" > "${WRITES_STRICT}"
715	fi
716}
717
718allow_user_defaults
719
720# Each bitmap iteration is one function to generate one KTAP report per iteration
721for i in $(seq 1 "$BITMAP_ITERATIONS"); do
722	eval "test_bitmap_range_check_$(printf '%02d' "$i")() \
723		{ \
724			bitmap_range_check_once $i; \
725		}"
726done
727
728# Discover test_* functions above this line
729ALL_TESTS=$(declare -F | awk '{print $3}' | grep '^test_' | sort)
730
731REPEAT=1
732while [ $# -gt 0 ]; do
733	case "$1" in
734	-r )
735		shift
736		REPEAT=$1
737		;;
738	-l )
739		list_tests
740		exit 0
741		;;
742	-h | --help )
743		usage
744		exit 0
745		;;
746	-t )
747		shift
748		SYSCTL_TESTS="$1"
749		;;
750	-s )
751		shift
752		BITMAP_SEED="$1"
753		;;
754	* )
755		usage
756		exit 1
757		;;
758	esac
759	shift
760done
761
762SELECTED=${SYSCTL_TESTS-$ALL_TESTS}
763
764ktap_print_header
765trap restore_sysctl_writes_strict EXIT
766check_args "$REPEAT" "$SELECTED" "$BITMAP_SEED"
767check_reqs
768check_production_sysctl_writes_strict
769load_req_mod
770
771ktap_print_msg "bitmap base seed: $BITMAP_SEED (replay with -s $BITMAP_SEED)"
772
773ktap_set_plan "$(( $(wc -w <<< "$SELECTED") * REPEAT ))"
774
775for ((i = 0; i < REPEAT; i++)); do
776	for t in $SELECTED; do
777		run_one "$t"
778	done
779done
780
781ktap_finished
782