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