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