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