]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/modules-load/modules-load.c
01987f2e3c2a2f84df6da2d4e5faa64c10dbcec4
[thirdparty/systemd.git] / src / modules-load / modules-load.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2010 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <unistd.h>
23 #include <fcntl.h>
24 #include <errno.h>
25 #include <string.h>
26 #include <sys/stat.h>
27 #include <limits.h>
28 #include <dirent.h>
29 #include <getopt.h>
30 #include <libkmod.h>
31
32 #include "log.h"
33 #include "util.h"
34 #include "strv.h"
35 #include "conf-files.h"
36 #include "fileio.h"
37 #include "build.h"
38
39 static char **arg_proc_cmdline_modules = NULL;
40
41 static const char conf_file_dirs[] =
42 "/etc/modules-load.d\0"
43 "/run/modules-load.d\0"
44 "/usr/local/lib/modules-load.d\0"
45 "/usr/lib/modules-load.d\0"
46 #ifdef HAVE_SPLIT_USR
47 "/lib/modules-load.d\0"
48 #endif
49 ;
50
51 #pragma GCC diagnostic push
52 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
53 static void systemd_kmod_log(void *data, int priority, const char *file, int line,
54 const char *fn, const char *format, va_list args) {
55 log_metav(priority, file, line, fn, format, args);
56 }
57 #pragma GCC diagnostic pop
58
59 static int add_modules(const char *p) {
60 char **t;
61 _cleanup_strv_free_ char **k = NULL;
62
63 k = strv_split(p, ",");
64 if (!k)
65 return log_oom();
66
67 if (strv_extend_strv(&arg_proc_cmdline_modules, k) < 0)
68 return log_oom();
69
70 return 0;
71 }
72
73 static int parse_proc_cmdline(void) {
74 _cleanup_free_ char *line = NULL;
75 char *w, *state;
76 size_t l;
77 int r;
78
79 r = proc_cmdline(&line);
80 if (r < 0)
81 log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
82 if (r <= 0)
83 return 0;
84
85 FOREACH_WORD_QUOTED(w, l, line, state) {
86 _cleanup_free_ char *word;
87
88 word = strndup(w, l);
89 if (!word)
90 return log_oom();
91
92 if (startswith(word, "modules-load=")) {
93
94 r = add_modules(word + 13);
95 if (r < 0)
96 return r;
97
98 } else if (startswith(word, "rd.modules-load=")) {
99
100 if (in_initrd()) {
101 r = add_modules(word + 16);
102 if (r < 0)
103 return r;
104 }
105
106 }
107 }
108
109 return 0;
110 }
111
112 static int load_module(struct kmod_ctx *ctx, const char *m) {
113 const int probe_flags = KMOD_PROBE_APPLY_BLACKLIST;
114 struct kmod_list *itr, *modlist = NULL;
115 int r = 0;
116
117 log_debug("load: %s", m);
118
119 r = kmod_module_new_from_lookup(ctx, m, &modlist);
120 if (r < 0) {
121 log_error("Failed to lookup alias '%s': %s", m, strerror(-r));
122 return r;
123 }
124
125 if (!modlist) {
126 log_error("Failed to find module '%s'", m);
127 return -ENOENT;
128 }
129
130 kmod_list_foreach(itr, modlist) {
131 struct kmod_module *mod;
132 int state, err;
133
134 mod = kmod_module_get_module(itr);
135 state = kmod_module_get_initstate(mod);
136
137 switch (state) {
138 case KMOD_MODULE_BUILTIN:
139 log_info("Module '%s' is builtin", kmod_module_get_name(mod));
140 break;
141
142 case KMOD_MODULE_LIVE:
143 log_debug("Module '%s' is already loaded", kmod_module_get_name(mod));
144 break;
145
146 default:
147 err = kmod_module_probe_insert_module(mod, probe_flags,
148 NULL, NULL, NULL, NULL);
149
150 if (err == 0)
151 log_info("Inserted module '%s'", kmod_module_get_name(mod));
152 else if (err == KMOD_PROBE_APPLY_BLACKLIST)
153 log_info("Module '%s' is blacklisted", kmod_module_get_name(mod));
154 else {
155 log_error("Failed to insert '%s': %s", kmod_module_get_name(mod),
156 strerror(-err));
157 r = err;
158 }
159 }
160
161 kmod_module_unref(mod);
162 }
163
164 kmod_module_unref_list(modlist);
165
166 return r;
167 }
168
169 static int apply_file(struct kmod_ctx *ctx, const char *path, bool ignore_enoent) {
170 _cleanup_fclose_ FILE *f = NULL;
171 int r;
172
173 assert(ctx);
174 assert(path);
175
176 r = search_and_fopen_nulstr(path, "re", conf_file_dirs, &f);
177 if (r < 0) {
178 if (ignore_enoent && r == -ENOENT)
179 return 0;
180
181 log_error("Failed to open %s, ignoring: %s", path, strerror(-r));
182 return r;
183 }
184
185 log_debug("apply: %s", path);
186 for (;;) {
187 char line[LINE_MAX], *l;
188 int k;
189
190 if (!fgets(line, sizeof(line), f)) {
191 if (feof(f))
192 break;
193
194 log_error("Failed to read file '%s', ignoring: %m", path);
195 return -errno;
196 }
197
198 l = strstrip(line);
199 if (!*l)
200 continue;
201 if (strchr(COMMENTS "\n", *l))
202 continue;
203
204 k = load_module(ctx, l);
205 if (k < 0 && r == 0)
206 r = k;
207 }
208
209 return r;
210 }
211
212 static int help(void) {
213
214 printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
215 "Loads statically configured kernel modules.\n\n"
216 " -h --help Show this help\n"
217 " --version Show package version\n",
218 program_invocation_short_name);
219
220 return 0;
221 }
222
223 static int parse_argv(int argc, char *argv[]) {
224
225 enum {
226 ARG_VERSION = 0x100,
227 };
228
229 static const struct option options[] = {
230 { "help", no_argument, NULL, 'h' },
231 { "version", no_argument, NULL, ARG_VERSION },
232 {}
233 };
234
235 int c;
236
237 assert(argc >= 0);
238 assert(argv);
239
240 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
241
242 switch (c) {
243
244 case 'h':
245 return help();
246
247 case ARG_VERSION:
248 puts(PACKAGE_STRING);
249 puts(SYSTEMD_FEATURES);
250 return 0;
251
252 case '?':
253 return -EINVAL;
254
255 default:
256 assert_not_reached("Unhandled option");
257 }
258 }
259
260 return 1;
261 }
262
263 int main(int argc, char *argv[]) {
264 int r, k;
265 struct kmod_ctx *ctx;
266
267 r = parse_argv(argc, argv);
268 if (r <= 0)
269 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
270
271 log_set_target(LOG_TARGET_AUTO);
272 log_parse_environment();
273 log_open();
274
275 umask(0022);
276
277 if (parse_proc_cmdline() < 0)
278 return EXIT_FAILURE;
279
280 ctx = kmod_new(NULL, NULL);
281 if (!ctx) {
282 log_error("Failed to allocate memory for kmod.");
283 goto finish;
284 }
285
286 kmod_load_resources(ctx);
287 kmod_set_log_fn(ctx, systemd_kmod_log, NULL);
288
289 r = 0;
290
291 if (argc > optind) {
292 int i;
293
294 for (i = optind; i < argc; i++) {
295 k = apply_file(ctx, argv[i], false);
296 if (k < 0 && r == 0)
297 r = k;
298 }
299
300 } else {
301 _cleanup_free_ char **files = NULL;
302 char **fn, **i;
303
304 STRV_FOREACH(i, arg_proc_cmdline_modules) {
305 k = load_module(ctx, *i);
306 if (k < 0 && r == 0)
307 r = k;
308 }
309
310 r = conf_files_list_nulstr(&files, ".conf", NULL, conf_file_dirs);
311 if (r < 0) {
312 log_error("Failed to enumerate modules-load.d files: %s", strerror(-r));
313 goto finish;
314 }
315
316 STRV_FOREACH(fn, files) {
317 k = apply_file(ctx, *fn, true);
318 if (k < 0 && r == 0)
319 r = k;
320 }
321 }
322
323 finish:
324 kmod_unref(ctx);
325 strv_free(arg_proc_cmdline_modules);
326
327 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
328 }