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