diff options
Diffstat (limited to 'tools/testing')
-rw-r--r-- | tools/testing/ktest/examples/include/patchcheck.conf | 37 | ||||
-rwxr-xr-x | tools/testing/ktest/ktest.pl | 190 | ||||
-rw-r--r-- | tools/testing/ktest/sample.conf | 44 | ||||
-rw-r--r-- | tools/testing/selftests/Makefile | 8 | ||||
-rw-r--r-- | tools/testing/selftests/README.txt | 42 | ||||
-rw-r--r-- | tools/testing/selftests/efivarfs/Makefile | 12 | ||||
-rw-r--r-- | tools/testing/selftests/efivarfs/create-read.c | 38 | ||||
-rw-r--r-- | tools/testing/selftests/efivarfs/efivarfs.sh | 198 | ||||
-rw-r--r-- | tools/testing/selftests/efivarfs/open-unlink.c | 63 |
9 files changed, 613 insertions, 19 deletions
diff --git a/tools/testing/ktest/examples/include/patchcheck.conf b/tools/testing/ktest/examples/include/patchcheck.conf index 339d3e1700ff..0eb0a5ac77da 100644 --- a/tools/testing/ktest/examples/include/patchcheck.conf +++ b/tools/testing/ktest/examples/include/patchcheck.conf @@ -14,6 +14,16 @@ PATCH_START := HEAD~3 PATCH_END := HEAD +# Use the oldconfig if build_type wasn't defined +DEFAULTS IF NOT DEFINED BUILD_TYPE +DO_BUILD_TYPE := oldconfig + +DEFAULTS ELSE +DO_BUILD_TYPE := ${BUILD_TYPE} + +DEFAULTS + + # Change PATCH_CHECKOUT to be the branch you want to test. The test will # do a git checkout of this branch before starting. Obviously both # PATCH_START and PATCH_END must be in this branch (and PATCH_START must @@ -43,6 +53,31 @@ PATCH_TEST_TYPE := boot # (space delimited) #IGNORE_WARNINGS = 39eaf7ef884dcc44f7ff1bac803ca2a1dcf43544 6edb2a8a385f0cdef51dae37ff23e74d76d8a6ce +# Instead of just checking for warnings to files that are changed +# it can be advantageous to check for any new warnings. If a +# header file is changed, it could cause a warning in a file not +# touched by the commit. To detect these kinds of warnings, you +# can use the WARNINGS_FILE option. +# +# If the variable CREATE_WARNINGS_FILE is set, this config will +# enable the WARNINGS_FILE during the patchcheck test. Also, +# before running the patchcheck test, it will create the +# warnings file. +# +DEFAULTS IF DEFINED CREATE_WARNINGS_FILE +WARNINGS_FILE = ${OUTPUT_DIR}/warnings_file + +TEST_START IF DEFINED CREATE_WARNINGS_FILE +# WARNINGS_FILE is already set by the DEFAULTS above +TEST_TYPE = make_warnings_file +# Checkout the commit before the patches to test, +# and record all the warnings that exist before the patches +# to test are added +CHECKOUT = ${PATCHCHECK_START}~1 +# Force a full build +BUILD_NOCLEAN = 0 +BUILD_TYPE = ${DO_BUILD_TYPE} + # If you are running a multi test, and the test failed on the first # test but on, say the 5th patch. If you want to restart on the # fifth patch, set PATCH_START1. This will make the first test start @@ -61,6 +96,7 @@ PATCHCHECK_TYPE = ${PATCH_TEST_TYPE} PATCHCHECK_START = ${PATCH_START1} PATCHCHECK_END = ${PATCH_END} CHECKOUT = ${PATCH_CHECKOUT} +BUILD_TYPE = ${DO_BUILD_TYPE} TEST_START IF ${TEST} == patchcheck && ${MULTI} TEST_TYPE = patchcheck @@ -72,3 +108,4 @@ PATCHCHECK_END = ${PATCH_END} CHECKOUT = ${PATCH_CHECKOUT} # Use multi to test different compilers? MAKE_CMD = CC=gcc-4.5.1 make +BUILD_TYPE = ${DO_BUILD_TYPE} diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index 35fc584a4ffe..4e67d52eb3a2 100755 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -126,6 +126,7 @@ my $start_minconfig_defined; my $output_minconfig; my $minconfig_type; my $use_output_minconfig; +my $warnings_file; my $ignore_config; my $ignore_errors; my $addconfig; @@ -193,6 +194,9 @@ my $patchcheck_end; # which would require more options. my $buildonly = 1; +# tell build not to worry about warnings, even when WARNINGS_FILE is set +my $warnings_ok = 0; + # set when creating a new config my $newconfig = 0; @@ -235,6 +239,7 @@ my %option_map = ( "START_MIN_CONFIG" => \$start_minconfig, "MIN_CONFIG_TYPE" => \$minconfig_type, "USE_OUTPUT_MIN_CONFIG" => \$use_output_minconfig, + "WARNINGS_FILE" => \$warnings_file, "IGNORE_CONFIG" => \$ignore_config, "TEST" => \$run_test, "ADD_CONFIG" => \$addconfig, @@ -619,6 +624,18 @@ sub set_value { # Note if a test is something other than build, then we # will need other manditory options. if ($prvalue ne "install") { + # for bisect, we need to check BISECT_TYPE + if ($prvalue ne "bisect") { + $buildonly = 0; + } + } else { + # install still limits some manditory options. + $buildonly = 2; + } + } + + if ($buildonly && $lvalue =~ /^BISECT_TYPE(\[.*\])?$/ && $prvalue ne "build") { + if ($prvalue ne "install") { $buildonly = 0; } else { # install still limits some manditory options. @@ -1062,7 +1079,7 @@ sub read_config { } sub __eval_option { - my ($option, $i) = @_; + my ($name, $option, $i) = @_; # Add space to evaluate the character before $ $option = " $option"; @@ -1094,7 +1111,11 @@ sub __eval_option { my $o = "$var\[$i\]"; my $parento = "$var\[$parent\]"; - if (defined($opt{$o})) { + # If a variable contains itself, use the default var + if (($var eq $name) && defined($opt{$var})) { + $o = $opt{$var}; + $retval = "$retval$o"; + } elsif (defined($opt{$o})) { $o = $opt{$o}; $retval = "$retval$o"; } elsif ($repeated && defined($opt{$parento})) { @@ -1118,7 +1139,7 @@ sub __eval_option { } sub eval_option { - my ($option, $i) = @_; + my ($name, $option, $i) = @_; my $prev = ""; @@ -1134,7 +1155,7 @@ sub eval_option { "Check for recursive variables\n"; } $prev = $option; - $option = __eval_option($option, $i); + $option = __eval_option($name, $option, $i); } return $option; @@ -1191,11 +1212,24 @@ sub reboot { } if (defined($time)) { - if (wait_for_monitor($time, $reboot_success_line)) { + + # We only want to get to the new kernel, don't fail + # if we stumble over a call trace. + my $save_ignore_errors = $ignore_errors; + $ignore_errors = 1; + + # Look for the good kernel to boot + if (wait_for_monitor($time, "Linux version")) { # reboot got stuck? doprint "Reboot did not finish. Forcing power cycle\n"; run_command "$power_cycle"; } + + $ignore_errors = $save_ignore_errors; + + # Still need to wait for the reboot to finish + wait_for_monitor($time, $reboot_success_line); + end_monitor; } } @@ -1279,6 +1313,7 @@ sub start_monitor { } sub end_monitor { + return if (!defined $console); if (--$monitor_cnt) { return; } @@ -1585,7 +1620,7 @@ sub wait_for_input $rin = ''; vec($rin, fileno($fp), 1) = 1; - $ready = select($rin, undef, undef, $time); + ($ready, $time) = select($rin, undef, undef, $time); $line = ""; @@ -1891,23 +1926,102 @@ sub get_version { sub start_monitor_and_boot { # Make sure the stable kernel has finished booting - start_monitor; - wait_for_monitor 5; - end_monitor; + + # Install bisects, don't need console + if (defined $console) { + start_monitor; + wait_for_monitor 5; + end_monitor; + } get_grub_index; get_version; install; - start_monitor; + start_monitor if (defined $console); return monitor; } +my $check_build_re = ".*:.*(warning|error|Error):.*"; +my $utf8_quote = "\\x{e2}\\x{80}(\\x{98}|\\x{99})"; + +sub process_warning_line { + my ($line) = @_; + + chomp $line; + + # for distcc heterogeneous systems, some compilers + # do things differently causing warning lines + # to be slightly different. This makes an attempt + # to fixe those issues. + + # chop off the index into the line + # using distcc, some compilers give different indexes + # depending on white space + $line =~ s/^(\s*\S+:\d+:)\d+/$1/; + + # Some compilers use UTF-8 extended for quotes and some don't. + $line =~ s/$utf8_quote/'/g; + + return $line; +} + +# Read buildlog and check against warnings file for any +# new warnings. +# +# Returns 1 if OK +# 0 otherwise sub check_buildlog { + return 1 if (!defined $warnings_file); + + my %warnings_list; + + # Failed builds should not reboot the target + my $save_no_reboot = $no_reboot; + $no_reboot = 1; + + if (-f $warnings_file) { + open(IN, $warnings_file) or + dodie "Error opening $warnings_file"; + + while (<IN>) { + if (/$check_build_re/) { + my $warning = process_warning_line $_; + + $warnings_list{$warning} = 1; + } + } + close(IN); + } + + # If warnings file didn't exist, and WARNINGS_FILE exist, + # then we fail on any warning! + + open(IN, $buildlog) or dodie "Can't open $buildlog"; + while (<IN>) { + if (/$check_build_re/) { + my $warning = process_warning_line $_; + + if (!defined $warnings_list{$warning}) { + fail "New warning found (not in $warnings_file)\n$_\n"; + $no_reboot = $save_no_reboot; + return 0; + } + } + } + $no_reboot = $save_no_reboot; + close(IN); +} + +sub check_patch_buildlog { my ($patch) = @_; my @files = `git show $patch | diffstat -l`; + foreach my $file (@files) { + chomp $file; + } + open(IN, "git show $patch |") or dodie "failed to show $patch"; while (<IN>) { @@ -3055,11 +3169,13 @@ sub patchcheck { build "oldconfig" or return 0; } - - if (!defined($ignored_warnings{$sha1})) { - check_buildlog $sha1 or return 0; + # No need to do per patch checking if warnings file exists + if (!defined($warnings_file) && !defined($ignored_warnings{$sha1})) { + check_patch_buildlog $sha1 or return 0; } + check_buildlog or return 0; + next if ($type eq "build"); my $failed = 0; @@ -3617,6 +3733,39 @@ sub make_min_config { return 1; } +sub make_warnings_file { + my ($i) = @_; + + if (!defined($warnings_file)) { + dodie "Must define WARNINGS_FILE for make_warnings_file test"; + } + + if ($build_type eq "nobuild") { + dodie "BUILD_TYPE can not be 'nobuild' for make_warnings_file test"; + } + + build $build_type or dodie "Failed to build"; + + open(OUT, ">$warnings_file") or dodie "Can't create $warnings_file"; + + open(IN, $buildlog) or dodie "Can't open $buildlog"; + while (<IN>) { + + # Some compilers use UTF-8 extended for quotes + # for distcc heterogeneous systems, this causes issues + s/$utf8_quote/'/g; + + if (/$check_build_re/) { + print OUT; + } + } + close(IN); + + close(OUT); + + success $i; +} + $#ARGV < 1 or die "ktest.pl version: $VERSION\n usage: ktest.pl config-file\n"; if ($#ARGV == 0) { @@ -3662,7 +3811,7 @@ EOF read_config $ktest_config; if (defined($opt{"LOG_FILE"})) { - $opt{"LOG_FILE"} = eval_option($opt{"LOG_FILE"}, -1); + $opt{"LOG_FILE"} = eval_option("LOG_FILE", $opt{"LOG_FILE"}, -1); } # Append any configs entered in manually to the config file. @@ -3739,7 +3888,7 @@ sub set_test_option { my $option = __set_test_option($name, $i); return $option if (!defined($option)); - return eval_option($option, $i); + return eval_option($name, $option, $i); } # First we need to do is the builds @@ -3818,9 +3967,9 @@ for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $i++) { $run_type = $bisect_type; } elsif ($test_type eq "config_bisect") { $run_type = $config_bisect_type; - } - - if ($test_type eq "make_min_config") { + } elsif ($test_type eq "make_min_config") { + $run_type = ""; + } elsif ($test_type eq "make_warnings_file") { $run_type = ""; } @@ -3877,10 +4026,15 @@ for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $i++) { } elsif ($test_type eq "make_min_config") { make_min_config $i; next; + } elsif ($test_type eq "make_warnings_file") { + $no_reboot = 1; + make_warnings_file $i; + next; } if ($build_type ne "nobuild") { build $build_type or next; + check_buildlog or next; } if ($test_type eq "install") { diff --git a/tools/testing/ktest/sample.conf b/tools/testing/ktest/sample.conf index 4012e9330344..0a290fb4cd5e 100644 --- a/tools/testing/ktest/sample.conf +++ b/tools/testing/ktest/sample.conf @@ -793,6 +793,20 @@ # Example for a virtual guest call "Guest". #POWER_OFF = virsh destroy Guest +# To have the build fail on "new" warnings, create a file that +# contains a list of all known warnings (they must match exactly +# to the line with 'warning:', 'error:' or 'Error:'. If the option +# WARNINGS_FILE is set, then that file will be read, and if the +# build detects a warning, it will examine this file and if the +# warning does not exist in it, it will fail the build. +# +# Note, if this option is defined to a file that does not exist +# then any warning will fail the build. +# (see make_warnings_file below) +# +# (optional, default undefined) +#WARNINGS_FILE = ${OUTPUT_DIR}/warnings_file + # The way to execute a command on the target # (default ssh $SSH_USER@$MACHINE $SSH_COMMAND";) # The variables SSH_USER, MACHINE and SSH_COMMAND are defined @@ -1222,3 +1236,33 @@ # MIN_CONFIG_TYPE = test # TEST = ssh ${USER}@${MACHINE} echo hi # +# +# +# +# For TEST_TYPE = make_warnings_file +# +# If you want the build to fail when a new warning is discovered +# you set the WARNINGS_FILE to point to a file of known warnings. +# +# The test "make_warnings_file" will let you create a new warnings +# file before you run other tests, like patchcheck. +# +# What this test does is to run just a build, you still need to +# specify BUILD_TYPE to tell the test what type of config to use. +# A BUILD_TYPE of nobuild will fail this test. +# +# The test will do the build and scan for all warnings. Any warning +# it discovers will be saved in the WARNINGS_FILE (required) option. +# +# It is recommended (but not necessary) to make sure BUILD_NOCLEAN is +# off, so that a full build is done (make mrproper is performed). +# That way, all warnings will be captured. +# +# Example: +# +# TEST_TYPE = make_warnings_file +# WARNINGS_FILE = ${OUTPUT_DIR} +# BUILD_TYPE = useconfig:oldconfig +# CHECKOUT = v3.8 +# BUILD_NOCLEAN = 0 +# diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index 85baf11e2acd..3cc0ad7ae863 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -1,4 +1,10 @@ -TARGETS = breakpoints kcmp mqueue vm cpu-hotplug memory-hotplug +TARGETS = breakpoints +TARGETS += kcmp +TARGETS += mqueue +TARGETS += vm +TARGETS += cpu-hotplug +TARGETS += memory-hotplug +TARGETS += efivarfs all: for TARGET in $(TARGETS); do \ diff --git a/tools/testing/selftests/README.txt b/tools/testing/selftests/README.txt new file mode 100644 index 000000000000..5e2faf9c55d3 --- /dev/null +++ b/tools/testing/selftests/README.txt @@ -0,0 +1,42 @@ +Linux Kernel Selftests + +The kernel contains a set of "self tests" under the tools/testing/selftests/ +directory. These are intended to be small unit tests to exercise individual +code paths in the kernel. + +Running the selftests +===================== + +To build the tests: + + $ make -C tools/testing/selftests + + +To run the tests: + + $ make -C tools/testing/selftests run_tests + +- note that some tests will require root privileges. + + +To run only tests targetted for a single subsystem: + + $ make -C tools/testing/selftests TARGETS=cpu-hotplug run_tests + +See the top-level tools/testing/selftests/Makefile for the list of all possible +targets. + + +Contributing new tests +====================== + +In general, the rules for for selftests are + + * Do as much as you can if you're not root; + + * Don't take too long; + + * Don't break the build on any architecture, and + + * Don't cause the top-level "make run_tests" to fail if your feature is + unconfigured. diff --git a/tools/testing/selftests/efivarfs/Makefile b/tools/testing/selftests/efivarfs/Makefile new file mode 100644 index 000000000000..29e8c6bc81b0 --- /dev/null +++ b/tools/testing/selftests/efivarfs/Makefile @@ -0,0 +1,12 @@ +CC = $(CROSS_COMPILE)gcc +CFLAGS = -Wall + +test_objs = open-unlink create-read + +all: $(test_objs) + +run_tests: all + @/bin/bash ./efivarfs.sh || echo "efivarfs selftests: [FAIL]" + +clean: + rm -f $(test_objs) diff --git a/tools/testing/selftests/efivarfs/create-read.c b/tools/testing/selftests/efivarfs/create-read.c new file mode 100644 index 000000000000..7feef1880968 --- /dev/null +++ b/tools/testing/selftests/efivarfs/create-read.c @@ -0,0 +1,38 @@ +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <string.h> + +int main(int argc, char **argv) +{ + const char *path; + char buf[4]; + int fd, rc; + + if (argc < 2) { + fprintf(stderr, "usage: %s <path>\n", argv[0]); + return EXIT_FAILURE; + } + + path = argv[1]; + + /* create a test variable */ + fd = open(path, O_RDWR | O_CREAT, 0600); + if (fd < 0) { + perror("open(O_WRONLY)"); + return EXIT_FAILURE; + } + + rc = read(fd, buf, sizeof(buf)); + if (rc != 0) { + fprintf(stderr, "Reading a new var should return EOF\n"); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/tools/testing/selftests/efivarfs/efivarfs.sh b/tools/testing/selftests/efivarfs/efivarfs.sh new file mode 100644 index 000000000000..77edcdcc016b --- /dev/null +++ b/tools/testing/selftests/efivarfs/efivarfs.sh @@ -0,0 +1,198 @@ +#!/bin/bash + +efivarfs_mount=/sys/firmware/efi/efivars +test_guid=210be57c-9849-4fc7-a635-e6382d1aec27 + +check_prereqs() +{ + local msg="skip all tests:" + + if [ $UID != 0 ]; then + echo $msg must be run as root >&2 + exit 0 + fi + + if ! grep -q "^\S\+ $efivarfs_mount efivarfs" /proc/mounts; then + echo $msg efivarfs is not mounted on $efivarfs_mount >&2 + exit 0 + fi +} + +run_test() +{ + local test="$1" + + echo "--------------------" + echo "running $test" + echo "--------------------" + + if [ "$(type -t $test)" = 'function' ]; then + ( $test ) + else + ( ./$test ) + fi + + if [ $? -ne 0 ]; then + echo " [FAIL]" + rc=1 + else + echo " [PASS]" + fi +} + +test_create() +{ + local attrs='\x07\x00\x00\x00' + local file=$efivarfs_mount/$FUNCNAME-$test_guid + + printf "$attrs\x00" > $file + + if [ ! -e $file ]; then + echo "$file couldn't be created" >&2 + exit 1 + fi + + if [ $(stat -c %s $file) -ne 5 ]; then + echo "$file has invalid size" >&2 + exit 1 + fi +} + +test_create_empty() +{ + local file=$efivarfs_mount/$FUNCNAME-$test_guid + + : > $file + + if [ ! -e $file ]; then + echo "$file can not be created without writing" >&2 + exit 1 + fi +} + +test_create_read() +{ + local file=$efivarfs_mount/$FUNCNAME-$test_guid + ./create-read $file +} + +test_delete() +{ + local attrs='\x07\x00\x00\x00' + local file=$efivarfs_mount/$FUNCNAME-$test_guid + + printf "$attrs\x00" > $file + + if [ ! -e $file ]; then + echo "$file couldn't be created" >&2 + exit 1 + fi + + rm $file + + if [ -e $file ]; then + echo "$file couldn't be deleted" >&2 + exit 1 + fi + +} + +# test that we can remove a variable by issuing a write with only +# attributes specified +test_zero_size_delete() +{ + local attrs='\x07\x00\x00\x00' + local file=$efivarfs_mount/$FUNCNAME-$test_guid + + printf "$attrs\x00" > $file + + if [ ! -e $file ]; then + echo "$file does not exist" >&2 + exit 1 + fi + + printf "$attrs" > $file + + if [ -e $file ]; then + echo "$file should have been deleted" >&2 + exit 1 + fi +} + +test_open_unlink() +{ + local file=$efivarfs_mount/$FUNCNAME-$test_guid + ./open-unlink $file +} + +# test that we can create a range of filenames +test_valid_filenames() +{ + local attrs='\x07\x00\x00\x00' + local ret=0 + + local file_list="abc dump-type0-11-1-1362436005 1234 -" + for f in $file_list; do + local file=$efivarfs_mount/$f-$test_guid + + printf "$attrs\x00" > $file + + if [ ! -e $file ]; then + echo "$file could not be created" >&2 + ret=1 + else + rm $file + fi + done + + exit $ret +} + +test_invalid_filenames() +{ + local attrs='\x07\x00\x00\x00' + local ret=0 + + local file_list=" + -1234-1234-1234-123456789abc + foo + foo-bar + -foo- + foo-barbazba-foob-foob-foob-foobarbazfoo + foo------------------------------------- + -12345678-1234-1234-1234-123456789abc + a-12345678=1234-1234-1234-123456789abc + a-12345678-1234=1234-1234-123456789abc + a-12345678-1234-1234=1234-123456789abc + a-12345678-1234-1234-1234=123456789abc + 1112345678-1234-1234-1234-123456789abc" + + for f in $file_list; do + local file=$efivarfs_mount/$f + + printf "$attrs\x00" 2>/dev/null > $file + + if [ -e $file ]; then + echo "Creating $file should have failed" >&2 + rm $file + ret=1 + fi + done + + exit $ret +} + +check_prereqs + +rc=0 + +run_test test_create +run_test test_create_empty +run_test test_create_read +run_test test_delete +run_test test_zero_size_delete +run_test test_open_unlink +run_test test_valid_filenames +run_test test_invalid_filenames + +exit $rc diff --git a/tools/testing/selftests/efivarfs/open-unlink.c b/tools/testing/selftests/efivarfs/open-unlink.c new file mode 100644 index 000000000000..8c0764407b3c --- /dev/null +++ b/tools/testing/selftests/efivarfs/open-unlink.c @@ -0,0 +1,63 @@ +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +int main(int argc, char **argv) +{ + const char *path; + char buf[5]; + int fd, rc; + + if (argc < 2) { + fprintf(stderr, "usage: %s <path>\n", argv[0]); + return EXIT_FAILURE; + } + + path = argv[1]; + + /* attributes: EFI_VARIABLE_NON_VOLATILE | + * EFI_VARIABLE_BOOTSERVICE_ACCESS | + * EFI_VARIABLE_RUNTIME_ACCESS + */ + *(uint32_t *)buf = 0x7; + buf[4] = 0; + + /* create a test variable */ + fd = open(path, O_WRONLY | O_CREAT); + if (fd < 0) { + perror("open(O_WRONLY)"); + return EXIT_FAILURE; + } + + rc = write(fd, buf, sizeof(buf)); + if (rc != sizeof(buf)) { + perror("write"); + return EXIT_FAILURE; + } + + close(fd); + + fd = open(path, O_RDONLY); + if (fd < 0) { + perror("open"); + return EXIT_FAILURE; + } + + if (unlink(path) < 0) { + perror("unlink"); + return EXIT_FAILURE; + } + + rc = read(fd, buf, sizeof(buf)); + if (rc > 0) { + fprintf(stderr, "reading from an unlinked variable " + "shouldn't be possible\n"); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} |