]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
3cb9b42a | 2 | |
69a283c5 | 3 | #include <syslog.h> |
3cb9b42a | 4 | |
69a283c5 | 5 | #include "log.h" |
3cb9b42a | 6 | #include "module-util.h" |
dab0156f MY |
7 | #include "proc-cmdline.h" |
8 | #include "strv.h" | |
9 | ||
1d98716e LP |
10 | #if HAVE_KMOD |
11 | ||
12 | static void *libkmod_dl = NULL; | |
13 | ||
5c672e90 ZJS |
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; | |
1d98716e LP |
27 | |
28 | int dlopen_libkmod(void) { | |
cd7c2077 LP |
29 | ELF_NOTE_DLOPEN("kmod", |
30 | "Support for loading kernel modules", | |
31 | ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED, | |
32 | "libkmod.so.2"); | |
33 | ||
1d98716e LP |
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 | ||
dab0156f | 53 | static int parse_proc_cmdline_item(const char *key, const char *value, void *data) { |
a2c8652a | 54 | char ***denylist = ASSERT_PTR(data); |
dab0156f MY |
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 | ||
a2c8652a | 62 | r = strv_split_and_extend(denylist, value, ",", /* filter_duplicates = */ true); |
dab0156f MY |
63 | if (r < 0) |
64 | return r; | |
65 | } | |
66 | ||
67 | return 0; | |
68 | } | |
3cb9b42a | 69 | |
c3ad9786 | 70 | int module_load_and_warn(struct kmod_ctx *ctx, const char *module, bool verbose) { |
1d98716e | 71 | _cleanup_(sym_kmod_module_unref_listp) struct kmod_list *modlist = NULL; |
dab0156f MY |
72 | _cleanup_strv_free_ char **denylist = NULL; |
73 | bool denylist_parsed = false; | |
1d98716e | 74 | struct kmod_list *itr; |
6d95e7d9 | 75 | int r; |
3cb9b42a | 76 | |
1d98716e LP |
77 | assert(ctx); |
78 | assert(module); | |
79 | ||
c3ad9786 ZJS |
80 | /* verbose==true means we should log at non-debug level if we |
81 | * fail to find or load the module. */ | |
82 | ||
3cb9b42a ZJS |
83 | log_debug("Loading module: %s", module); |
84 | ||
1d98716e | 85 | r = sym_kmod_module_new_from_lookup(ctx, module, &modlist); |
3cb9b42a | 86 | if (r < 0) |
c3ad9786 | 87 | return log_full_errno(verbose ? LOG_ERR : LOG_DEBUG, r, |
105a1a36 | 88 | "Failed to look up module alias '%s': %m", module); |
3cb9b42a | 89 | |
a2eb2267 ZJS |
90 | if (!modlist) |
91 | return log_full_errno(verbose ? LOG_ERR : LOG_DEBUG, | |
92 | SYNTHETIC_ERRNO(ENOENT), | |
93 | "Failed to find module '%s'", module); | |
3cb9b42a | 94 | |
1d98716e LP |
95 | sym_kmod_list_foreach(itr, modlist) { |
96 | _cleanup_(sym_kmod_module_unrefp) struct kmod_module *mod = NULL; | |
3cb9b42a ZJS |
97 | int state, err; |
98 | ||
1d98716e LP |
99 | mod = sym_kmod_module_get_module(itr); |
100 | state = sym_kmod_module_get_initstate(mod); | |
3cb9b42a ZJS |
101 | |
102 | switch (state) { | |
103 | case KMOD_MODULE_BUILTIN: | |
c3ad9786 | 104 | log_full(verbose ? LOG_INFO : LOG_DEBUG, |
1d98716e | 105 | "Module '%s' is built in", sym_kmod_module_get_name(mod)); |
3cb9b42a ZJS |
106 | break; |
107 | ||
108 | case KMOD_MODULE_LIVE: | |
1d98716e | 109 | log_debug("Module '%s' is already loaded", sym_kmod_module_get_name(mod)); |
3cb9b42a ZJS |
110 | break; |
111 | ||
112 | default: | |
1d98716e LP |
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); | |
3cb9b42a | 120 | if (err == 0) |
c3ad9786 | 121 | log_full(verbose ? LOG_INFO : LOG_DEBUG, |
1d98716e | 122 | "Inserted module '%s'", sym_kmod_module_get_name(mod)); |
3cb9b42a | 123 | else if (err == KMOD_PROBE_APPLY_BLACKLIST) |
c3ad9786 | 124 | log_full(verbose ? LOG_INFO : LOG_DEBUG, |
1d98716e | 125 | "Module '%s' is deny-listed (by kmod)", sym_kmod_module_get_name(mod)); |
3cb9b42a ZJS |
126 | else { |
127 | assert(err < 0); | |
128 | ||
dab0156f MY |
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 | } | |
1d98716e | 139 | if (strv_contains(denylist, sym_kmod_module_get_name(mod))) { |
dab0156f | 140 | log_full(verbose ? LOG_INFO : LOG_DEBUG, |
1d98716e | 141 | "Module '%s' is deny-listed (by kernel)", sym_kmod_module_get_name(mod)); |
dab0156f MY |
142 | continue; |
143 | } | |
144 | } | |
145 | ||
c3ad9786 | 146 | log_full_errno(!verbose ? LOG_DEBUG : |
9b38ec87 ZJS |
147 | err == -ENODEV ? LOG_NOTICE : |
148 | err == -ENOENT ? LOG_WARNING : | |
149 | LOG_ERR, | |
3cb9b42a ZJS |
150 | err, |
151 | "Failed to insert module '%s': %m", | |
1d98716e | 152 | sym_kmod_module_get_name(mod)); |
9b38ec87 | 153 | if (!IN_SET(err, -ENODEV, -ENOENT)) |
3cb9b42a ZJS |
154 | r = err; |
155 | } | |
156 | } | |
157 | } | |
158 | ||
159 | return r; | |
160 | } | |
1d98716e LP |
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 |