]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/modules-load/modules-load.c
load-modules: properly return a failing error code if some module fails to load
[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 _cleanup_strv_free_ char **k = NULL;
61
62 k = strv_split(p, ",");
63 if (!k)
64 return log_oom();
65
66 if (strv_extend_strv(&arg_proc_cmdline_modules, k) < 0)
67 return log_oom();
68
69 return 0;
70 }
71
72 static int parse_proc_cmdline_word(const char *word) {
73 int r;
74
75 if (startswith(word, "modules-load=")) {
76 r = add_modules(word + 13);
77 if (r < 0)
78 return r;
79
80 } else if (startswith(word, "rd.modules-load=")) {
81 if (in_initrd()) {
82 r = add_modules(word + 16);
83 if (r < 0)
84 return r;
85 }
86 }
87
88 return 0;
89 }
90
91 static int load_module(struct kmod_ctx *ctx, const char *m) {
92 const int probe_flags = KMOD_PROBE_APPLY_BLACKLIST;
93 struct kmod_list *itr, *modlist = NULL;
94 int r = 0;
95
96 log_debug("load: %s", m);
97
98 r = kmod_module_new_from_lookup(ctx, m, &modlist);
99 if (r < 0) {
100 log_error("Failed to lookup alias '%s': %s", m, strerror(-r));
101 return r;
102 }
103
104 if (!modlist) {
105 log_error("Failed to find module '%s'", m);
106 return -ENOENT;
107 }
108
109 kmod_list_foreach(itr, modlist) {
110 struct kmod_module *mod;
111 int state, err;
112
113 mod = kmod_module_get_module(itr);
114 state = kmod_module_get_initstate(mod);
115
116 switch (state) {
117 case KMOD_MODULE_BUILTIN:
118 log_info("Module '%s' is builtin", kmod_module_get_name(mod));
119 break;
120
121 case KMOD_MODULE_LIVE:
122 log_debug("Module '%s' is already loaded", kmod_module_get_name(mod));
123 break;
124
125 default:
126 err = kmod_module_probe_insert_module(mod, probe_flags,
127 NULL, NULL, NULL, NULL);
128
129 if (err == 0)
130 log_info("Inserted module '%s'", kmod_module_get_name(mod));
131 else if (err == KMOD_PROBE_APPLY_BLACKLIST)
132 log_info("Module '%s' is blacklisted", kmod_module_get_name(mod));
133 else {
134 log_error("Failed to insert '%s': %s", kmod_module_get_name(mod),
135 strerror(-err));
136 r = err;
137 }
138 }
139
140 kmod_module_unref(mod);
141 }
142
143 kmod_module_unref_list(modlist);
144
145 return r;
146 }
147
148 static int apply_file(struct kmod_ctx *ctx, const char *path, bool ignore_enoent) {
149 _cleanup_fclose_ FILE *f = NULL;
150 int r;
151
152 assert(ctx);
153 assert(path);
154
155 r = search_and_fopen_nulstr(path, "re", conf_file_dirs, &f);
156 if (r < 0) {
157 if (ignore_enoent && r == -ENOENT)
158 return 0;
159
160 log_error("Failed to open %s, ignoring: %s", path, strerror(-r));
161 return r;
162 }
163
164 log_debug("apply: %s", path);
165 for (;;) {
166 char line[LINE_MAX], *l;
167 int k;
168
169 if (!fgets(line, sizeof(line), f)) {
170 if (feof(f))
171 break;
172
173 log_error("Failed to read file '%s', ignoring: %m", path);
174 return -errno;
175 }
176
177 l = strstrip(line);
178 if (!*l)
179 continue;
180 if (strchr(COMMENTS "\n", *l))
181 continue;
182
183 k = load_module(ctx, l);
184 if (k < 0 && r == 0)
185 r = k;
186 }
187
188 return r;
189 }
190
191 static int help(void) {
192
193 printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
194 "Loads statically configured kernel modules.\n\n"
195 " -h --help Show this help\n"
196 " --version Show package version\n",
197 program_invocation_short_name);
198
199 return 0;
200 }
201
202 static int parse_argv(int argc, char *argv[]) {
203
204 enum {
205 ARG_VERSION = 0x100,
206 };
207
208 static const struct option options[] = {
209 { "help", no_argument, NULL, 'h' },
210 { "version", no_argument, NULL, ARG_VERSION },
211 {}
212 };
213
214 int c;
215
216 assert(argc >= 0);
217 assert(argv);
218
219 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
220
221 switch (c) {
222
223 case 'h':
224 return help();
225
226 case ARG_VERSION:
227 puts(PACKAGE_STRING);
228 puts(SYSTEMD_FEATURES);
229 return 0;
230
231 case '?':
232 return -EINVAL;
233
234 default:
235 assert_not_reached("Unhandled option");
236 }
237 }
238
239 return 1;
240 }
241
242 int main(int argc, char *argv[]) {
243 int r, k;
244 struct kmod_ctx *ctx;
245
246 r = parse_argv(argc, argv);
247 if (r <= 0)
248 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
249
250 log_set_target(LOG_TARGET_AUTO);
251 log_parse_environment();
252 log_open();
253
254 umask(0022);
255
256 if (parse_proc_cmdline(parse_proc_cmdline_word) < 0)
257 return EXIT_FAILURE;
258
259 ctx = kmod_new(NULL, NULL);
260 if (!ctx) {
261 log_error("Failed to allocate memory for kmod.");
262 goto finish;
263 }
264
265 kmod_load_resources(ctx);
266 kmod_set_log_fn(ctx, systemd_kmod_log, NULL);
267
268 r = 0;
269
270 if (argc > optind) {
271 int i;
272
273 for (i = optind; i < argc; i++) {
274 k = apply_file(ctx, argv[i], false);
275 if (k < 0 && r == 0)
276 r = k;
277 }
278
279 } else {
280 _cleanup_free_ char **files = NULL;
281 char **fn, **i;
282
283 STRV_FOREACH(i, arg_proc_cmdline_modules) {
284 k = load_module(ctx, *i);
285 if (k < 0 && r == 0)
286 r = k;
287 }
288
289 k = conf_files_list_nulstr(&files, ".conf", NULL, conf_file_dirs);
290 if (k < 0) {
291 log_error("Failed to enumerate modules-load.d files: %s", strerror(-k));
292 if (r == 0)
293 r = k;
294 goto finish;
295 }
296
297 STRV_FOREACH(fn, files) {
298 k = apply_file(ctx, *fn, true);
299 if (k < 0 && r == 0)
300 r = k;
301 }
302 }
303
304 finish:
305 kmod_unref(ctx);
306 strv_free(arg_proc_cmdline_modules);
307
308 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
309 }