]> git.ipfire.org Git - thirdparty/kmod.git/blame - libkmod/libkmod-config.c
implement softdeps.
[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
1c522600
GSB
51struct kmod_softdep {
52 char *name;
53 const char **pre;
54 const char **post;
55 unsigned int n_pre;
56 unsigned int n_post;
57};
58
b0ef19f7 59const char *kmod_alias_get_name(const struct kmod_list *l) {
1ce08a56 60 const struct kmod_alias *alias = l->data;
b0ef19f7
LDM
61 return alias->name;
62}
63
64const char *kmod_alias_get_modname(const struct kmod_list *l) {
1ce08a56 65 const struct kmod_alias *alias = l->data;
b0ef19f7
LDM
66 return alias->modname;
67}
68
bd3f5535
GSB
69const char *kmod_option_get_options(const struct kmod_list *l) {
70 const struct kmod_options *alias = l->data;
71 return alias->options;
72}
73
74const char *kmod_option_get_modname(const struct kmod_list *l) {
75 const struct kmod_options *alias = l->data;
76 return alias->modname;
77}
78
79const char *kmod_command_get_command(const struct kmod_list *l) {
80 const struct kmod_command *alias = l->data;
81 return alias->command;
82}
83
84const char *kmod_command_get_modname(const struct kmod_list *l) {
85 const struct kmod_command *alias = l->data;
86 return alias->modname;
87}
88
1c522600
GSB
89const char *kmod_softdep_get_name(const struct kmod_list *l) {
90 const struct kmod_softdep *dep = l->data;
91 return dep->name;
92}
93
94const char * const *kmod_softdep_get_pre(const struct kmod_list *l, unsigned int *count) {
95 const struct kmod_softdep *dep = l->data;
96 *count = dep->n_pre;
97 return dep->pre;
98}
99
100const char * const *kmod_softdep_get_post(const struct kmod_list *l, unsigned int *count) {
101 const struct kmod_softdep *dep = l->data;
102 *count = dep->n_post;
103 return dep->post;
104}
105
a5cce6d6
LDM
106static int kmod_config_add_command(struct kmod_config *config,
107 const char *modname,
108 const char *command,
109 const char *command_name,
110 struct kmod_list **list)
111{
112 struct kmod_command *cmd;
113 struct kmod_list *l;
114 size_t modnamelen = strlen(modname) + 1;
115 size_t commandlen = strlen(command) + 1;
116
117 DBG(config->ctx, "modname'%s' cmd='%s %s'\n", modname, command_name,
118 command);
119
120 cmd = malloc(sizeof(*cmd) + modnamelen + commandlen);
121 if (cmd == NULL)
122 goto oom_error_init;
123
124 cmd->command = sizeof(*cmd) + modnamelen + (char *)cmd;
125 memcpy(cmd->modname, modname, modnamelen);
126 memcpy(cmd->command, command, commandlen);
127
128 l = kmod_list_append(*list, cmd);
129 if (l == NULL)
130 goto oom_error;
131
132 *list = l;
133 return 0;
134
135oom_error:
136 free(cmd);
137oom_error_init:
138 ERR(config->ctx, "out-of-memory\n");
139 return -ENOMEM;
140}
141
142static void kmod_config_free_command(struct kmod_config *config,
143 struct kmod_list *l,
144 struct kmod_list **list)
145{
146 struct kmod_command *cmd = l->data;
147
148 free(cmd);
149 *list = kmod_list_remove(l);
150}
151
615c42be
LDM
152static int kmod_config_add_options(struct kmod_config *config,
153 const char *modname, const char *options)
154{
155 struct kmod_options *opt;
156 struct kmod_list *list;
157 size_t modnamelen = strlen(modname) + 1;
158 size_t optionslen = strlen(options) + 1;
159
23c0d012 160 DBG(config->ctx, "modname='%s' options='%s'\n", modname, options);
615c42be
LDM
161
162 opt = malloc(sizeof(*opt) + modnamelen + optionslen);
163 if (opt == NULL)
164 goto oom_error_init;
165
166 opt->options = sizeof(*opt) + modnamelen + (char *)opt;
167
168 memcpy(opt->modname, modname, modnamelen);
169 memcpy(opt->options, options, optionslen);
170 strchr_replace(opt->options, '\t', ' ');
171
172 list = kmod_list_append(config->options, opt);
173 if (list == NULL)
174 goto oom_error;
175
176 config->options = list;
177 return 0;
178
179oom_error:
180 free(opt);
181oom_error_init:
182 ERR(config->ctx, "out-of-memory\n");
183 return -ENOMEM;
184}
185
c35347f1
LDM
186static void kmod_config_free_options(struct kmod_config *config,
187 struct kmod_list *l)
615c42be
LDM
188{
189 struct kmod_options *opt = l->data;
190
191 free(opt);
192
193 config->options = kmod_list_remove(l);
194}
195
d13e606f 196static int kmod_config_add_alias(struct kmod_config *config,
c35347f1 197 const char *name, const char *modname)
7c2ab358
LDM
198{
199 struct kmod_alias *alias;
d13e606f 200 struct kmod_list *list;
43c29d10 201 size_t namelen = strlen(name) + 1, modnamelen = strlen(modname) + 1;
7c2ab358 202
d13e606f 203 DBG(config->ctx, "name=%s modname=%s\n", name, modname);
7c2ab358 204
43c29d10 205 alias = malloc(sizeof(*alias) + namelen + modnamelen);
d13e606f
GSB
206 if (!alias)
207 goto oom_error_init;
28c175ed 208
43c29d10
GSB
209 alias->name = sizeof(*alias) + modnamelen + (char *)alias;
210
211 memcpy(alias->modname, modname, modnamelen);
212 memcpy(alias->name, name, namelen);
7c2ab358 213
d13e606f
GSB
214 list = kmod_list_append(config->aliases, alias);
215 if (!list)
216 goto oom_error;
28c175ed 217
d13e606f
GSB
218 config->aliases = list;
219 return 0;
220
221oom_error:
d13e606f
GSB
222 free(alias);
223oom_error_init:
224 ERR(config->ctx, "out-of-memory name=%s modname=%s\n", name, modname);
225 return -ENOMEM;
7c2ab358
LDM
226}
227
c35347f1
LDM
228static void kmod_config_free_alias(struct kmod_config *config,
229 struct kmod_list *l)
7c2ab358
LDM
230{
231 struct kmod_alias *alias = l->data;
232
7c2ab358
LDM
233 free(alias);
234
d13e606f 235 config->aliases = kmod_list_remove(l);
7c2ab358
LDM
236}
237
d13e606f 238static int kmod_config_add_blacklist(struct kmod_config *config,
c35347f1 239 const char *modname)
81cf2060 240{
81cf2060 241 char *p;
d13e606f 242 struct kmod_list *list;
81cf2060 243
d13e606f 244 DBG(config->ctx, "modname=%s\n", modname);
81cf2060
LDM
245
246 p = strdup(modname);
d13e606f
GSB
247 if (!p)
248 goto oom_error_init;
249
250 list = kmod_list_append(config->blacklists, p);
251 if (!list)
252 goto oom_error;
253 config->blacklists = list;
254 return 0;
81cf2060 255
d13e606f
GSB
256oom_error:
257 free(p);
258oom_error_init:
259 ERR(config->ctx, "out-of-memory modname=%s\n", modname);
260 return -ENOMEM;
81cf2060
LDM
261}
262
d13e606f 263static void kmod_config_free_blacklist(struct kmod_config *config,
81cf2060
LDM
264 struct kmod_list *l)
265{
266 free(l->data);
d13e606f 267 config->blacklists = kmod_list_remove(l);
81cf2060
LDM
268}
269
1c522600
GSB
270static int kmod_config_add_softdep(struct kmod_config *config,
271 const char *modname,
272 const char *line)
273{
274 struct kmod_list *list;
275 struct kmod_softdep *dep;
276 const char *s, *p;
277 char *itr;
278 unsigned int n_pre = 0, n_post = 0;
279 size_t modnamelen = strlen(modname) + 1;
280 size_t buflen = 0;
281 bool was_space = false;
282 enum { S_NONE, S_PRE, S_POST } mode = S_NONE;
283
284 DBG(config->ctx, "modname=%s\n", modname);
285
286 /* analyze and count */
287 for (p = s = line; ; s++) {
288 size_t plen;
289
290 if (*s != '\0') {
291 if (!isspace(*s)) {
292 was_space = false;
293 continue;
294 }
295
296 if (was_space) {
297 p = s + 1;
298 continue;
299 }
300 was_space = true;
301
302 if (p >= s)
303 continue;
304 }
305 plen = s - p;
306
307 if (plen == sizeof("pre:") - 1 &&
308 memcmp(p, "pre:", sizeof("pre:") - 1) == 0)
309 mode = S_PRE;
310 else if (plen == sizeof("post:") - 1 &&
311 memcmp(p, "post:", sizeof("post:") - 1) == 0)
312 mode = S_POST;
313 else if (*s != '\0' || (*s == '\0' && !was_space)) {
314 if (mode == S_PRE) {
315 buflen += plen + 1;
316 n_pre++;
317 } else if (mode == S_POST) {
318 buflen += plen + 1;
319 n_post++;
320 }
321 }
322 p = s + 1;
323 if (*s == '\0')
324 break;
325 }
326
327 DBG(config->ctx, "%u pre, %u post\n", n_pre, n_post);
328
329 dep = malloc(sizeof(struct kmod_softdep) + modnamelen +
330 n_pre * sizeof(const char *) +
331 n_post * sizeof(const char *) +
332 buflen);
333 if (dep == NULL) {
334 ERR(config->ctx, "out-of-memory modname=%s\n", modname);
335 return -ENOMEM;
336 }
337 dep->n_pre = n_pre;
338 dep->n_post = n_post;
339 dep->pre = (const char **)((char *)dep + sizeof(struct kmod_softdep));
340 dep->post = dep->pre + n_pre;
341 dep->name = (char *)(dep->post + n_post);
342
343 memcpy(dep->name, modname, modnamelen);
344
345 /* copy strings */
346 itr = dep->name + modnamelen;
347 n_pre = 0;
348 n_post = 0;
349 mode = S_NONE;
350 for (p = s = line; ; s++) {
351 size_t plen;
352
353 if (*s != '\0') {
354 if (!isspace(*s)) {
355 was_space = false;
356 continue;
357 }
358
359 if (was_space) {
360 p = s + 1;
361 continue;
362 }
363 was_space = true;
364
365 if (p >= s)
366 continue;
367 }
368 plen = s - p;
369
370 if (plen == sizeof("pre:") - 1 &&
371 memcmp(p, "pre:", sizeof("pre:") - 1) == 0)
372 mode = S_PRE;
373 else if (plen == sizeof("post:") - 1 &&
374 memcmp(p, "post:", sizeof("post:") - 1) == 0)
375 mode = S_POST;
376 else if (*s != '\0' || (*s == '\0' && !was_space)) {
377 if (mode == S_PRE) {
378 dep->pre[n_pre] = itr;
379 memcpy(itr, p, plen);
380 itr[plen] = '\0';
381 itr += plen + 1;
382 n_pre++;
383 } else if (mode == S_POST) {
384 dep->post[n_post] = itr;
385 memcpy(itr, p, plen);
386 itr[plen] = '\0';
387 itr += plen + 1;
388 n_post++;
389 }
390 }
391 p = s + 1;
392 if (*s == '\0')
393 break;
394 }
395
396 list = kmod_list_append(config->softdeps, dep);
397 if (list == NULL) {
398 free(dep);
399 return -ENOMEM;
400 }
401 config->softdeps = list;
402
403 return 0;
404}
405
406static void kmod_config_free_softdep(struct kmod_config *config,
407 struct kmod_list *l)
408{
409 free(l->data);
410 config->softdeps = kmod_list_remove(l);
411}
412
1684e440
LDM
413static void kcmdline_parse_result(struct kmod_config *config, char *modname,
414 char *param, char *value)
415{
416 if (modname == NULL || param == NULL || value == NULL)
417 return;
418
419 DBG(config->ctx, "%s %s\n", modname, param);
420
421 if (streq(modname, "modprobe") && !strncmp(param, "blacklist=", 10)) {
422 for (;;) {
423 char *t = strsep(&value, ",");
424 if (t == NULL)
425 break;
426
427 kmod_config_add_blacklist(config, t);
428 }
429 } else {
430 kmod_config_add_options(config,
431 underscores(config->ctx, modname), param);
432 }
433}
434
435static int kmod_config_parse_kcmdline(struct kmod_config *config)
436{
437 char buf[KCMD_LINE_SIZE];
438 int fd, err;
439 char *p, *modname, *param = NULL, *value = NULL;
440
79e5ea91 441 fd = open("/proc/cmdline", O_RDONLY|O_CLOEXEC);
1684e440
LDM
442 err = read_str_safe(fd, buf, sizeof(buf));
443 close(fd);
444 if (err < 0) {
445 ERR(config->ctx, "could not read from '/proc/cmdline': %s\n",
446 strerror(-err));
447 return err;
448 }
449
450 for (p = buf, modname = buf; *p != '\0' && *p != '\n'; p++) {
451 switch (*p) {
452 case ' ':
453 *p = '\0';
454 kcmdline_parse_result(config, modname, param, value);
455 param = value = NULL;
456 modname = p + 1;
457 break;
458 case '.':
459 *p = '\0';
460 param = p + 1;
461 break;
462 case '=':
463 value = p + 1;
464 break;
465 }
466 }
467
468 *p = '\0';
469 kcmdline_parse_result(config, modname, param, value);
470
471 return 0;
472}
473
b7b7ac29
LDM
474/*
475 * Take an fd and own it. It will be closed on return. filename is used only
476 * for debug messages
477 */
478static int kmod_config_parse(struct kmod_config *config, int fd,
479 const char *filename)
7c2ab358 480{
d13e606f 481 struct kmod_ctx *ctx = config->ctx;
7c2ab358
LDM
482 char *line;
483 FILE *fp;
484 unsigned int linenum;
b7b7ac29 485 int err;
7c2ab358 486
b7b7ac29
LDM
487 fp = fdopen(fd, "r");
488 if (fp == NULL) {
489 err = -errno;
490 ERR(config->ctx, "fd %d: %m", fd);
491 close(fd);
492 return err;
493 }
7c2ab358
LDM
494
495 while ((line = getline_wrapped(fp, &linenum)) != NULL) {
c11e62bf 496 char *cmd, *saveptr;
7c2ab358
LDM
497
498 if (line[0] == '\0' || line[0] == '#')
499 goto done_next;
500
c11e62bf 501 cmd = strtok_r(line, "\t ", &saveptr);
7c2ab358
LDM
502 if (cmd == NULL)
503 goto done_next;
504
877e80cd 505 if (streq(cmd, "alias")) {
c11e62bf
LDM
506 char *alias = strtok_r(NULL, "\t ", &saveptr);
507 char *modname = strtok_r(NULL, "\t ", &saveptr);
7c2ab358
LDM
508
509 if (alias == NULL || modname == NULL)
510 goto syntax_error;
511
d13e606f 512 kmod_config_add_alias(config,
30be7513
LDM
513 underscores(ctx, alias),
514 underscores(ctx, modname));
877e80cd 515 } else if (streq(cmd, "blacklist")) {
c11e62bf 516 char *modname = strtok_r(NULL, "\t ", &saveptr);
81cf2060
LDM
517
518 if (modname == NULL)
519 goto syntax_error;
520
d13e606f 521 kmod_config_add_blacklist(config,
2295acc5 522 underscores(ctx, modname));
615c42be
LDM
523 } else if (streq(cmd, "options")) {
524 char *modname = strtok_r(NULL, "\t ", &saveptr);
525
526 if (modname == NULL)
527 goto syntax_error;
528
529 kmod_config_add_options(config,
530 underscores(ctx, modname),
531 strtok_r(NULL, "\0", &saveptr));
a5cce6d6
LDM
532 } else if streq(cmd, "install") {
533 char *modname = strtok_r(NULL, "\t ", &saveptr);
534
535 if (modname == NULL)
536 goto syntax_error;
537
538 kmod_config_add_command(config,
539 underscores(ctx, modname),
540 strtok_r(NULL, "\0", &saveptr),
541 cmd, &config->install_commands);
542 } else if streq(cmd, "remove") {
543 char *modname = strtok_r(NULL, "\t ", &saveptr);
544
545 if (modname == NULL)
546 goto syntax_error;
547
548 kmod_config_add_command(config,
549 underscores(ctx, modname),
550 strtok_r(NULL, "\0", &saveptr),
551 cmd, &config->remove_commands);
1c522600
GSB
552 } else if streq(cmd, "softdep") {
553 char *modname = strtok_r(NULL, "\t ", &saveptr);
554
555 if (modname == NULL)
556 goto syntax_error;
557
558 kmod_config_add_softdep(config,
559 underscores(ctx, modname),
560 strtok_r(NULL, "\0", &saveptr));
615c42be 561 } else if (streq(cmd, "include")
877e80cd 562 || streq(cmd, "config")) {
81cf2060
LDM
563 INFO(ctx, "%s: command %s not implemented yet\n",
564 filename, cmd);
7c2ab358
LDM
565 } else {
566syntax_error:
567 ERR(ctx, "%s line %u: ignoring bad line starting with '%s'\n",
568 filename, linenum, cmd);
569 }
570
571done_next:
572 free(line);
573 }
574
575 fclose(fp);
576
577 return 0;
578}
579
d13e606f 580void kmod_config_free(struct kmod_config *config)
7c2ab358
LDM
581{
582 while (config->aliases)
d13e606f 583 kmod_config_free_alias(config, config->aliases);
81cf2060
LDM
584
585 while (config->blacklists)
d13e606f
GSB
586 kmod_config_free_blacklist(config, config->blacklists);
587
615c42be
LDM
588 while (config->options)
589 kmod_config_free_options(config, config->options);
590
a5cce6d6
LDM
591 while (config->install_commands) {
592 kmod_config_free_command(config, config->install_commands,
593 &config->install_commands);
594 }
595
596 while (config->remove_commands) {
597 kmod_config_free_command(config, config->remove_commands,
598 &config->remove_commands);
599 }
600
1c522600
GSB
601 while (config->softdeps)
602 kmod_config_free_softdep(config, config->softdeps);
603
d13e606f 604 free(config);
7c2ab358
LDM
605}
606
98c80f44
LDM
607static bool conf_files_filter_out(struct kmod_ctx *ctx, DIR *d,
608 const char *path, const char *fn)
7c2ab358
LDM
609{
610 size_t len = strlen(fn);
98c80f44 611 struct stat st;
7c2ab358
LDM
612
613 if (fn[0] == '.')
8f767e2d 614 return true;
7c2ab358 615
877e80cd
LDM
616 if (len < 6 || (!streq(&fn[len - 5], ".conf")
617 && !streq(&fn[len - 6], ".alias"))) {
7c2ab358
LDM
618 INFO(ctx, "All config files need .conf: %s/%s, "
619 "it will be ignored in a future release\n",
620 path, fn);
8f767e2d 621 return true;
7c2ab358
LDM
622 }
623
98c80f44
LDM
624 fstatat(dirfd(d), fn, &st, 0);
625
626 if (S_ISDIR(st.st_mode)) {
627 ERR(ctx, "Directories inside directories are not supported: "
628 "%s/%s\n", path, fn);
8f767e2d 629 return true;
98c80f44
LDM
630 }
631
8f767e2d 632 return false;
7c2ab358
LDM
633}
634
4782396c
LDM
635/*
636 * Iterate over a directory (given by @path) and save the list of
637 * configuration files in @list.
638 */
b7b7ac29
LDM
639static DIR *conf_files_list(struct kmod_ctx *ctx, struct kmod_list **list,
640 const char *path)
7c2ab358 641{
7c2ab358
LDM
642 DIR *d;
643 int err;
644
1c250ec1
LDM
645 *list = NULL;
646
7c2ab358
LDM
647 d = opendir(path);
648 if (d == NULL) {
7c2ab358 649 ERR(ctx, "%m\n");
b7b7ac29 650 return NULL;
7c2ab358
LDM
651 }
652
653 for (;;) {
654 struct dirent ent, *entp;
b7b7ac29
LDM
655 struct kmod_list *l, *tmp;
656 const char *dname;
7c2ab358
LDM
657
658 err = readdir_r(d, &ent, &entp);
659 if (err != 0) {
b7b7ac29
LDM
660 ERR(ctx, "reading entry %s\n", strerror(-err));
661 goto fail_read;
7c2ab358
LDM
662 }
663
664 if (entp == NULL)
665 break;
666
8f767e2d 667 if (conf_files_filter_out(ctx, d, path, entp->d_name))
7c2ab358
LDM
668 continue;
669
b7b7ac29
LDM
670 /* insert sorted */
671 kmod_list_foreach(l, *list) {
672 if (strcmp(entp->d_name, l->data) < 0)
673 break;
7c2ab358
LDM
674 }
675
b7b7ac29
LDM
676 dname = strdup(entp->d_name);
677 if (dname == NULL)
678 goto fail_oom;
7c2ab358 679
b7b7ac29
LDM
680 if (l == NULL)
681 tmp = kmod_list_append(*list, dname);
682 else if (l == *list)
683 tmp = kmod_list_prepend(*list, dname);
684 else
685 tmp = kmod_list_insert_before(l, dname);
7c2ab358 686
b7b7ac29
LDM
687 if (tmp == NULL)
688 goto fail_oom;
7c2ab358 689
b7b7ac29
LDM
690 if (l == NULL || l == *list)
691 *list = tmp;
692 }
7c2ab358 693
b7b7ac29 694 return d;
7c2ab358 695
b7b7ac29
LDM
696fail_oom:
697 ERR(ctx, "out of memory while scanning '%s'\n", path);
698fail_read:
699 for (; *list != NULL; *list = kmod_list_remove(*list))
700 free((*list)->data);
701 closedir(d);
702 return NULL;
7c2ab358
LDM
703}
704
c35347f1
LDM
705int kmod_config_new(struct kmod_ctx *ctx, struct kmod_config **p_config,
706 const char * const *config_paths)
7c2ab358 707{
d13e606f 708 struct kmod_config *config;
b7b7ac29 709 size_t i;
7c2ab358 710
d13e606f 711 *p_config = config = calloc(1, sizeof(struct kmod_config));
2295acc5 712 if (config == NULL)
d13e606f 713 return -ENOMEM;
2295acc5 714
d13e606f
GSB
715 config->ctx = ctx;
716
cb8d4d3e
GSB
717 for (i = 0; config_paths[i] != NULL; i++) {
718 const char *path = config_paths[i];
4782396c 719 struct kmod_list *list;
cb8d4d3e 720 struct stat st;
b7b7ac29 721 DIR *d;
7c2ab358 722
cb8d4d3e
GSB
723 if (stat(path, &st) != 0) {
724 DBG(ctx, "could not load '%s': %s\n",
725 path, strerror(errno));
726 continue;
727 }
728
729 if (S_ISREG(st.st_mode)) {
8e3e5839 730 int fd = open(path, O_RDONLY|O_CLOEXEC);
cb8d4d3e
GSB
731 DBG(ctx, "parsing file '%s': %d\n", path, fd);
732 if (fd >= 0)
733 kmod_config_parse(config, fd, path);
734 continue;
735 } else if (!S_ISDIR(st.st_mode)) {
736 ERR(ctx, "unsupported file mode %s: %#x\n",
737 path, st.st_mode);
738 continue;
739 }
740
741 d = conf_files_list(ctx, &list, path);
7c2ab358 742
b7b7ac29 743 for (; list != NULL; list = kmod_list_remove(list)) {
8e3e5839 744 int fd = openat(dirfd(d), list->data, O_RDONLY|O_CLOEXEC);
cb8d4d3e
GSB
745 DBG(ctx, "parsing file '%s/%s': %d\n", path,
746 (const char *) list->data, fd);
b7b7ac29
LDM
747 if (fd >= 0)
748 kmod_config_parse(config, fd, list->data);
7c2ab358 749
b7b7ac29
LDM
750 free(list->data);
751 }
7c2ab358 752
b7b7ac29 753 closedir(d);
7c2ab358
LDM
754 }
755
1684e440
LDM
756 kmod_config_parse_kcmdline(config);
757
b7b7ac29 758 return 0;
7c2ab358 759}