]> git.ipfire.org Git - thirdparty/kmod.git/blame - libkmod/libkmod-config.c
Add address of mailing list
[thirdparty/kmod.git] / libkmod / libkmod-config.c
CommitLineData
7c2ab358
LDM
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
cb451f35
LDM
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
7c2ab358
LDM
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
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 <sys/stat.h>
30#include <sys/types.h>
31#include <dirent.h>
32
33#include "libkmod.h"
34#include "libkmod-private.h"
35
7c2ab358
LDM
36struct kmod_alias {
37 char *name;
43c29d10 38 char modname[];
7c2ab358
LDM
39};
40
615c42be
LDM
41struct kmod_options {
42 char *options;
43 char modname[];
44};
45
a5cce6d6
LDM
46struct kmod_command {
47 char *command;
48 char modname[];
49};
50
b0ef19f7 51const char *kmod_alias_get_name(const struct kmod_list *l) {
1ce08a56 52 const struct kmod_alias *alias = l->data;
b0ef19f7
LDM
53 return alias->name;
54}
55
56const char *kmod_alias_get_modname(const struct kmod_list *l) {
1ce08a56 57 const struct kmod_alias *alias = l->data;
b0ef19f7
LDM
58 return alias->modname;
59}
60
bd3f5535
GSB
61const char *kmod_option_get_options(const struct kmod_list *l) {
62 const struct kmod_options *alias = l->data;
63 return alias->options;
64}
65
66const char *kmod_option_get_modname(const struct kmod_list *l) {
67 const struct kmod_options *alias = l->data;
68 return alias->modname;
69}
70
71const char *kmod_command_get_command(const struct kmod_list *l) {
72 const struct kmod_command *alias = l->data;
73 return alias->command;
74}
75
76const char *kmod_command_get_modname(const struct kmod_list *l) {
77 const struct kmod_command *alias = l->data;
78 return alias->modname;
79}
80
a5cce6d6
LDM
81static int kmod_config_add_command(struct kmod_config *config,
82 const char *modname,
83 const char *command,
84 const char *command_name,
85 struct kmod_list **list)
86{
87 struct kmod_command *cmd;
88 struct kmod_list *l;
89 size_t modnamelen = strlen(modname) + 1;
90 size_t commandlen = strlen(command) + 1;
91
92 DBG(config->ctx, "modname'%s' cmd='%s %s'\n", modname, command_name,
93 command);
94
95 cmd = malloc(sizeof(*cmd) + modnamelen + commandlen);
96 if (cmd == NULL)
97 goto oom_error_init;
98
99 cmd->command = sizeof(*cmd) + modnamelen + (char *)cmd;
100 memcpy(cmd->modname, modname, modnamelen);
101 memcpy(cmd->command, command, commandlen);
102
103 l = kmod_list_append(*list, cmd);
104 if (l == NULL)
105 goto oom_error;
106
107 *list = l;
108 return 0;
109
110oom_error:
111 free(cmd);
112oom_error_init:
113 ERR(config->ctx, "out-of-memory\n");
114 return -ENOMEM;
115}
116
117static void kmod_config_free_command(struct kmod_config *config,
118 struct kmod_list *l,
119 struct kmod_list **list)
120{
121 struct kmod_command *cmd = l->data;
122
123 free(cmd);
124 *list = kmod_list_remove(l);
125}
126
615c42be
LDM
127static int kmod_config_add_options(struct kmod_config *config,
128 const char *modname, const char *options)
129{
130 struct kmod_options *opt;
131 struct kmod_list *list;
132 size_t modnamelen = strlen(modname) + 1;
133 size_t optionslen = strlen(options) + 1;
134
23c0d012 135 DBG(config->ctx, "modname='%s' options='%s'\n", modname, options);
615c42be
LDM
136
137 opt = malloc(sizeof(*opt) + modnamelen + optionslen);
138 if (opt == NULL)
139 goto oom_error_init;
140
141 opt->options = sizeof(*opt) + modnamelen + (char *)opt;
142
143 memcpy(opt->modname, modname, modnamelen);
144 memcpy(opt->options, options, optionslen);
145 strchr_replace(opt->options, '\t', ' ');
146
147 list = kmod_list_append(config->options, opt);
148 if (list == NULL)
149 goto oom_error;
150
151 config->options = list;
152 return 0;
153
154oom_error:
155 free(opt);
156oom_error_init:
157 ERR(config->ctx, "out-of-memory\n");
158 return -ENOMEM;
159}
160
c35347f1
LDM
161static void kmod_config_free_options(struct kmod_config *config,
162 struct kmod_list *l)
615c42be
LDM
163{
164 struct kmod_options *opt = l->data;
165
166 free(opt);
167
168 config->options = kmod_list_remove(l);
169}
170
d13e606f 171static int kmod_config_add_alias(struct kmod_config *config,
c35347f1 172 const char *name, const char *modname)
7c2ab358
LDM
173{
174 struct kmod_alias *alias;
d13e606f 175 struct kmod_list *list;
43c29d10 176 size_t namelen = strlen(name) + 1, modnamelen = strlen(modname) + 1;
7c2ab358 177
d13e606f 178 DBG(config->ctx, "name=%s modname=%s\n", name, modname);
7c2ab358 179
43c29d10 180 alias = malloc(sizeof(*alias) + namelen + modnamelen);
d13e606f
GSB
181 if (!alias)
182 goto oom_error_init;
28c175ed 183
43c29d10
GSB
184 alias->name = sizeof(*alias) + modnamelen + (char *)alias;
185
186 memcpy(alias->modname, modname, modnamelen);
187 memcpy(alias->name, name, namelen);
7c2ab358 188
d13e606f
GSB
189 list = kmod_list_append(config->aliases, alias);
190 if (!list)
191 goto oom_error;
28c175ed 192
d13e606f
GSB
193 config->aliases = list;
194 return 0;
195
196oom_error:
d13e606f
GSB
197 free(alias);
198oom_error_init:
199 ERR(config->ctx, "out-of-memory name=%s modname=%s\n", name, modname);
200 return -ENOMEM;
7c2ab358
LDM
201}
202
c35347f1
LDM
203static void kmod_config_free_alias(struct kmod_config *config,
204 struct kmod_list *l)
7c2ab358
LDM
205{
206 struct kmod_alias *alias = l->data;
207
7c2ab358
LDM
208 free(alias);
209
d13e606f 210 config->aliases = kmod_list_remove(l);
7c2ab358
LDM
211}
212
d13e606f 213static int kmod_config_add_blacklist(struct kmod_config *config,
c35347f1 214 const char *modname)
81cf2060 215{
81cf2060 216 char *p;
d13e606f 217 struct kmod_list *list;
81cf2060 218
d13e606f 219 DBG(config->ctx, "modname=%s\n", modname);
81cf2060
LDM
220
221 p = strdup(modname);
d13e606f
GSB
222 if (!p)
223 goto oom_error_init;
224
225 list = kmod_list_append(config->blacklists, p);
226 if (!list)
227 goto oom_error;
228 config->blacklists = list;
229 return 0;
81cf2060 230
d13e606f
GSB
231oom_error:
232 free(p);
233oom_error_init:
234 ERR(config->ctx, "out-of-memory modname=%s\n", modname);
235 return -ENOMEM;
81cf2060
LDM
236}
237
d13e606f 238static void kmod_config_free_blacklist(struct kmod_config *config,
81cf2060
LDM
239 struct kmod_list *l)
240{
241 free(l->data);
d13e606f 242 config->blacklists = kmod_list_remove(l);
81cf2060
LDM
243}
244
1684e440
LDM
245static void kcmdline_parse_result(struct kmod_config *config, char *modname,
246 char *param, char *value)
247{
248 if (modname == NULL || param == NULL || value == NULL)
249 return;
250
251 DBG(config->ctx, "%s %s\n", modname, param);
252
253 if (streq(modname, "modprobe") && !strncmp(param, "blacklist=", 10)) {
254 for (;;) {
255 char *t = strsep(&value, ",");
256 if (t == NULL)
257 break;
258
259 kmod_config_add_blacklist(config, t);
260 }
261 } else {
262 kmod_config_add_options(config,
263 underscores(config->ctx, modname), param);
264 }
265}
266
267static int kmod_config_parse_kcmdline(struct kmod_config *config)
268{
269 char buf[KCMD_LINE_SIZE];
270 int fd, err;
271 char *p, *modname, *param = NULL, *value = NULL;
272
273 fd = open("/proc/cmdline", O_RDONLY);
274 err = read_str_safe(fd, buf, sizeof(buf));
275 close(fd);
276 if (err < 0) {
277 ERR(config->ctx, "could not read from '/proc/cmdline': %s\n",
278 strerror(-err));
279 return err;
280 }
281
282 for (p = buf, modname = buf; *p != '\0' && *p != '\n'; p++) {
283 switch (*p) {
284 case ' ':
285 *p = '\0';
286 kcmdline_parse_result(config, modname, param, value);
287 param = value = NULL;
288 modname = p + 1;
289 break;
290 case '.':
291 *p = '\0';
292 param = p + 1;
293 break;
294 case '=':
295 value = p + 1;
296 break;
297 }
298 }
299
300 *p = '\0';
301 kcmdline_parse_result(config, modname, param, value);
302
303 return 0;
304}
305
b7b7ac29
LDM
306/*
307 * Take an fd and own it. It will be closed on return. filename is used only
308 * for debug messages
309 */
310static int kmod_config_parse(struct kmod_config *config, int fd,
311 const char *filename)
7c2ab358 312{
d13e606f 313 struct kmod_ctx *ctx = config->ctx;
7c2ab358
LDM
314 char *line;
315 FILE *fp;
316 unsigned int linenum;
b7b7ac29 317 int err;
7c2ab358 318
b7b7ac29
LDM
319 fp = fdopen(fd, "r");
320 if (fp == NULL) {
321 err = -errno;
322 ERR(config->ctx, "fd %d: %m", fd);
323 close(fd);
324 return err;
325 }
7c2ab358
LDM
326
327 while ((line = getline_wrapped(fp, &linenum)) != NULL) {
c11e62bf 328 char *cmd, *saveptr;
7c2ab358
LDM
329
330 if (line[0] == '\0' || line[0] == '#')
331 goto done_next;
332
c11e62bf 333 cmd = strtok_r(line, "\t ", &saveptr);
7c2ab358
LDM
334 if (cmd == NULL)
335 goto done_next;
336
877e80cd 337 if (streq(cmd, "alias")) {
c11e62bf
LDM
338 char *alias = strtok_r(NULL, "\t ", &saveptr);
339 char *modname = strtok_r(NULL, "\t ", &saveptr);
7c2ab358
LDM
340
341 if (alias == NULL || modname == NULL)
342 goto syntax_error;
343
d13e606f 344 kmod_config_add_alias(config,
30be7513
LDM
345 underscores(ctx, alias),
346 underscores(ctx, modname));
877e80cd 347 } else if (streq(cmd, "blacklist")) {
c11e62bf 348 char *modname = strtok_r(NULL, "\t ", &saveptr);
81cf2060
LDM
349
350 if (modname == NULL)
351 goto syntax_error;
352
d13e606f 353 kmod_config_add_blacklist(config,
2295acc5 354 underscores(ctx, modname));
615c42be
LDM
355 } else if (streq(cmd, "options")) {
356 char *modname = strtok_r(NULL, "\t ", &saveptr);
357
358 if (modname == NULL)
359 goto syntax_error;
360
361 kmod_config_add_options(config,
362 underscores(ctx, modname),
363 strtok_r(NULL, "\0", &saveptr));
a5cce6d6
LDM
364 } else if streq(cmd, "install") {
365 char *modname = strtok_r(NULL, "\t ", &saveptr);
366
367 if (modname == NULL)
368 goto syntax_error;
369
370 kmod_config_add_command(config,
371 underscores(ctx, modname),
372 strtok_r(NULL, "\0", &saveptr),
373 cmd, &config->install_commands);
374 } else if streq(cmd, "remove") {
375 char *modname = strtok_r(NULL, "\t ", &saveptr);
376
377 if (modname == NULL)
378 goto syntax_error;
379
380 kmod_config_add_command(config,
381 underscores(ctx, modname),
382 strtok_r(NULL, "\0", &saveptr),
383 cmd, &config->remove_commands);
615c42be 384 } else if (streq(cmd, "include")
877e80cd
LDM
385 || streq(cmd, "softdep")
386 || streq(cmd, "config")) {
81cf2060
LDM
387 INFO(ctx, "%s: command %s not implemented yet\n",
388 filename, cmd);
7c2ab358
LDM
389 } else {
390syntax_error:
391 ERR(ctx, "%s line %u: ignoring bad line starting with '%s'\n",
392 filename, linenum, cmd);
393 }
394
395done_next:
396 free(line);
397 }
398
399 fclose(fp);
400
401 return 0;
402}
403
d13e606f 404void kmod_config_free(struct kmod_config *config)
7c2ab358
LDM
405{
406 while (config->aliases)
d13e606f 407 kmod_config_free_alias(config, config->aliases);
81cf2060
LDM
408
409 while (config->blacklists)
d13e606f
GSB
410 kmod_config_free_blacklist(config, config->blacklists);
411
615c42be
LDM
412 while (config->options)
413 kmod_config_free_options(config, config->options);
414
a5cce6d6
LDM
415 while (config->install_commands) {
416 kmod_config_free_command(config, config->install_commands,
417 &config->install_commands);
418 }
419
420 while (config->remove_commands) {
421 kmod_config_free_command(config, config->remove_commands,
422 &config->remove_commands);
423 }
424
d13e606f 425 free(config);
7c2ab358
LDM
426}
427
98c80f44
LDM
428static bool conf_files_filter_out(struct kmod_ctx *ctx, DIR *d,
429 const char *path, const char *fn)
7c2ab358
LDM
430{
431 size_t len = strlen(fn);
98c80f44 432 struct stat st;
7c2ab358
LDM
433
434 if (fn[0] == '.')
8f767e2d 435 return true;
7c2ab358 436
877e80cd
LDM
437 if (len < 6 || (!streq(&fn[len - 5], ".conf")
438 && !streq(&fn[len - 6], ".alias"))) {
7c2ab358
LDM
439 INFO(ctx, "All config files need .conf: %s/%s, "
440 "it will be ignored in a future release\n",
441 path, fn);
8f767e2d 442 return true;
7c2ab358
LDM
443 }
444
98c80f44
LDM
445 fstatat(dirfd(d), fn, &st, 0);
446
447 if (S_ISDIR(st.st_mode)) {
448 ERR(ctx, "Directories inside directories are not supported: "
449 "%s/%s\n", path, fn);
8f767e2d 450 return true;
98c80f44
LDM
451 }
452
8f767e2d 453 return false;
7c2ab358
LDM
454}
455
4782396c
LDM
456/*
457 * Iterate over a directory (given by @path) and save the list of
458 * configuration files in @list.
459 */
b7b7ac29
LDM
460static DIR *conf_files_list(struct kmod_ctx *ctx, struct kmod_list **list,
461 const char *path)
7c2ab358 462{
7c2ab358
LDM
463 DIR *d;
464 int err;
465
1c250ec1
LDM
466 *list = NULL;
467
7c2ab358
LDM
468 d = opendir(path);
469 if (d == NULL) {
7c2ab358 470 ERR(ctx, "%m\n");
b7b7ac29 471 return NULL;
7c2ab358
LDM
472 }
473
474 for (;;) {
475 struct dirent ent, *entp;
b7b7ac29
LDM
476 struct kmod_list *l, *tmp;
477 const char *dname;
7c2ab358
LDM
478
479 err = readdir_r(d, &ent, &entp);
480 if (err != 0) {
b7b7ac29
LDM
481 ERR(ctx, "reading entry %s\n", strerror(-err));
482 goto fail_read;
7c2ab358
LDM
483 }
484
485 if (entp == NULL)
486 break;
487
8f767e2d 488 if (conf_files_filter_out(ctx, d, path, entp->d_name))
7c2ab358
LDM
489 continue;
490
b7b7ac29
LDM
491 /* insert sorted */
492 kmod_list_foreach(l, *list) {
493 if (strcmp(entp->d_name, l->data) < 0)
494 break;
7c2ab358
LDM
495 }
496
b7b7ac29
LDM
497 dname = strdup(entp->d_name);
498 if (dname == NULL)
499 goto fail_oom;
7c2ab358 500
b7b7ac29
LDM
501 if (l == NULL)
502 tmp = kmod_list_append(*list, dname);
503 else if (l == *list)
504 tmp = kmod_list_prepend(*list, dname);
505 else
506 tmp = kmod_list_insert_before(l, dname);
7c2ab358 507
b7b7ac29
LDM
508 if (tmp == NULL)
509 goto fail_oom;
7c2ab358 510
b7b7ac29
LDM
511 if (l == NULL || l == *list)
512 *list = tmp;
513 }
7c2ab358 514
b7b7ac29 515 return d;
7c2ab358 516
b7b7ac29
LDM
517fail_oom:
518 ERR(ctx, "out of memory while scanning '%s'\n", path);
519fail_read:
520 for (; *list != NULL; *list = kmod_list_remove(*list))
521 free((*list)->data);
522 closedir(d);
523 return NULL;
7c2ab358
LDM
524}
525
c35347f1
LDM
526int kmod_config_new(struct kmod_ctx *ctx, struct kmod_config **p_config,
527 const char * const *config_paths)
7c2ab358 528{
d13e606f 529 struct kmod_config *config;
b7b7ac29 530 size_t i;
7c2ab358 531
d13e606f 532 *p_config = config = calloc(1, sizeof(struct kmod_config));
2295acc5 533 if (config == NULL)
d13e606f 534 return -ENOMEM;
2295acc5 535
d13e606f
GSB
536 config->ctx = ctx;
537
cb8d4d3e
GSB
538 for (i = 0; config_paths[i] != NULL; i++) {
539 const char *path = config_paths[i];
4782396c 540 struct kmod_list *list;
cb8d4d3e 541 struct stat st;
b7b7ac29 542 DIR *d;
7c2ab358 543
cb8d4d3e
GSB
544 if (stat(path, &st) != 0) {
545 DBG(ctx, "could not load '%s': %s\n",
546 path, strerror(errno));
547 continue;
548 }
549
550 if (S_ISREG(st.st_mode)) {
551 int fd = open(path, O_RDONLY);
552 DBG(ctx, "parsing file '%s': %d\n", path, fd);
553 if (fd >= 0)
554 kmod_config_parse(config, fd, path);
555 continue;
556 } else if (!S_ISDIR(st.st_mode)) {
557 ERR(ctx, "unsupported file mode %s: %#x\n",
558 path, st.st_mode);
559 continue;
560 }
561
562 d = conf_files_list(ctx, &list, path);
7c2ab358 563
b7b7ac29 564 for (; list != NULL; list = kmod_list_remove(list)) {
cb8d4d3e
GSB
565 int fd = openat(dirfd(d), list->data, O_RDONLY);
566 DBG(ctx, "parsing file '%s/%s': %d\n", path,
567 (const char *) list->data, fd);
b7b7ac29
LDM
568 if (fd >= 0)
569 kmod_config_parse(config, fd, list->data);
7c2ab358 570
b7b7ac29
LDM
571 free(list->data);
572 }
7c2ab358 573
b7b7ac29 574 closedir(d);
7c2ab358
LDM
575 }
576
1684e440
LDM
577 kmod_config_parse_kcmdline(config);
578
b7b7ac29 579 return 0;
7c2ab358 580}