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 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; 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 */ 84 static 85 void 86 require_modular(void) 87 { 88 89 if (!have_modular) 90 atf_tc_skip("Kernel does not have 'options MODULAR'."); 91 } 92 93 static 94 bool 95 get_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 */ 136 static 137 bool 138 get_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 */ 156 static 157 bool 158 k_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 */ 169 static 170 bool 171 k_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 */ 184 static 185 bool 186 k_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 assert(false); 206 } 207 208 return found; 209 } 210 211 /* 212 * Loads the specified module from a file. If fatal is set and an error 213 * occurs when loading the module, an error message is printed and the 214 * test case is aborted. 215 */ 216 static 217 int 218 load(prop_dictionary_t props, bool fatal, const char *fmt, ...) 219 { 220 int err; 221 va_list ap; 222 char filename[MAXPATHLEN], *propsstr; 223 modctl_load_t ml; 224 225 if (props == NULL) { 226 props = prop_dictionary_create(); 227 propsstr = prop_dictionary_externalize(props); 228 ATF_CHECK(propsstr != NULL); 229 prop_object_release(props); 230 } else { 231 propsstr = prop_dictionary_externalize(props); 232 ATF_CHECK(propsstr != NULL); 233 } 234 235 va_start(ap, fmt); 236 vsnprintf(filename, sizeof(filename), fmt, ap); 237 va_end(ap); 238 239 ml.ml_filename = filename; 240 ml.ml_flags = 0; 241 ml.ml_props = propsstr; 242 ml.ml_propslen = strlen(propsstr); 243 244 printf("Loading module %s\n", filename); 245 err = 0; 246 if (modctl(MODCTL_LOAD, &ml) == -1) { 247 err = errno; 248 fprintf(stderr, "modctl(MODCTL_LOAD, %s), failed: %s\n", 249 filename, strerror(err)); 250 if (fatal) 251 atf_tc_fail("Module load failed"); 252 } 253 254 free(propsstr); 255 256 return err; 257 } 258 259 /* 260 * Unloads the specified module. If silent is true, nothing will be 261 * printed and no errors will be raised if the unload was unsuccessful. 262 */ 263 static 264 int 265 unload(const char *name, bool fatal) 266 { 267 int err; 268 269 printf("Unloading module %s\n", name); 270 err = 0; 271 if (modctl(MODCTL_UNLOAD, __UNCONST(name)) == -1) { 272 err = errno; 273 fprintf(stderr, "modctl(MODCTL_UNLOAD, %s) failed: %s\n", 274 name, strerror(err)); 275 if (fatal) 276 atf_tc_fail("Module unload failed"); 277 } 278 return err; 279 } 280 281 /* 282 * A silent version of unload, to be called as part of the cleanup 283 * process only. 284 */ 285 static 286 int 287 unload_cleanup(const char *name) 288 { 289 290 (void)modctl(MODCTL_UNLOAD, __UNCONST(name)); 291 } 292 293 /* --------------------------------------------------------------------- */ 294 /* Test cases */ 295 /* --------------------------------------------------------------------- */ 296 297 ATF_TC_WITH_CLEANUP(cmd_load); 298 ATF_TC_HEAD(cmd_load, tc) 299 { 300 atf_tc_set_md_var(tc, "descr", "Tests for the MODCTL_LOAD command"); 301 atf_tc_set_md_var(tc, "require.user", "root"); 302 } 303 ATF_TC_BODY(cmd_load, tc) 304 { 305 char longname[MAXPATHLEN]; 306 size_t i; 307 308 require_modular(); 309 310 ATF_CHECK(load(NULL, false, "") == ENOENT); 311 ATF_CHECK(load(NULL, false, "non-existent.o") == ENOENT); 312 313 for (i = 0; i < MAXPATHLEN - 1; i++) 314 longname[i] = 'a'; 315 longname[MAXPATHLEN - 1] = '\0'; 316 ATF_CHECK(load(NULL, false, longname) == ENAMETOOLONG); 317 318 ATF_CHECK(!k_helper_is_present(stat_check)); 319 load(NULL, true, "%s/k_helper/k_helper.kmod", 320 atf_tc_get_config_var(tc, "srcdir")); 321 printf("Checking if load was successful\n"); 322 ATF_CHECK(k_helper_is_present(stat_check)); 323 } 324 ATF_TC_CLEANUP(cmd_load, tc) 325 { 326 unload_cleanup("k_helper"); 327 } 328 329 ATF_TC_WITH_CLEANUP(cmd_load_props); 330 ATF_TC_HEAD(cmd_load_props, tc) 331 { 332 atf_tc_set_md_var(tc, "descr", "Tests for the MODCTL_LOAD command, " 333 "providing extra load-time properties"); 334 atf_tc_set_md_var(tc, "require.user", "root"); 335 } 336 ATF_TC_BODY(cmd_load_props, tc) 337 { 338 prop_dictionary_t props; 339 340 require_modular(); 341 342 printf("Loading module without properties\n"); 343 props = prop_dictionary_create(); 344 load(props, true, "%s/k_helper/k_helper.kmod", 345 atf_tc_get_config_var(tc, "srcdir")); 346 prop_object_release(props); 347 { 348 int ok; 349 ATF_CHECK(get_sysctl("vendor.k_helper.prop_str_ok", 350 &ok, sizeof(ok))); 351 ATF_CHECK(!ok); 352 } 353 unload("k_helper", true); 354 355 printf("Loading module with a string property\n"); 356 props = prop_dictionary_create(); 357 prop_dictionary_set(props, "prop_str", 358 prop_string_create_cstring("1st string")); 359 load(props, true, "%s/k_helper/k_helper.kmod", 360 atf_tc_get_config_var(tc, "srcdir")); 361 prop_object_release(props); 362 { 363 int ok; 364 ATF_CHECK(get_sysctl("vendor.k_helper.prop_str_ok", 365 &ok, sizeof(ok))); 366 ATF_CHECK(ok); 367 368 char val[128]; 369 ATF_CHECK(get_sysctl("vendor.k_helper.prop_str_val", 370 &val, sizeof(val))); 371 ATF_CHECK(strcmp(val, "1st string") == 0); 372 } 373 unload("k_helper", true); 374 375 printf("Loading module with a different string property\n"); 376 props = prop_dictionary_create(); 377 prop_dictionary_set(props, "prop_str", 378 prop_string_create_cstring("2nd string")); 379 load(props, true, "%s/k_helper/k_helper.kmod", 380 atf_tc_get_config_var(tc, "srcdir")); 381 prop_object_release(props); 382 { 383 int ok; 384 ATF_CHECK(get_sysctl("vendor.k_helper.prop_str_ok", 385 &ok, sizeof(ok))); 386 ATF_CHECK(ok); 387 388 char val[128]; 389 ATF_CHECK(get_sysctl("vendor.k_helper.prop_str_val", 390 &val, sizeof(val))); 391 ATF_CHECK(strcmp(val, "2nd string") == 0); 392 } 393 unload("k_helper", true); 394 } 395 ATF_TC_CLEANUP(cmd_load_props, tc) 396 { 397 unload_cleanup("k_helper"); 398 } 399 400 ATF_TC_WITH_CLEANUP(cmd_load_recurse); 401 ATF_TC_HEAD(cmd_load_recurse, tc) 402 { 403 atf_tc_set_md_var(tc, "descr", "Tests for the MODCTL_LOAD command, " 404 "with recursive module_load()"); 405 atf_tc_set_md_var(tc, "require.user", "root"); 406 } 407 ATF_TC_BODY(cmd_load_recurse, tc) 408 { 409 prop_dictionary_t props; 410 char filename[MAXPATHLEN]; 411 412 require_modular(); 413 414 printf("Loading module with request to load another module\n"); 415 props = prop_dictionary_create(); 416 snprintf(filename, sizeof(filename), "%s/k_helper2/k_helper2.kmod", 417 atf_tc_get_config_var(tc, "srcdir")); 418 prop_dictionary_set(props, "prop_recurse", 419 prop_string_create_cstring(filename)); 420 load(props, true, "%s/k_helper/k_helper.kmod", 421 atf_tc_get_config_var(tc, "srcdir")); 422 { 423 int ok; 424 ATF_CHECK(get_sysctl("vendor.k_helper.prop_int_load", 425 &ok, sizeof(ok))); 426 ATF_CHECK(ok == 0); 427 ATF_CHECK(get_sysctl("vendor.k_helper2.present", 428 &ok, sizeof(ok))); 429 ATF_CHECK(ok); 430 } 431 unload("k_helper", true); 432 unload("k_helper2", true); 433 } 434 ATF_TC_CLEANUP(cmd_load_recurse, tc) 435 { 436 unload_cleanup("k_helper"); 437 unload_cleanup("k_helper2"); 438 } 439 440 ATF_TC_WITH_CLEANUP(cmd_stat); 441 ATF_TC_HEAD(cmd_stat, tc) 442 { 443 atf_tc_set_md_var(tc, "descr", "Tests for the MODCTL_STAT command"); 444 atf_tc_set_md_var(tc, "require.user", "root"); 445 } 446 ATF_TC_BODY(cmd_stat, tc) 447 { 448 require_modular(); 449 450 ATF_CHECK(!k_helper_is_present(both_checks)); 451 452 load(NULL, true, "%s/k_helper/k_helper.kmod", 453 atf_tc_get_config_var(tc, "srcdir")); 454 ATF_CHECK(k_helper_is_present(both_checks)); 455 { 456 modstat_t ms; 457 ATF_CHECK(get_modstat_info("k_helper", &ms)); 458 459 ATF_CHECK(ms.ms_class == MODULE_CLASS_MISC); 460 ATF_CHECK(ms.ms_source == MODULE_SOURCE_FILESYS); 461 ATF_CHECK(ms.ms_refcnt == 0); 462 } 463 unload("k_helper", true); 464 465 ATF_CHECK(!k_helper_is_present(both_checks)); 466 } 467 ATF_TC_CLEANUP(cmd_stat, tc) 468 { 469 unload_cleanup("k_helper"); 470 } 471 472 ATF_TC_WITH_CLEANUP(cmd_unload); 473 ATF_TC_HEAD(cmd_unload, tc) 474 { 475 atf_tc_set_md_var(tc, "descr", "Tests for the MODCTL_UNLOAD command"); 476 atf_tc_set_md_var(tc, "require.user", "root"); 477 } 478 ATF_TC_BODY(cmd_unload, tc) 479 { 480 require_modular(); 481 482 load(NULL, true, "%s/k_helper/k_helper.kmod", 483 atf_tc_get_config_var(tc, "srcdir")); 484 485 ATF_CHECK(unload("", false) == ENOENT); 486 ATF_CHECK(unload("non-existent.kmod", false) == ENOENT); 487 ATF_CHECK(unload("k_helper.kmod", false) == ENOENT); 488 489 ATF_CHECK(k_helper_is_present(stat_check)); 490 unload("k_helper", true); 491 printf("Checking if unload was successful\n"); 492 ATF_CHECK(!k_helper_is_present(stat_check)); 493 } 494 ATF_TC_CLEANUP(cmd_unload, tc) 495 { 496 unload_cleanup("k_helper"); 497 } 498 499 /* --------------------------------------------------------------------- */ 500 /* Main */ 501 /* --------------------------------------------------------------------- */ 502 503 ATF_TP_ADD_TCS(tp) 504 { 505 have_modular = check_modular(); 506 507 ATF_TP_ADD_TC(tp, cmd_load); 508 ATF_TP_ADD_TC(tp, cmd_load_props); 509 ATF_TP_ADD_TC(tp, cmd_stat); 510 ATF_TP_ADD_TC(tp, cmd_load_recurse); 511 ATF_TP_ADD_TC(tp, cmd_unload); 512 513 return atf_no_error(); 514 } 515