chiark / gitweb /
load-modules: properly return a failing error code if some module fails to load
[elogind.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 }