From 256b5bd1a90bc7f8a0c111665258ce5d37361c85 Mon Sep 17 00:00:00 2001 From: jmmv Date: Sun, 2 Mar 2008 11:22:10 +0000 Subject: [PATCH] Add tests for load-time parameter passing to modules, both at the syscall level through modctl(2) and at the user level through the modload(8) utility. --- tests/modules/Makefile | 5 +- tests/modules/k_helper/k_helper.c | 77 ++++++++++++++- tests/modules/t_modctl.cpp | 141 +++++++++++++++++++++++---- tests/modules/t_modload.sh | 156 ++++++++++++++++++++++++++++++ 4 files changed, 356 insertions(+), 23 deletions(-) create mode 100644 tests/modules/t_modload.sh diff --git a/tests/modules/Makefile b/tests/modules/Makefile index 09a32b43df82..e71c0a2945b8 100644 --- a/tests/modules/Makefile +++ b/tests/modules/Makefile @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.1 2008/02/10 12:40:10 jmmv Exp $ +# $NetBSD: Makefile,v 1.2 2008/03/02 11:22:10 jmmv Exp $ .include @@ -12,6 +12,9 @@ TESTSDIR= ${TESTSBASE}/modules .if ${MKMODULAR} != no TESTS_CXX= t_modctl +LDADD= -lprop + +TESTS_SH= t_modload SUBDIR= k_helper diff --git a/tests/modules/k_helper/k_helper.c b/tests/modules/k_helper/k_helper.c index 15147d044d38..f8cc46dddba1 100644 --- a/tests/modules/k_helper/k_helper.c +++ b/tests/modules/k_helper/k_helper.c @@ -1,4 +1,4 @@ -/* $NetBSD: k_helper.c,v 1.1 2008/02/10 12:40:10 jmmv Exp $ */ +/* $NetBSD: k_helper.c,v 1.2 2008/03/02 11:22:11 jmmv Exp $ */ /* * Copyright (c) 2008 The NetBSD Foundation, Inc. * All rights reserved. @@ -34,24 +34,37 @@ */ #include -__KERNEL_RCSID(0, "$NetBSD: k_helper.c,v 1.1 2008/02/10 12:40:10 jmmv Exp $"); +__KERNEL_RCSID(0, "$NetBSD: k_helper.c,v 1.2 2008/03/02 11:22:11 jmmv Exp $"); #include #include #include #include +#include + MODULE(MODULE_CLASS_MISC, k_helper, NULL); /* --------------------------------------------------------------------- */ /* Sysctl interface to query information about the module. */ /* --------------------------------------------------------------------- */ +/* TODO: Change the integer variables below that represent booleans to + * bools, once sysctl(8) supports CTLTYPE_BOOL nodes. */ + static struct sysctllog *clog; static int present = 1; +static int prop_str_ok; +static char prop_str_val[128]; +static int prop_int_ok; +static int prop_int_val; #define K_HELPER 0x12345678 #define K_HELPER_PRESENT 0 +#define K_HELPER_PROP_STR_OK 1 +#define K_HELPER_PROP_STR_VAL 2 +#define K_HELPER_PROP_INT_OK 3 +#define K_HELPER_PROP_INT_VAL 4 SYSCTL_SETUP(sysctl_k_helper_setup, "sysctl k_helper subtree setup") { @@ -68,6 +81,34 @@ SYSCTL_SETUP(sysctl_k_helper_setup, "sysctl k_helper subtree setup") SYSCTL_DESCR("Whether the module was loaded or not"), NULL, 0, &present, 0, CTL_VENDOR, K_HELPER, K_HELPER_PRESENT, CTL_EOL); + + sysctl_createv(clog, 0, NULL, NULL, + CTLFLAG_PERMANENT, + CTLTYPE_INT, "prop_str_ok", + SYSCTL_DESCR("String property's validity"), + NULL, 0, &prop_str_ok, 0, + CTL_VENDOR, K_HELPER, K_HELPER_PROP_STR_OK, CTL_EOL); + + sysctl_createv(clog, 0, NULL, NULL, + CTLFLAG_PERMANENT, + CTLTYPE_STRING, "prop_str_val", + SYSCTL_DESCR("String property's value"), + NULL, 0, &prop_str_val, 0, + CTL_VENDOR, K_HELPER, K_HELPER_PROP_STR_VAL, CTL_EOL); + + sysctl_createv(clog, 0, NULL, NULL, + CTLFLAG_PERMANENT, + CTLTYPE_INT, "prop_int_ok", + SYSCTL_DESCR("String property's validity"), + NULL, 0, &prop_int_ok, 0, + CTL_VENDOR, K_HELPER, K_HELPER_PROP_INT_OK, CTL_EOL); + + sysctl_createv(clog, 0, NULL, NULL, + CTLFLAG_PERMANENT, + CTLTYPE_INT, "prop_int_val", + SYSCTL_DESCR("String property's value"), + NULL, 0, &prop_int_val, 0, + CTL_VENDOR, K_HELPER, K_HELPER_PROP_INT_VAL, CTL_EOL); } /* --------------------------------------------------------------------- */ @@ -76,8 +117,38 @@ SYSCTL_SETUP(sysctl_k_helper_setup, "sysctl k_helper subtree setup") static int -k_helper_init(void *arg) +k_helper_init(prop_dictionary_t props) { + prop_object_t p; + + p = prop_dictionary_get(props, "prop_str"); + if (p == NULL) + prop_str_ok = 0; + else if (prop_object_type(p) != PROP_TYPE_STRING) + prop_str_ok = 0; + else { + const char *msg = prop_string_cstring_nocopy(p); + if (msg == NULL) + prop_str_ok = 0; + else { + strlcpy(prop_str_val, msg, sizeof(prop_str_val)); + prop_str_ok = 1; + } + } + if (!prop_str_ok) + strlcpy(prop_str_val, "", sizeof(prop_str_val)); + + p = prop_dictionary_get(props, "prop_int"); + if (p == NULL) + prop_int_ok = 0; + else if (prop_object_type(p) != PROP_TYPE_NUMBER) + prop_int_ok = 0; + else { + prop_int_val = prop_number_integer_value(p); + prop_int_ok = 1; + } + if (!prop_int_ok) + prop_int_val = -1; sysctl_k_helper_setup(&clog); diff --git a/tests/modules/t_modctl.cpp b/tests/modules/t_modctl.cpp index 279d9fbcf491..aaebaf978d92 100644 --- a/tests/modules/t_modctl.cpp +++ b/tests/modules/t_modctl.cpp @@ -1,4 +1,4 @@ -// $NetBSD: t_modctl.cpp,v 1.2 2008/02/10 16:02:24 jmmv Exp $ +// $NetBSD: t_modctl.cpp,v 1.3 2008/03/02 11:22:10 jmmv Exp $ // // Copyright (c) 2008 The NetBSD Foundation, Inc. // All rights reserved. @@ -35,14 +35,17 @@ extern "C" { #include -__KERNEL_RCSID(0, "$NetBSD: t_modctl.cpp,v 1.2 2008/02/10 16:02:24 jmmv Exp $"); +__KERNEL_RCSID(0, "$NetBSD: t_modctl.cpp,v 1.3 2008/03/02 11:22:10 jmmv Exp $"); #include #include + +#include } #include #include +#include #include #include @@ -135,6 +138,24 @@ get_modstat_info(const char *name, modstat_t *msdest) return found; } +/* + * Queries a sysctl property. + */ +static +bool +get_sysctl(const std::string& name, void *buf, const size_t len) +{ + size_t len2 = len; + std::cout << "Querying sysctl variable: " << name << std::endl; + int ret = ::sysctlbyname(name.c_str(), buf, &len2, NULL, 0); + if (ret == -1 && errno != ENOENT) { + std::cerr << "sysctlbyname(2) failed: " + << std::strerror(errno) << std::endl; + ATF_FAIL("Failed to query " + name); + } + return ret != -1; +} + /* * Returns a boolean indicating if the k_helper module was loaded * successfully. This implementation uses modctl(2)'s MODCTL_STAT @@ -157,19 +178,10 @@ static bool k_helper_is_present_sysctl(void) { - size_t present, presentsize; - int ret; + size_t present; - presentsize = sizeof(present); - ret = ::sysctlbyname("vendor.k_helper.present", &present, - &presentsize, NULL, 0); - if (ret == -1 && errno != ENOENT) { - int err = errno; - std::cerr << "sysctlbyname(2) failed: " << std::strerror(err) - << std::endl; - ATF_FAIL("Failed to query module status"); - } - return ret == 0; + return get_sysctl("vendor.k_helper.present", &present, + sizeof(present)); } /* @@ -211,13 +223,31 @@ k_helper_is_present(presence_check how) */ static int -load(const std::string& filename, bool fatal = true) +load(const std::string& filename, prop_dictionary_t props = NULL, + bool fatal = true) { int err; + char *propsstr; + modctl_load_t ml; + + if (props == NULL) { + props = prop_dictionary_create(); + propsstr = prop_dictionary_externalize(props); + ATF_CHECK(propsstr != NULL); + prop_object_release(props); + } else { + propsstr = prop_dictionary_externalize(props); + ATF_CHECK(propsstr != NULL); + } + + ml.ml_filename = filename.c_str(); + ml.ml_flags = 0; + ml.ml_props = propsstr; + ml.ml_propslen = strlen(propsstr); std::cout << "Loading module " << filename << std::endl; err = 0; - if (::modctl(MODCTL_LOAD, __UNCONST(filename.c_str())) == -1) { + if (::modctl(MODCTL_LOAD, &ml) == -1) { err = errno; std::cerr << "modctl(MODCTL_LOAD, " << filename << ") failed: " << std::strerror(err) @@ -225,6 +255,9 @@ load(const std::string& filename, bool fatal = true) if (fatal) ATF_FAIL("Module load failed"); } + + ::free(propsstr); + return err; } @@ -277,11 +310,11 @@ ATF_TEST_CASE_BODY(cmd_load) { require_modular(); - ATF_CHECK(load("", false) == ENOENT); - ATF_CHECK(load("non-existent.o", false) == ENOENT); + ATF_CHECK(load("", NULL, false) == ENOENT); + ATF_CHECK(load("non-existent.o", NULL, false) == ENOENT); std::string longname(MAXPATHLEN, 'a'); - ATF_CHECK(load(longname.c_str(), false) == ENAMETOOLONG); + ATF_CHECK(load(longname.c_str(), NULL, false) == ENAMETOOLONG); ATF_CHECK(!k_helper_is_present(stat_check)); load(get_srcdir() + "/k_helper.o"); @@ -293,6 +326,75 @@ ATF_TEST_CASE_CLEANUP(cmd_load) unload_cleanup("k_helper"); } +ATF_TEST_CASE_WITH_CLEANUP(cmd_load_props); +ATF_TEST_CASE_HEAD(cmd_load_props) +{ + set("descr", "Tests for the MODCTL_LOAD command, providing extra " + "load-time properties"); + set("require.user", "root"); +} +ATF_TEST_CASE_BODY(cmd_load_props) +{ + prop_dictionary_t props; + + require_modular(); + + std::cout << "Loading module without properties" << std::endl; + props = prop_dictionary_create(); + load(get_srcdir() + "/k_helper.o", props); + prop_object_release(props); + { + int ok; + ATF_CHECK(get_sysctl("vendor.k_helper.prop_str_ok", + &ok, sizeof(ok))); + ATF_CHECK(!ok); + } + unload("k_helper"); + + std::cout << "Loading module with a string property" << std::endl; + props = prop_dictionary_create(); + prop_dictionary_set(props, "prop_str", + prop_string_create_cstring("1st string")); + load(get_srcdir() + "/k_helper.o", props); + prop_object_release(props); + { + int ok; + ATF_CHECK(get_sysctl("vendor.k_helper.prop_str_ok", + &ok, sizeof(ok))); + ATF_CHECK(ok); + + char val[128]; + ATF_CHECK(get_sysctl("vendor.k_helper.prop_str_val", + &val, sizeof(val))); + ATF_CHECK(std::strcmp(val, "1st string") == 0); + } + unload("k_helper"); + + std::cout << "Loading module with a different string property" + << std::endl; + props = prop_dictionary_create(); + prop_dictionary_set(props, "prop_str", + prop_string_create_cstring("2nd string")); + load(get_srcdir() + "/k_helper.o", props); + prop_object_release(props); + { + int ok; + ATF_CHECK(get_sysctl("vendor.k_helper.prop_str_ok", + &ok, sizeof(ok))); + ATF_CHECK(ok); + + char val[128]; + ATF_CHECK(get_sysctl("vendor.k_helper.prop_str_val", + &val, sizeof(val))); + ATF_CHECK(std::strcmp(val, "2nd string") == 0); + } + unload("k_helper"); +} +ATF_TEST_CASE_CLEANUP(cmd_load_props) +{ + unload_cleanup("k_helper"); +} + ATF_TEST_CASE_WITH_CLEANUP(cmd_stat); ATF_TEST_CASE_HEAD(cmd_stat) { @@ -359,6 +461,7 @@ ATF_INIT_TEST_CASES(tcs) have_modular = check_modular(); ATF_ADD_TEST_CASE(tcs, cmd_load); + ATF_ADD_TEST_CASE(tcs, cmd_load_props); ATF_ADD_TEST_CASE(tcs, cmd_stat); ATF_ADD_TEST_CASE(tcs, cmd_unload); } diff --git a/tests/modules/t_modload.sh b/tests/modules/t_modload.sh new file mode 100644 index 000000000000..7d1061d96c65 --- /dev/null +++ b/tests/modules/t_modload.sh @@ -0,0 +1,156 @@ +# $NetBSD: t_modload.sh,v 1.1 2008/03/02 11:22:10 jmmv Exp $ +# +# Copyright (c) 2008 The NetBSD Foundation, Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the NetBSD +# Foundation, Inc. and its contributors. +# 4. Neither the name of The NetBSD Foundation nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS +# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS +# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# + +check_sysctl() { + echo "${1} = ${2}" >expout + atf_check "sysctl ${1}" 0 expout null +} + +atf_test_case plain +plain_head() { + atf_set "descr" "Test load without arguments" + atf_set "require.user" "root" +} +plain_body() { + cat >experr </dev/null 2>&1 +} + +atf_test_case bflag +bflag_head() { + atf_set "descr" "Test the -b flag" + atf_set "require.user" "root" +} +bflag_body() { + echo "Checking error conditions" + + atf_check "modload -b foo k_helper.o" 1 null stderr + atf_check "grep 'Invalid parameter.*foo' stderr" 0 ignore null + + atf_check "modload -b foo= k_helper.o" 1 null stderr + atf_check "grep 'Invalid boolean value' stderr" 0 ignore null + + atf_check "modload -b foo=bar k_helper.o" 1 null stderr + atf_check "grep 'Invalid boolean value.*bar' stderr" 0 ignore null + + atf_check "modload -b foo=falsea k_helper.o" 1 null stderr + atf_check "grep 'Invalid boolean value.*falsea' stderr" 0 ignore null + + atf_check "modload -b foo=truea k_helper.o" 1 null stderr + atf_check "grep 'Invalid boolean value.*truea' stderr" 0 ignore null + + # TODO Once sysctl(8) supports CTLTYPE_BOOL nodes. + #echo "Checking valid values" +} +bflag_cleanup() { + modunload k_helper >/dev/null 2>&1 +} + +atf_test_case iflag +iflag_head() { + atf_set "descr" "Test the -i flag" + atf_set "require.user" "root" +} +iflag_body() { + echo "Checking error conditions" + + atf_check "modload -i foo k_helper.o" 1 null stderr + atf_check "grep 'Invalid parameter.*foo' stderr" 0 ignore null + + atf_check "modload -i foo= k_helper.o" 1 null stderr + atf_check "grep 'Invalid integer value' stderr" 0 ignore null + + atf_check "modload -i foo=bar k_helper.o" 1 null stderr + atf_check "grep 'Invalid integer value.*bar' stderr" 0 ignore null + + atf_check "modload -i foo=123a k_helper.o" 1 null stderr + atf_check "grep 'Invalid integer value.*123a' stderr" 0 ignore null + + echo "Checking valid values" + + for v in 5 10; do + atf_check "modload -i prop_int='${v}' \ + $(atf_get_srcdir)/k_helper.o" 0 null null + check_sysctl vendor.k_helper.prop_int_ok 1 + check_sysctl vendor.k_helper.prop_int_val "${v}" + atf_check "modunload k_helper" 0 null null + done +} +iflag_cleanup() { + modunload k_helper >/dev/null 2>&1 +} + +atf_test_case sflag +sflag_head() { + atf_set "descr" "Test the -s flag" + atf_set "require.user" "root" +} +sflag_body() { + echo "Checking error conditions" + + atf_check "modload -s foo k_helper.o" 1 null stderr + atf_check "grep 'Invalid parameter.*foo' stderr" 0 ignore null + + echo "Checking valid values" + + for v in '1st string' '2nd string'; do + atf_check "modload -s prop_str='${v}' \ + $(atf_get_srcdir)/k_helper.o" 0 null null + check_sysctl vendor.k_helper.prop_str_ok 1 + check_sysctl vendor.k_helper.prop_str_val "${v}" + atf_check "modunload k_helper" 0 null null + done +} +sflag_cleanup() { + modunload k_helper >/dev/null 2>&1 +} + +atf_init_test_cases() +{ + atf_add_test_case plain + atf_add_test_case bflag + atf_add_test_case iflag + atf_add_test_case sflag +}