]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/module-util.c
dcdc122ffbbe8bc903b3d1722115956ec5e77c03
[thirdparty/systemd.git] / src / shared / module-util.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <syslog.h>
4
5 #include "log.h"
6 #include "module-util.h"
7 #include "proc-cmdline.h"
8 #include "strv.h"
9
10 #if HAVE_KMOD
11
12 static void *libkmod_dl = NULL;
13
14 DLSYM_PROTOTYPE(kmod_list_next) = NULL;
15 DLSYM_PROTOTYPE(kmod_load_resources) = NULL;
16 DLSYM_PROTOTYPE(kmod_module_get_initstate) = NULL;
17 DLSYM_PROTOTYPE(kmod_module_get_module) = NULL;
18 DLSYM_PROTOTYPE(kmod_module_get_name) = NULL;
19 DLSYM_PROTOTYPE(kmod_module_new_from_lookup) = NULL;
20 DLSYM_PROTOTYPE(kmod_module_probe_insert_module) = NULL;
21 DLSYM_PROTOTYPE(kmod_module_unref) = NULL;
22 DLSYM_PROTOTYPE(kmod_module_unref_list) = NULL;
23 DLSYM_PROTOTYPE(kmod_new) = NULL;
24 DLSYM_PROTOTYPE(kmod_set_log_fn) = NULL;
25 DLSYM_PROTOTYPE(kmod_unref) = NULL;
26 DLSYM_PROTOTYPE(kmod_validate_resources) = NULL;
27
28 int dlopen_libkmod(void) {
29 ELF_NOTE_DLOPEN("kmod",
30 "Support for loading kernel modules",
31 ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED,
32 "libkmod.so.2");
33
34 return dlopen_many_sym_or_warn(
35 &libkmod_dl,
36 "libkmod.so.2",
37 LOG_DEBUG,
38 DLSYM_ARG(kmod_list_next),
39 DLSYM_ARG(kmod_load_resources),
40 DLSYM_ARG(kmod_module_get_initstate),
41 DLSYM_ARG(kmod_module_get_module),
42 DLSYM_ARG(kmod_module_get_name),
43 DLSYM_ARG(kmod_module_new_from_lookup),
44 DLSYM_ARG(kmod_module_probe_insert_module),
45 DLSYM_ARG(kmod_module_unref),
46 DLSYM_ARG(kmod_module_unref_list),
47 DLSYM_ARG(kmod_new),
48 DLSYM_ARG(kmod_set_log_fn),
49 DLSYM_ARG(kmod_unref),
50 DLSYM_ARG(kmod_validate_resources));
51 }
52
53 static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
54 char ***denylist = ASSERT_PTR(data);
55 int r;
56
57 if (proc_cmdline_key_streq(key, "module_blacklist")) {
58
59 if (proc_cmdline_value_missing(key, value))
60 return 0;
61
62 r = strv_split_and_extend(denylist, value, ",", /* filter_duplicates = */ true);
63 if (r < 0)
64 return r;
65 }
66
67 return 0;
68 }
69
70 int module_load_and_warn(struct kmod_ctx *ctx, const char *module, bool verbose) {
71 _cleanup_(sym_kmod_module_unref_listp) struct kmod_list *modlist = NULL;
72 _cleanup_strv_free_ char **denylist = NULL;
73 bool denylist_parsed = false;
74 struct kmod_list *itr;
75 int r;
76
77 assert(ctx);
78 assert(module);
79
80 /* verbose==true means we should log at non-debug level if we
81 * fail to find or load the module. */
82
83 log_debug("Loading module: %s", module);
84
85 r = sym_kmod_module_new_from_lookup(ctx, module, &modlist);
86 if (r < 0)
87 return log_full_errno(verbose ? LOG_ERR : LOG_DEBUG, r,
88 "Failed to look up module alias '%s': %m", module);
89
90 if (!modlist)
91 return log_full_errno(verbose ? LOG_ERR : LOG_DEBUG,
92 SYNTHETIC_ERRNO(ENOENT),
93 "Failed to find module '%s'", module);
94
95 sym_kmod_list_foreach(itr, modlist) {
96 _cleanup_(sym_kmod_module_unrefp) struct kmod_module *mod = NULL;
97 int state, err;
98
99 mod = sym_kmod_module_get_module(itr);
100 state = sym_kmod_module_get_initstate(mod);
101
102 switch (state) {
103 case KMOD_MODULE_BUILTIN:
104 log_full(verbose ? LOG_INFO : LOG_DEBUG,
105 "Module '%s' is built in", sym_kmod_module_get_name(mod));
106 break;
107
108 case KMOD_MODULE_LIVE:
109 log_debug("Module '%s' is already loaded", sym_kmod_module_get_name(mod));
110 break;
111
112 default:
113 err = sym_kmod_module_probe_insert_module(
114 mod,
115 KMOD_PROBE_APPLY_BLACKLIST,
116 /* extra_options= */ NULL,
117 /* run_install= */ NULL,
118 /* data= */ NULL,
119 /* print_action= */ NULL);
120 if (err == 0)
121 log_full(verbose ? LOG_INFO : LOG_DEBUG,
122 "Inserted module '%s'", sym_kmod_module_get_name(mod));
123 else if (err == KMOD_PROBE_APPLY_BLACKLIST)
124 log_full(verbose ? LOG_INFO : LOG_DEBUG,
125 "Module '%s' is deny-listed (by kmod)", sym_kmod_module_get_name(mod));
126 else {
127 assert(err < 0);
128
129 if (err == -EPERM) {
130 if (!denylist_parsed) {
131 r = proc_cmdline_parse(parse_proc_cmdline_item, &denylist, 0);
132 if (r < 0)
133 log_full_errno(!verbose ? LOG_DEBUG : LOG_WARNING,
134 r,
135 "Failed to parse kernel command line, ignoring: %m");
136
137 denylist_parsed = true;
138 }
139 if (strv_contains(denylist, sym_kmod_module_get_name(mod))) {
140 log_full(verbose ? LOG_INFO : LOG_DEBUG,
141 "Module '%s' is deny-listed (by kernel)", sym_kmod_module_get_name(mod));
142 continue;
143 }
144 }
145
146 log_full_errno(!verbose ? LOG_DEBUG :
147 err == -ENODEV ? LOG_NOTICE :
148 err == -ENOENT ? LOG_WARNING :
149 LOG_ERR,
150 err,
151 "Failed to insert module '%s': %m",
152 sym_kmod_module_get_name(mod));
153 if (!IN_SET(err, -ENODEV, -ENOENT))
154 r = err;
155 }
156 }
157 }
158
159 return r;
160 }
161
162 _printf_(6,0) static void systemd_kmod_log(
163 void *data,
164 int priority,
165 const char *file,
166 int line,
167 const char *fn,
168 const char *format,
169 va_list args) {
170
171 log_internalv(priority, 0, file, line, fn, format, args);
172 }
173
174 int module_setup_context(struct kmod_ctx **ret) {
175 _cleanup_(sym_kmod_unrefp) struct kmod_ctx *ctx = NULL;
176 int r;
177
178 assert(ret);
179
180 r = dlopen_libkmod();
181 if (r < 0)
182 return r;
183
184 ctx = sym_kmod_new(NULL, NULL);
185 if (!ctx)
186 return -ENOMEM;
187
188 (void) sym_kmod_load_resources(ctx);
189 sym_kmod_set_log_fn(ctx, systemd_kmod_log, NULL);
190
191 *ret = TAKE_PTR(ctx);
192 return 0;
193 }
194
195 #endif