diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2008-07-04 10:25:44 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2008-07-04 10:25:44 +0000 |
commit | 671691cf2107b846b40c165e6d5b2806172b6030 (patch) | |
tree | 0a2210ed36cb41846d0bacd1b08777ddf7e6ec95 | |
parent | 8ae5b28b4bff5f1099a4a77af89e1a7e23ad77e2 (diff) | |
download | busybox-w32-671691cf2107b846b40c165e6d5b2806172b6030.tar.gz busybox-w32-671691cf2107b846b40c165e6d5b2806172b6030.tar.bz2 busybox-w32-671691cf2107b846b40c165e6d5b2806172b6030.zip |
modutils: optional modutils-small by Vladimir Dronnikov.
15kb smaller than standard one.
libbb/recursive_action.c: commented-out code for aborting the scan.
-rw-r--r-- | include/applets.h | 5 | ||||
-rw-r--r-- | libbb/recursive_action.c | 13 | ||||
-rw-r--r-- | modutils/Config.in | 61 | ||||
-rw-r--r-- | modutils/Kbuild | 11 | ||||
-rw-r--r-- | modutils/modprobe-small.c | 648 |
5 files changed, 726 insertions, 12 deletions
diff --git a/include/applets.h b/include/applets.h index 591d715c8..aff9070bb 100644 --- a/include/applets.h +++ b/include/applets.h | |||
@@ -116,6 +116,7 @@ USE_DEALLOCVT(APPLET(deallocvt, _BB_DIR_USR_BIN, _BB_SUID_NEVER)) | |||
116 | USE_DELGROUP(APPLET_ODDNAME(delgroup, deluser, _BB_DIR_BIN, _BB_SUID_NEVER, delgroup)) | 116 | USE_DELGROUP(APPLET_ODDNAME(delgroup, deluser, _BB_DIR_BIN, _BB_SUID_NEVER, delgroup)) |
117 | USE_DELUSER(APPLET(deluser, _BB_DIR_BIN, _BB_SUID_NEVER)) | 117 | USE_DELUSER(APPLET(deluser, _BB_DIR_BIN, _BB_SUID_NEVER)) |
118 | USE_DEPMOD(APPLET(depmod, _BB_DIR_SBIN, _BB_SUID_NEVER)) | 118 | USE_DEPMOD(APPLET(depmod, _BB_DIR_SBIN, _BB_SUID_NEVER)) |
119 | USE_MODPROBE_SMALL(APPLET_ODDNAME(depmod, modprobe, _BB_DIR_SBIN, _BB_SUID_NEVER, modprobe)) | ||
119 | USE_DEVFSD(APPLET(devfsd, _BB_DIR_SBIN, _BB_SUID_NEVER)) | 120 | USE_DEVFSD(APPLET(devfsd, _BB_DIR_SBIN, _BB_SUID_NEVER)) |
120 | USE_DF(APPLET(df, _BB_DIR_BIN, _BB_SUID_NEVER)) | 121 | USE_DF(APPLET(df, _BB_DIR_BIN, _BB_SUID_NEVER)) |
121 | USE_APP_DHCPRELAY(APPLET(dhcprelay, _BB_DIR_USR_SBIN, _BB_SUID_NEVER)) | 122 | USE_APP_DHCPRELAY(APPLET(dhcprelay, _BB_DIR_USR_SBIN, _BB_SUID_NEVER)) |
@@ -188,6 +189,7 @@ USE_INETD(APPLET(inetd, _BB_DIR_USR_SBIN, _BB_SUID_NEVER)) | |||
188 | USE_INIT(APPLET(init, _BB_DIR_SBIN, _BB_SUID_NEVER)) | 189 | USE_INIT(APPLET(init, _BB_DIR_SBIN, _BB_SUID_NEVER)) |
189 | USE_INOTIFYD(APPLET(inotifyd, _BB_DIR_SBIN, _BB_SUID_NEVER)) | 190 | USE_INOTIFYD(APPLET(inotifyd, _BB_DIR_SBIN, _BB_SUID_NEVER)) |
190 | USE_INSMOD(APPLET(insmod, _BB_DIR_SBIN, _BB_SUID_NEVER)) | 191 | USE_INSMOD(APPLET(insmod, _BB_DIR_SBIN, _BB_SUID_NEVER)) |
192 | USE_MODPROBE_SMALL(APPLET_ODDNAME(insmod, modprobe, _BB_DIR_SBIN, _BB_SUID_NEVER, modprobe)) | ||
191 | USE_INSTALL(APPLET(install, _BB_DIR_USR_BIN, _BB_SUID_NEVER)) | 193 | USE_INSTALL(APPLET(install, _BB_DIR_USR_BIN, _BB_SUID_NEVER)) |
192 | #if ENABLE_FEATURE_IP_ADDRESS \ | 194 | #if ENABLE_FEATURE_IP_ADDRESS \ |
193 | || ENABLE_FEATURE_IP_ROUTE \ | 195 | || ENABLE_FEATURE_IP_ROUTE \ |
@@ -231,6 +233,7 @@ USE_LPR(APPLET_ODDNAME(lpr, lpqr, _BB_DIR_USR_BIN, _BB_SUID_NEVER, lpr)) | |||
231 | USE_LS(APPLET_NOEXEC(ls, ls, _BB_DIR_BIN, _BB_SUID_NEVER, ls)) | 233 | USE_LS(APPLET_NOEXEC(ls, ls, _BB_DIR_BIN, _BB_SUID_NEVER, ls)) |
232 | USE_LSATTR(APPLET(lsattr, _BB_DIR_BIN, _BB_SUID_NEVER)) | 234 | USE_LSATTR(APPLET(lsattr, _BB_DIR_BIN, _BB_SUID_NEVER)) |
233 | USE_LSMOD(APPLET(lsmod, _BB_DIR_SBIN, _BB_SUID_NEVER)) | 235 | USE_LSMOD(APPLET(lsmod, _BB_DIR_SBIN, _BB_SUID_NEVER)) |
236 | USE_MODPROBE_SMALL(APPLET_ODDNAME(lsmod, modprobe, _BB_DIR_SBIN, _BB_SUID_NEVER, modprobe)) | ||
234 | USE_UNLZMA(APPLET_ODDNAME(lzmacat, unlzma, _BB_DIR_USR_BIN, _BB_SUID_NEVER, lzmacat)) | 237 | USE_UNLZMA(APPLET_ODDNAME(lzmacat, unlzma, _BB_DIR_USR_BIN, _BB_SUID_NEVER, lzmacat)) |
235 | USE_MAKEDEVS(APPLET(makedevs, _BB_DIR_SBIN, _BB_SUID_NEVER)) | 238 | USE_MAKEDEVS(APPLET(makedevs, _BB_DIR_SBIN, _BB_SUID_NEVER)) |
236 | USE_MAN(APPLET(man, _BB_DIR_SBIN, _BB_SUID_NEVER)) | 239 | USE_MAN(APPLET(man, _BB_DIR_SBIN, _BB_SUID_NEVER)) |
@@ -249,6 +252,7 @@ USE_MKNOD(APPLET(mknod, _BB_DIR_BIN, _BB_SUID_NEVER)) | |||
249 | USE_MKSWAP(APPLET(mkswap, _BB_DIR_SBIN, _BB_SUID_NEVER)) | 252 | USE_MKSWAP(APPLET(mkswap, _BB_DIR_SBIN, _BB_SUID_NEVER)) |
250 | USE_MKTEMP(APPLET(mktemp, _BB_DIR_BIN, _BB_SUID_NEVER)) | 253 | USE_MKTEMP(APPLET(mktemp, _BB_DIR_BIN, _BB_SUID_NEVER)) |
251 | USE_MODPROBE(APPLET(modprobe, _BB_DIR_SBIN, _BB_SUID_NEVER)) | 254 | USE_MODPROBE(APPLET(modprobe, _BB_DIR_SBIN, _BB_SUID_NEVER)) |
255 | USE_MODPROBE_SMALL(APPLET(modprobe, _BB_DIR_SBIN, _BB_SUID_NEVER)) | ||
252 | USE_MORE(APPLET(more, _BB_DIR_BIN, _BB_SUID_NEVER)) | 256 | USE_MORE(APPLET(more, _BB_DIR_BIN, _BB_SUID_NEVER)) |
253 | USE_MOUNT(APPLET(mount, _BB_DIR_BIN, USE_DESKTOP(_BB_SUID_MAYBE) SKIP_DESKTOP(_BB_SUID_NEVER))) | 257 | USE_MOUNT(APPLET(mount, _BB_DIR_BIN, USE_DESKTOP(_BB_SUID_MAYBE) SKIP_DESKTOP(_BB_SUID_NEVER))) |
254 | USE_MOUNTPOINT(APPLET(mountpoint, _BB_DIR_BIN, _BB_SUID_NEVER)) | 258 | USE_MOUNTPOINT(APPLET(mountpoint, _BB_DIR_BIN, _BB_SUID_NEVER)) |
@@ -293,6 +297,7 @@ USE_RESTORECON(APPLET_ODDNAME(restorecon, setfiles, _BB_DIR_SBIN, _BB_SUID_NEVER | |||
293 | USE_RM(APPLET_NOFORK(rm, rm, _BB_DIR_BIN, _BB_SUID_NEVER, rm)) | 297 | USE_RM(APPLET_NOFORK(rm, rm, _BB_DIR_BIN, _BB_SUID_NEVER, rm)) |
294 | USE_RMDIR(APPLET_NOFORK(rmdir, rmdir, _BB_DIR_BIN, _BB_SUID_NEVER, rmdir)) | 298 | USE_RMDIR(APPLET_NOFORK(rmdir, rmdir, _BB_DIR_BIN, _BB_SUID_NEVER, rmdir)) |
295 | USE_RMMOD(APPLET(rmmod, _BB_DIR_SBIN, _BB_SUID_NEVER)) | 299 | USE_RMMOD(APPLET(rmmod, _BB_DIR_SBIN, _BB_SUID_NEVER)) |
300 | USE_MODPROBE_SMALL(APPLET_ODDNAME(rmmod, modprobe, _BB_DIR_SBIN, _BB_SUID_NEVER, modprobe)) | ||
296 | USE_ROUTE(APPLET(route, _BB_DIR_SBIN, _BB_SUID_NEVER)) | 301 | USE_ROUTE(APPLET(route, _BB_DIR_SBIN, _BB_SUID_NEVER)) |
297 | USE_RPM(APPLET(rpm, _BB_DIR_BIN, _BB_SUID_NEVER)) | 302 | USE_RPM(APPLET(rpm, _BB_DIR_BIN, _BB_SUID_NEVER)) |
298 | USE_RPM2CPIO(APPLET(rpm2cpio, _BB_DIR_USR_BIN, _BB_SUID_NEVER)) | 303 | USE_RPM2CPIO(APPLET(rpm2cpio, _BB_DIR_USR_BIN, _BB_SUID_NEVER)) |
diff --git a/libbb/recursive_action.c b/libbb/recursive_action.c index 9b6951f43..66bf6aa62 100644 --- a/libbb/recursive_action.c +++ b/libbb/recursive_action.c | |||
@@ -111,16 +111,23 @@ int FAST_FUNC recursive_action(const char *fileName, | |||
111 | } | 111 | } |
112 | status = TRUE; | 112 | status = TRUE; |
113 | while ((next = readdir(dir)) != NULL) { | 113 | while ((next = readdir(dir)) != NULL) { |
114 | /*int s;*/ | ||
114 | char *nextFile; | 115 | char *nextFile; |
115 | 116 | ||
116 | nextFile = concat_subpath_file(fileName, next->d_name); | 117 | nextFile = concat_subpath_file(fileName, next->d_name); |
117 | if (nextFile == NULL) | 118 | if (nextFile == NULL) |
118 | continue; | 119 | continue; |
119 | /* process every file (NB: ACTION_RECURSE is set in flags) */ | 120 | /* process every file (NB: ACTION_RECURSE is set in flags) */ |
120 | if (!recursive_action(nextFile, flags, fileAction, dirAction, | 121 | /*s =*/ recursive_action(nextFile, flags, fileAction, dirAction, |
121 | userData, depth + 1)) | 122 | userData, depth + 1); |
122 | status = FALSE; | ||
123 | free(nextFile); | 123 | free(nextFile); |
124 | //#define RECURSE_RESULT_ABORT 3 | ||
125 | // if (s == RECURSE_RESULT_ABORT) { | ||
126 | // closedir(dir); | ||
127 | // return s; | ||
128 | // } | ||
129 | // if (s == FALSE) | ||
130 | // status = FALSE; | ||
124 | } | 131 | } |
125 | closedir(dir); | 132 | closedir(dir); |
126 | 133 | ||
diff --git a/modutils/Config.in b/modutils/Config.in index 2b0bcdd6e..25841b8ff 100644 --- a/modutils/Config.in +++ b/modutils/Config.in | |||
@@ -5,14 +5,63 @@ | |||
5 | 5 | ||
6 | menu "Linux Module Utilities" | 6 | menu "Linux Module Utilities" |
7 | 7 | ||
8 | config MODPROBE_SMALL | ||
9 | bool "Simplified modutils" | ||
10 | default n | ||
11 | help | ||
12 | Simplified modutils. | ||
13 | |||
14 | With this option modprobe does not use or require | ||
15 | modules.dep or /etc/modules.conf files. | ||
16 | It scans module files in /lib/modules/`uname -r` and | ||
17 | determines dependencies and module alias names on the fly. | ||
18 | This may make module loading slower, most notably | ||
19 | when one needs to load module by alias (this requires | ||
20 | scanning through module _bodies_). | ||
21 | |||
22 | Additional module parameters can be stored in | ||
23 | /etc/modules/$module_name files. | ||
24 | |||
25 | Apart from modprobe, other utilities are also provided: | ||
26 | - insmod is an alias to modprobe | ||
27 | - rmmod is an alias to modprobe -r | ||
28 | - depmod is provided but does nothing | ||
29 | |||
30 | As of 2008-07, this code is experimental. It it 15kb smaller | ||
31 | than "non-small" modutils. | ||
32 | |||
33 | config FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE | ||
34 | bool "module options on cmdline" | ||
35 | default n | ||
36 | depends on MODPROBE_SMALL | ||
37 | help | ||
38 | Allow insmod and modprobe take module options from command line. | ||
39 | N.B. Very bloaty. | ||
40 | |||
41 | config FEATURE_MODPROBE_SMALL_CHECK_ALREADY_LOADED | ||
42 | bool "Skip loading of already loaded modules" | ||
43 | default n | ||
44 | depends on MODPROBE_SMALL | ||
45 | help | ||
46 | Check if the module is already loaded. | ||
47 | N.B. It's racy. | ||
48 | |||
49 | config FEATURE_MODPROBE_SMALL_ZIPPED | ||
50 | bool "Handle gzipped or bzipped modules" | ||
51 | default n | ||
52 | depends on MODPROBE_SMALL | ||
53 | help | ||
54 | Handle compressed modules. Bloaty. Sloooow. | ||
55 | |||
8 | config DEPMOD | 56 | config DEPMOD |
9 | bool "depmod" | 57 | bool "depmod" |
10 | default n | 58 | default n |
59 | depends on !MODPROBE_SMALL | ||
11 | help | 60 | help |
12 | depmod generates modules.dep (FIXME: elaborate) | 61 | depmod generates modules.dep (FIXME: elaborate) |
13 | 62 | ||
14 | config FEATURE_DEPMOD_PRUNE_FANCY | 63 | config FEATURE_DEPMOD_PRUNE_FANCY |
15 | bool "fancy dependency pruning" | 64 | bool "Fancy dependency pruning" |
16 | default n | 65 | default n |
17 | depends on DEPMOD | 66 | depends on DEPMOD |
18 | help | 67 | help |
@@ -26,7 +75,7 @@ config FEATURE_DEPMOD_PRUNE_FANCY | |||
26 | If unsure, say N. | 75 | If unsure, say N. |
27 | 76 | ||
28 | config FEATURE_DEPMOD_ALIAS | 77 | config FEATURE_DEPMOD_ALIAS |
29 | bool "alias support" | 78 | bool "Alias support" |
30 | default n | 79 | default n |
31 | depends on DEPMOD | 80 | depends on DEPMOD |
32 | help | 81 | help |
@@ -38,6 +87,7 @@ config FEATURE_DEPMOD_ALIAS | |||
38 | config INSMOD | 87 | config INSMOD |
39 | bool "insmod" | 88 | bool "insmod" |
40 | default n | 89 | default n |
90 | depends on !MODPROBE_SMALL | ||
41 | help | 91 | help |
42 | insmod is used to load specified modules in the running kernel. | 92 | insmod is used to load specified modules in the running kernel. |
43 | 93 | ||
@@ -93,12 +143,14 @@ config FEATURE_INSMOD_LOAD_MAP_FULL | |||
93 | config RMMOD | 143 | config RMMOD |
94 | bool "rmmod" | 144 | bool "rmmod" |
95 | default n | 145 | default n |
146 | depends on !MODPROBE_SMALL | ||
96 | help | 147 | help |
97 | rmmod is used to unload specified modules from the kernel. | 148 | rmmod is used to unload specified modules from the kernel. |
98 | 149 | ||
99 | config LSMOD | 150 | config LSMOD |
100 | bool "lsmod" | 151 | bool "lsmod" |
101 | default n | 152 | default n |
153 | depends on !MODPROBE_SMALL | ||
102 | help | 154 | help |
103 | lsmod is used to display a list of loaded modules. | 155 | lsmod is used to display a list of loaded modules. |
104 | 156 | ||
@@ -113,6 +165,7 @@ config FEATURE_LSMOD_PRETTY_2_6_OUTPUT | |||
113 | config MODPROBE | 165 | config MODPROBE |
114 | bool "modprobe" | 166 | bool "modprobe" |
115 | default n | 167 | default n |
168 | depends on !MODPROBE_SMALL | ||
116 | help | 169 | help |
117 | Handle the loading of modules, and their dependencies on a high | 170 | Handle the loading of modules, and their dependencies on a high |
118 | level. | 171 | level. |
@@ -196,7 +249,7 @@ config DEFAULT_MODULES_DIR | |||
196 | # Simulate indentation | 249 | # Simulate indentation |
197 | string "Default directory containing modules" | 250 | string "Default directory containing modules" |
198 | default "/lib/modules" | 251 | default "/lib/modules" |
199 | depends on INSMOD || RMMOD || MODPROBE || DEPMOD | 252 | depends on INSMOD || RMMOD || MODPROBE || MODPROBE_SMALL || DEPMOD |
200 | help | 253 | help |
201 | Directory that contains kernel modules. | 254 | Directory that contains kernel modules. |
202 | Defaults to "/lib/modules" | 255 | Defaults to "/lib/modules" |
@@ -205,7 +258,7 @@ config DEFAULT_DEPMOD_FILE | |||
205 | # Simulate indentation | 258 | # Simulate indentation |
206 | string "Default name of modules.dep" | 259 | string "Default name of modules.dep" |
207 | default "modules.dep" | 260 | default "modules.dep" |
208 | depends on INSMOD || RMMOD || MODPROBE || DEPMOD | 261 | depends on INSMOD || RMMOD || MODPROBE || MODPROBE_SMALL || DEPMOD |
209 | help | 262 | help |
210 | Filename that contains kernel modules dependencies. | 263 | Filename that contains kernel modules dependencies. |
211 | Defaults to "modules.dep" | 264 | Defaults to "modules.dep" |
diff --git a/modutils/Kbuild b/modutils/Kbuild index 40ea0efbe..828070114 100644 --- a/modutils/Kbuild +++ b/modutils/Kbuild | |||
@@ -5,8 +5,9 @@ | |||
5 | # Licensed under the GPL v2, see the file LICENSE in this tarball. | 5 | # Licensed under the GPL v2, see the file LICENSE in this tarball. |
6 | 6 | ||
7 | lib-y:= | 7 | lib-y:= |
8 | lib-$(CONFIG_DEPMOD) += depmod.o | 8 | lib-$(CONFIG_DEPMOD) += depmod.o |
9 | lib-$(CONFIG_INSMOD) += insmod.o | 9 | lib-$(CONFIG_INSMOD) += insmod.o |
10 | lib-$(CONFIG_LSMOD) += lsmod.o | 10 | lib-$(CONFIG_LSMOD) += lsmod.o |
11 | lib-$(CONFIG_MODPROBE) += modprobe.o | 11 | lib-$(CONFIG_MODPROBE) += modprobe.o |
12 | lib-$(CONFIG_RMMOD) += rmmod.o | 12 | lib-$(CONFIG_MODPROBE_SMALL) += modprobe-small.o |
13 | lib-$(CONFIG_RMMOD) += rmmod.o | ||
diff --git a/modutils/modprobe-small.c b/modutils/modprobe-small.c new file mode 100644 index 000000000..bbcd9895e --- /dev/null +++ b/modutils/modprobe-small.c | |||
@@ -0,0 +1,648 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * simplified modprobe | ||
4 | * | ||
5 | * Copyright (c) 2008 Vladimir Dronnikov | ||
6 | * Copyright (c) 2008 Bernhard Fischer (initial depmod code) | ||
7 | * | ||
8 | * Licensed under GPLv2, see file LICENSE in this tarball for details. | ||
9 | */ | ||
10 | |||
11 | #include "libbb.h" | ||
12 | #include "unarchive.h" | ||
13 | |||
14 | #include <sys/utsname.h> /* uname() */ | ||
15 | #include <fnmatch.h> | ||
16 | |||
17 | /* libbb candidate */ | ||
18 | static void *xmalloc_read(int fd, size_t *sizep) | ||
19 | { | ||
20 | char *buf; | ||
21 | size_t size, rd_size, total; | ||
22 | off_t to_read; | ||
23 | struct stat st; | ||
24 | |||
25 | to_read = sizep ? *sizep : INT_MAX; /* max to read */ | ||
26 | |||
27 | /* Estimate file size */ | ||
28 | st.st_size = 0; /* in case fstat fails, assume 0 */ | ||
29 | fstat(fd, &st); | ||
30 | /* /proc/N/stat files report st_size 0 */ | ||
31 | /* In order to make such files readable, we add small const */ | ||
32 | size = (st.st_size | 0x3ff) + 1; | ||
33 | |||
34 | total = 0; | ||
35 | buf = NULL; | ||
36 | while (1) { | ||
37 | if (to_read < size) | ||
38 | size = to_read; | ||
39 | buf = xrealloc(buf, total + size + 1); | ||
40 | rd_size = full_read(fd, buf + total, size); | ||
41 | if ((ssize_t)rd_size < 0) { /* error */ | ||
42 | free(buf); | ||
43 | return NULL; | ||
44 | } | ||
45 | total += rd_size; | ||
46 | if (rd_size < size) /* EOF */ | ||
47 | break; | ||
48 | to_read -= rd_size; | ||
49 | if (to_read <= 0) | ||
50 | break; | ||
51 | /* grow by 1/8, but in [1k..64k] bounds */ | ||
52 | size = ((total / 8) | 0x3ff) + 1; | ||
53 | if (size > 64*1024) | ||
54 | size = 64*1024; | ||
55 | } | ||
56 | xrealloc(buf, total + 1); | ||
57 | buf[total] = '\0'; | ||
58 | |||
59 | if (sizep) | ||
60 | *sizep = total; | ||
61 | return buf; | ||
62 | } | ||
63 | |||
64 | |||
65 | //#define dbg1_error_msg(...) ((void)0) | ||
66 | //#define dbg2_error_msg(...) ((void)0) | ||
67 | #define dbg1_error_msg(...) bb_error_msg(__VA_ARGS__) | ||
68 | #define dbg2_error_msg(...) bb_error_msg(__VA_ARGS__) | ||
69 | |||
70 | extern int init_module(void *module, unsigned long len, const char *options); | ||
71 | extern int delete_module(const char *module, unsigned flags); | ||
72 | extern int query_module(const char *name, int which, void *buf, size_t bufsize, size_t *ret); | ||
73 | |||
74 | enum { | ||
75 | OPT_q = (1 << 0), /* be quiet */ | ||
76 | OPT_r = (1 << 1), /* module removal instead of loading */ | ||
77 | }; | ||
78 | |||
79 | typedef struct module_info { | ||
80 | char *pathname; | ||
81 | char *desc; | ||
82 | } module_info; | ||
83 | |||
84 | /* | ||
85 | * GLOBALS | ||
86 | */ | ||
87 | struct globals { | ||
88 | module_info *modinfo; | ||
89 | char *module_load_options; | ||
90 | int module_count; | ||
91 | int module_found_idx; | ||
92 | int stringbuf_idx; | ||
93 | char stringbuf[32 * 1024]; /* some modules have lots of stuff */ | ||
94 | /* for example, drivers/media/video/saa7134/saa7134.ko */ | ||
95 | }; | ||
96 | #define G (*ptr_to_globals) | ||
97 | #define modinfo (G.modinfo ) | ||
98 | #define module_count (G.module_count ) | ||
99 | #define module_found_idx (G.module_found_idx ) | ||
100 | #define module_load_options (G.module_load_options) | ||
101 | #define stringbuf_idx (G.stringbuf_idx ) | ||
102 | #define stringbuf (G.stringbuf ) | ||
103 | #define INIT_G() do { \ | ||
104 | SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ | ||
105 | } while (0) | ||
106 | |||
107 | |||
108 | static void appendc(char c) | ||
109 | { | ||
110 | if (stringbuf_idx < sizeof(stringbuf)) | ||
111 | stringbuf[stringbuf_idx++] = c; | ||
112 | } | ||
113 | |||
114 | static void append(const char *s) | ||
115 | { | ||
116 | size_t len = strlen(s); | ||
117 | if (stringbuf_idx + len < sizeof(stringbuf)) { | ||
118 | memcpy(stringbuf + stringbuf_idx, s, len); | ||
119 | stringbuf_idx += len; | ||
120 | } | ||
121 | } | ||
122 | |||
123 | static void reset_stringbuf(void) | ||
124 | { | ||
125 | stringbuf_idx = 0; | ||
126 | } | ||
127 | |||
128 | static char* copy_stringbuf(void) | ||
129 | { | ||
130 | char *copy = xmalloc(stringbuf_idx); | ||
131 | return memcpy(copy, stringbuf, stringbuf_idx); | ||
132 | } | ||
133 | |||
134 | static char* find_keyword(char *ptr, size_t len, const char *word) | ||
135 | { | ||
136 | int wlen; | ||
137 | |||
138 | if (!ptr) /* happens if read_module cannot read it */ | ||
139 | return NULL; | ||
140 | |||
141 | wlen = strlen(word); | ||
142 | len -= wlen - 1; | ||
143 | while ((ssize_t)len > 0) { | ||
144 | char *old = ptr; | ||
145 | /* search for the first char in word */ | ||
146 | ptr = memchr(ptr, *word, len); | ||
147 | if (ptr == NULL) /* no occurance left, done */ | ||
148 | break; | ||
149 | if (strncmp(ptr, word, wlen) == 0) | ||
150 | return ptr + wlen; /* found, return ptr past it */ | ||
151 | ++ptr; | ||
152 | len -= (ptr - old); | ||
153 | } | ||
154 | return NULL; | ||
155 | } | ||
156 | |||
157 | static void replace(char *s, char what, char with) | ||
158 | { | ||
159 | while (*s) { | ||
160 | if (what == *s) | ||
161 | *s = with; | ||
162 | ++s; | ||
163 | } | ||
164 | } | ||
165 | |||
166 | #if ENABLE_FEATURE_MODPROBE_SMALL_ZIPPED | ||
167 | static char *xmalloc_open_zipped_read_close(const char *fname, size_t *sizep) | ||
168 | { | ||
169 | size_t len; | ||
170 | char *image; | ||
171 | char *suffix; | ||
172 | |||
173 | int fd = open_or_warn(fname, O_RDONLY); | ||
174 | if (fd < 0) | ||
175 | return NULL; | ||
176 | |||
177 | suffix = strrchr(fname, '.'); | ||
178 | if (suffix) { | ||
179 | if (strcmp(suffix, ".gz") == 0) | ||
180 | fd = open_transformer(fd, unpack_gz_stream, "gunzip"); | ||
181 | else if (strcmp(suffix, ".bz2") == 0) | ||
182 | fd = open_transformer(fd, unpack_bz2_stream, "bunzip2"); | ||
183 | } | ||
184 | |||
185 | len = (sizep) ? *sizep : 64 * 1024 * 1024; | ||
186 | image = xmalloc_read(fd, &len); | ||
187 | if (!image) | ||
188 | bb_perror_msg("read error from '%s'", fname); | ||
189 | close(fd); | ||
190 | |||
191 | if (sizep) | ||
192 | *sizep = len; | ||
193 | return image; | ||
194 | } | ||
195 | # define read_module xmalloc_open_zipped_read_close | ||
196 | #else | ||
197 | # define read_module xmalloc_open_read_close | ||
198 | #endif | ||
199 | |||
200 | /* We use error numbers in a loose translation... */ | ||
201 | static const char *moderror(int err) | ||
202 | { | ||
203 | switch (err) { | ||
204 | case ENOEXEC: | ||
205 | return "invalid module format"; | ||
206 | case ENOENT: | ||
207 | return "unknown symbol in module or invalid parameter"; | ||
208 | case ESRCH: | ||
209 | return "module has wrong symbol version"; | ||
210 | case EINVAL: /* "invalid parameter" */ | ||
211 | return "unknown symbol in module or invalid parameter" | ||
212 | + sizeof("unknown symbol in module or"); | ||
213 | default: | ||
214 | return strerror(err); | ||
215 | } | ||
216 | } | ||
217 | |||
218 | static int load_module(const char *fname, const char *options) | ||
219 | { | ||
220 | #if 1 | ||
221 | int r; | ||
222 | size_t len = MAXINT(ssize_t); | ||
223 | char *module_image; | ||
224 | dbg1_error_msg("load_module('%s','%s')", fname, options); | ||
225 | |||
226 | module_image = read_module(fname, &len); | ||
227 | r = (!module_image || init_module(module_image, len, options ? options : "") != 0); | ||
228 | free(module_image); | ||
229 | dbg1_error_msg("load_module:%d", r); | ||
230 | return r; /* 0 = success */ | ||
231 | #else | ||
232 | /* For testing */ | ||
233 | dbg1_error_msg("load_module('%s','%s')", fname, options); | ||
234 | return 1; | ||
235 | #endif | ||
236 | } | ||
237 | |||
238 | static char* parse_module(const char *pathname, const char *name) | ||
239 | { | ||
240 | char *module_image; | ||
241 | char *ptr; | ||
242 | size_t len; | ||
243 | size_t pos; | ||
244 | dbg1_error_msg("parse_module('%s','%s')", pathname, name); | ||
245 | |||
246 | /* Read (possibly compressed) module */ | ||
247 | len = 64 * 1024 * 1024; /* 64 Mb at most */ | ||
248 | module_image = read_module(pathname, &len); | ||
249 | |||
250 | reset_stringbuf(); | ||
251 | |||
252 | /* First desc line's format is | ||
253 | * "modname alias1 symbol:sym1 alias2 symbol:sym2 " (note trailing ' ') | ||
254 | */ | ||
255 | append(name); | ||
256 | appendc(' '); | ||
257 | /* Aliases */ | ||
258 | pos = 0; | ||
259 | while (1) { | ||
260 | ptr = find_keyword(module_image + pos, len - pos, "alias="); | ||
261 | if (!ptr) { | ||
262 | ptr = find_keyword(module_image + pos, len - pos, "__ksymtab_"); | ||
263 | if (!ptr) | ||
264 | break; | ||
265 | /* DOCME: __ksymtab_gpl and __ksymtab_strings occur | ||
266 | * in many modules. What do they mean? */ | ||
267 | if (strcmp(ptr, "gpl") != 0 && strcmp(ptr, "strings") != 0) { | ||
268 | dbg2_error_msg("alias: 'symbol:%s'", ptr); | ||
269 | append("symbol:"); | ||
270 | } | ||
271 | } else { | ||
272 | dbg2_error_msg("alias: '%s'", ptr); | ||
273 | } | ||
274 | append(ptr); | ||
275 | appendc(' '); | ||
276 | pos = (ptr - module_image); | ||
277 | } | ||
278 | appendc('\0'); | ||
279 | |||
280 | /* Second line: "dependency1 depandency2 " (note trailing ' ') */ | ||
281 | ptr = find_keyword(module_image, len, "depends="); | ||
282 | if (ptr && *ptr) { | ||
283 | replace(ptr, ',', ' '); | ||
284 | replace(ptr, '-', '_'); | ||
285 | dbg2_error_msg("dep:'%s'", ptr); | ||
286 | append(ptr); | ||
287 | } | ||
288 | appendc(' '); appendc('\0'); | ||
289 | |||
290 | free(module_image); | ||
291 | return copy_stringbuf(); | ||
292 | } | ||
293 | |||
294 | static char* pathname_2_modname(const char *pathname) | ||
295 | { | ||
296 | const char *fname = bb_get_last_path_component_nostrip(pathname); | ||
297 | const char *suffix = strrstr(fname, ".ko"); | ||
298 | char *name = xstrndup(fname, suffix - fname); | ||
299 | replace(name, '-', '_'); | ||
300 | return name; | ||
301 | } | ||
302 | |||
303 | static FAST_FUNC int fileAction(const char *pathname, | ||
304 | struct stat *sb ATTRIBUTE_UNUSED, | ||
305 | void *data, | ||
306 | int depth ATTRIBUTE_UNUSED) | ||
307 | { | ||
308 | int cur; | ||
309 | char *name; | ||
310 | const char *fname; | ||
311 | |||
312 | pathname += 2; /* skip "./" */ | ||
313 | fname = bb_get_last_path_component_nostrip(pathname); | ||
314 | if (!strrstr(fname, ".ko")) { | ||
315 | dbg1_error_msg("'%s' is not a module", pathname); | ||
316 | return TRUE; /* not a module, continue search */ | ||
317 | } | ||
318 | |||
319 | cur = module_count++; | ||
320 | if (!(cur & 0xfff)) { | ||
321 | modinfo = xrealloc(modinfo, sizeof(modinfo[0]) * (cur + 0x1001)); | ||
322 | } | ||
323 | modinfo[cur].pathname = xstrdup(pathname); | ||
324 | modinfo[cur].desc = NULL; | ||
325 | modinfo[cur+1].pathname = NULL; | ||
326 | modinfo[cur+1].desc = NULL; | ||
327 | |||
328 | name = pathname_2_modname(fname); | ||
329 | if (strcmp(name, data) != 0) { | ||
330 | free(name); | ||
331 | dbg1_error_msg("'%s' module name doesn't match", pathname); | ||
332 | return TRUE; /* module name doesn't match, continue search */ | ||
333 | } | ||
334 | |||
335 | dbg1_error_msg("'%s' module name matches", pathname); | ||
336 | module_found_idx = cur; | ||
337 | modinfo[cur].desc = parse_module(pathname, name); | ||
338 | |||
339 | if (!(option_mask32 & OPT_r)) { | ||
340 | if (load_module(pathname, module_load_options) == 0) { | ||
341 | /* Load was successful, there is nothing else to do. | ||
342 | * This can happen ONLY for "top-level" module load, | ||
343 | * not a dep, because deps dont do dirscan. */ | ||
344 | exit(EXIT_SUCCESS); | ||
345 | /*free(name);return RECURSE_RESULT_ABORT;*/ | ||
346 | } | ||
347 | } | ||
348 | |||
349 | free(name); | ||
350 | return TRUE; | ||
351 | } | ||
352 | |||
353 | static module_info* find_alias(const char *alias) | ||
354 | { | ||
355 | int i; | ||
356 | dbg1_error_msg("find_alias('%s')", alias); | ||
357 | |||
358 | /* First try to find by name (cheaper) */ | ||
359 | i = 0; | ||
360 | while (modinfo[i].pathname) { | ||
361 | char *name = pathname_2_modname(modinfo[i].pathname); | ||
362 | if (strcmp(name, alias) == 0) { | ||
363 | dbg1_error_msg("found '%s' in module '%s'", | ||
364 | alias, modinfo[i].pathname); | ||
365 | if (!modinfo[i].desc) | ||
366 | modinfo[i].desc = parse_module(modinfo[i].pathname, name); | ||
367 | free(name); | ||
368 | return &modinfo[i]; | ||
369 | } | ||
370 | free(name); | ||
371 | i++; | ||
372 | } | ||
373 | |||
374 | /* Scan all module bodies, extract modinfo (it contains aliases) */ | ||
375 | i = 0; | ||
376 | while (modinfo[i].pathname) { | ||
377 | char *desc, *s; | ||
378 | if (!modinfo[i].desc) { | ||
379 | char *name = pathname_2_modname(modinfo[i].pathname); | ||
380 | modinfo[i].desc = parse_module(modinfo[i].pathname, name); | ||
381 | free(name); | ||
382 | } | ||
383 | /* "modname alias1 symbol:sym1 alias2 symbol:sym2 " */ | ||
384 | desc = xstrdup(modinfo[i].desc); | ||
385 | /* Does matching substring exist? */ | ||
386 | replace(desc, ' ', '\0'); | ||
387 | for (s = desc; *s; s += strlen(s) + 1) { | ||
388 | if (strcmp(s, alias) == 0) { | ||
389 | free(desc); | ||
390 | dbg1_error_msg("found alias '%s' in module '%s'", | ||
391 | alias, modinfo[i].pathname); | ||
392 | return &modinfo[i]; | ||
393 | } | ||
394 | } | ||
395 | free(desc); | ||
396 | i++; | ||
397 | } | ||
398 | dbg1_error_msg("find_alias '%s' returns NULL", alias); | ||
399 | return NULL; | ||
400 | } | ||
401 | |||
402 | #if ENABLE_FEATURE_MODPROBE_SMALL_CHECK_ALREADY_LOADED | ||
403 | static int already_loaded(const char *name) | ||
404 | { | ||
405 | int ret = 0; | ||
406 | int len = strlen(name); | ||
407 | char *line; | ||
408 | FILE* modules; | ||
409 | |||
410 | modules = xfopen("/proc/modules", "r"); | ||
411 | while ((line = xmalloc_fgets(modules)) != NULL) { | ||
412 | if (strncmp(line, name, len) == 0 && line[len] == ' ') { | ||
413 | free(line); | ||
414 | ret = 1; | ||
415 | break; | ||
416 | } | ||
417 | free(line); | ||
418 | } | ||
419 | fclose(modules); | ||
420 | return ret; | ||
421 | } | ||
422 | #else | ||
423 | #define already_loaded(name) is_rmmod | ||
424 | #endif | ||
425 | |||
426 | /* | ||
427 | Given modules definition and module name (or alias, or symbol) | ||
428 | load/remove the module respecting dependencies | ||
429 | */ | ||
430 | #if !ENABLE_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE | ||
431 | #define process_module(a,b) process_module(a) | ||
432 | #define cmdline_options "" | ||
433 | #endif | ||
434 | static void process_module(char *name, const char *cmdline_options) | ||
435 | { | ||
436 | char *s, *deps, *options; | ||
437 | module_info *info; | ||
438 | int is_rmmod = (option_mask32 & OPT_r) != 0; | ||
439 | dbg1_error_msg("process_module('%s','%s')", name, cmdline_options); | ||
440 | |||
441 | replace(name, '-', '_'); | ||
442 | |||
443 | dbg1_error_msg("already_loaded:%d is_rmmod:%d", already_loaded(name), is_rmmod); | ||
444 | if (already_loaded(name) != is_rmmod) { | ||
445 | dbg1_error_msg("nothing to do for '%s'", name); | ||
446 | return; | ||
447 | } | ||
448 | |||
449 | options = NULL; | ||
450 | if (!is_rmmod) { | ||
451 | char *opt_filename = xasprintf("/etc/modules/%s", name); | ||
452 | options = xmalloc_open_read_close(opt_filename, NULL); | ||
453 | if (options) | ||
454 | replace(options, '\n', ' '); | ||
455 | #if ENABLE_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE | ||
456 | if (cmdline_options) { | ||
457 | /* NB: cmdline_options always have one leading ' ' | ||
458 | * (see main()), we remove it here */ | ||
459 | char *op = xasprintf(options ? "%s %s" : "%s %s" + 3, | ||
460 | cmdline_options + 1, options); | ||
461 | free(options); | ||
462 | options = op; | ||
463 | } | ||
464 | #endif | ||
465 | free(opt_filename); | ||
466 | module_load_options = options; | ||
467 | dbg1_error_msg("process_module('%s'): options:'%s'", name, options); | ||
468 | } | ||
469 | |||
470 | if (!module_count) { | ||
471 | /* Scan module directory. This is done only once. | ||
472 | * It will attempt module load, and will exit(EXIT_SUCCESS) | ||
473 | * on success. */ | ||
474 | module_found_idx = -1; | ||
475 | recursive_action(".", | ||
476 | ACTION_RECURSE, /* flags */ | ||
477 | fileAction, /* file action */ | ||
478 | NULL, /* dir action */ | ||
479 | name, /* user data */ | ||
480 | 0); /* depth */ | ||
481 | dbg1_error_msg("dirscan complete"); | ||
482 | /* Module was not found, or load failed, or is_rmmod */ | ||
483 | if (module_found_idx >= 0) { /* module was found */ | ||
484 | info = &modinfo[module_found_idx]; | ||
485 | } else { /* search for alias, not a plain module name */ | ||
486 | info = find_alias(name); | ||
487 | } | ||
488 | } else { | ||
489 | info = find_alias(name); | ||
490 | } | ||
491 | |||
492 | /* rmmod? unload it by name */ | ||
493 | if (is_rmmod) { | ||
494 | if (delete_module(name, O_NONBLOCK|O_EXCL) != 0 | ||
495 | && !(option_mask32 & OPT_q) | ||
496 | ) { | ||
497 | bb_perror_msg("remove '%s'", name); | ||
498 | goto ret; | ||
499 | } | ||
500 | /* N.B. we do not stop here - | ||
501 | * continue to unload modules on which the module depends: | ||
502 | * "-r --remove: option causes modprobe to remove a module. | ||
503 | * If the modules it depends on are also unused, modprobe | ||
504 | * will try to remove them, too." */ | ||
505 | } | ||
506 | |||
507 | if (!info) { /* both dirscan and find_alias found nothing */ | ||
508 | goto ret; | ||
509 | } | ||
510 | |||
511 | /* Second line of desc contains dependencies */ | ||
512 | deps = xstrdup(info->desc + strlen(info->desc) + 1); | ||
513 | |||
514 | /* Transform deps to string list */ | ||
515 | replace(deps, ' ', '\0'); | ||
516 | /* Iterate thru dependencies, trying to (un)load them */ | ||
517 | for (s = deps; *s; s += strlen(s) + 1) { | ||
518 | //if (strcmp(name, s) != 0) // N.B. do loops exist? | ||
519 | dbg1_error_msg("recurse on dep '%s'", s); | ||
520 | process_module(s, NULL); | ||
521 | dbg1_error_msg("recurse on dep '%s' done", s); | ||
522 | } | ||
523 | free(deps); | ||
524 | |||
525 | /* insmod -> load it */ | ||
526 | if (!is_rmmod) { | ||
527 | errno = 0; | ||
528 | if (load_module(info->pathname, options) != 0) { | ||
529 | if (EEXIST != errno) { | ||
530 | bb_error_msg("insert '%s' %s: %s", | ||
531 | info->pathname, options, | ||
532 | moderror(errno)); | ||
533 | } else { | ||
534 | dbg1_error_msg("insert '%s' %s: %s", | ||
535 | info->pathname, options, | ||
536 | moderror(errno)); | ||
537 | } | ||
538 | } | ||
539 | } | ||
540 | ret: | ||
541 | free(options); | ||
542 | //TODO: return load attempt result from process_module. | ||
543 | //If dep didn't load ok, continuing makes little sense. | ||
544 | } | ||
545 | #undef cmdline_options | ||
546 | |||
547 | |||
548 | /* For reference, module-init-tools-0.9.15-pre2 options: | ||
549 | |||
550 | # insmod | ||
551 | Usage: insmod filename [args] | ||
552 | |||
553 | # rmmod --help | ||
554 | Usage: rmmod [-fhswvV] modulename ... | ||
555 | -f (or --force) forces a module unload, and may crash your machine. | ||
556 | -s (or --syslog) says use syslog, not stderr | ||
557 | -v (or --verbose) enables more messages | ||
558 | -w (or --wait) begins a module removal even if it is used | ||
559 | and will stop new users from accessing the module (so it | ||
560 | should eventually fall to zero). | ||
561 | |||
562 | # modprobe | ||
563 | Usage: modprobe [--verbose|--version|--config|--remove] filename [options] | ||
564 | |||
565 | # depmod --help | ||
566 | depmod 0.9.15-pre2 -- part of module-init-tools | ||
567 | depmod -[aA] [-n -e -v -q -V -r -u] [-b basedirectory] [forced_version] | ||
568 | depmod [-n -e -v -q -r -u] [-F kernelsyms] module1.o module2.o ... | ||
569 | If no arguments (except options) are given, "depmod -a" is assumed | ||
570 | |||
571 | depmod will output a dependancy list suitable for the modprobe utility. | ||
572 | |||
573 | Options: | ||
574 | -a, --all Probe all modules | ||
575 | -n, --show Write the dependency file on stdout only | ||
576 | -b basedirectory | ||
577 | --basedir basedirectory Use an image of a module tree. | ||
578 | -F kernelsyms | ||
579 | --filesyms kernelsyms Use the file instead of the | ||
580 | current kernel symbols. | ||
581 | */ | ||
582 | |||
583 | int modprobe_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | ||
584 | int modprobe_main(int ATTRIBUTE_UNUSED argc, char **argv) | ||
585 | { | ||
586 | struct utsname uts; | ||
587 | char applet0 = applet_name[0]; | ||
588 | USE_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE(char *options;) | ||
589 | |||
590 | /* depmod is a stub */ | ||
591 | if ('d' == applet0) | ||
592 | return EXIT_SUCCESS; | ||
593 | |||
594 | /* are we lsmod? -> just dump /proc/modules */ | ||
595 | if ('l' == applet0) { | ||
596 | xprint_and_close_file(xfopen("/proc/modules", "r")); | ||
597 | return EXIT_SUCCESS; | ||
598 | } | ||
599 | |||
600 | INIT_G(); | ||
601 | |||
602 | /* insmod, modprobe, rmmod require at least one argument */ | ||
603 | opt_complementary = "-1"; | ||
604 | /* only -q (quiet) and -r (rmmod), | ||
605 | * the rest are accepted and ignored (compat) */ | ||
606 | getopt32(argv, "qrfsvw"); | ||
607 | argv += optind; | ||
608 | |||
609 | /* are we rmmod? -> simulate modprobe -r */ | ||
610 | if ('r' == applet0) { | ||
611 | option_mask32 |= OPT_r; | ||
612 | } | ||
613 | |||
614 | /* goto modules directory */ | ||
615 | xchdir(CONFIG_DEFAULT_MODULES_DIR); | ||
616 | uname(&uts); /* never fails */ | ||
617 | xchdir(uts.release); | ||
618 | |||
619 | #if ENABLE_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE | ||
620 | /* If not rmmod, parse possible module options given on command line. | ||
621 | * insmod/modprobe takes one module name, the rest are parameters. */ | ||
622 | options = NULL; | ||
623 | if ('r' != applet0) { | ||
624 | char **arg = argv; | ||
625 | while (*++arg) { | ||
626 | /* Enclose options in quotes */ | ||
627 | char *s = options; | ||
628 | options = xasprintf("%s \"%s\"", s ? s : "", *arg); | ||
629 | free(s); | ||
630 | *arg = NULL; | ||
631 | } | ||
632 | } | ||
633 | #else | ||
634 | if ('r' != applet0) | ||
635 | argv[1] = NULL; | ||
636 | #endif | ||
637 | |||
638 | /* Load/remove modules. | ||
639 | * Only rmmod loops here, insmod/modprobe has only argv[0] */ | ||
640 | do { | ||
641 | process_module(*argv++, options); | ||
642 | } while (*argv); | ||
643 | |||
644 | if (ENABLE_FEATURE_CLEAN_UP) { | ||
645 | USE_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE(free(options);) | ||
646 | } | ||
647 | return EXIT_SUCCESS; | ||
648 | } | ||