chiark / gitweb /
util: unify reading of /proc/cmdline
[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
38 static char **arg_proc_cmdline_modules = NULL;
39
40 static const char conf_file_dirs[] =
41         "/etc/modules-load.d\0"
42         "/run/modules-load.d\0"
43         "/usr/local/lib/modules-load.d\0"
44         "/usr/lib/modules-load.d\0"
45 #ifdef HAVE_SPLIT_USR
46         "/lib/modules-load.d\0"
47 #endif
48         ;
49
50 #pragma GCC diagnostic push
51 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
52 static void systemd_kmod_log(void *data, int priority, const char *file, int line,
53                              const char *fn, const char *format, va_list args)
54 {
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                program_invocation_short_name);
222
223         return 0;
224 }
225
226 static int parse_argv(int argc, char *argv[]) {
227
228         static const struct option options[] = {
229                 { "help",      no_argument,       NULL, 'h'           },
230                 { NULL,        0,                 NULL, 0             }
231         };
232
233         int c;
234
235         assert(argc >= 0);
236         assert(argv);
237
238         while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
239
240                 switch (c) {
241
242                 case 'h':
243                         help();
244                         return 0;
245
246                 case '?':
247                         return -EINVAL;
248
249                 default:
250                         log_error("Unknown option code %c", c);
251                         return -EINVAL;
252                 }
253         }
254
255         return 1;
256 }
257
258 int main(int argc, char *argv[]) {
259         int r, k;
260         struct kmod_ctx *ctx;
261
262         r = parse_argv(argc, argv);
263         if (r <= 0)
264                 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
265
266         log_set_target(LOG_TARGET_AUTO);
267         log_parse_environment();
268         log_open();
269
270         umask(0022);
271
272         if (parse_proc_cmdline() < 0)
273                 return EXIT_FAILURE;
274
275         ctx = kmod_new(NULL, NULL);
276         if (!ctx) {
277                 log_error("Failed to allocate memory for kmod.");
278                 goto finish;
279         }
280
281         kmod_load_resources(ctx);
282         kmod_set_log_fn(ctx, systemd_kmod_log, NULL);
283
284         r = 0;
285
286         if (argc > optind) {
287                 int i;
288
289                 for (i = optind; i < argc; i++) {
290                         k = apply_file(ctx, argv[i], false);
291                         if (k < 0 && r == 0)
292                                 r = k;
293                 }
294
295         } else {
296                 _cleanup_free_ char **files = NULL;
297                 char **fn, **i;
298
299                 STRV_FOREACH(i, arg_proc_cmdline_modules) {
300                         k = load_module(ctx, *i);
301                         if (k < 0 && r == 0)
302                                 r = k;
303                 }
304
305                 r = conf_files_list_nulstr(&files, ".conf", NULL, conf_file_dirs);
306                 if (r < 0) {
307                         log_error("Failed to enumerate modules-load.d files: %s", strerror(-r));
308                         goto finish;
309                 }
310
311                 STRV_FOREACH(fn, files) {
312                         k = apply_file(ctx, *fn, true);
313                         if (k < 0 && r == 0)
314                                 r = k;
315                 }
316         }
317
318 finish:
319         kmod_unref(ctx);
320         strv_free(arg_proc_cmdline_modules);
321
322         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
323 }