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