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