]> git.ipfire.org Git - thirdparty/kmod.git/blame - libkmod/libkmod-config.c
Move strbuf implementation to shared/
[thirdparty/kmod.git] / libkmod / libkmod-config.c
CommitLineData
7c2ab358
LDM
1/*
2 * libkmod - interface to kernel module operations
3 *
e6b0e49b 4 * Copyright (C) 2011-2013 ProFUSION embedded systems
342e9cea 5 * Copyright (C) 2013 Intel Corporation. All rights reserved.
7c2ab358
LDM
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
cb451f35
LDM
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
7c2ab358
LDM
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
c2e4286b
LDM
22#include <ctype.h>
23#include <dirent.h>
24#include <errno.h>
25#include <stdarg.h>
26#include <stddef.h>
7c2ab358
LDM
27#include <stdio.h>
28#include <stdlib.h>
7c2ab358 29#include <string.h>
c2e4286b 30#include <unistd.h>
7c2ab358
LDM
31#include <sys/stat.h>
32#include <sys/types.h>
7c2ab358 33
96573a02
LDM
34#include <shared/util.h>
35
7c2ab358 36#include "libkmod.h"
83b855a6 37#include "libkmod-internal.h"
7c2ab358 38
7c2ab358
LDM
39struct kmod_alias {
40 char *name;
43c29d10 41 char modname[];
7c2ab358
LDM
42};
43
615c42be
LDM
44struct kmod_options {
45 char *options;
46 char modname[];
47};
48
a5cce6d6
LDM
49struct kmod_command {
50 char *command;
51 char modname[];
52};
53
1c522600
GSB
54struct kmod_softdep {
55 char *name;
56 const char **pre;
57 const char **post;
58 unsigned int n_pre;
59 unsigned int n_post;
60};
61
c1c9c446
LDM
62const char *kmod_blacklist_get_modname(const struct kmod_list *l)
63{
64 return l->data;
65}
66
b0ef19f7 67const char *kmod_alias_get_name(const struct kmod_list *l) {
1ce08a56 68 const struct kmod_alias *alias = l->data;
b0ef19f7
LDM
69 return alias->name;
70}
71
72const char *kmod_alias_get_modname(const struct kmod_list *l) {
1ce08a56 73 const struct kmod_alias *alias = l->data;
b0ef19f7
LDM
74 return alias->modname;
75}
76
bd3f5535
GSB
77const char *kmod_option_get_options(const struct kmod_list *l) {
78 const struct kmod_options *alias = l->data;
79 return alias->options;
80}
81
82const char *kmod_option_get_modname(const struct kmod_list *l) {
83 const struct kmod_options *alias = l->data;
84 return alias->modname;
85}
86
87const char *kmod_command_get_command(const struct kmod_list *l) {
88 const struct kmod_command *alias = l->data;
89 return alias->command;
90}
91
92const char *kmod_command_get_modname(const struct kmod_list *l) {
93 const struct kmod_command *alias = l->data;
94 return alias->modname;
95}
96
1c522600
GSB
97const char *kmod_softdep_get_name(const struct kmod_list *l) {
98 const struct kmod_softdep *dep = l->data;
99 return dep->name;
100}
101
102const char * const *kmod_softdep_get_pre(const struct kmod_list *l, unsigned int *count) {
103 const struct kmod_softdep *dep = l->data;
104 *count = dep->n_pre;
105 return dep->pre;
106}
107
108const char * const *kmod_softdep_get_post(const struct kmod_list *l, unsigned int *count) {
109 const struct kmod_softdep *dep = l->data;
110 *count = dep->n_post;
111 return dep->post;
112}
113
a5cce6d6
LDM
114static int kmod_config_add_command(struct kmod_config *config,
115 const char *modname,
116 const char *command,
117 const char *command_name,
118 struct kmod_list **list)
119{
342e9cea 120 _cleanup_free_ struct kmod_command *cmd;
a5cce6d6
LDM
121 struct kmod_list *l;
122 size_t modnamelen = strlen(modname) + 1;
123 size_t commandlen = strlen(command) + 1;
124
e5a7f6ac 125 DBG(config->ctx, "modname='%s' cmd='%s %s'\n", modname, command_name,
a5cce6d6
LDM
126 command);
127
128 cmd = malloc(sizeof(*cmd) + modnamelen + commandlen);
342e9cea
LDM
129 if (!cmd)
130 return -ENOMEM;
a5cce6d6
LDM
131
132 cmd->command = sizeof(*cmd) + modnamelen + (char *)cmd;
133 memcpy(cmd->modname, modname, modnamelen);
134 memcpy(cmd->command, command, commandlen);
135
136 l = kmod_list_append(*list, cmd);
342e9cea
LDM
137 if (!l)
138 return -ENOMEM;
a5cce6d6
LDM
139
140 *list = l;
342e9cea 141 cmd = NULL;
a5cce6d6 142 return 0;
a5cce6d6
LDM
143}
144
145static void kmod_config_free_command(struct kmod_config *config,
146 struct kmod_list *l,
147 struct kmod_list **list)
148{
149 struct kmod_command *cmd = l->data;
150
151 free(cmd);
152 *list = kmod_list_remove(l);
153}
154
615c42be
LDM
155static int kmod_config_add_options(struct kmod_config *config,
156 const char *modname, const char *options)
157{
342e9cea 158 _cleanup_free_ struct kmod_options *opt;
615c42be
LDM
159 struct kmod_list *list;
160 size_t modnamelen = strlen(modname) + 1;
161 size_t optionslen = strlen(options) + 1;
162
23c0d012 163 DBG(config->ctx, "modname='%s' options='%s'\n", modname, options);
615c42be
LDM
164
165 opt = malloc(sizeof(*opt) + modnamelen + optionslen);
342e9cea
LDM
166 if (!opt)
167 return -ENOMEM;
615c42be
LDM
168
169 opt->options = sizeof(*opt) + modnamelen + (char *)opt;
170
171 memcpy(opt->modname, modname, modnamelen);
172 memcpy(opt->options, options, optionslen);
173 strchr_replace(opt->options, '\t', ' ');
174
175 list = kmod_list_append(config->options, opt);
342e9cea
LDM
176 if (!list)
177 return -ENOMEM;
615c42be 178
342e9cea 179 opt = NULL;
615c42be
LDM
180 config->options = list;
181 return 0;
615c42be
LDM
182}
183
c35347f1
LDM
184static void kmod_config_free_options(struct kmod_config *config,
185 struct kmod_list *l)
615c42be
LDM
186{
187 struct kmod_options *opt = l->data;
188
189 free(opt);
190
191 config->options = kmod_list_remove(l);
192}
193
d13e606f 194static int kmod_config_add_alias(struct kmod_config *config,
c35347f1 195 const char *name, const char *modname)
7c2ab358 196{
342e9cea 197 _cleanup_free_ struct kmod_alias *alias;
d13e606f 198 struct kmod_list *list;
43c29d10 199 size_t namelen = strlen(name) + 1, modnamelen = strlen(modname) + 1;
7c2ab358 200
d13e606f 201 DBG(config->ctx, "name=%s modname=%s\n", name, modname);
7c2ab358 202
43c29d10 203 alias = malloc(sizeof(*alias) + namelen + modnamelen);
d13e606f 204 if (!alias)
342e9cea 205 return -ENOMEM;
28c175ed 206
43c29d10
GSB
207 alias->name = sizeof(*alias) + modnamelen + (char *)alias;
208
209 memcpy(alias->modname, modname, modnamelen);
210 memcpy(alias->name, name, namelen);
7c2ab358 211
d13e606f
GSB
212 list = kmod_list_append(config->aliases, alias);
213 if (!list)
342e9cea 214 return -ENOMEM;
28c175ed 215
342e9cea 216 alias = NULL;
d13e606f
GSB
217 config->aliases = list;
218 return 0;
7c2ab358
LDM
219}
220
c35347f1
LDM
221static void kmod_config_free_alias(struct kmod_config *config,
222 struct kmod_list *l)
7c2ab358
LDM
223{
224 struct kmod_alias *alias = l->data;
225
7c2ab358
LDM
226 free(alias);
227
d13e606f 228 config->aliases = kmod_list_remove(l);
7c2ab358
LDM
229}
230
d13e606f 231static int kmod_config_add_blacklist(struct kmod_config *config,
c35347f1 232 const char *modname)
81cf2060 233{
342e9cea 234 _cleanup_free_ char *p;
d13e606f 235 struct kmod_list *list;
81cf2060 236
d13e606f 237 DBG(config->ctx, "modname=%s\n", modname);
81cf2060
LDM
238
239 p = strdup(modname);
d13e606f 240 if (!p)
342e9cea 241 return -ENOMEM;
d13e606f
GSB
242
243 list = kmod_list_append(config->blacklists, p);
244 if (!list)
342e9cea
LDM
245 return -ENOMEM;
246
247 p = NULL;
d13e606f
GSB
248 config->blacklists = list;
249 return 0;
81cf2060
LDM
250}
251
d13e606f 252static void kmod_config_free_blacklist(struct kmod_config *config,
81cf2060
LDM
253 struct kmod_list *l)
254{
255 free(l->data);
d13e606f 256 config->blacklists = kmod_list_remove(l);
81cf2060
LDM
257}
258
1c522600
GSB
259static int kmod_config_add_softdep(struct kmod_config *config,
260 const char *modname,
261 const char *line)
262{
263 struct kmod_list *list;
264 struct kmod_softdep *dep;
265 const char *s, *p;
266 char *itr;
267 unsigned int n_pre = 0, n_post = 0;
268 size_t modnamelen = strlen(modname) + 1;
269 size_t buflen = 0;
270 bool was_space = false;
271 enum { S_NONE, S_PRE, S_POST } mode = S_NONE;
272
273 DBG(config->ctx, "modname=%s\n", modname);
274
275 /* analyze and count */
276 for (p = s = line; ; s++) {
277 size_t plen;
278
279 if (*s != '\0') {
280 if (!isspace(*s)) {
281 was_space = false;
282 continue;
283 }
284
285 if (was_space) {
286 p = s + 1;
287 continue;
288 }
289 was_space = true;
290
291 if (p >= s)
292 continue;
293 }
294 plen = s - p;
295
296 if (plen == sizeof("pre:") - 1 &&
297 memcmp(p, "pre:", sizeof("pre:") - 1) == 0)
298 mode = S_PRE;
299 else if (plen == sizeof("post:") - 1 &&
300 memcmp(p, "post:", sizeof("post:") - 1) == 0)
301 mode = S_POST;
302 else if (*s != '\0' || (*s == '\0' && !was_space)) {
303 if (mode == S_PRE) {
304 buflen += plen + 1;
305 n_pre++;
306 } else if (mode == S_POST) {
307 buflen += plen + 1;
308 n_post++;
309 }
310 }
311 p = s + 1;
312 if (*s == '\0')
313 break;
314 }
315
316 DBG(config->ctx, "%u pre, %u post\n", n_pre, n_post);
317
318 dep = malloc(sizeof(struct kmod_softdep) + modnamelen +
319 n_pre * sizeof(const char *) +
320 n_post * sizeof(const char *) +
321 buflen);
322 if (dep == NULL) {
323 ERR(config->ctx, "out-of-memory modname=%s\n", modname);
324 return -ENOMEM;
325 }
326 dep->n_pre = n_pre;
327 dep->n_post = n_post;
328 dep->pre = (const char **)((char *)dep + sizeof(struct kmod_softdep));
329 dep->post = dep->pre + n_pre;
330 dep->name = (char *)(dep->post + n_post);
331
332 memcpy(dep->name, modname, modnamelen);
333
334 /* copy strings */
335 itr = dep->name + modnamelen;
336 n_pre = 0;
337 n_post = 0;
338 mode = S_NONE;
339 for (p = s = line; ; s++) {
340 size_t plen;
341
342 if (*s != '\0') {
343 if (!isspace(*s)) {
344 was_space = false;
345 continue;
346 }
347
348 if (was_space) {
349 p = s + 1;
350 continue;
351 }
352 was_space = true;
353
354 if (p >= s)
355 continue;
356 }
357 plen = s - p;
358
359 if (plen == sizeof("pre:") - 1 &&
360 memcmp(p, "pre:", sizeof("pre:") - 1) == 0)
361 mode = S_PRE;
362 else if (plen == sizeof("post:") - 1 &&
363 memcmp(p, "post:", sizeof("post:") - 1) == 0)
364 mode = S_POST;
365 else if (*s != '\0' || (*s == '\0' && !was_space)) {
366 if (mode == S_PRE) {
367 dep->pre[n_pre] = itr;
368 memcpy(itr, p, plen);
369 itr[plen] = '\0';
370 itr += plen + 1;
371 n_pre++;
372 } else if (mode == S_POST) {
373 dep->post[n_post] = itr;
374 memcpy(itr, p, plen);
375 itr[plen] = '\0';
376 itr += plen + 1;
377 n_post++;
378 }
379 }
380 p = s + 1;
381 if (*s == '\0')
382 break;
383 }
384
385 list = kmod_list_append(config->softdeps, dep);
386 if (list == NULL) {
387 free(dep);
388 return -ENOMEM;
389 }
390 config->softdeps = list;
391
392 return 0;
393}
394
6b04ef32
LDM
395static char *softdep_to_char(struct kmod_softdep *dep) {
396 const size_t sz_preprefix = sizeof("pre: ") - 1;
397 const size_t sz_postprefix = sizeof("post: ") - 1;
398 size_t sz = 1; /* at least '\0' */
399 size_t sz_pre, sz_post;
400 const char *start, *end;
401 char *s, *itr;
402
403 /*
404 * Rely on the fact that dep->pre[] and dep->post[] are strv's that
405 * point to a contiguous buffer
406 */
407 if (dep->n_pre > 0) {
408 start = dep->pre[0];
409 end = dep->pre[dep->n_pre - 1]
410 + strlen(dep->pre[dep->n_pre - 1]);
411 sz_pre = end - start;
412 sz += sz_pre + sz_preprefix;
413 } else
414 sz_pre = 0;
415
416 if (dep->n_post > 0) {
417 start = dep->post[0];
418 end = dep->post[dep->n_post - 1]
419 + strlen(dep->post[dep->n_post - 1]);
420 sz_post = end - start;
421 sz += sz_post + sz_postprefix;
422 } else
423 sz_post = 0;
424
425 itr = s = malloc(sz);
426 if (s == NULL)
427 return NULL;
428
429 if (sz_pre) {
430 char *p;
431
432 memcpy(itr, "pre: ", sz_preprefix);
433 itr += sz_preprefix;
434
435 /* include last '\0' */
436 memcpy(itr, dep->pre[0], sz_pre + 1);
437 for (p = itr; p < itr + sz_pre; p++) {
438 if (*p == '\0')
439 *p = ' ';
440 }
441 itr = p;
442 }
443
444 if (sz_post) {
445 char *p;
446
447 memcpy(itr, "post: ", sz_postprefix);
448 itr += sz_postprefix;
449
450 /* include last '\0' */
451 memcpy(itr, dep->post[0], sz_post + 1);
452 for (p = itr; p < itr + sz_post; p++) {
453 if (*p == '\0')
454 *p = ' ';
455 }
456 itr = p;
457 }
458
459 *itr = '\0';
460
461 return s;
462}
463
1c522600
GSB
464static void kmod_config_free_softdep(struct kmod_config *config,
465 struct kmod_list *l)
466{
467 free(l->data);
468 config->softdeps = kmod_list_remove(l);
469}
470
1684e440
LDM
471static void kcmdline_parse_result(struct kmod_config *config, char *modname,
472 char *param, char *value)
473{
493dc650 474 if (modname == NULL || param == NULL)
1684e440
LDM
475 return;
476
477 DBG(config->ctx, "%s %s\n", modname, param);
478
479 if (streq(modname, "modprobe") && !strncmp(param, "blacklist=", 10)) {
480 for (;;) {
481 char *t = strsep(&value, ",");
482 if (t == NULL)
483 break;
484
485 kmod_config_add_blacklist(config, t);
486 }
487 } else {
52c9c990
LDM
488 if (underscores(modname) < 0) {
489 ERR(config->ctx, "Ignoring bad option on kernel command line while parsing module name: '%s'\n",
490 modname);
491 }
492 kmod_config_add_options(config, modname, param);
1684e440
LDM
493 }
494}
495
496static int kmod_config_parse_kcmdline(struct kmod_config *config)
497{
498 char buf[KCMD_LINE_SIZE];
499 int fd, err;
aa878540 500 char *p, *modname, *param = NULL, *value = NULL, is_module = 1;
1684e440 501
79e5ea91 502 fd = open("/proc/cmdline", O_RDONLY|O_CLOEXEC);
dd1cf10f
LDM
503 if (fd < 0) {
504 err = -errno;
505 DBG(config->ctx, "could not open '/proc/cmdline' for reading: %m\n");
506 return err;
507 }
508
1684e440
LDM
509 err = read_str_safe(fd, buf, sizeof(buf));
510 close(fd);
511 if (err < 0) {
512 ERR(config->ctx, "could not read from '/proc/cmdline': %s\n",
513 strerror(-err));
514 return err;
515 }
516
517 for (p = buf, modname = buf; *p != '\0' && *p != '\n'; p++) {
518 switch (*p) {
519 case ' ':
520 *p = '\0';
aa878540
MM
521 if (is_module)
522 kcmdline_parse_result(config, modname, param, value);
1684e440
LDM
523 param = value = NULL;
524 modname = p + 1;
aa878540 525 is_module = 1;
1684e440
LDM
526 break;
527 case '.':
66f3228d
LDM
528 if (param == NULL) {
529 *p = '\0';
530 param = p + 1;
531 }
1684e440
LDM
532 break;
533 case '=':
135bffd6
LDM
534 if (param != NULL)
535 value = p + 1;
aa878540
MM
536 else
537 is_module = 0;
1684e440
LDM
538 break;
539 }
540 }
541
542 *p = '\0';
aa878540
MM
543 if (is_module)
544 kcmdline_parse_result(config, modname, param, value);
1684e440
LDM
545
546 return 0;
547}
548
b7b7ac29
LDM
549/*
550 * Take an fd and own it. It will be closed on return. filename is used only
551 * for debug messages
552 */
553static int kmod_config_parse(struct kmod_config *config, int fd,
554 const char *filename)
7c2ab358 555{
d13e606f 556 struct kmod_ctx *ctx = config->ctx;
7c2ab358
LDM
557 char *line;
558 FILE *fp;
759214fa 559 unsigned int linenum = 0;
b7b7ac29 560 int err;
7c2ab358 561
b7b7ac29
LDM
562 fp = fdopen(fd, "r");
563 if (fp == NULL) {
564 err = -errno;
050db08c 565 ERR(config->ctx, "fd %d: %m\n", fd);
b7b7ac29
LDM
566 close(fd);
567 return err;
568 }
7c2ab358 569
aafd3835 570 while ((line = freadline_wrapped(fp, &linenum)) != NULL) {
c11e62bf 571 char *cmd, *saveptr;
7c2ab358
LDM
572
573 if (line[0] == '\0' || line[0] == '#')
574 goto done_next;
575
c11e62bf 576 cmd = strtok_r(line, "\t ", &saveptr);
7c2ab358
LDM
577 if (cmd == NULL)
578 goto done_next;
579
877e80cd 580 if (streq(cmd, "alias")) {
c11e62bf
LDM
581 char *alias = strtok_r(NULL, "\t ", &saveptr);
582 char *modname = strtok_r(NULL, "\t ", &saveptr);
7c2ab358 583
52c9c990 584 if (underscores(alias) < 0 || underscores(modname) < 0)
7c2ab358
LDM
585 goto syntax_error;
586
52c9c990 587 kmod_config_add_alias(config, alias, modname);
877e80cd 588 } else if (streq(cmd, "blacklist")) {
c11e62bf 589 char *modname = strtok_r(NULL, "\t ", &saveptr);
81cf2060 590
52c9c990 591 if (underscores(modname) < 0)
81cf2060
LDM
592 goto syntax_error;
593
52c9c990 594 kmod_config_add_blacklist(config, modname);
615c42be
LDM
595 } else if (streq(cmd, "options")) {
596 char *modname = strtok_r(NULL, "\t ", &saveptr);
83121fde 597 char *options = strtok_r(NULL, "\0", &saveptr);
615c42be 598
52c9c990 599 if (underscores(modname) < 0 || options == NULL)
615c42be
LDM
600 goto syntax_error;
601
52c9c990 602 kmod_config_add_options(config, modname, options);
40ee8dad 603 } else if (streq(cmd, "install")) {
a5cce6d6 604 char *modname = strtok_r(NULL, "\t ", &saveptr);
83121fde 605 char *installcmd = strtok_r(NULL, "\0", &saveptr);
a5cce6d6 606
52c9c990 607 if (underscores(modname) < 0 || installcmd == NULL)
a5cce6d6
LDM
608 goto syntax_error;
609
52c9c990 610 kmod_config_add_command(config, modname, installcmd,
a5cce6d6 611 cmd, &config->install_commands);
40ee8dad 612 } else if (streq(cmd, "remove")) {
a5cce6d6 613 char *modname = strtok_r(NULL, "\t ", &saveptr);
83121fde 614 char *removecmd = strtok_r(NULL, "\0", &saveptr);
a5cce6d6 615
52c9c990 616 if (underscores(modname) < 0 || removecmd == NULL)
a5cce6d6
LDM
617 goto syntax_error;
618
52c9c990 619 kmod_config_add_command(config, modname, removecmd,
a5cce6d6 620 cmd, &config->remove_commands);
40ee8dad 621 } else if (streq(cmd, "softdep")) {
1c522600 622 char *modname = strtok_r(NULL, "\t ", &saveptr);
83121fde 623 char *softdeps = strtok_r(NULL, "\0", &saveptr);
1c522600 624
52c9c990 625 if (underscores(modname) < 0 || softdeps == NULL)
1c522600
GSB
626 goto syntax_error;
627
52c9c990 628 kmod_config_add_softdep(config, modname, softdeps);
615c42be 629 } else if (streq(cmd, "include")
877e80cd 630 || streq(cmd, "config")) {
0ad5dd08 631 ERR(ctx, "%s: command %s is deprecated and not parsed anymore\n",
81cf2060 632 filename, cmd);
7c2ab358
LDM
633 } else {
634syntax_error:
635 ERR(ctx, "%s line %u: ignoring bad line starting with '%s'\n",
636 filename, linenum, cmd);
637 }
638
639done_next:
640 free(line);
641 }
642
643 fclose(fp);
644
645 return 0;
646}
647
d13e606f 648void kmod_config_free(struct kmod_config *config)
7c2ab358
LDM
649{
650 while (config->aliases)
d13e606f 651 kmod_config_free_alias(config, config->aliases);
81cf2060
LDM
652
653 while (config->blacklists)
d13e606f
GSB
654 kmod_config_free_blacklist(config, config->blacklists);
655
615c42be
LDM
656 while (config->options)
657 kmod_config_free_options(config, config->options);
658
a5cce6d6
LDM
659 while (config->install_commands) {
660 kmod_config_free_command(config, config->install_commands,
661 &config->install_commands);
662 }
663
664 while (config->remove_commands) {
665 kmod_config_free_command(config, config->remove_commands,
666 &config->remove_commands);
667 }
668
1c522600
GSB
669 while (config->softdeps)
670 kmod_config_free_softdep(config, config->softdeps);
671
b6a4dfb1
LDM
672 for (; config->paths != NULL;
673 config->paths = kmod_list_remove(config->paths))
674 free(config->paths->data);
675
d13e606f 676 free(config);
7c2ab358
LDM
677}
678
98c80f44
LDM
679static bool conf_files_filter_out(struct kmod_ctx *ctx, DIR *d,
680 const char *path, const char *fn)
7c2ab358
LDM
681{
682 size_t len = strlen(fn);
98c80f44 683 struct stat st;
7c2ab358
LDM
684
685 if (fn[0] == '.')
8f767e2d 686 return true;
7c2ab358 687
877e80cd 688 if (len < 6 || (!streq(&fn[len - 5], ".conf")
9070b117 689 && !streq(&fn[len - 6], ".alias")))
8f767e2d 690 return true;
7c2ab358 691
98c80f44
LDM
692 fstatat(dirfd(d), fn, &st, 0);
693
694 if (S_ISDIR(st.st_mode)) {
695 ERR(ctx, "Directories inside directories are not supported: "
696 "%s/%s\n", path, fn);
8f767e2d 697 return true;
98c80f44
LDM
698 }
699
8f767e2d 700 return false;
7c2ab358
LDM
701}
702
7fe5f7ab
LDM
703struct conf_file {
704 const char *path;
705 bool is_single;
706 char name[];
707};
708
709static int conf_files_insert_sorted(struct kmod_ctx *ctx,
710 struct kmod_list **list,
711 const char *path, const char *name)
712{
713 struct kmod_list *lpos, *tmp;
714 struct conf_file *cf;
715 size_t namelen;
716 int cmp = -1;
717 bool is_single = false;
718
719 if (name == NULL) {
720 name = basename(path);
721 is_single = true;
722 }
723
724 kmod_list_foreach(lpos, *list) {
725 cf = lpos->data;
726
727 if ((cmp = strcmp(name, cf->name)) <= 0)
728 break;
729 }
730
731 if (cmp == 0) {
732 DBG(ctx, "Ignoring duplicate config file: %s/%s\n", path,
733 name);
734 return -EEXIST;
735 }
736
737 namelen = strlen(name);
738 cf = malloc(sizeof(*cf) + namelen + 1);
739 if (cf == NULL)
740 return -ENOMEM;
741
742 memcpy(cf->name, name, namelen + 1);
743 cf->path = path;
744 cf->is_single = is_single;
745
746 if (lpos == NULL)
747 tmp = kmod_list_append(*list, cf);
748 else if (lpos == *list)
749 tmp = kmod_list_prepend(*list, cf);
750 else
751 tmp = kmod_list_insert_before(lpos, cf);
752
753 if (tmp == NULL) {
754 free(cf);
755 return -ENOMEM;
756 }
757
758 if (lpos == NULL || lpos == *list)
759 *list = tmp;
760
761 return 0;
762}
763
4782396c 764/*
7fe5f7ab 765 * Insert configuration files in @list, ignoring duplicates
4782396c 766 */
7fe5f7ab 767static int conf_files_list(struct kmod_ctx *ctx, struct kmod_list **list,
b6a4dfb1
LDM
768 const char *path,
769 unsigned long long *path_stamp)
7c2ab358 770{
7c2ab358
LDM
771 DIR *d;
772 int err;
7fe5f7ab 773 struct stat st;
7e0385c4 774 struct dirent *dent;
7c2ab358 775
7fe5f7ab
LDM
776 if (stat(path, &st) != 0) {
777 err = -errno;
778 DBG(ctx, "could not stat '%s': %m\n", path);
779 return err;
780 }
781
6068aaae 782 *path_stamp = stat_mstamp(&st);
b6a4dfb1 783
519d27de 784 if (!S_ISDIR(st.st_mode)) {
7fe5f7ab
LDM
785 conf_files_insert_sorted(ctx, list, path, NULL);
786 return 0;
7fe5f7ab 787 }
1c250ec1 788
7c2ab358
LDM
789 d = opendir(path);
790 if (d == NULL) {
dfa96f15 791 ERR(ctx, "opendir(%s): %m\n", path);
7fe5f7ab 792 return -EINVAL;
7c2ab358
LDM
793 }
794
7e0385c4
LDM
795 for (dent = readdir(d); dent != NULL; dent = readdir(d)) {
796 if (conf_files_filter_out(ctx, d, path, dent->d_name))
7c2ab358
LDM
797 continue;
798
7e0385c4 799 conf_files_insert_sorted(ctx, list, path, dent->d_name);
b7b7ac29 800 }
7c2ab358 801
7fe5f7ab
LDM
802 closedir(d);
803 return 0;
7c2ab358
LDM
804}
805
c35347f1
LDM
806int kmod_config_new(struct kmod_ctx *ctx, struct kmod_config **p_config,
807 const char * const *config_paths)
7c2ab358 808{
d13e606f 809 struct kmod_config *config;
7fe5f7ab 810 struct kmod_list *list = NULL;
b6a4dfb1 811 struct kmod_list *path_list = NULL;
b7b7ac29 812 size_t i;
7c2ab358 813
8240333b
TG
814 conf_files_insert_sorted(ctx, &list, kmod_get_dirname(ctx), "modules.softdep");
815
cb8d4d3e
GSB
816 for (i = 0; config_paths[i] != NULL; i++) {
817 const char *path = config_paths[i];
b6a4dfb1
LDM
818 unsigned long long path_stamp = 0;
819 size_t pathlen;
820 struct kmod_list *tmp;
821 struct kmod_config_path *cf;
822
823 if (conf_files_list(ctx, &list, path, &path_stamp) < 0)
824 continue;
825
826 pathlen = strlen(path) + 1;
827 cf = malloc(sizeof(*cf) + pathlen);
828 if (cf == NULL)
829 goto oom;
7c2ab358 830
b6a4dfb1
LDM
831 cf->stamp = path_stamp;
832 memcpy(cf->path, path, pathlen);
833
834 tmp = kmod_list_append(path_list, cf);
835 if (tmp == NULL)
836 goto oom;
837 path_list = tmp;
7fe5f7ab 838 }
cb8d4d3e 839
b6a4dfb1
LDM
840 *p_config = config = calloc(1, sizeof(struct kmod_config));
841 if (config == NULL)
842 goto oom;
843
844 config->paths = path_list;
29b69c0b 845 config->ctx = ctx;
b6a4dfb1 846
7fe5f7ab
LDM
847 for (; list != NULL; list = kmod_list_remove(list)) {
848 char fn[PATH_MAX];
849 struct conf_file *cf = list->data;
850 int fd;
cb8d4d3e 851
7fe5f7ab
LDM
852 if (cf->is_single)
853 strcpy(fn, cf->path);
854 else
855 snprintf(fn, sizeof(fn),"%s/%s", cf->path,
856 cf->name);
7c2ab358 857
7fe5f7ab
LDM
858 fd = open(fn, O_RDONLY|O_CLOEXEC);
859 DBG(ctx, "parsing file '%s' fd=%d\n", fn, fd);
7c2ab358 860
7fe5f7ab
LDM
861 if (fd >= 0)
862 kmod_config_parse(config, fd, fn);
7c2ab358 863
7fe5f7ab 864 free(cf);
7c2ab358
LDM
865 }
866
1684e440
LDM
867 kmod_config_parse_kcmdline(config);
868
b7b7ac29 869 return 0;
b6a4dfb1
LDM
870
871oom:
872 for (; list != NULL; list = kmod_list_remove(list))
873 free(list->data);
874
875 for (; path_list != NULL; path_list = kmod_list_remove(path_list))
876 free(path_list->data);
877
878 return -ENOMEM;
7c2ab358 879}
00178629
LDM
880
881/**********************************************************************
882 * struct kmod_config_iter functions
883 **********************************************************************/
884
885enum config_type {
886 CONFIG_TYPE_BLACKLIST = 0,
887 CONFIG_TYPE_INSTALL,
888 CONFIG_TYPE_REMOVE,
889 CONFIG_TYPE_ALIAS,
890 CONFIG_TYPE_OPTION,
891 CONFIG_TYPE_SOFTDEP,
892};
893
894struct kmod_config_iter {
895 enum config_type type;
6b04ef32 896 bool intermediate;
00178629
LDM
897 const struct kmod_list *list;
898 const struct kmod_list *curr;
6b04ef32 899 void *data;
00178629
LDM
900 const char *(*get_key)(const struct kmod_list *l);
901 const char *(*get_value)(const struct kmod_list *l);
902};
903
6b04ef32
LDM
904static const char *softdep_get_plain_softdep(const struct kmod_list *l)
905{
906 char *s = softdep_to_char(l->data);
907 return s;
908}
909
00178629
LDM
910static struct kmod_config_iter *kmod_config_iter_new(const struct kmod_ctx* ctx,
911 enum config_type type)
912{
913 struct kmod_config_iter *iter = calloc(1, sizeof(*iter));
e7fc2c86 914 const struct kmod_config *config = kmod_get_config(ctx);
00178629
LDM
915
916 if (iter == NULL)
917 return NULL;
918
919 iter->type = type;
920
921 switch (type) {
922 case CONFIG_TYPE_BLACKLIST:
e7fc2c86 923 iter->list = config->blacklists;
00178629
LDM
924 iter->get_key = kmod_blacklist_get_modname;
925 break;
926 case CONFIG_TYPE_INSTALL:
e7fc2c86 927 iter->list = config->install_commands;
00178629
LDM
928 iter->get_key = kmod_command_get_modname;
929 iter->get_value = kmod_command_get_command;
930 break;
931 case CONFIG_TYPE_REMOVE:
e7fc2c86 932 iter->list = config->remove_commands;
00178629
LDM
933 iter->get_key = kmod_command_get_modname;
934 iter->get_value = kmod_command_get_command;
935 break;
936 case CONFIG_TYPE_ALIAS:
e7fc2c86 937 iter->list = config->aliases;
00178629
LDM
938 iter->get_key = kmod_alias_get_name;
939 iter->get_value = kmod_alias_get_modname;
940 break;
941 case CONFIG_TYPE_OPTION:
e7fc2c86 942 iter->list = config->options;
00178629
LDM
943 iter->get_key = kmod_option_get_modname;
944 iter->get_value = kmod_option_get_options;
945 break;
946 case CONFIG_TYPE_SOFTDEP:
e7fc2c86 947 iter->list = config->softdeps;
00178629 948 iter->get_key = kmod_softdep_get_name;
6b04ef32
LDM
949 iter->get_value = softdep_get_plain_softdep;
950 iter->intermediate = true;
00178629
LDM
951 break;
952 }
953
954 return iter;
955}
956
2f47c7fa
LDM
957/**
958 * SECTION:libkmod-config
959 * @short_description: retrieve current libkmod configuration
960 */
961
962/**
963 * kmod_config_get_blacklists:
964 * @ctx: kmod library context
965 *
966 * Retrieve an iterator to deal with the blacklist maintained inside the
967 * library. See kmod_config_iter_get_key(), kmod_config_iter_get_value() and
968 * kmod_config_iter_next(). At least one call to kmod_config_iter_next() must
969 * be made to initialize the iterator and check if it's valid.
970 *
883d8c42 971 * Returns: a new iterator over the blacklists or NULL on failure. Free it
2f47c7fa
LDM
972 * with kmod_config_iter_free_iter().
973 */
00178629
LDM
974KMOD_EXPORT struct kmod_config_iter *kmod_config_get_blacklists(const struct kmod_ctx *ctx)
975{
976 if (ctx == NULL)
977 return NULL;;
978
979 return kmod_config_iter_new(ctx, CONFIG_TYPE_BLACKLIST);
980}
981
2f47c7fa
LDM
982/**
983 * kmod_config_get_install_commands:
984 * @ctx: kmod library context
985 *
986 * Retrieve an iterator to deal with the install commands maintained inside the
987 * library. See kmod_config_iter_get_key(), kmod_config_iter_get_value() and
988 * kmod_config_iter_next(). At least one call to kmod_config_iter_next() must
989 * be made to initialize the iterator and check if it's valid.
990 *
883d8c42 991 * Returns: a new iterator over the install commands or NULL on failure. Free
2f47c7fa
LDM
992 * it with kmod_config_iter_free_iter().
993 */
00178629
LDM
994KMOD_EXPORT struct kmod_config_iter *kmod_config_get_install_commands(const struct kmod_ctx *ctx)
995{
996 if (ctx == NULL)
997 return NULL;;
998
999 return kmod_config_iter_new(ctx, CONFIG_TYPE_INSTALL);
1000}
1001
2f47c7fa
LDM
1002/**
1003 * kmod_config_get_remove_commands:
1004 * @ctx: kmod library context
1005 *
1006 * Retrieve an iterator to deal with the remove commands maintained inside the
1007 * library. See kmod_config_iter_get_key(), kmod_config_iter_get_value() and
1008 * kmod_config_iter_next(). At least one call to kmod_config_iter_next() must
1009 * be made to initialize the iterator and check if it's valid.
1010 *
883d8c42 1011 * Returns: a new iterator over the remove commands or NULL on failure. Free
2f47c7fa
LDM
1012 * it with kmod_config_iter_free_iter().
1013 */
00178629
LDM
1014KMOD_EXPORT struct kmod_config_iter *kmod_config_get_remove_commands(const struct kmod_ctx *ctx)
1015{
1016 if (ctx == NULL)
1017 return NULL;;
1018
1019 return kmod_config_iter_new(ctx, CONFIG_TYPE_REMOVE);
1020}
1021
2f47c7fa
LDM
1022/**
1023 * kmod_config_get_aliases:
1024 * @ctx: kmod library context
1025 *
1026 * Retrieve an iterator to deal with the aliases maintained inside the
1027 * library. See kmod_config_iter_get_key(), kmod_config_iter_get_value() and
1028 * kmod_config_iter_next(). At least one call to kmod_config_iter_next() must
1029 * be made to initialize the iterator and check if it's valid.
1030 *
883d8c42 1031 * Returns: a new iterator over the aliases or NULL on failure. Free it with
2f47c7fa
LDM
1032 * kmod_config_iter_free_iter().
1033 */
00178629
LDM
1034KMOD_EXPORT struct kmod_config_iter *kmod_config_get_aliases(const struct kmod_ctx *ctx)
1035{
1036 if (ctx == NULL)
1037 return NULL;;
1038
1039 return kmod_config_iter_new(ctx, CONFIG_TYPE_ALIAS);
1040}
1041
2f47c7fa
LDM
1042/**
1043 * kmod_config_get_options:
1044 * @ctx: kmod library context
1045 *
1046 * Retrieve an iterator to deal with the options maintained inside the
1047 * library. See kmod_config_iter_get_key(), kmod_config_iter_get_value() and
1048 * kmod_config_iter_next(). At least one call to kmod_config_iter_next() must
1049 * be made to initialize the iterator and check if it's valid.
1050 *
883d8c42 1051 * Returns: a new iterator over the options or NULL on failure. Free it with
2f47c7fa
LDM
1052 * kmod_config_iter_free_iter().
1053 */
00178629
LDM
1054KMOD_EXPORT struct kmod_config_iter *kmod_config_get_options(const struct kmod_ctx *ctx)
1055{
1056 if (ctx == NULL)
1057 return NULL;;
1058
1059 return kmod_config_iter_new(ctx, CONFIG_TYPE_OPTION);
1060}
1061
2f47c7fa
LDM
1062/**
1063 * kmod_config_get_softdeps:
1064 * @ctx: kmod library context
1065 *
1066 * Retrieve an iterator to deal with the softdeps maintained inside the
1067 * library. See kmod_config_iter_get_key(), kmod_config_iter_get_value() and
1068 * kmod_config_iter_next(). At least one call to kmod_config_iter_next() must
1069 * be made to initialize the iterator and check if it's valid.
1070 *
883d8c42 1071 * Returns: a new iterator over the softdeps or NULL on failure. Free it with
2f47c7fa
LDM
1072 * kmod_config_iter_free_iter().
1073 */
00178629
LDM
1074KMOD_EXPORT struct kmod_config_iter *kmod_config_get_softdeps(const struct kmod_ctx *ctx)
1075{
1076 if (ctx == NULL)
1077 return NULL;;
1078
1079 return kmod_config_iter_new(ctx, CONFIG_TYPE_SOFTDEP);
1080}
1081
2f47c7fa
LDM
1082/**
1083 * kmod_config_iter_get_key:
1084 * @iter: iterator over a certain configuration
1085 *
1086 * When using a new allocated iterator, user must perform a call to
1087 * kmod_config_iter_next() to initialize iterator's position and check if it's
1088 * valid.
1089 *
1090 * Returns: the key of the current configuration pointed by @iter.
1091 */
00178629
LDM
1092KMOD_EXPORT const char *kmod_config_iter_get_key(const struct kmod_config_iter *iter)
1093{
1094 if (iter == NULL || iter->curr == NULL)
1095 return NULL;
1096
1097 return iter->get_key(iter->curr);
1098}
1099
2f47c7fa
LDM
1100/**
1101 * kmod_config_iter_get_value:
1102 * @iter: iterator over a certain configuration
1103 *
1104 * When using a new allocated iterator, user must perform a call to
1105 * kmod_config_iter_next() to initialize iterator's position and check if it's
1106 * valid.
1107 *
1108 * Returns: the value of the current configuration pointed by @iter.
1109 */
00178629
LDM
1110KMOD_EXPORT const char *kmod_config_iter_get_value(const struct kmod_config_iter *iter)
1111{
6b04ef32
LDM
1112 const char *s;
1113
00178629
LDM
1114 if (iter == NULL || iter->curr == NULL)
1115 return NULL;
1116
1117 if (iter->get_value == NULL)
1118 return NULL;
1119
6b04ef32
LDM
1120 if (iter->intermediate) {
1121 struct kmod_config_iter *i = (struct kmod_config_iter *)iter;
1122
1123 free(i->data);
1124 s = i->data = (void *) iter->get_value(iter->curr);
1125 } else
1126 s = iter->get_value(iter->curr);
1127
1128 return s;
00178629
LDM
1129}
1130
2f47c7fa
LDM
1131/**
1132 * kmod_config_iter_next:
1133 * @iter: iterator over a certain configuration
1134 *
1135 * Make @iter point to the next item of a certain configuration. It's an
1136 * automatically recycling iterator. When it reaches the end, false is
1137 * returned; then if user wants to iterate again, it's sufficient to call this
1138 * function once more.
1139 *
1140 * Returns: true if next position of @iter is valid or false if its end is
1141 * reached.
1142 */
00178629
LDM
1143KMOD_EXPORT bool kmod_config_iter_next(struct kmod_config_iter *iter)
1144{
1145 if (iter == NULL)
1146 return false;
1147
1148 if (iter->curr == NULL) {
1149 iter->curr = iter->list;
1150 return iter->curr != NULL;
1151 }
1152
1153 iter->curr = kmod_list_next(iter->list, iter->curr);
1154
1155 return iter->curr != NULL;
1156}
1157
2f47c7fa
LDM
1158/**
1159 * kmod_config_iter_free_iter:
1160 * @iter: iterator over a certain configuration
1161 *
1162 * Free resources used by the iterator.
1163 */
00178629
LDM
1164KMOD_EXPORT void kmod_config_iter_free_iter(struct kmod_config_iter *iter)
1165{
6b04ef32 1166 free(iter->data);
00178629
LDM
1167 free(iter);
1168}