chiark / gitweb /
49b153d411373daf5f549b4c168be52a6db456d5
[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", 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 int help(void) {
185
186         printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
187                "Loads statically configured kernel modules.\n\n"
188                "  -h --help             Show this help\n"
189                "     --version          Show package version\n",
190                program_invocation_short_name);
191
192         return 0;
193 }
194
195 static int parse_argv(int argc, char *argv[]) {
196
197         enum {
198                 ARG_VERSION = 0x100,
199         };
200
201         static const struct option options[] = {
202                 { "help",      no_argument,       NULL, 'h'           },
203                 { "version",   no_argument,       NULL, ARG_VERSION   },
204                 {}
205         };
206
207         int c;
208
209         assert(argc >= 0);
210         assert(argv);
211
212         while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
213
214                 switch (c) {
215
216                 case 'h':
217                         return help();
218
219                 case ARG_VERSION:
220                         puts(PACKAGE_STRING);
221                         puts(SYSTEMD_FEATURES);
222                         return 0;
223
224                 case '?':
225                         return -EINVAL;
226
227                 default:
228                         assert_not_reached("Unhandled option");
229                 }
230         }
231
232         return 1;
233 }
234
235 int main(int argc, char *argv[]) {
236         int r, k;
237         struct kmod_ctx *ctx;
238
239         r = parse_argv(argc, argv);
240         if (r <= 0)
241                 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
242
243         log_set_target(LOG_TARGET_AUTO);
244         log_parse_environment();
245         log_open();
246
247         umask(0022);
248
249         if (parse_proc_cmdline(parse_proc_cmdline_item) < 0)
250                 return EXIT_FAILURE;
251
252         ctx = kmod_new(NULL, NULL);
253         if (!ctx) {
254                 log_error("Failed to allocate memory for kmod.");
255                 goto finish;
256         }
257
258         kmod_load_resources(ctx);
259         kmod_set_log_fn(ctx, systemd_kmod_log, NULL);
260
261         r = 0;
262
263         if (argc > optind) {
264                 int i;
265
266                 for (i = optind; i < argc; i++) {
267                         k = apply_file(ctx, argv[i], false);
268                         if (k < 0 && r == 0)
269                                 r = k;
270                 }
271
272         } else {
273                 _cleanup_free_ char **files = NULL;
274                 char **fn, **i;
275
276                 STRV_FOREACH(i, arg_proc_cmdline_modules) {
277                         k = load_module(ctx, *i);
278                         if (k < 0 && r == 0)
279                                 r = k;
280                 }
281
282                 k = conf_files_list_nulstr(&files, ".conf", NULL, conf_file_dirs);
283                 if (k < 0) {
284                         log_error("Failed to enumerate modules-load.d files: %s", strerror(-k));
285                         if (r == 0)
286                                 r = k;
287                         goto finish;
288                 }
289
290                 STRV_FOREACH(fn, files) {
291                         k = apply_file(ctx, *fn, true);
292                         if (k < 0 && r == 0)
293                                 r = k;
294                 }
295         }
296
297 finish:
298         kmod_unref(ctx);
299         strv_free(arg_proc_cmdline_modules);
300
301         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
302 }