]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/modules-load/modules-load.c
Add SPDX license identifiers to source files under the LGPL
[thirdparty/systemd.git] / src / modules-load / modules-load.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 This file is part of systemd.
4
5 Copyright 2010 Lennart Poettering
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #include <errno.h>
22 #include <getopt.h>
23 #include <libkmod.h>
24 #include <limits.h>
25 #include <string.h>
26 #include <sys/stat.h>
27
28 #include "conf-files.h"
29 #include "def.h"
30 #include "fd-util.h"
31 #include "fileio.h"
32 #include "log.h"
33 #include "module-util.h"
34 #include "proc-cmdline.h"
35 #include "string-util.h"
36 #include "strv.h"
37 #include "util.h"
38
39 static char **arg_proc_cmdline_modules = NULL;
40
41 static const char conf_file_dirs[] = CONF_PATHS_NULSTR("modules-load.d");
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, true) < 0)
59 return log_oom();
60
61 return 0;
62 }
63
64 static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
65 int r;
66
67 if (proc_cmdline_key_streq(key, "modules_load")) {
68
69 if (proc_cmdline_value_missing(key, value))
70 return 0;
71
72 r = add_modules(value);
73 if (r < 0)
74 return r;
75 }
76
77 return 0;
78 }
79
80 static int load_module(struct kmod_ctx *ctx, const char *m) {
81 const int probe_flags = KMOD_PROBE_APPLY_BLACKLIST;
82 struct kmod_list *itr;
83 _cleanup_(kmod_module_unref_listp) struct kmod_list *modlist = NULL;
84 int r = 0;
85
86 log_debug("load: %s", m);
87
88 r = kmod_module_new_from_lookup(ctx, m, &modlist);
89 if (r < 0)
90 return log_error_errno(r, "Failed to lookup alias '%s': %m", m);
91
92 if (!modlist) {
93 log_error("Failed to find module '%s'", m);
94 return -ENOENT;
95 }
96
97 kmod_list_foreach(itr, modlist) {
98 _cleanup_(kmod_module_unrefp) struct kmod_module *mod = NULL;
99 int state, err;
100
101 mod = kmod_module_get_module(itr);
102 state = kmod_module_get_initstate(mod);
103
104 switch (state) {
105 case KMOD_MODULE_BUILTIN:
106 log_info("Module '%s' is builtin", kmod_module_get_name(mod));
107 break;
108
109 case KMOD_MODULE_LIVE:
110 log_debug("Module '%s' is already loaded", kmod_module_get_name(mod));
111 break;
112
113 default:
114 err = kmod_module_probe_insert_module(mod, probe_flags,
115 NULL, NULL, NULL, NULL);
116
117 if (err == 0)
118 log_info("Inserted module '%s'", kmod_module_get_name(mod));
119 else if (err == KMOD_PROBE_APPLY_BLACKLIST)
120 log_info("Module '%s' is blacklisted", kmod_module_get_name(mod));
121 else {
122 assert(err < 0);
123
124 log_full_errno(err == ENODEV ? LOG_NOTICE :
125 err == ENOENT ? LOG_WARNING :
126 LOG_ERR,
127 err,
128 "Failed to insert '%s': %m",
129 kmod_module_get_name(mod));
130 if (!IN_SET(err, ENODEV, ENOENT))
131 r = err;
132 }
133 }
134 }
135
136 return r;
137 }
138
139 static int apply_file(struct kmod_ctx *ctx, const char *path, bool ignore_enoent) {
140 _cleanup_fclose_ FILE *f = NULL;
141 int r;
142
143 assert(ctx);
144 assert(path);
145
146 r = search_and_fopen_nulstr(path, "re", NULL, conf_file_dirs, &f);
147 if (r < 0) {
148 if (ignore_enoent && r == -ENOENT)
149 return 0;
150
151 return log_error_errno(r, "Failed to open %s, ignoring: %m", path);
152 }
153
154 log_debug("apply: %s", path);
155 for (;;) {
156 char line[LINE_MAX], *l;
157 int k;
158
159 if (!fgets(line, sizeof(line), f)) {
160 if (feof(f))
161 break;
162
163 return log_error_errno(errno, "Failed to read file '%s', ignoring: %m", path);
164 }
165
166 l = strstrip(line);
167 if (!*l)
168 continue;
169 if (strchr(COMMENTS "\n", *l))
170 continue;
171
172 k = load_module(ctx, l);
173 if (k < 0 && r == 0)
174 r = k;
175 }
176
177 return r;
178 }
179
180 static void help(void) {
181 printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
182 "Loads statically configured kernel modules.\n\n"
183 " -h --help Show this help\n"
184 " --version Show package version\n",
185 program_invocation_short_name);
186 }
187
188 static int parse_argv(int argc, char *argv[]) {
189
190 enum {
191 ARG_VERSION = 0x100,
192 };
193
194 static const struct option options[] = {
195 { "help", no_argument, NULL, 'h' },
196 { "version", no_argument, NULL, ARG_VERSION },
197 {}
198 };
199
200 int c;
201
202 assert(argc >= 0);
203 assert(argv);
204
205 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
206
207 switch (c) {
208
209 case 'h':
210 help();
211 return 0;
212
213 case ARG_VERSION:
214 return version();
215
216 case '?':
217 return -EINVAL;
218
219 default:
220 assert_not_reached("Unhandled option");
221 }
222
223 return 1;
224 }
225
226 int main(int argc, char *argv[]) {
227 int r, k;
228 _cleanup_(kmod_unrefp) struct kmod_ctx *ctx = NULL;
229
230 r = parse_argv(argc, argv);
231 if (r <= 0)
232 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
233
234 log_set_target(LOG_TARGET_AUTO);
235 log_parse_environment();
236 log_open();
237
238 umask(0022);
239
240 r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, PROC_CMDLINE_STRIP_RD_PREFIX);
241 if (r < 0)
242 log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
243
244 ctx = kmod_new(NULL, NULL);
245 if (!ctx) {
246 log_error("Failed to allocate memory for kmod.");
247 goto finish;
248 }
249
250 kmod_load_resources(ctx);
251 kmod_set_log_fn(ctx, systemd_kmod_log, NULL);
252
253 r = 0;
254
255 if (argc > optind) {
256 int i;
257
258 for (i = optind; i < argc; i++) {
259 k = apply_file(ctx, argv[i], false);
260 if (k < 0 && r == 0)
261 r = k;
262 }
263
264 } else {
265 _cleanup_strv_free_ char **files = NULL;
266 char **fn, **i;
267
268 STRV_FOREACH(i, arg_proc_cmdline_modules) {
269 k = load_module(ctx, *i);
270 if (k < 0 && r == 0)
271 r = k;
272 }
273
274 k = conf_files_list_nulstr(&files, ".conf", NULL, 0, conf_file_dirs);
275 if (k < 0) {
276 log_error_errno(k, "Failed to enumerate modules-load.d files: %m");
277 if (r == 0)
278 r = k;
279 goto finish;
280 }
281
282 STRV_FOREACH(fn, files) {
283 k = apply_file(ctx, *fn, true);
284 if (k < 0 && r == 0)
285 r = k;
286 }
287 }
288
289 finish:
290 strv_free(arg_proc_cmdline_modules);
291
292 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
293 }