1/* $NetBSD: t_modctl.c,v 1.4 2010/08/21 13:21:48 pgoyette Exp $ */ 2/* 3 * Copyright (c) 2008 The NetBSD Foundation, Inc. 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND 16 * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 17 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY 20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 22 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 24 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 25 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 26 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29#include <sys/cdefs.h> 30__KERNEL_RCSID(0, "$NetBSD: t_modctl.c,v 1.4 2010/08/21 13:21:48 pgoyette Exp $"); 31 32#include <sys/module.h> 33#include <sys/sysctl.h> 34 35#include <assert.h> 36#include <errno.h> 37#include <stdarg.h> 38#include <stdbool.h> 39#include <stdio.h> 40#include <stdlib.h> 41#include <string.h> 42 43#include <prop/proplib.h> 44 45#include <atf-c.h> 46 47static bool have_modular = false; 48 49enum presence_check { both_checks, stat_check, sysctl_check }; 50 51/* --------------------------------------------------------------------- */ 52/* Auxiliary functions */ 53/* --------------------------------------------------------------------- */ 54 55/* 56 * Checks if the kernel has 'options MODULAR' built into it and returns 57 * a boolean indicating this condition. This function must be called 58 * during the test program's initialization and the result be stored 59 * globally for further (efficient) usage of require_modular(). 60 */ 61static 62bool 63check_modular(void) 64{ 65 bool res; 66 struct iovec iov; 67 68 iov.iov_base = NULL; 69 iov.iov_len = 0; 70 71 if (modctl(MODCTL_STAT, &iov) == 0) 72 res = true; 73 else 74 res = (errno != ENOSYS); 75 76 return res; 77} 78 79/* 80 * Makes sure that the kernel has 'options MODULAR' built into it and 81 * skips the test otherwise. Cannot be called unless check_modular() 82 * has been executed before. 83 */ 84static 85void 86require_modular(void) 87{ 88 89 if (!have_modular) 90 atf_tc_skip("Kernel does not have 'options MODULAR'."); 91} 92 93static 94bool 95get_modstat_info(const char *name, modstat_t *msdest) 96{ 97 bool found; 98 size_t len; 99 struct iovec iov; 100 modstat_t *ms; 101 102 for (len = 4096; ;) { 103 iov.iov_base = malloc(len); 104 iov.iov_len = len; 105 if (modctl(MODCTL_STAT, &iov) != 0) { 106 int err = errno; 107 fprintf(stderr, "modctl(MODCTL_STAT) failed: %s\n", 108 strerror(err)); 109 atf_tc_fail("Failed to query module status"); 110 } 111 if (len >= iov.iov_len) 112 break; 113 free(iov.iov_base); 114 len = iov.iov_len; 115 } 116 117 found = false; 118 len = iov.iov_len / sizeof(modstat_t); 119 for (ms = (modstat_t *)iov.iov_base; len != 0 && !found; 120 ms++, len--) { 121 if (strcmp(ms->ms_name, name) == 0) { 122 if (msdest != NULL) 123 *msdest = *ms; 124 found = true; 125 } 126 } 127 128 free(iov.iov_base); 129 130 return found; 131} 132 133/* 134 * Queries a sysctl property. 135 */ 136static 137bool 138get_sysctl(const char *name, void *buf, const size_t len) 139{ 140 size_t len2 = len; 141 printf("Querying sysctl variable: %s\n", name); 142 int ret = sysctlbyname(name, buf, &len2, NULL, 0); 143 if (ret == -1 && errno != ENOENT) { 144 fprintf(stderr, "sysctlbyname(2) failed: %s\n", 145 strerror(errno)); 146 atf_tc_fail("Failed to query %s", name); 147 } 148 return ret != -1; 149} 150 151/* 152 * Returns a boolean indicating if the k_helper module was loaded 153 * successfully. This implementation uses modctl(2)'s MODCTL_STAT 154 * subcommand to do the check. 155 */ 156static 157bool 158k_helper_is_present_stat(void) 159{ 160 161 return get_modstat_info("k_helper", NULL); 162} 163 164/* 165 * Returns a boolean indicating if the k_helper module was loaded 166 * successfully. This implementation uses the module's sysctl 167 * installed node to do the check. 168 */ 169static 170bool 171k_helper_is_present_sysctl(void) 172{ 173 size_t present; 174 175 return get_sysctl("vendor.k_helper.present", &present, 176 sizeof(present)); 177} 178 179/* 180 * Returns a boolean indicating if the k_helper module was loaded 181 * successfully. The 'how' parameter specifies the implementation to 182 * use to do the check. 183 */ 184static 185bool 186k_helper_is_present(enum presence_check how) 187{ 188 bool found; 189 190 switch (how) { 191 case both_checks: 192 found = k_helper_is_present_stat(); 193 ATF_CHECK(k_helper_is_present_sysctl() == found); 194 break; 195 196 case stat_check: 197 found = k_helper_is_present_stat(); 198 break; 199 200 case sysctl_check: 201 found = k_helper_is_present_sysctl(); 202 break; 203 204 default: 205 found = false; 206 assert(found); 207 } 208 209 return found; 210} 211 212/* 213 * Loads the specified module from a file. If fatal is set and an error 214 * occurs when loading the module, an error message is printed and the 215 * test case is aborted. 216 */ 217static 218int 219load(prop_dictionary_t props, bool fatal, const char *fmt, ...) 220{ 221 int err; 222 va_list ap; 223 char filename[MAXPATHLEN], *propsstr; 224 modctl_load_t ml; 225 226 if (props == NULL) { 227 props = prop_dictionary_create(); 228 propsstr = prop_dictionary_externalize(props); 229 ATF_CHECK(propsstr != NULL); 230 prop_object_release(props); 231 } else { 232 propsstr = prop_dictionary_externalize(props); 233 ATF_CHECK(propsstr != NULL); 234 } 235 236 va_start(ap, fmt); 237 vsnprintf(filename, sizeof(filename), fmt, ap); 238 va_end(ap); 239 240 ml.ml_filename = filename; 241 ml.ml_flags = 0; 242 ml.ml_props = propsstr; 243 ml.ml_propslen = strlen(propsstr); 244 245 printf("Loading module %s\n", filename); 246 err = 0; 247 if (modctl(MODCTL_LOAD, &ml) == -1) { 248 err = errno; 249 fprintf(stderr, "modctl(MODCTL_LOAD, %s), failed: %s\n", 250 filename, strerror(err)); 251 if (fatal) 252 atf_tc_fail("Module load failed"); 253 } 254 255 free(propsstr); 256 257 return err; 258} 259 260/* 261 * Unloads the specified module. If silent is true, nothing will be 262 * printed and no errors will be raised if the unload was unsuccessful. 263 */ 264static 265int 266unload(const char *name, bool fatal) 267{ 268 int err; 269 270 printf("Unloading module %s\n", name); 271 err = 0; 272 if (modctl(MODCTL_UNLOAD, __UNCONST(name)) == -1) { 273 err = errno; 274 fprintf(stderr, "modctl(MODCTL_UNLOAD, %s) failed: %s\n", 275 name, strerror(err)); 276 if (fatal) 277 atf_tc_fail("Module unload failed"); 278 } 279 return err; 280} 281 282/* 283 * A silent version of unload, to be called as part of the cleanup 284 * process only. 285 */ 286static 287void 288unload_cleanup(const char *name) 289{ 290 291 (void)modctl(MODCTL_UNLOAD, __UNCONST(name)); 292} 293 294/* --------------------------------------------------------------------- */ 295/* Test cases */ 296/* --------------------------------------------------------------------- */ 297 298ATF_TC_WITH_CLEANUP(cmd_load); 299ATF_TC_HEAD(cmd_load, tc) 300{ 301 atf_tc_set_md_var(tc, "descr", "Tests for the MODCTL_LOAD command"); 302 atf_tc_set_md_var(tc, "require.user", "root"); 303} 304ATF_TC_BODY(cmd_load, tc) 305{ 306 char longname[MAXPATHLEN]; 307 size_t i; 308 309 require_modular(); 310 311 ATF_CHECK(load(NULL, false, "") == ENOENT); 312 ATF_CHECK(load(NULL, false, "non-existent.o") == ENOENT); 313 314 for (i = 0; i < MAXPATHLEN - 1; i++) 315 longname[i] = 'a'; 316 longname[MAXPATHLEN - 1] = '\0'; 317 ATF_CHECK(load(NULL, false, longname) == ENAMETOOLONG); 318 319 ATF_CHECK(!k_helper_is_present(stat_check)); 320 load(NULL, true, "%s/k_helper/k_helper.kmod", 321 atf_tc_get_config_var(tc, "srcdir")); 322 printf("Checking if load was successful\n"); 323 ATF_CHECK(k_helper_is_present(stat_check)); 324} 325ATF_TC_CLEANUP(cmd_load, tc) 326{ 327 unload_cleanup("k_helper"); 328} 329 330ATF_TC_WITH_CLEANUP(cmd_load_props); 331ATF_TC_HEAD(cmd_load_props, tc) 332{ 333 atf_tc_set_md_var(tc, "descr", "Tests for the MODCTL_LOAD command, " 334 "providing extra load-time properties"); 335 atf_tc_set_md_var(tc, "require.user", "root"); 336} 337ATF_TC_BODY(cmd_load_props, tc) 338{ 339 prop_dictionary_t props; 340 341 require_modular(); 342 343 printf("Loading module without properties\n"); 344 props = prop_dictionary_create(); 345 load(props, true, "%s/k_helper/k_helper.kmod", 346 atf_tc_get_config_var(tc, "srcdir")); 347 prop_object_release(props); 348 { 349 int ok; 350 ATF_CHECK(get_sysctl("vendor.k_helper.prop_str_ok", 351 &ok, sizeof(ok))); 352 ATF_CHECK(!ok); 353 } 354 unload("k_helper", true); 355 356 printf("Loading module with a string property\n"); 357 props = prop_dictionary_create(); 358 prop_dictionary_set(props, "prop_str", 359 prop_string_create_cstring("1st string")); 360 load(props, true, "%s/k_helper/k_helper.kmod", 361 atf_tc_get_config_var(tc, "srcdir")); 362 prop_object_release(props); 363 { 364 int ok; 365 ATF_CHECK(get_sysctl("vendor.k_helper.prop_str_ok", 366 &ok, sizeof(ok))); 367 ATF_CHECK(ok); 368 369 char val[128]; 370 ATF_CHECK(get_sysctl("vendor.k_helper.prop_str_val", 371 &val, sizeof(val))); 372 ATF_CHECK(strcmp(val, "1st string") == 0); 373 } 374 unload("k_helper", true); 375 376 printf("Loading module with a different string property\n"); 377 props = prop_dictionary_create(); 378 prop_dictionary_set(props, "prop_str", 379 prop_string_create_cstring("2nd string")); 380 load(props, true, "%s/k_helper/k_helper.kmod", 381 atf_tc_get_config_var(tc, "srcdir")); 382 prop_object_release(props); 383 { 384 int ok; 385 ATF_CHECK(get_sysctl("vendor.k_helper.prop_str_ok", 386 &ok, sizeof(ok))); 387 ATF_CHECK(ok); 388 389 char val[128]; 390 ATF_CHECK(get_sysctl("vendor.k_helper.prop_str_val", 391 &val, sizeof(val))); 392 ATF_CHECK(strcmp(val, "2nd string") == 0); 393 } 394 unload("k_helper", true); 395} 396ATF_TC_CLEANUP(cmd_load_props, tc) 397{ 398 unload_cleanup("k_helper"); 399} 400 401ATF_TC_WITH_CLEANUP(cmd_load_recurse); 402ATF_TC_HEAD(cmd_load_recurse, tc) 403{ 404 atf_tc_set_md_var(tc, "descr", "Tests for the MODCTL_LOAD command, " 405 "with recursive module_load()"); 406 atf_tc_set_md_var(tc, "require.user", "root"); 407} 408ATF_TC_BODY(cmd_load_recurse, tc) 409{ 410 prop_dictionary_t props; 411 char filename[MAXPATHLEN]; 412 413 require_modular(); 414 415 printf("Loading module with request to load another module\n"); 416 props = prop_dictionary_create(); 417 snprintf(filename, sizeof(filename), "%s/k_helper2/k_helper2.kmod", 418 atf_tc_get_config_var(tc, "srcdir")); 419 prop_dictionary_set(props, "prop_recurse", 420 prop_string_create_cstring(filename)); 421 load(props, true, "%s/k_helper/k_helper.kmod", 422 atf_tc_get_config_var(tc, "srcdir")); 423 { 424 int ok; 425 ATF_CHECK(get_sysctl("vendor.k_helper.prop_int_load", 426 &ok, sizeof(ok))); 427 ATF_CHECK(ok == 0); 428 ATF_CHECK(get_sysctl("vendor.k_helper2.present", 429 &ok, sizeof(ok))); 430 ATF_CHECK(ok); 431 } 432 unload("k_helper", true); 433 unload("k_helper2", true); 434} 435ATF_TC_CLEANUP(cmd_load_recurse, tc) 436{ 437 unload_cleanup("k_helper"); 438 unload_cleanup("k_helper2"); 439} 440 441ATF_TC_WITH_CLEANUP(cmd_stat); 442ATF_TC_HEAD(cmd_stat, tc) 443{ 444 atf_tc_set_md_var(tc, "descr", "Tests for the MODCTL_STAT command"); 445 atf_tc_set_md_var(tc, "require.user", "root"); 446} 447ATF_TC_BODY(cmd_stat, tc) 448{ 449 require_modular(); 450 451 ATF_CHECK(!k_helper_is_present(both_checks)); 452 453 load(NULL, true, "%s/k_helper/k_helper.kmod", 454 atf_tc_get_config_var(tc, "srcdir")); 455 ATF_CHECK(k_helper_is_present(both_checks)); 456 { 457 modstat_t ms; 458 ATF_CHECK(get_modstat_info("k_helper", &ms)); 459 460 ATF_CHECK(ms.ms_class == MODULE_CLASS_MISC); 461 ATF_CHECK(ms.ms_source == MODULE_SOURCE_FILESYS); 462 ATF_CHECK(ms.ms_refcnt == 0); 463 } 464 unload("k_helper", true); 465 466 ATF_CHECK(!k_helper_is_present(both_checks)); 467} 468ATF_TC_CLEANUP(cmd_stat, tc) 469{ 470 unload_cleanup("k_helper"); 471} 472 473ATF_TC_WITH_CLEANUP(cmd_unload); 474ATF_TC_HEAD(cmd_unload, tc) 475{ 476 atf_tc_set_md_var(tc, "descr", "Tests for the MODCTL_UNLOAD command"); 477 atf_tc_set_md_var(tc, "require.user", "root"); 478} 479ATF_TC_BODY(cmd_unload, tc) 480{ 481 require_modular(); 482 483 load(NULL, true, "%s/k_helper/k_helper.kmod", 484 atf_tc_get_config_var(tc, "srcdir")); 485 486 ATF_CHECK(unload("", false) == ENOENT); 487 ATF_CHECK(unload("non-existent.kmod", false) == ENOENT); 488 ATF_CHECK(unload("k_helper.kmod", false) == ENOENT); 489 490 ATF_CHECK(k_helper_is_present(stat_check)); 491 unload("k_helper", true); 492 printf("Checking if unload was successful\n"); 493 ATF_CHECK(!k_helper_is_present(stat_check)); 494} 495ATF_TC_CLEANUP(cmd_unload, tc) 496{ 497 unload_cleanup("k_helper"); 498} 499 500/* --------------------------------------------------------------------- */ 501/* Main */ 502/* --------------------------------------------------------------------- */ 503 504ATF_TP_ADD_TCS(tp) 505{ 506 have_modular = check_modular(); 507 508 ATF_TP_ADD_TC(tp, cmd_load); 509 ATF_TP_ADD_TC(tp, cmd_load_props); 510 ATF_TP_ADD_TC(tp, cmd_stat); 511 ATF_TP_ADD_TC(tp, cmd_load_recurse); 512 ATF_TP_ADD_TC(tp, cmd_unload); 513 514 return atf_no_error(); 515} 516