]>
Commit | Line | Data |
---|---|---|
8f788d58 LDM |
1 | /* |
2 | * libkmod - interface to kernel module operations | |
3 | * | |
4 | * Copyright (C) 2011 ProFUSION embedded systems | |
8f788d58 LDM |
5 | * |
6 | * This library is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU Lesser General Public | |
8 | * License as published by the Free Software Foundation version 2.1. | |
9 | * | |
10 | * This library is distributed in the hope that it will be useful, | |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
13 | * Lesser General Public License for more details. | |
14 | * | |
15 | * You should have received a copy of the GNU Lesser General Public | |
16 | * License along with this library; if not, write to the Free Software | |
17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | |
18 | */ | |
19 | ||
7636e72b | 20 | #include <assert.h> |
8f788d58 LDM |
21 | #include <stdio.h> |
22 | #include <stdlib.h> | |
23 | #include <stddef.h> | |
24 | #include <stdarg.h> | |
25 | #include <unistd.h> | |
26 | #include <errno.h> | |
27 | #include <string.h> | |
28 | #include <ctype.h> | |
29 | #include <inttypes.h> | |
30 | #include <sys/stat.h> | |
31 | #include <sys/types.h> | |
32 | #include <sys/mman.h> | |
33 | #include <string.h> | |
34 | ||
35 | #include "libkmod.h" | |
36 | #include "libkmod-private.h" | |
37 | ||
38 | /** | |
39 | * kmod_module: | |
40 | * | |
41 | * Opaque object representing a module. | |
42 | */ | |
43 | struct kmod_module { | |
44 | struct kmod_ctx *ctx; | |
45 | int refcount; | |
46 | const char *path; | |
47 | const char *name; | |
7636e72b LDM |
48 | struct kmod_list *dep; |
49 | ||
50 | struct { | |
51 | bool dep : 1; | |
52 | } init; | |
8f788d58 LDM |
53 | }; |
54 | ||
9eaad1f6 | 55 | static char *path_to_modname(const char *path, bool alloc) |
8f788d58 LDM |
56 | { |
57 | char *modname; | |
58 | char *c; | |
59 | ||
60 | modname = basename(path); | |
61 | if (modname == NULL || modname[0] == '\0') | |
62 | return NULL; | |
63 | ||
9eaad1f6 LDM |
64 | if (alloc) |
65 | modname = strdup(modname); | |
66 | ||
8f788d58 LDM |
67 | for (c = modname; *c != '\0' && *c != '.'; c++) { |
68 | if (*c == '-') | |
69 | *c = '_'; | |
70 | } | |
71 | ||
72 | *c = '\0'; | |
73 | return modname; | |
74 | } | |
75 | ||
76 | static const char *get_modname(struct kmod_module *mod) | |
77 | { | |
78 | if (mod->name == NULL) | |
9eaad1f6 | 79 | mod->name = path_to_modname(mod->path, true); |
8f788d58 LDM |
80 | |
81 | return mod->name; | |
82 | } | |
83 | ||
7636e72b LDM |
84 | int kmod_module_parse_dep(struct kmod_module *mod, char *line) |
85 | { | |
86 | struct kmod_ctx *ctx = mod->ctx; | |
87 | struct kmod_list *list = NULL; | |
88 | char *p, *saveptr; | |
89 | int err, n = 0; | |
90 | ||
91 | assert(!mod->init.dep && mod->dep == NULL); | |
92 | mod->init.dep = true; | |
93 | ||
94 | p = strchr(line, ':'); | |
95 | if (p == NULL) | |
96 | return 0; | |
97 | ||
98 | p++; | |
99 | ||
100 | for (p = strtok_r(p, " \t", &saveptr); p != NULL; | |
101 | p = strtok_r(NULL, " \t", &saveptr)) { | |
102 | const char *modname = path_to_modname(p, false); | |
1fc1c9a0 | 103 | struct kmod_module *depmod; |
7636e72b | 104 | |
1fc1c9a0 | 105 | err = kmod_module_new_from_name(ctx, modname, &depmod); |
7636e72b LDM |
106 | if (err < 0) { |
107 | ERR(ctx, "ctx=%p modname=%s error=%s\n", | |
108 | ctx, modname, strerror(-err)); | |
109 | goto fail; | |
110 | } | |
111 | ||
112 | DBG(ctx, "add dep: %s\n", modname); | |
113 | ||
1fc1c9a0 | 114 | list = kmod_list_append(list, depmod); |
7636e72b LDM |
115 | n++; |
116 | } | |
117 | ||
118 | DBG(ctx, "%d dependencies for %s\n", n, mod->name); | |
119 | ||
120 | mod->dep = list; | |
121 | return n; | |
122 | ||
123 | fail: | |
124 | kmod_module_unref_list(list); | |
125 | mod->init.dep = false; | |
126 | return err; | |
127 | } | |
128 | ||
8f788d58 LDM |
129 | KMOD_EXPORT int kmod_module_new_from_name(struct kmod_ctx *ctx, |
130 | const char *name, | |
131 | struct kmod_module **mod) | |
132 | { | |
133 | struct kmod_module *m; | |
134 | ||
135 | if (ctx == NULL || name == NULL) | |
136 | return -ENOENT; | |
137 | ||
138 | m = calloc(1, sizeof(*m)); | |
139 | if (m == NULL) { | |
140 | free(m); | |
141 | return -ENOMEM; | |
142 | } | |
143 | ||
144 | m->ctx = kmod_ref(ctx); | |
145 | m->name = strdup(name); | |
146 | ||
147 | *mod = m; | |
148 | ||
149 | return 0; | |
150 | } | |
151 | ||
152 | KMOD_EXPORT int kmod_module_new_from_path(struct kmod_ctx *ctx, | |
153 | const char *path, | |
154 | struct kmod_module **mod) | |
155 | { | |
156 | struct kmod_module *m; | |
157 | int err; | |
158 | struct stat st; | |
159 | ||
160 | if (ctx == NULL || path == NULL) | |
161 | return -ENOENT; | |
162 | ||
163 | err = stat(path, &st); | |
164 | if (err < 0) | |
165 | return -errno; | |
166 | ||
167 | m = calloc(1, sizeof(*m)); | |
168 | if (m == NULL) { | |
169 | free(m); | |
170 | return -ENOMEM; | |
171 | } | |
172 | ||
173 | m->ctx = kmod_ref(ctx); | |
174 | m->path = strdup(path); | |
175 | ||
176 | *mod = m; | |
177 | ||
178 | return 0; | |
179 | } | |
180 | ||
181 | KMOD_EXPORT struct kmod_module *kmod_module_unref(struct kmod_module *mod) | |
182 | { | |
183 | if (mod == NULL) | |
184 | return NULL; | |
185 | ||
186 | if (--mod->refcount > 0) | |
187 | return mod; | |
188 | ||
189 | DBG(mod->ctx, "kmod_module %p released\n", mod); | |
190 | ||
7636e72b | 191 | kmod_module_unref_list(mod->dep); |
8f788d58 LDM |
192 | kmod_unref(mod->ctx); |
193 | free((char *) mod->path); | |
194 | free((char *) mod->name); | |
195 | free(mod); | |
196 | return NULL; | |
197 | } | |
198 | ||
199 | KMOD_EXPORT struct kmod_module *kmod_module_ref(struct kmod_module *mod) | |
200 | { | |
201 | if (mod == NULL) | |
202 | return NULL; | |
203 | ||
204 | mod->refcount++; | |
205 | ||
206 | return mod; | |
207 | } | |
208 | ||
b14dcfda LDM |
209 | #define CHECK_ERR_AND_FINISH(_err, _label_err, _list, label_finish) \ |
210 | do { \ | |
211 | if ((_err) < 0) \ | |
212 | goto _label_err; \ | |
213 | if (*(_list) != NULL) \ | |
214 | goto finish; \ | |
215 | } while (0) | |
216 | ||
7f3eb0cc LDM |
217 | KMOD_EXPORT int kmod_module_new_from_lookup(struct kmod_ctx *ctx, |
218 | const char *alias, | |
219 | struct kmod_list **list) | |
220 | { | |
7f3eb0cc LDM |
221 | int err; |
222 | ||
223 | if (ctx == NULL || alias == NULL) | |
224 | return -ENOENT; | |
225 | ||
7f3eb0cc LDM |
226 | if (list == NULL || *list != NULL) { |
227 | ERR(ctx, "An empty list is needed to create lookup\n"); | |
228 | return -ENOSYS; | |
229 | } | |
230 | ||
b14dcfda | 231 | /* Aliases from config file override all the others */ |
7f3eb0cc | 232 | err = kmod_lookup_alias_from_config(ctx, alias, list); |
b14dcfda | 233 | CHECK_ERR_AND_FINISH(err, fail, list, finish); |
7f3eb0cc | 234 | |
64700e47 LDM |
235 | err = kmod_lookup_alias_from_moddep_file(ctx, alias, list); |
236 | CHECK_ERR_AND_FINISH(err, fail, list, finish); | |
237 | ||
9ba6f57b LDM |
238 | err = kmod_lookup_alias_from_symbols_file(ctx, alias, list); |
239 | CHECK_ERR_AND_FINISH(err, fail, list, finish); | |
b14dcfda | 240 | |
49e61ca3 LDM |
241 | err = kmod_lookup_alias_from_aliases_file(ctx, alias, list); |
242 | CHECK_ERR_AND_FINISH(err, fail, list, finish); | |
243 | ||
b14dcfda | 244 | finish: |
7f3eb0cc LDM |
245 | |
246 | return err; | |
b14dcfda LDM |
247 | fail: |
248 | kmod_module_unref_list(*list); | |
249 | *list = NULL; | |
84f42204 | 250 | return err; |
7f3eb0cc | 251 | } |
b14dcfda LDM |
252 | #undef CHECK_ERR_AND_FINISH |
253 | ||
7f3eb0cc LDM |
254 | |
255 | KMOD_EXPORT int kmod_module_unref_list(struct kmod_list *list) | |
256 | { | |
257 | for (; list != NULL; list = kmod_list_remove(list)) | |
258 | kmod_module_unref(list->data); | |
259 | ||
260 | return 0; | |
261 | } | |
262 | ||
0835fc3b LDM |
263 | /* |
264 | * We don't increase the refcount. Maybe we should. | |
265 | */ | |
1ce08a56 | 266 | KMOD_EXPORT struct kmod_list *kmod_module_get_dependency(const struct kmod_module *mod) |
0835fc3b LDM |
267 | { |
268 | // FIXME calculate dependency if it's not initialized | |
269 | return mod->dep; | |
270 | } | |
271 | ||
1ce08a56 | 272 | KMOD_EXPORT struct kmod_module *kmod_module_get_module(const struct kmod_list *l) |
6e869df7 LDM |
273 | { |
274 | struct kmod_module *mod = l->data; | |
275 | return kmod_module_ref(mod); | |
276 | } | |
277 | ||
1ce08a56 | 278 | KMOD_EXPORT const char *kmod_module_get_name(const struct kmod_module *mod) |
6e869df7 LDM |
279 | { |
280 | // FIXME calculate name if name == NULL | |
281 | return mod->name; | |
282 | } | |
283 | ||
1ce08a56 | 284 | KMOD_EXPORT const char *kmod_module_get_path(const struct kmod_module *mod) |
6e869df7 LDM |
285 | { |
286 | // FIXME calculate path if path == NULL | |
287 | return mod->path; | |
288 | } | |
289 | ||
290 | ||
8f788d58 LDM |
291 | extern long delete_module(const char *name, unsigned int flags); |
292 | ||
293 | KMOD_EXPORT int kmod_module_remove_module(struct kmod_module *mod, | |
294 | unsigned int flags) | |
295 | { | |
296 | int err; | |
297 | const char *modname; | |
298 | ||
299 | if (mod == NULL) | |
300 | return -ENOENT; | |
301 | ||
302 | /* Filter out other flags */ | |
303 | flags &= (KMOD_REMOVE_FORCE | KMOD_REMOVE_NOWAIT); | |
304 | ||
305 | modname = get_modname(mod); | |
306 | err = delete_module(modname, flags); | |
307 | if (err != 0) { | |
308 | ERR(mod->ctx, "Removing '%s': %s\n", modname, | |
309 | strerror(-err)); | |
310 | return err; | |
311 | } | |
312 | ||
313 | return 0; | |
314 | } | |
315 | ||
316 | extern long init_module(void *mem, unsigned long len, const char *args); | |
317 | ||
318 | KMOD_EXPORT int kmod_module_insert_module(struct kmod_module *mod, | |
319 | unsigned int flags) | |
320 | { | |
321 | int err; | |
322 | void *mmaped_file; | |
323 | struct stat st; | |
324 | int fd; | |
325 | const char *args = ""; | |
326 | ||
327 | if (mod == NULL) | |
328 | return -ENOENT; | |
329 | ||
330 | if (mod->path == NULL) { | |
1b2e26a1 | 331 | ERR(mod->ctx, "Not supported to load a module by name yet\n"); |
8f788d58 LDM |
332 | return -ENOSYS; |
333 | } | |
334 | ||
335 | if (flags != 0) | |
1b2e26a1 | 336 | INFO(mod->ctx, "Flags are not implemented yet\n"); |
8f788d58 LDM |
337 | |
338 | if ((fd = open(mod->path, O_RDONLY)) < 0) { | |
339 | err = -errno; | |
340 | return err; | |
341 | } | |
342 | ||
b418a820 | 343 | fstat(fd, &st); |
8f788d58 LDM |
344 | |
345 | if ((mmaped_file = mmap(0, st.st_size, PROT_READ, | |
346 | MAP_PRIVATE, fd, 0)) == MAP_FAILED) { | |
347 | close(fd); | |
348 | return -errno; | |
349 | } | |
350 | ||
351 | err = init_module(mmaped_file, st.st_size, args); | |
352 | if (err < 0) | |
1b2e26a1 | 353 | ERR(mod->ctx, "Failed to insert module '%s'\n", mod->path); |
8f788d58 LDM |
354 | |
355 | munmap(mmaped_file, st.st_size); | |
356 | close(fd); | |
357 | ||
358 | return err; | |
359 | } |