chiark / gitweb /
01987f2e3c2a2f84df6da2d4e5faa64c10dbcec4
[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         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 }