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