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