]> git.ipfire.org Git - thirdparty/kmod.git/blob - libkmod/libkmod-config.c
kmod_config: parse install and remove commands
[thirdparty/kmod.git] / libkmod / libkmod-config.c
1 /*
2 * libkmod - interface to kernel module operations
3 *
4 * Copyright (C) 2011 ProFUSION embedded systems
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 <sys/stat.h>
29 #include <sys/types.h>
30 #include <dirent.h>
31
32 #include "libkmod.h"
33 #include "libkmod-private.h"
34
35 static const char *config_files[] = {
36 "/run/modprobe.d",
37 "/etc/modprobe.d",
38 "/lib/modprobe.d",
39 };
40
41 struct kmod_alias {
42 char *name;
43 char modname[];
44 };
45
46 struct kmod_options {
47 char *options;
48 char modname[];
49 };
50
51 struct kmod_command {
52 char *command;
53 char modname[];
54 };
55
56 const char *kmod_alias_get_name(const struct kmod_list *l) {
57 const struct kmod_alias *alias = l->data;
58 return alias->name;
59 }
60
61 const char *kmod_alias_get_modname(const struct kmod_list *l) {
62 const struct kmod_alias *alias = l->data;
63 return alias->modname;
64 }
65
66 static int kmod_config_add_command(struct kmod_config *config,
67 const char *modname,
68 const char *command,
69 const char *command_name,
70 struct kmod_list **list)
71 {
72 struct kmod_command *cmd;
73 struct kmod_list *l;
74 size_t modnamelen = strlen(modname) + 1;
75 size_t commandlen = strlen(command) + 1;
76
77 DBG(config->ctx, "modname'%s' cmd='%s %s'\n", modname, command_name,
78 command);
79
80 cmd = malloc(sizeof(*cmd) + modnamelen + commandlen);
81 if (cmd == NULL)
82 goto oom_error_init;
83
84 cmd->command = sizeof(*cmd) + modnamelen + (char *)cmd;
85 memcpy(cmd->modname, modname, modnamelen);
86 memcpy(cmd->command, command, commandlen);
87
88 l = kmod_list_append(*list, cmd);
89 if (l == NULL)
90 goto oom_error;
91
92 *list = l;
93 return 0;
94
95 oom_error:
96 free(cmd);
97 oom_error_init:
98 ERR(config->ctx, "out-of-memory\n");
99 return -ENOMEM;
100 }
101
102 static void kmod_config_free_command(struct kmod_config *config,
103 struct kmod_list *l,
104 struct kmod_list **list)
105 {
106 struct kmod_command *cmd = l->data;
107
108 free(cmd);
109 *list = kmod_list_remove(l);
110 }
111
112 static int kmod_config_add_options(struct kmod_config *config,
113 const char *modname, const char *options)
114 {
115 struct kmod_options *opt;
116 struct kmod_list *list;
117 size_t modnamelen = strlen(modname) + 1;
118 size_t optionslen = strlen(options) + 1;
119
120 DBG(config->ctx, "modname'%s' options='%s'\n", modname, options);
121
122 opt = malloc(sizeof(*opt) + modnamelen + optionslen);
123 if (opt == NULL)
124 goto oom_error_init;
125
126 opt->options = sizeof(*opt) + modnamelen + (char *)opt;
127
128 memcpy(opt->modname, modname, modnamelen);
129 memcpy(opt->options, options, optionslen);
130 strchr_replace(opt->options, '\t', ' ');
131
132 list = kmod_list_append(config->options, opt);
133 if (list == NULL)
134 goto oom_error;
135
136 config->options = list;
137 return 0;
138
139 oom_error:
140 free(opt);
141 oom_error_init:
142 ERR(config->ctx, "out-of-memory\n");
143 return -ENOMEM;
144 }
145
146 static void kmod_config_free_options(struct kmod_config *config, struct kmod_list *l)
147 {
148 struct kmod_options *opt = l->data;
149
150 free(opt);
151
152 config->options = kmod_list_remove(l);
153 }
154
155 static int kmod_config_add_alias(struct kmod_config *config,
156 const char *name, const char *modname)
157 {
158 struct kmod_alias *alias;
159 struct kmod_list *list;
160 size_t namelen = strlen(name) + 1, modnamelen = strlen(modname) + 1;
161
162 DBG(config->ctx, "name=%s modname=%s\n", name, modname);
163
164 alias = malloc(sizeof(*alias) + namelen + modnamelen);
165 if (!alias)
166 goto oom_error_init;
167 alias->name = sizeof(*alias) + modnamelen + (char *)alias;
168
169 memcpy(alias->modname, modname, modnamelen);
170 memcpy(alias->name, name, namelen);
171
172 list = kmod_list_append(config->aliases, alias);
173 if (!list)
174 goto oom_error;
175 config->aliases = list;
176 return 0;
177
178 oom_error:
179 free(alias);
180 oom_error_init:
181 ERR(config->ctx, "out-of-memory name=%s modname=%s\n", name, modname);
182 return -ENOMEM;
183 }
184
185 static void kmod_config_free_alias(struct kmod_config *config, struct kmod_list *l)
186 {
187 struct kmod_alias *alias = l->data;
188
189 free(alias);
190
191 config->aliases = kmod_list_remove(l);
192 }
193
194 static int kmod_config_add_blacklist(struct kmod_config *config,
195 const char *modname)
196 {
197 char *p;
198 struct kmod_list *list;
199
200 DBG(config->ctx, "modname=%s\n", modname);
201
202 p = strdup(modname);
203 if (!p)
204 goto oom_error_init;
205
206 list = kmod_list_append(config->blacklists, p);
207 if (!list)
208 goto oom_error;
209 config->blacklists = list;
210 return 0;
211
212 oom_error:
213 free(p);
214 oom_error_init:
215 ERR(config->ctx, "out-of-memory modname=%s\n", modname);
216 return -ENOMEM;
217 }
218
219 static void kmod_config_free_blacklist(struct kmod_config *config,
220 struct kmod_list *l)
221 {
222 free(l->data);
223 config->blacklists = kmod_list_remove(l);
224 }
225
226 /*
227 * Take an fd and own it. It will be closed on return. filename is used only
228 * for debug messages
229 */
230 static int kmod_config_parse(struct kmod_config *config, int fd,
231 const char *filename)
232 {
233 struct kmod_ctx *ctx = config->ctx;
234 char *line;
235 FILE *fp;
236 unsigned int linenum;
237 int err;
238
239 fp = fdopen(fd, "r");
240 if (fp == NULL) {
241 err = -errno;
242 ERR(config->ctx, "fd %d: %m", fd);
243 close(fd);
244 return err;
245 }
246
247 while ((line = getline_wrapped(fp, &linenum)) != NULL) {
248 char *cmd, *saveptr;
249
250 if (line[0] == '\0' || line[0] == '#')
251 goto done_next;
252
253 cmd = strtok_r(line, "\t ", &saveptr);
254 if (cmd == NULL)
255 goto done_next;
256
257 if (streq(cmd, "alias")) {
258 char *alias = strtok_r(NULL, "\t ", &saveptr);
259 char *modname = strtok_r(NULL, "\t ", &saveptr);
260
261 if (alias == NULL || modname == NULL)
262 goto syntax_error;
263
264 kmod_config_add_alias(config,
265 underscores(ctx, alias),
266 underscores(ctx, modname));
267 } else if (streq(cmd, "blacklist")) {
268 char *modname = strtok_r(NULL, "\t ", &saveptr);
269
270 if (modname == NULL)
271 goto syntax_error;
272
273 kmod_config_add_blacklist(config,
274 underscores(ctx, modname));
275 } else if (streq(cmd, "options")) {
276 char *modname = strtok_r(NULL, "\t ", &saveptr);
277
278 if (modname == NULL)
279 goto syntax_error;
280
281 kmod_config_add_options(config,
282 underscores(ctx, modname),
283 strtok_r(NULL, "\0", &saveptr));
284 } else if streq(cmd, "install") {
285 char *modname = strtok_r(NULL, "\t ", &saveptr);
286
287 if (modname == NULL)
288 goto syntax_error;
289
290 kmod_config_add_command(config,
291 underscores(ctx, modname),
292 strtok_r(NULL, "\0", &saveptr),
293 cmd, &config->install_commands);
294 } else if streq(cmd, "remove") {
295 char *modname = strtok_r(NULL, "\t ", &saveptr);
296
297 if (modname == NULL)
298 goto syntax_error;
299
300 kmod_config_add_command(config,
301 underscores(ctx, modname),
302 strtok_r(NULL, "\0", &saveptr),
303 cmd, &config->remove_commands);
304 } else if (streq(cmd, "include")
305 || streq(cmd, "softdep")
306 || streq(cmd, "config")) {
307 INFO(ctx, "%s: command %s not implemented yet\n",
308 filename, cmd);
309 } else {
310 syntax_error:
311 ERR(ctx, "%s line %u: ignoring bad line starting with '%s'\n",
312 filename, linenum, cmd);
313 }
314
315 done_next:
316 free(line);
317 }
318
319 fclose(fp);
320
321 return 0;
322 }
323
324 void kmod_config_free(struct kmod_config *config)
325 {
326 while (config->aliases)
327 kmod_config_free_alias(config, config->aliases);
328
329 while (config->blacklists)
330 kmod_config_free_blacklist(config, config->blacklists);
331
332 while (config->options)
333 kmod_config_free_options(config, config->options);
334
335 while (config->install_commands) {
336 kmod_config_free_command(config, config->install_commands,
337 &config->install_commands);
338 }
339
340 while (config->remove_commands) {
341 kmod_config_free_command(config, config->remove_commands,
342 &config->remove_commands);
343 }
344
345 free(config);
346 }
347
348 static bool conf_files_filter_out(struct kmod_ctx *ctx, const char *path,
349 const char *fn)
350 {
351 size_t len = strlen(fn);
352
353 if (fn[0] == '.')
354 return 1;
355
356 if (len < 6 || (!streq(&fn[len - 5], ".conf")
357 && !streq(&fn[len - 6], ".alias"))) {
358 INFO(ctx, "All config files need .conf: %s/%s, "
359 "it will be ignored in a future release\n",
360 path, fn);
361 return 1;
362 }
363
364 return 0;
365 }
366
367 static DIR *conf_files_list(struct kmod_ctx *ctx, struct kmod_list **list,
368 const char *path)
369 {
370 struct stat st;
371 DIR *d;
372 int err;
373
374 if (stat(path, &st) < 0)
375 return NULL;
376
377 if (!S_ISDIR(st.st_mode)) {
378 *list = kmod_list_append(*list, path);
379 return NULL;
380 }
381
382 d = opendir(path);
383 if (d == NULL) {
384 err = errno;
385 ERR(ctx, "%m\n");
386 return NULL;
387 }
388
389 for (;;) {
390 struct dirent ent, *entp;
391 struct kmod_list *l, *tmp;
392 const char *dname;
393
394 err = readdir_r(d, &ent, &entp);
395 if (err != 0) {
396 ERR(ctx, "reading entry %s\n", strerror(-err));
397 goto fail_read;
398 }
399
400 if (entp == NULL)
401 break;
402
403 if (conf_files_filter_out(ctx, path, entp->d_name) == 1)
404 continue;
405
406 /* insert sorted */
407 kmod_list_foreach(l, *list) {
408 if (strcmp(entp->d_name, l->data) < 0)
409 break;
410 }
411
412 dname = strdup(entp->d_name);
413 if (dname == NULL)
414 goto fail_oom;
415
416 if (l == NULL)
417 tmp = kmod_list_append(*list, dname);
418 else if (l == *list)
419 tmp = kmod_list_prepend(*list, dname);
420 else
421 tmp = kmod_list_insert_before(l, dname);
422
423 if (tmp == NULL)
424 goto fail_oom;
425
426 if (l == NULL || l == *list)
427 *list = tmp;
428 }
429
430 return d;
431
432 fail_oom:
433 ERR(ctx, "out of memory while scanning '%s'\n", path);
434 fail_read:
435 for (; *list != NULL; *list = kmod_list_remove(*list))
436 free((*list)->data);
437 closedir(d);
438 return NULL;
439 }
440
441 int kmod_config_new(struct kmod_ctx *ctx, struct kmod_config **p_config)
442 {
443 struct kmod_config *config;
444 size_t i;
445
446 *p_config = config = calloc(1, sizeof(struct kmod_config));
447 if (config == NULL)
448 return -ENOMEM;
449
450 config->ctx = ctx;
451
452 for (i = 0; i < ARRAY_SIZE(config_files); i++) {
453 struct kmod_list *list = NULL;
454 DIR *d;
455 int fd;
456
457 d = conf_files_list(ctx, &list, config_files[i]);
458
459 /* there's no entry */
460 if (list == NULL)
461 continue;
462
463 /* there's only one entry, and it's a file */
464 if (d == NULL) {
465 DBG(ctx, "parsing file '%s'\n", config_files[i]);
466 list = kmod_list_remove(list);
467 fd = open(config_files[i], O_RDONLY);
468 if (fd >= 0)
469 kmod_config_parse(config, fd, config_files[i]);
470
471 continue;
472 }
473
474 /* treat all the entries in that dir */
475 for (; list != NULL; list = kmod_list_remove(list)) {
476 DBG(ctx, "parsing file '%s/%s'\n", config_files[i],
477 (char *) list->data);
478 fd = openat(dirfd(d), list->data, O_RDONLY);
479 if (fd >= 0)
480 kmod_config_parse(config, fd, list->data);
481
482 free(list->data);
483 }
484
485 closedir(d);
486 }
487
488 return 0;
489 }