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