]>
Commit | Line | Data |
---|---|---|
5369797d LDM |
1 | /* |
2 | * libkmod - interface to kernel module operations | |
3 | * | |
4 | * Copyright (C) 2011 ProFUSION embedded systems | |
5369797d 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 | ||
20 | #include <stdio.h> | |
21 | #include <stdlib.h> | |
22 | #include <stddef.h> | |
23 | #include <stdarg.h> | |
24 | #include <unistd.h> | |
25 | #include <errno.h> | |
26 | #include <string.h> | |
27 | #include <ctype.h> | |
28 | #include <inttypes.h> | |
29 | ||
30 | #include "libkmod.h" | |
31 | #include "libkmod-private.h" | |
32 | ||
33 | /** | |
34 | * SECTION:libkmod-loaded | |
35 | * @short_description: currently loaded modules | |
36 | * | |
37 | * Information about currently loaded modules, as reported by Linux kernel | |
38 | */ | |
39 | ||
40 | /** | |
41 | * kmod_loaded: | |
42 | * | |
43 | * Opaque object representing a loaded module. | |
44 | */ | |
45 | struct kmod_loaded { | |
46 | struct kmod_ctx *ctx; | |
47 | int refcount; | |
48 | struct kmod_list *modules; | |
49 | bool parsed; | |
50 | }; | |
51 | ||
52 | struct kmod_loaded_module { | |
53 | char *name; | |
54 | long size; | |
55 | int use_count; | |
56 | char *deps; | |
57 | uintptr_t addr; | |
58 | }; | |
59 | ||
60 | KMOD_EXPORT int kmod_loaded_new(struct kmod_ctx *ctx, struct kmod_loaded **mod) | |
61 | { | |
62 | struct kmod_loaded *m; | |
63 | ||
64 | m = calloc(1, sizeof(*m)); | |
65 | if (m == NULL) | |
66 | return -ENOMEM; | |
67 | ||
68 | m->refcount = 1; | |
853b5fc5 | 69 | m->ctx = kmod_ref(ctx); |
5369797d LDM |
70 | *mod = m; |
71 | return 0; | |
72 | } | |
73 | ||
74 | KMOD_EXPORT struct kmod_loaded *kmod_loaded_ref(struct kmod_loaded *mod) | |
75 | { | |
76 | if (mod == NULL) | |
77 | return NULL; | |
78 | mod->refcount++; | |
79 | return mod; | |
80 | } | |
81 | ||
82 | static void loaded_modules_free_module(struct kmod_loaded_module *m) | |
83 | { | |
84 | free(m->name); | |
85 | free(m->deps); | |
86 | free(m); | |
87 | } | |
88 | ||
89 | static void loaded_modules_free(struct kmod_loaded *mod) | |
90 | { | |
91 | while (mod->modules != NULL) { | |
92 | loaded_modules_free_module(mod->modules->data); | |
93 | mod->modules = kmod_list_remove(mod->modules); | |
94 | } | |
95 | } | |
96 | ||
97 | KMOD_EXPORT struct kmod_loaded *kmod_loaded_unref(struct kmod_loaded *mod) | |
98 | { | |
99 | if (mod == NULL) | |
100 | return NULL; | |
cf9aadeb LDM |
101 | |
102 | if (--mod->refcount > 0) | |
103 | return mod; | |
104 | ||
ae6df84a | 105 | DBG(mod->ctx, "kmod_loaded %p released\n", mod); |
853b5fc5 LDM |
106 | |
107 | kmod_unref(mod->ctx); | |
5369797d LDM |
108 | loaded_modules_free(mod); |
109 | free(mod); | |
110 | return NULL; | |
111 | } | |
112 | ||
113 | static int loaded_modules_parse(struct kmod_loaded *mod, | |
114 | struct kmod_list **list) | |
115 | { | |
116 | struct kmod_list *l = NULL; | |
117 | FILE *fp; | |
118 | char line[4096]; | |
119 | ||
120 | fp = fopen("/proc/modules", "r"); | |
121 | if (fp == NULL) | |
122 | return -errno; | |
123 | ||
124 | while (fgets(line, sizeof(line), fp)) { | |
125 | char *tok; | |
126 | struct kmod_loaded_module *m; | |
127 | ||
128 | m = calloc(1, sizeof(*m)); | |
129 | if (m == NULL) | |
130 | goto err; | |
131 | ||
132 | tok = strtok(line, " \t"); | |
133 | m->name = strdup(tok); | |
134 | ||
135 | tok = strtok(NULL, " \t\n"); | |
136 | m->size = atoi(tok); | |
137 | ||
138 | /* Null if no module unloading is supported */ | |
139 | tok = strtok(NULL, " \t\n"); | |
140 | if (tok == NULL) | |
141 | goto done; | |
142 | ||
143 | m->use_count = atoi(tok); | |
144 | tok = strtok(NULL, "\n"); | |
145 | if (tok == NULL) | |
146 | goto done; | |
147 | ||
148 | /* Strip trailing comma */ | |
149 | if (strchr(tok, ',')) { | |
150 | char *end; | |
151 | tok = strtok(tok, " \t"); | |
152 | end = &tok[strlen(tok) - 1]; | |
153 | if (*end == ',') | |
154 | *end = '\0'; | |
155 | m->deps = strdup(tok); | |
156 | tok = &end[2]; | |
157 | } else if (tok[0] == '-' && tok[1] == '\0') | |
158 | goto done; | |
159 | else if (tok[0] == '-' && isspace(tok[1])) | |
160 | tok = &tok[3]; | |
161 | ||
162 | tok = strtok(tok, " \t\n"); | |
163 | if (tok == NULL) | |
164 | goto done; | |
165 | ||
166 | tok = strtok(NULL, " \t\n"); | |
167 | if (tok == NULL) | |
168 | goto done; | |
169 | ||
6fc20bbf | 170 | m->addr = strtoull(tok, NULL, 16); |
5369797d LDM |
171 | |
172 | done: | |
173 | l = kmod_list_append(l, m); | |
174 | } | |
175 | ||
176 | fclose(fp); | |
177 | mod->parsed = 1; | |
178 | *list = l; | |
179 | ||
180 | return 0; | |
181 | ||
182 | err: | |
183 | fclose(fp); | |
184 | mod->modules = l; | |
185 | loaded_modules_free(mod); | |
186 | mod->modules = NULL; | |
187 | return -ENOMEM; | |
188 | } | |
189 | ||
190 | KMOD_EXPORT int kmod_loaded_get_list(struct kmod_loaded *mod, | |
191 | struct kmod_list **list) | |
192 | { | |
193 | if (mod == NULL) | |
194 | return -ENOENT; | |
195 | ||
196 | if (!mod->parsed) { | |
197 | int err = loaded_modules_parse(mod, &mod->modules); | |
198 | if (err < 0) | |
199 | return err; | |
200 | } | |
201 | ||
202 | *list = mod->modules; | |
203 | ||
204 | return 0; | |
205 | } | |
206 | ||
6d177553 | 207 | KMOD_EXPORT int kmod_loaded_get_module_info(const struct kmod_list *entry, |
5369797d LDM |
208 | const char **name, |
209 | long *size, int *use_count, | |
210 | const char **deps, | |
211 | uintptr_t *addr) | |
212 | { | |
1ce08a56 | 213 | const struct kmod_loaded_module *m; |
5369797d LDM |
214 | |
215 | if (entry == NULL) | |
216 | return -ENOENT; | |
217 | ||
218 | m = entry->data; | |
219 | ||
220 | if (name) | |
221 | *name = m->name; | |
222 | if (size) | |
223 | *size = m->size; | |
224 | if (use_count) | |
225 | *use_count = m->use_count; | |
226 | if (addr) | |
227 | *addr = m->addr; | |
228 | if (deps) | |
229 | *deps = m->deps; | |
230 | ||
231 | return 0; | |
232 | } | |
6806a043 LDM |
233 | |
234 | extern long delete_module(const char *name, unsigned int flags); | |
235 | ||
236 | KMOD_EXPORT int kmod_loaded_remove_module(struct kmod_loaded *mod, | |
237 | struct kmod_list *entry, | |
238 | unsigned int flags) | |
239 | { | |
240 | struct kmod_loaded_module *m; | |
241 | int err; | |
242 | ||
243 | if (mod == NULL) | |
244 | return -ENOSYS; | |
245 | ||
246 | if (entry == NULL) | |
247 | return -ENOENT; | |
248 | ||
249 | m = entry->data; | |
250 | ||
251 | /* Filter out other flags */ | |
252 | flags &= (KMOD_REMOVE_FORCE | KMOD_REMOVE_NOWAIT); | |
253 | ||
254 | err = delete_module(m->name, flags); | |
255 | if (err != 0) { | |
ae6df84a | 256 | ERR(mod->ctx, "Removing '%s': %s\n", m->name, |
6806a043 LDM |
257 | strerror(-err)); |
258 | return err; | |
259 | } | |
260 | ||
261 | loaded_modules_free_module(m); | |
262 | kmod_list_remove(entry); | |
263 | ||
264 | return 0; | |
265 | } |