1272343Sngie/* $NetBSD: t_modctl.c,v 1.12 2012/08/20 08:07:52 martin Exp $ */ 2272343Sngie/* 3272343Sngie * Copyright (c) 2008 The NetBSD Foundation, Inc. 4272343Sngie * All rights reserved. 5272343Sngie * 6272343Sngie * Redistribution and use in source and binary forms, with or without 7272343Sngie * modification, are permitted provided that the following conditions 8272343Sngie * are met: 9272343Sngie * 1. Redistributions of source code must retain the above copyright 10272343Sngie * notice, this list of conditions and the following disclaimer. 11272343Sngie * 2. Redistributions in binary form must reproduce the above copyright 12272343Sngie * notice, this list of conditions and the following disclaimer in the 13272343Sngie * documentation and/or other materials provided with the distribution. 14272343Sngie * 15272343Sngie * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND 16272343Sngie * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 17272343Sngie * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 18272343Sngie * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19272343Sngie * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY 20272343Sngie * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21272343Sngie * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 22272343Sngie * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23272343Sngie * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 24272343Sngie * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 25272343Sngie * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 26272343Sngie * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27272343Sngie */ 28272343Sngie 29272343Sngie#include <sys/cdefs.h> 30272343Sngie__KERNEL_RCSID(0, "$NetBSD: t_modctl.c,v 1.12 2012/08/20 08:07:52 martin Exp $"); 31272343Sngie 32272343Sngie#include <sys/module.h> 33272343Sngie#include <sys/sysctl.h> 34272343Sngie 35272343Sngie#include <assert.h> 36272343Sngie#include <errno.h> 37272343Sngie#include <stdarg.h> 38272343Sngie#include <stdbool.h> 39272343Sngie#include <stdio.h> 40272343Sngie#include <stdlib.h> 41272343Sngie#include <string.h> 42272343Sngie 43272343Sngie#include <prop/proplib.h> 44272343Sngie 45272343Sngie#include <atf-c.h> 46272343Sngie 47272343Sngieenum presence_check { both_checks, stat_check, sysctl_check }; 48272343Sngie 49272343Sngiestatic void check_permission(void); 50272343Sngiestatic bool get_modstat_info(const char *, modstat_t *); 51272343Sngiestatic bool get_sysctl(const char *, void *buf, const size_t); 52272343Sngiestatic bool k_helper_is_present_stat(void); 53272343Sngiestatic bool k_helper_is_present_sysctl(void); 54272343Sngiestatic bool k_helper_is_present(enum presence_check); 55272343Sngiestatic int load(prop_dictionary_t, bool, const char *, ...); 56272343Sngiestatic int unload(const char *, bool); 57272343Sngiestatic void unload_cleanup(const char *); 58272343Sngie 59272343Sngie/* --------------------------------------------------------------------- */ 60272343Sngie/* Auxiliary functions */ 61272343Sngie/* --------------------------------------------------------------------- */ 62272343Sngie 63272343Sngie/* 64272343Sngie * A function checking wether we are allowed to load modules currently 65272343Sngie * (either the kernel is not modular, or securelevel may prevent it) 66272343Sngie */ 67272343Sngiestatic void 68272343Sngiecheck_permission(void) 69272343Sngie{ 70272343Sngie int err; 71272343Sngie 72272343Sngie err = modctl(MODCTL_EXISTS, 0); 73272343Sngie if (err == 0) return; 74272343Sngie if (errno == ENOSYS) 75272343Sngie atf_tc_skip("Kernel does not have 'options MODULAR'."); 76272343Sngie else if (errno == EPERM) 77272343Sngie atf_tc_skip("Module loading administratively forbidden"); 78272343Sngie ATF_REQUIRE_EQ_MSG(errno, 0, "unexpected error %d from " 79272343Sngie "modctl(MODCTL_EXISTS, 0)", errno); 80272343Sngie} 81272343Sngie 82272343Sngiestatic bool 83272343Sngieget_modstat_info(const char *name, modstat_t *msdest) 84272343Sngie{ 85272343Sngie bool found; 86272343Sngie size_t len; 87272343Sngie struct iovec iov; 88272343Sngie modstat_t *ms; 89272343Sngie 90272343Sngie check_permission(); 91272343Sngie for (len = 4096; ;) { 92272343Sngie iov.iov_base = malloc(len); 93272343Sngie iov.iov_len = len; 94272343Sngie 95272343Sngie errno = 0; 96272343Sngie 97272343Sngie if (modctl(MODCTL_STAT, &iov) != 0) { 98272343Sngie int err = errno; 99272343Sngie fprintf(stderr, "modctl(MODCTL_STAT) failed: %s\n", 100272343Sngie strerror(err)); 101272343Sngie atf_tc_fail("Failed to query module status"); 102272343Sngie } 103272343Sngie if (len >= iov.iov_len) 104272343Sngie break; 105272343Sngie free(iov.iov_base); 106272343Sngie len = iov.iov_len; 107272343Sngie } 108272343Sngie 109272343Sngie found = false; 110272343Sngie len = iov.iov_len / sizeof(modstat_t); 111272343Sngie for (ms = (modstat_t *)iov.iov_base; len != 0 && !found; 112272343Sngie ms++, len--) { 113272343Sngie if (strcmp(ms->ms_name, name) == 0) { 114272343Sngie if (msdest != NULL) 115272343Sngie *msdest = *ms; 116272343Sngie found = true; 117272343Sngie } 118272343Sngie } 119272343Sngie 120272343Sngie free(iov.iov_base); 121272343Sngie 122272343Sngie return found; 123272343Sngie} 124272343Sngie 125272343Sngie/* 126272343Sngie * Queries a sysctl property. 127272343Sngie */ 128272343Sngiestatic bool 129272343Sngieget_sysctl(const char *name, void *buf, const size_t len) 130272343Sngie{ 131272343Sngie size_t len2 = len; 132272343Sngie printf("Querying sysctl variable: %s\n", name); 133272343Sngie int ret = sysctlbyname(name, buf, &len2, NULL, 0); 134272343Sngie if (ret == -1 && errno != ENOENT) { 135272343Sngie fprintf(stderr, "sysctlbyname(2) failed: %s\n", 136272343Sngie strerror(errno)); 137272343Sngie atf_tc_fail("Failed to query %s", name); 138272343Sngie } 139272343Sngie return ret != -1; 140272343Sngie} 141272343Sngie 142272343Sngie/* 143272343Sngie * Returns a boolean indicating if the k_helper module was loaded 144272343Sngie * successfully. This implementation uses modctl(2)'s MODCTL_STAT 145272343Sngie * subcommand to do the check. 146272343Sngie */ 147272343Sngiestatic bool 148272343Sngiek_helper_is_present_stat(void) 149272343Sngie{ 150272343Sngie 151272343Sngie return get_modstat_info("k_helper", NULL); 152272343Sngie} 153272343Sngie 154272343Sngie/* 155272343Sngie * Returns a boolean indicating if the k_helper module was loaded 156272343Sngie * successfully. This implementation uses the module's sysctl 157272343Sngie * installed node to do the check. 158272343Sngie */ 159272343Sngiestatic bool 160272343Sngiek_helper_is_present_sysctl(void) 161272343Sngie{ 162272343Sngie size_t present; 163272343Sngie 164272343Sngie return get_sysctl("vendor.k_helper.present", &present, 165272343Sngie sizeof(present)); 166272343Sngie} 167272343Sngie 168272343Sngie/* 169272343Sngie * Returns a boolean indicating if the k_helper module was loaded 170272343Sngie * successfully. The 'how' parameter specifies the implementation to 171272343Sngie * use to do the check. 172272343Sngie */ 173272343Sngiestatic bool 174272343Sngiek_helper_is_present(enum presence_check how) 175272343Sngie{ 176272343Sngie bool found; 177272343Sngie 178272343Sngie switch (how) { 179272343Sngie case both_checks: 180272343Sngie found = k_helper_is_present_stat(); 181272343Sngie ATF_CHECK(k_helper_is_present_sysctl() == found); 182272343Sngie break; 183272343Sngie 184272343Sngie case stat_check: 185272343Sngie found = k_helper_is_present_stat(); 186272343Sngie break; 187272343Sngie 188272343Sngie case sysctl_check: 189272343Sngie found = k_helper_is_present_sysctl(); 190272343Sngie break; 191272343Sngie 192272343Sngie default: 193272343Sngie found = false; 194272343Sngie assert(found); 195272343Sngie } 196272343Sngie 197272343Sngie return found; 198272343Sngie} 199272343Sngie 200272343Sngie/* 201272343Sngie * Loads the specified module from a file. If fatal is set and an error 202272343Sngie * occurs when loading the module, an error message is printed and the 203272343Sngie * test case is aborted. 204272343Sngie */ 205272343Sngiestatic __printflike(3, 4) int 206272343Sngieload(prop_dictionary_t props, bool fatal, const char *fmt, ...) 207272343Sngie{ 208272343Sngie int err; 209272343Sngie va_list ap; 210272343Sngie char filename[MAXPATHLEN], *propsstr; 211272343Sngie modctl_load_t ml; 212272343Sngie 213272343Sngie check_permission(); 214272343Sngie if (props == NULL) { 215272343Sngie props = prop_dictionary_create(); 216272343Sngie propsstr = prop_dictionary_externalize(props); 217272343Sngie ATF_CHECK(propsstr != NULL); 218272343Sngie prop_object_release(props); 219272343Sngie } else { 220272343Sngie propsstr = prop_dictionary_externalize(props); 221272343Sngie ATF_CHECK(propsstr != NULL); 222272343Sngie } 223272343Sngie 224272343Sngie va_start(ap, fmt); 225272343Sngie vsnprintf(filename, sizeof(filename), fmt, ap); 226272343Sngie va_end(ap); 227272343Sngie 228272343Sngie ml.ml_filename = filename; 229272343Sngie ml.ml_flags = 0; 230272343Sngie ml.ml_props = propsstr; 231272343Sngie ml.ml_propslen = strlen(propsstr); 232272343Sngie 233272343Sngie printf("Loading module %s\n", filename); 234272343Sngie errno = err = 0; 235272343Sngie 236272343Sngie if (modctl(MODCTL_LOAD, &ml) == -1) { 237272343Sngie err = errno; 238272343Sngie fprintf(stderr, "modctl(MODCTL_LOAD, %s), failed: %s\n", 239272343Sngie filename, strerror(err)); 240272343Sngie if (fatal) 241272343Sngie atf_tc_fail("Module load failed"); 242272343Sngie } 243272343Sngie 244272343Sngie free(propsstr); 245272343Sngie 246272343Sngie return err; 247272343Sngie} 248272343Sngie 249272343Sngie/* 250272343Sngie * Unloads the specified module. If silent is true, nothing will be 251272343Sngie * printed and no errors will be raised if the unload was unsuccessful. 252272343Sngie */ 253272343Sngiestatic int 254272343Sngieunload(const char *name, bool fatal) 255272343Sngie{ 256272343Sngie int err; 257272343Sngie 258272343Sngie check_permission(); 259272343Sngie printf("Unloading module %s\n", name); 260272343Sngie errno = err = 0; 261272343Sngie 262272343Sngie if (modctl(MODCTL_UNLOAD, __UNCONST(name)) == -1) { 263272343Sngie err = errno; 264272343Sngie fprintf(stderr, "modctl(MODCTL_UNLOAD, %s) failed: %s\n", 265272343Sngie name, strerror(err)); 266272343Sngie if (fatal) 267272343Sngie atf_tc_fail("Module unload failed"); 268272343Sngie } 269272343Sngie return err; 270272343Sngie} 271272343Sngie 272272343Sngie/* 273272343Sngie * A silent version of unload, to be called as part of the cleanup 274272343Sngie * process only. 275272343Sngie */ 276272343Sngiestatic void 277272343Sngieunload_cleanup(const char *name) 278272343Sngie{ 279272343Sngie 280272343Sngie (void)modctl(MODCTL_UNLOAD, __UNCONST(name)); 281272343Sngie} 282272343Sngie 283272343Sngie/* --------------------------------------------------------------------- */ 284272343Sngie/* Test cases */ 285272343Sngie/* --------------------------------------------------------------------- */ 286272343Sngie 287272343SngieATF_TC_WITH_CLEANUP(cmd_load); 288272343SngieATF_TC_HEAD(cmd_load, tc) 289272343Sngie{ 290272343Sngie atf_tc_set_md_var(tc, "descr", "Tests for the MODCTL_LOAD command"); 291272343Sngie atf_tc_set_md_var(tc, "require.user", "root"); 292272343Sngie} 293272343SngieATF_TC_BODY(cmd_load, tc) 294272343Sngie{ 295272343Sngie char longname[MAXPATHLEN]; 296272343Sngie size_t i; 297272343Sngie 298272343Sngie ATF_CHECK(load(NULL, false, " ") == ENOENT); 299272343Sngie ATF_CHECK(load(NULL, false, "non-existent.o") == ENOENT); 300272343Sngie 301272343Sngie for (i = 0; i < MAXPATHLEN - 1; i++) 302272343Sngie longname[i] = 'a'; 303272343Sngie longname[MAXPATHLEN - 1] = '\0'; 304272343Sngie ATF_CHECK(load(NULL, false, "%s", longname) == ENAMETOOLONG); 305272343Sngie 306272343Sngie ATF_CHECK(!k_helper_is_present(stat_check)); 307272343Sngie load(NULL, true, "%s/k_helper/k_helper.kmod", 308272343Sngie atf_tc_get_config_var(tc, "srcdir")); 309272343Sngie printf("Checking if load was successful\n"); 310272343Sngie ATF_CHECK(k_helper_is_present(stat_check)); 311272343Sngie} 312272343SngieATF_TC_CLEANUP(cmd_load, tc) 313272343Sngie{ 314272343Sngie unload_cleanup("k_helper"); 315272343Sngie} 316272343Sngie 317272343SngieATF_TC_WITH_CLEANUP(cmd_load_props); 318272343SngieATF_TC_HEAD(cmd_load_props, tc) 319272343Sngie{ 320272343Sngie atf_tc_set_md_var(tc, "descr", "Tests for the MODCTL_LOAD command, " 321272343Sngie "providing extra load-time properties"); 322272343Sngie atf_tc_set_md_var(tc, "require.user", "root"); 323272343Sngie} 324272343SngieATF_TC_BODY(cmd_load_props, tc) 325272343Sngie{ 326272343Sngie prop_dictionary_t props; 327272343Sngie 328272343Sngie printf("Loading module without properties\n"); 329272343Sngie props = prop_dictionary_create(); 330272343Sngie load(props, true, "%s/k_helper/k_helper.kmod", 331272343Sngie atf_tc_get_config_var(tc, "srcdir")); 332272343Sngie prop_object_release(props); 333272343Sngie { 334272343Sngie int ok; 335272343Sngie ATF_CHECK(get_sysctl("vendor.k_helper.prop_str_ok", 336272343Sngie &ok, sizeof(ok))); 337272343Sngie ATF_CHECK(!ok); 338272343Sngie } 339272343Sngie unload("k_helper", true); 340272343Sngie 341272343Sngie printf("Loading module with a string property\n"); 342272343Sngie props = prop_dictionary_create(); 343272343Sngie prop_dictionary_set(props, "prop_str", 344272343Sngie prop_string_create_cstring("1st string")); 345272343Sngie load(props, true, "%s/k_helper/k_helper.kmod", 346272343Sngie atf_tc_get_config_var(tc, "srcdir")); 347272343Sngie prop_object_release(props); 348272343Sngie { 349272343Sngie int ok; 350272343Sngie ATF_CHECK(get_sysctl("vendor.k_helper.prop_str_ok", 351272343Sngie &ok, sizeof(ok))); 352272343Sngie ATF_CHECK(ok); 353272343Sngie 354272343Sngie char val[128]; 355272343Sngie ATF_CHECK(get_sysctl("vendor.k_helper.prop_str_val", 356272343Sngie &val, sizeof(val))); 357272343Sngie ATF_CHECK(strcmp(val, "1st string") == 0); 358272343Sngie } 359272343Sngie unload("k_helper", true); 360272343Sngie 361272343Sngie printf("Loading module with a different string property\n"); 362272343Sngie props = prop_dictionary_create(); 363272343Sngie prop_dictionary_set(props, "prop_str", 364272343Sngie prop_string_create_cstring("2nd string")); 365272343Sngie load(props, true, "%s/k_helper/k_helper.kmod", 366272343Sngie atf_tc_get_config_var(tc, "srcdir")); 367272343Sngie prop_object_release(props); 368272343Sngie { 369272343Sngie int ok; 370272343Sngie ATF_CHECK(get_sysctl("vendor.k_helper.prop_str_ok", 371272343Sngie &ok, sizeof(ok))); 372272343Sngie ATF_CHECK(ok); 373272343Sngie 374272343Sngie char val[128]; 375272343Sngie ATF_CHECK(get_sysctl("vendor.k_helper.prop_str_val", 376272343Sngie &val, sizeof(val))); 377272343Sngie ATF_CHECK(strcmp(val, "2nd string") == 0); 378272343Sngie } 379272343Sngie unload("k_helper", true); 380272343Sngie} 381272343SngieATF_TC_CLEANUP(cmd_load_props, tc) 382272343Sngie{ 383272343Sngie unload_cleanup("k_helper"); 384272343Sngie} 385272343Sngie 386272343SngieATF_TC_WITH_CLEANUP(cmd_load_recurse); 387272343SngieATF_TC_HEAD(cmd_load_recurse, tc) 388272343Sngie{ 389272343Sngie atf_tc_set_md_var(tc, "descr", "Tests for the MODCTL_LOAD command, " 390272343Sngie "with recursive module_load()"); 391272343Sngie atf_tc_set_md_var(tc, "require.user", "root"); 392272343Sngie} 393272343SngieATF_TC_BODY(cmd_load_recurse, tc) 394272343Sngie{ 395272343Sngie prop_dictionary_t props; 396272343Sngie char filename[MAXPATHLEN]; 397272343Sngie 398272343Sngie printf("Loading module with request to load another module\n"); 399272343Sngie props = prop_dictionary_create(); 400272343Sngie snprintf(filename, sizeof(filename), "%s/k_helper2/k_helper2.kmod", 401272343Sngie atf_tc_get_config_var(tc, "srcdir")); 402272343Sngie prop_dictionary_set(props, "prop_recurse", 403272343Sngie prop_string_create_cstring(filename)); 404272343Sngie load(props, true, "%s/k_helper/k_helper.kmod", 405272343Sngie atf_tc_get_config_var(tc, "srcdir")); 406272343Sngie { 407272343Sngie int ok; 408272343Sngie ATF_CHECK(get_sysctl("vendor.k_helper.prop_int_load", 409272343Sngie &ok, sizeof(ok))); 410272343Sngie ATF_CHECK(ok == 0); 411272343Sngie ATF_CHECK(get_sysctl("vendor.k_helper2.present", 412272343Sngie &ok, sizeof(ok))); 413272343Sngie ATF_CHECK(ok); 414272343Sngie } 415272343Sngie unload("k_helper", true); 416272343Sngie unload("k_helper2", true); 417272343Sngie} 418272343SngieATF_TC_CLEANUP(cmd_load_recurse, tc) 419272343Sngie{ 420272343Sngie unload_cleanup("k_helper"); 421272343Sngie unload_cleanup("k_helper2"); 422272343Sngie} 423272343Sngie 424272343SngieATF_TC_WITH_CLEANUP(cmd_stat); 425272343SngieATF_TC_HEAD(cmd_stat, tc) 426272343Sngie{ 427272343Sngie atf_tc_set_md_var(tc, "descr", "Tests for the MODCTL_STAT command"); 428272343Sngie atf_tc_set_md_var(tc, "require.user", "root"); 429272343Sngie} 430272343SngieATF_TC_BODY(cmd_stat, tc) 431272343Sngie{ 432272343Sngie ATF_CHECK(!k_helper_is_present(both_checks)); 433272343Sngie 434272343Sngie load(NULL, true, "%s/k_helper/k_helper.kmod", 435272343Sngie atf_tc_get_config_var(tc, "srcdir")); 436272343Sngie ATF_CHECK(k_helper_is_present(both_checks)); 437272343Sngie { 438272343Sngie modstat_t ms; 439272343Sngie ATF_CHECK(get_modstat_info("k_helper", &ms)); 440272343Sngie 441272343Sngie ATF_CHECK(ms.ms_class == MODULE_CLASS_MISC); 442272343Sngie ATF_CHECK(ms.ms_source == MODULE_SOURCE_FILESYS); 443272343Sngie ATF_CHECK(ms.ms_refcnt == 0); 444272343Sngie } 445272343Sngie unload("k_helper", true); 446272343Sngie 447272343Sngie ATF_CHECK(!k_helper_is_present(both_checks)); 448272343Sngie} 449272343SngieATF_TC_CLEANUP(cmd_stat, tc) 450272343Sngie{ 451272343Sngie unload_cleanup("k_helper"); 452272343Sngie} 453272343Sngie 454272343SngieATF_TC_WITH_CLEANUP(cmd_unload); 455272343SngieATF_TC_HEAD(cmd_unload, tc) 456272343Sngie{ 457272343Sngie atf_tc_set_md_var(tc, "descr", "Tests for the MODCTL_UNLOAD command"); 458272343Sngie atf_tc_set_md_var(tc, "require.user", "root"); 459272343Sngie} 460272343SngieATF_TC_BODY(cmd_unload, tc) 461272343Sngie{ 462272343Sngie load(NULL, true, "%s/k_helper/k_helper.kmod", 463272343Sngie atf_tc_get_config_var(tc, "srcdir")); 464272343Sngie 465272343Sngie ATF_CHECK(unload("", false) == ENOENT); 466272343Sngie ATF_CHECK(unload("non-existent.kmod", false) == ENOENT); 467272343Sngie ATF_CHECK(unload("k_helper.kmod", false) == ENOENT); 468272343Sngie 469272343Sngie ATF_CHECK(k_helper_is_present(stat_check)); 470272343Sngie unload("k_helper", true); 471272343Sngie printf("Checking if unload was successful\n"); 472272343Sngie ATF_CHECK(!k_helper_is_present(stat_check)); 473272343Sngie} 474272343SngieATF_TC_CLEANUP(cmd_unload, tc) 475272343Sngie{ 476272343Sngie unload_cleanup("k_helper"); 477272343Sngie} 478272343Sngie 479272343Sngie/* --------------------------------------------------------------------- */ 480272343Sngie/* Main */ 481272343Sngie/* --------------------------------------------------------------------- */ 482272343Sngie 483272343SngieATF_TP_ADD_TCS(tp) 484272343Sngie{ 485272343Sngie 486272343Sngie ATF_TP_ADD_TC(tp, cmd_load); 487272343Sngie ATF_TP_ADD_TC(tp, cmd_load_props); 488272343Sngie ATF_TP_ADD_TC(tp, cmd_stat); 489272343Sngie ATF_TP_ADD_TC(tp, cmd_load_recurse); 490272343Sngie ATF_TP_ADD_TC(tp, cmd_unload); 491272343Sngie 492272343Sngie return atf_no_error(); 493272343Sngie} 494