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