1/* vi: set sw=4 ts=4: */
2/*
3 * Mini insmod implementation for busybox
4 *
5 * Copyright (C) 2008 Timo Teras <timo.teras@iki.fi>
6 *
7 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
8 */
9
10#include "libbb.h"
11#include "modutils.h"
12#include <fnmatch.h>
13
14/* 2.6 style insmod has no options and required filename
15 * (not module name - .ko can't be omitted) */
16
17//usage:#define insmod_trivial_usage
18//usage:	IF_FEATURE_2_4_MODULES("[OPTIONS] MODULE ")
19//usage:	IF_NOT_FEATURE_2_4_MODULES("FILE ")
20//usage:	"[symbol=value]..."
21//usage:#define insmod_full_usage "\n\n"
22//usage:       "Load the specified kernel modules into the kernel"
23//usage:	IF_FEATURE_2_4_MODULES( "\n"
24//usage:     "\nOptions:"
25//usage:     "\n	-f	Force module to load into the wrong kernel version"
26//usage:     "\n	-k	Make module autoclean-able"
27//usage:     "\n	-v	Verbose"
28//usage:     "\n	-q	Quiet"
29//usage:     "\n	-L	Lock: prevent simultaneous loads"
30//usage:	IF_FEATURE_INSMOD_LOAD_MAP(
31//usage:     "\n	-m	Output load map to stdout"
32//usage:	)
33//usage:     "\n	-x	Don't export externs"
34//usage:	)
35
36static char *m_filename;
37
38static int FAST_FUNC check_module_name_match(const char *filename,
39		struct stat *statbuf UNUSED_PARAM,
40		void *userdata, int depth UNUSED_PARAM)
41{
42	char *fullname = (char *) userdata;
43	char *tmp;
44
45	if (fullname[0] == '\0')
46		return FALSE;
47
48	tmp = bb_get_last_path_component_nostrip(filename);
49	if (strcmp(tmp, fullname) == 0) {
50		/* Stop searching if we find a match */
51		m_filename = xstrdup(filename);
52		return FALSE;
53	}
54	return TRUE;
55}
56
57int insmod_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
58int insmod_main(int argc UNUSED_PARAM, char **argv)
59{
60	struct stat st;
61	char *filename;
62	FILE *fp = NULL;
63	int pos;
64	int rc;
65
66	/* Compat note:
67	 * 2.6 style insmod has no options and required filename
68	 * (not module name - .ko can't be omitted).
69	 * 2.4 style insmod can take module name without .o
70	 * and performs module search in default directories
71	 * or in $MODPATH.
72	 */
73
74	IF_FEATURE_2_4_MODULES(
75		getopt32(argv, INSMOD_OPTS INSMOD_ARGS);
76		argv += optind - 1;
77	);
78
79	filename = *++argv;
80	if (!filename)
81		bb_show_usage();
82
83	m_filename = NULL;
84
85	pos = strlen(filename) - 2;
86	if (get_linux_version_code() < KERNEL_VERSION(2,6,0)) {
87		if (pos < 0) pos = 0;
88		if (strncmp(&filename[pos], ".o", 2) !=0)
89			filename = xasprintf("%s.o", filename);
90	} else {
91		if (--pos < 0) pos = 0;
92		if (strncmp(&filename[pos], ".ko", 3) !=0)
93			filename = xasprintf("%s.ko", filename);
94	}
95
96	/* Get a filedesc for the module.  Check if we have a complete path */
97	if (stat(filename, &st) < 0 || !S_ISREG(st.st_mode) ||
98		(fp = fopen_for_read(filename)) == NULL) {
99		/* Hmm.  Could not open it. Search /lib/modules/ */
100		int r;
101		char *module_dir;
102
103		module_dir = xmalloc_readlink(CONFIG_DEFAULT_MODULES_DIR);
104		if (!module_dir)
105			module_dir = xstrdup(CONFIG_DEFAULT_MODULES_DIR);
106		r = recursive_action(module_dir, ACTION_RECURSE,
107			check_module_name_match, NULL, filename, 0);
108		free(module_dir);
109		if (r)
110			bb_error_msg_and_die("'%s': module not found", filename);
111		if (m_filename == NULL || ((fp = fopen_for_read(m_filename)) == NULL))
112			bb_error_msg_and_die("'%s': module not found", filename);
113		filename = m_filename;
114	}
115	if (fp != NULL)
116		fclose(fp);
117
118	rc = bb_init_module(filename, parse_cmdline_module_options(argv));
119	if (rc)
120		bb_error_msg("can't insert '%s': %s", filename, moderror(rc));
121
122	free(m_filename);
123	return rc;
124}
125