chiark / gitweb /
treewide: no need to negate errno for log_*_errno()
[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("Failed to insert '%s': %s", kmod_module_get_name(mod),
120                                           strerror(-err));
121                                 r = err;
122                         }
123                 }
124
125                 kmod_module_unref(mod);
126         }
127
128         kmod_module_unref_list(modlist);
129
130         return r;
131 }
132
133 static int apply_file(struct kmod_ctx *ctx, const char *path, bool ignore_enoent) {
134         _cleanup_fclose_ FILE *f = NULL;
135         int r;
136
137         assert(ctx);
138         assert(path);
139
140         r = search_and_fopen_nulstr(path, "re", NULL, conf_file_dirs, &f);
141         if (r < 0) {
142                 if (ignore_enoent && r == -ENOENT)
143                         return 0;
144
145                 log_error_errno(r, "Failed to open %s, ignoring: %m", path);
146                 return r;
147         }
148
149         log_debug("apply: %s", path);
150         for (;;) {
151                 char line[LINE_MAX], *l;
152                 int k;
153
154                 if (!fgets(line, sizeof(line), f)) {
155                         if (feof(f))
156                                 break;
157
158                         log_error("Failed to read file '%s', ignoring: %m", path);
159                         return -errno;
160                 }
161
162                 l = strstrip(line);
163                 if (!*l)
164                         continue;
165                 if (strchr(COMMENTS "\n", *l))
166                         continue;
167
168                 k = load_module(ctx, l);
169                 if (k < 0 && r == 0)
170                         r = k;
171         }
172
173         return r;
174 }
175
176 static void help(void) {
177         printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
178                "Loads statically configured kernel modules.\n\n"
179                "  -h --help             Show this help\n"
180                "     --version          Show package version\n",
181                program_invocation_short_name);
182 }
183
184 static int parse_argv(int argc, char *argv[]) {
185
186         enum {
187                 ARG_VERSION = 0x100,
188         };
189
190         static const struct option options[] = {
191                 { "help",      no_argument,       NULL, 'h'           },
192                 { "version",   no_argument,       NULL, ARG_VERSION   },
193                 {}
194         };
195
196         int c;
197
198         assert(argc >= 0);
199         assert(argv);
200
201         while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
202
203                 switch (c) {
204
205                 case 'h':
206                         help();
207                         return 0;
208
209                 case ARG_VERSION:
210                         puts(PACKAGE_STRING);
211                         puts(SYSTEMD_FEATURES);
212                         return 0;
213
214                 case '?':
215                         return -EINVAL;
216
217                 default:
218                         assert_not_reached("Unhandled option");
219                 }
220
221         return 1;
222 }
223
224 int main(int argc, char *argv[]) {
225         int r, k;
226         struct kmod_ctx *ctx;
227
228         r = parse_argv(argc, argv);
229         if (r <= 0)
230                 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
231
232         log_set_target(LOG_TARGET_AUTO);
233         log_parse_environment();
234         log_open();
235
236         umask(0022);
237
238         r = parse_proc_cmdline(parse_proc_cmdline_item);
239         if (r < 0)
240                 log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
241
242         ctx = kmod_new(NULL, NULL);
243         if (!ctx) {
244                 log_error("Failed to allocate memory for kmod.");
245                 goto finish;
246         }
247
248         kmod_load_resources(ctx);
249         kmod_set_log_fn(ctx, systemd_kmod_log, NULL);
250
251         r = 0;
252
253         if (argc > optind) {
254                 int i;
255
256                 for (i = optind; i < argc; i++) {
257                         k = apply_file(ctx, argv[i], false);
258                         if (k < 0 && r == 0)
259                                 r = k;
260                 }
261
262         } else {
263                 _cleanup_free_ char **files = NULL;
264                 char **fn, **i;
265
266                 STRV_FOREACH(i, arg_proc_cmdline_modules) {
267                         k = load_module(ctx, *i);
268                         if (k < 0 && r == 0)
269                                 r = k;
270                 }
271
272                 k = conf_files_list_nulstr(&files, ".conf", NULL, conf_file_dirs);
273                 if (k < 0) {
274                         log_error_errno(k, "Failed to enumerate modules-load.d files: %m");
275                         if (r == 0)
276                                 r = k;
277                         goto finish;
278                 }
279
280                 STRV_FOREACH(fn, files) {
281                         k = apply_file(ctx, *fn, true);
282                         if (k < 0 && r == 0)
283                                 r = k;
284                 }
285         }
286
287 finish:
288         kmod_unref(ctx);
289         strv_free(arg_proc_cmdline_modules);
290
291         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
292 }