1 /* $NetBSD: t_modctl.c,v 1.6 2012/03/11 19:33:17 jruoho 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.6 2012/03/11 19:33:17 jruoho 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 47 static bool have_modular = false; 48 49 enum 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 */ 61 static 62 bool 63 check_modular(void) 64 { 65 bool res = false; 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 74 return res; 75 } 76 77 /* 78 * Makes sure that the kernel has 'options MODULAR' built into it and 79 * skips the test otherwise. Cannot be called unless check_modular() 80 * has been executed before. 81 */ 82 static 83 void 84 require_modular(void) 85 { 86 87 if (!have_modular) 88 atf_tc_skip("Kernel does not have 'options MODULAR'."); 89 } 90 91 static 92 bool 93 get_modstat_info(const char *name, modstat_t *msdest) 94 { 95 bool found; 96 size_t len; 97 struct iovec iov; 98 modstat_t *ms; 99 100 for (len = 4096; ;) { 101 iov.iov_base = malloc(len); 102 iov.iov_len = len; 103 if (modctl(MODCTL_STAT, &iov) != 0) { 104 int err = errno; 105 fprintf(stderr, "modctl(MODCTL_STAT) failed: %s\n", 106 strerror(err)); 107 atf_tc_fail("Failed to query module status"); 108 } 109 if (len >= iov.iov_len) 110 break; 111 free(iov.iov_base); 112 len = iov.iov_len; 113 } 114 115 found = false; 116 len = iov.iov_len / sizeof(modstat_t); 117 for (ms = (modstat_t *)iov.iov_base; len != 0 && !found; 118 ms++, len--) { 119 if (strcmp(ms->ms_name, name) == 0) { 120 if (msdest != NULL) 121 *msdest = *ms; 122 found = true; 123 } 124 } 125 126 free(iov.iov_base); 127 128 return found; 129 } 130 131 /* 132 * Queries a sysctl property. 133 */ 134 static 135 bool 136 get_sysctl(const char *name, void *buf, const size_t len) 137 { 138 size_t len2 = len; 139 printf("Querying sysctl variable: %s\n", name); 140 int ret = sysctlbyname(name, buf, &len2, NULL, 0); 141 if (ret == -1 && errno != ENOENT) { 142 fprintf(stderr, "sysctlbyname(2) failed: %s\n", 143 strerror(errno)); 144 atf_tc_fail("Failed to query %s", name); 145 } 146 return ret != -1; 147 } 148 149 /* 150 * Returns a boolean indicating if the k_helper module was loaded 151 * successfully. This implementation uses modctl(2)'s MODCTL_STAT 152 * subcommand to do the check. 153 */ 154 static 155 bool 156 k_helper_is_present_stat(void) 157 { 158 159 return get_modstat_info("k_helper", NULL); 160 } 161 162 /* 163 * Returns a boolean indicating if the k_helper module was loaded 164 * successfully. This implementation uses the module's sysctl 165 * installed node to do the check. 166 */ 167 static 168 bool 169 k_helper_is_present_sysctl(void) 170 { 171 size_t present; 172 173 return get_sysctl("vendor.k_helper.present", &present, 174 sizeof(present)); 175 } 176 177 /* 178 * Returns a boolean indicating if the k_helper module was loaded 179 * successfully. The 'how' parameter specifies the implementation to 180 * use to do the check. 181 */ 182 static 183 bool 184 k_helper_is_present(enum presence_check how) 185 { 186 bool found; 187 188 switch (how) { 189 case both_checks: 190 found = k_helper_is_present_stat(); 191 ATF_CHECK(k_helper_is_present_sysctl() == found); 192 break; 193 194 case stat_check: 195 found = k_helper_is_present_stat(); 196 break; 197 198 case sysctl_check: 199 found = k_helper_is_present_sysctl(); 200 break; 201 202 default: 203 found = false; 204 assert(found); 205 } 206 207 return found; 208 } 209 210 /* 211 * Loads the specified module from a file. If fatal is set and an error 212 * occurs when loading the module, an error message is printed and the 213 * test case is aborted. 214 */ 215 static 216 int 217 load(prop_dictionary_t props, bool fatal, const char *fmt, ...) 218 { 219 int err; 220 va_list ap; 221 char filename[MAXPATHLEN], *propsstr; 222 modctl_load_t ml; 223 224 if (props == NULL) { 225 props = prop_dictionary_create(); 226 propsstr = prop_dictionary_externalize(props); 227 ATF_CHECK(propsstr != NULL); 228 prop_object_release(props); 229 } else { 230 propsstr = prop_dictionary_externalize(props); 231 ATF_CHECK(propsstr != NULL); 232 } 233 234 va_start(ap, fmt); 235 vsnprintf(filename, sizeof(filename), fmt, ap); 236 va_end(ap); 237 238 ml.ml_filename = filename; 239 ml.ml_flags = 0; 240 ml.ml_props = propsstr; 241 ml.ml_propslen = strlen(propsstr); 242 243 printf("Loading module %s\n", filename); 244 err = 0; 245 if (modctl(MODCTL_LOAD, &ml) == -1) { 246 err = errno; 247 fprintf(stderr, "modctl(MODCTL_LOAD, %s), failed: %s\n", 248 filename, strerror(err)); 249 if (fatal) 250 atf_tc_fail("Module load failed"); 251 } 252 253 free(propsstr); 254 255 return err; 256 } 257 258 /* 259 * Unloads the specified module. If silent is true, nothing will be 260 * printed and no errors will be raised if the unload was unsuccessful. 261 */ 262 static 263 int 264 unload(const char *name, bool fatal) 265 { 266 int err; 267 268 printf("Unloading module %s\n", name); 269 err = 0; 270 if (modctl(MODCTL_UNLOAD, __UNCONST(name)) == -1) { 271 err = errno; 272 fprintf(stderr, "modctl(MODCTL_UNLOAD, %s) failed: %s\n", 273 name, strerror(err)); 274 if (fatal) 275 atf_tc_fail("Module unload failed"); 276 } 277 return err; 278 } 279 280 /* 281 * A silent version of unload, to be called as part of the cleanup 282 * process only. 283 */ 284 static 285 void 286 unload_cleanup(const char *name) 287 { 288 289 (void)modctl(MODCTL_UNLOAD, __UNCONST(name)); 290 } 291 292 /* --------------------------------------------------------------------- */ 293 /* Test cases */ 294 /* --------------------------------------------------------------------- */ 295 296 ATF_TC_WITH_CLEANUP(cmd_load); 297 ATF_TC_HEAD(cmd_load, tc) 298 { 299 atf_tc_set_md_var(tc, "descr", "Tests for the MODCTL_LOAD command"); 300 atf_tc_set_md_var(tc, "require.user", "root"); 301 } 302 ATF_TC_BODY(cmd_load, tc) 303 { 304 char longname[MAXPATHLEN]; 305 size_t i; 306 307 require_modular(); 308 309 ATF_CHECK(load(NULL, false, "") == ENOENT); 310 ATF_CHECK(load(NULL, false, "non-existent.o") == ENOENT); 311 312 for (i = 0; i < MAXPATHLEN - 1; i++) 313 longname[i] = 'a'; 314 longname[MAXPATHLEN - 1] = '\0'; 315 ATF_CHECK(load(NULL, false, longname) == ENAMETOOLONG); 316 317 ATF_CHECK(!k_helper_is_present(stat_check)); 318 load(NULL, true, "%s/k_helper/k_helper.kmod", 319 atf_tc_get_config_var(tc, "srcdir")); 320 printf("Checking if load was successful\n"); 321 ATF_CHECK(k_helper_is_present(stat_check)); 322 } 323 ATF_TC_CLEANUP(cmd_load, tc) 324 { 325 unload_cleanup("k_helper"); 326 } 327 328 ATF_TC_WITH_CLEANUP(cmd_load_props); 329 ATF_TC_HEAD(cmd_load_props, tc) 330 { 331 atf_tc_set_md_var(tc, "descr", "Tests for the MODCTL_LOAD command, " 332 "providing extra load-time properties"); 333 atf_tc_set_md_var(tc, "require.user", "root"); 334 } 335 ATF_TC_BODY(cmd_load_props, tc) 336 { 337 prop_dictionary_t props; 338 339 require_modular(); 340 341 printf("Loading module without properties\n"); 342 props = prop_dictionary_create(); 343 load(props, true, "%s/k_helper/k_helper.kmod", 344 atf_tc_get_config_var(tc, "srcdir")); 345 prop_object_release(props); 346 { 347 int ok; 348 ATF_CHECK(get_sysctl("vendor.k_helper.prop_str_ok", 349 &ok, sizeof(ok))); 350 ATF_CHECK(!ok); 351 } 352 unload("k_helper", true); 353 354 printf("Loading module with a string property\n"); 355 props = prop_dictionary_create(); 356 prop_dictionary_set(props, "prop_str", 357 prop_string_create_cstring("1st string")); 358 load(props, true, "%s/k_helper/k_helper.kmod", 359 atf_tc_get_config_var(tc, "srcdir")); 360 prop_object_release(props); 361 { 362 int ok; 363 ATF_CHECK(get_sysctl("vendor.k_helper.prop_str_ok", 364 &ok, sizeof(ok))); 365 ATF_CHECK(ok); 366 367 char val[128]; 368 ATF_CHECK(get_sysctl("vendor.k_helper.prop_str_val", 369 &val, sizeof(val))); 370 ATF_CHECK(strcmp(val, "1st string") == 0); 371 } 372 unload("k_helper", true); 373 374 printf("Loading module with a different string property\n"); 375 props = prop_dictionary_create(); 376 prop_dictionary_set(props, "prop_str", 377 prop_string_create_cstring("2nd string")); 378 load(props, true, "%s/k_helper/k_helper.kmod", 379 atf_tc_get_config_var(tc, "srcdir")); 380 prop_object_release(props); 381 { 382 int ok; 383 ATF_CHECK(get_sysctl("vendor.k_helper.prop_str_ok", 384 &ok, sizeof(ok))); 385 ATF_CHECK(ok); 386 387 char val[128]; 388 ATF_CHECK(get_sysctl("vendor.k_helper.prop_str_val", 389 &val, sizeof(val))); 390 ATF_CHECK(strcmp(val, "2nd string") == 0); 391 } 392 unload("k_helper", true); 393 } 394 ATF_TC_CLEANUP(cmd_load_props, tc) 395 { 396 unload_cleanup("k_helper"); 397 } 398 399 ATF_TC_WITH_CLEANUP(cmd_load_recurse); 400 ATF_TC_HEAD(cmd_load_recurse, tc) 401 { 402 atf_tc_set_md_var(tc, "descr", "Tests for the MODCTL_LOAD command, " 403 "with recursive module_load()"); 404 atf_tc_set_md_var(tc, "require.user", "root"); 405 } 406 ATF_TC_BODY(cmd_load_recurse, tc) 407 { 408 prop_dictionary_t props; 409 char filename[MAXPATHLEN]; 410 411 require_modular(); 412 413 printf("Loading module with request to load another module\n"); 414 props = prop_dictionary_create(); 415 snprintf(filename, sizeof(filename), "%s/k_helper2/k_helper2.kmod", 416 atf_tc_get_config_var(tc, "srcdir")); 417 prop_dictionary_set(props, "prop_recurse", 418 prop_string_create_cstring(filename)); 419 load(props, true, "%s/k_helper/k_helper.kmod", 420 atf_tc_get_config_var(tc, "srcdir")); 421 { 422 int ok; 423 ATF_CHECK(get_sysctl("vendor.k_helper.prop_int_load", 424 &ok, sizeof(ok))); 425 ATF_CHECK(ok == 0); 426 ATF_CHECK(get_sysctl("vendor.k_helper2.present", 427 &ok, sizeof(ok))); 428 ATF_CHECK(ok); 429 } 430 unload("k_helper", true); 431 unload("k_helper2", true); 432 } 433 ATF_TC_CLEANUP(cmd_load_recurse, tc) 434 { 435 unload_cleanup("k_helper"); 436 unload_cleanup("k_helper2"); 437 } 438 439 ATF_TC_WITH_CLEANUP(cmd_stat); 440 ATF_TC_HEAD(cmd_stat, tc) 441 { 442 atf_tc_set_md_var(tc, "descr", "Tests for the MODCTL_STAT command"); 443 atf_tc_set_md_var(tc, "require.user", "root"); 444 } 445 ATF_TC_BODY(cmd_stat, tc) 446 { 447 require_modular(); 448 449 ATF_CHECK(!k_helper_is_present(both_checks)); 450 451 load(NULL, true, "%s/k_helper/k_helper.kmod", 452 atf_tc_get_config_var(tc, "srcdir")); 453 ATF_CHECK(k_helper_is_present(both_checks)); 454 { 455 modstat_t ms; 456 ATF_CHECK(get_modstat_info("k_helper", &ms)); 457 458 ATF_CHECK(ms.ms_class == MODULE_CLASS_MISC); 459 ATF_CHECK(ms.ms_source == MODULE_SOURCE_FILESYS); 460 ATF_CHECK(ms.ms_refcnt == 0); 461 } 462 unload("k_helper", true); 463 464 ATF_CHECK(!k_helper_is_present(both_checks)); 465 } 466 ATF_TC_CLEANUP(cmd_stat, tc) 467 { 468 unload_cleanup("k_helper"); 469 } 470 471 ATF_TC_WITH_CLEANUP(cmd_unload); 472 ATF_TC_HEAD(cmd_unload, tc) 473 { 474 atf_tc_set_md_var(tc, "descr", "Tests for the MODCTL_UNLOAD command"); 475 atf_tc_set_md_var(tc, "require.user", "root"); 476 } 477 ATF_TC_BODY(cmd_unload, tc) 478 { 479 require_modular(); 480 481 load(NULL, true, "%s/k_helper/k_helper.kmod", 482 atf_tc_get_config_var(tc, "srcdir")); 483 484 ATF_CHECK(unload("", false) == ENOENT); 485 ATF_CHECK(unload("non-existent.kmod", false) == ENOENT); 486 ATF_CHECK(unload("k_helper.kmod", false) == ENOENT); 487 488 ATF_CHECK(k_helper_is_present(stat_check)); 489 unload("k_helper", true); 490 printf("Checking if unload was successful\n"); 491 ATF_CHECK(!k_helper_is_present(stat_check)); 492 } 493 ATF_TC_CLEANUP(cmd_unload, tc) 494 { 495 unload_cleanup("k_helper"); 496 } 497 498 /* --------------------------------------------------------------------- */ 499 /* Main */ 500 /* --------------------------------------------------------------------- */ 501 502 ATF_TP_ADD_TCS(tp) 503 { 504 have_modular = check_modular(); 505 506 ATF_TP_ADD_TC(tp, cmd_load); 507 ATF_TP_ADD_TC(tp, cmd_load_props); 508 ATF_TP_ADD_TC(tp, cmd_stat); 509 ATF_TP_ADD_TC(tp, cmd_load_recurse); 510 ATF_TP_ADD_TC(tp, cmd_unload); 511 512 return atf_no_error(); 513 } 514