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