]>
Commit | Line | Data |
---|---|---|
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 |