]> git.ipfire.org Git - thirdparty/kmod.git/blame - libkmod/libkmod-config.c
handle case where modname was not detected.
[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
135 DBG(config->ctx, "modname'%s' options='%s'\n", modname, options);
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
b7b7ac29
LDM
245/*
246 * Take an fd and own it. It will be closed on return. filename is used only
247 * for debug messages
248 */
249static int kmod_config_parse(struct kmod_config *config, int fd,
250 const char *filename)
7c2ab358 251{
d13e606f 252 struct kmod_ctx *ctx = config->ctx;
7c2ab358
LDM
253 char *line;
254 FILE *fp;
255 unsigned int linenum;
b7b7ac29 256 int err;
7c2ab358 257
b7b7ac29
LDM
258 fp = fdopen(fd, "r");
259 if (fp == NULL) {
260 err = -errno;
261 ERR(config->ctx, "fd %d: %m", fd);
262 close(fd);
263 return err;
264 }
7c2ab358
LDM
265
266 while ((line = getline_wrapped(fp, &linenum)) != NULL) {
c11e62bf 267 char *cmd, *saveptr;
7c2ab358
LDM
268
269 if (line[0] == '\0' || line[0] == '#')
270 goto done_next;
271
c11e62bf 272 cmd = strtok_r(line, "\t ", &saveptr);
7c2ab358
LDM
273 if (cmd == NULL)
274 goto done_next;
275
877e80cd 276 if (streq(cmd, "alias")) {
c11e62bf
LDM
277 char *alias = strtok_r(NULL, "\t ", &saveptr);
278 char *modname = strtok_r(NULL, "\t ", &saveptr);
7c2ab358
LDM
279
280 if (alias == NULL || modname == NULL)
281 goto syntax_error;
282
d13e606f 283 kmod_config_add_alias(config,
30be7513
LDM
284 underscores(ctx, alias),
285 underscores(ctx, modname));
877e80cd 286 } else if (streq(cmd, "blacklist")) {
c11e62bf 287 char *modname = strtok_r(NULL, "\t ", &saveptr);
81cf2060
LDM
288
289 if (modname == NULL)
290 goto syntax_error;
291
d13e606f 292 kmod_config_add_blacklist(config,
2295acc5 293 underscores(ctx, modname));
615c42be
LDM
294 } else if (streq(cmd, "options")) {
295 char *modname = strtok_r(NULL, "\t ", &saveptr);
296
297 if (modname == NULL)
298 goto syntax_error;
299
300 kmod_config_add_options(config,
301 underscores(ctx, modname),
302 strtok_r(NULL, "\0", &saveptr));
a5cce6d6
LDM
303 } else if streq(cmd, "install") {
304 char *modname = strtok_r(NULL, "\t ", &saveptr);
305
306 if (modname == NULL)
307 goto syntax_error;
308
309 kmod_config_add_command(config,
310 underscores(ctx, modname),
311 strtok_r(NULL, "\0", &saveptr),
312 cmd, &config->install_commands);
313 } else if streq(cmd, "remove") {
314 char *modname = strtok_r(NULL, "\t ", &saveptr);
315
316 if (modname == NULL)
317 goto syntax_error;
318
319 kmod_config_add_command(config,
320 underscores(ctx, modname),
321 strtok_r(NULL, "\0", &saveptr),
322 cmd, &config->remove_commands);
615c42be 323 } else if (streq(cmd, "include")
877e80cd
LDM
324 || streq(cmd, "softdep")
325 || streq(cmd, "config")) {
81cf2060
LDM
326 INFO(ctx, "%s: command %s not implemented yet\n",
327 filename, cmd);
7c2ab358
LDM
328 } else {
329syntax_error:
330 ERR(ctx, "%s line %u: ignoring bad line starting with '%s'\n",
331 filename, linenum, cmd);
332 }
333
334done_next:
335 free(line);
336 }
337
338 fclose(fp);
339
340 return 0;
341}
342
d13e606f 343void kmod_config_free(struct kmod_config *config)
7c2ab358
LDM
344{
345 while (config->aliases)
d13e606f 346 kmod_config_free_alias(config, config->aliases);
81cf2060
LDM
347
348 while (config->blacklists)
d13e606f
GSB
349 kmod_config_free_blacklist(config, config->blacklists);
350
615c42be
LDM
351 while (config->options)
352 kmod_config_free_options(config, config->options);
353
a5cce6d6
LDM
354 while (config->install_commands) {
355 kmod_config_free_command(config, config->install_commands,
356 &config->install_commands);
357 }
358
359 while (config->remove_commands) {
360 kmod_config_free_command(config, config->remove_commands,
361 &config->remove_commands);
362 }
363
d13e606f 364 free(config);
7c2ab358
LDM
365}
366
98c80f44
LDM
367static bool conf_files_filter_out(struct kmod_ctx *ctx, DIR *d,
368 const char *path, const char *fn)
7c2ab358
LDM
369{
370 size_t len = strlen(fn);
98c80f44 371 struct stat st;
7c2ab358
LDM
372
373 if (fn[0] == '.')
8f767e2d 374 return true;
7c2ab358 375
877e80cd
LDM
376 if (len < 6 || (!streq(&fn[len - 5], ".conf")
377 && !streq(&fn[len - 6], ".alias"))) {
7c2ab358
LDM
378 INFO(ctx, "All config files need .conf: %s/%s, "
379 "it will be ignored in a future release\n",
380 path, fn);
8f767e2d 381 return true;
7c2ab358
LDM
382 }
383
98c80f44
LDM
384 fstatat(dirfd(d), fn, &st, 0);
385
386 if (S_ISDIR(st.st_mode)) {
387 ERR(ctx, "Directories inside directories are not supported: "
388 "%s/%s\n", path, fn);
8f767e2d 389 return true;
98c80f44
LDM
390 }
391
8f767e2d 392 return false;
7c2ab358
LDM
393}
394
4782396c
LDM
395/*
396 * Iterate over a directory (given by @path) and save the list of
397 * configuration files in @list.
398 */
b7b7ac29
LDM
399static DIR *conf_files_list(struct kmod_ctx *ctx, struct kmod_list **list,
400 const char *path)
7c2ab358 401{
7c2ab358
LDM
402 DIR *d;
403 int err;
404
7c2ab358
LDM
405 d = opendir(path);
406 if (d == NULL) {
407 err = errno;
408 ERR(ctx, "%m\n");
b7b7ac29 409 return NULL;
7c2ab358
LDM
410 }
411
4782396c
LDM
412 *list = NULL;
413
7c2ab358
LDM
414 for (;;) {
415 struct dirent ent, *entp;
b7b7ac29
LDM
416 struct kmod_list *l, *tmp;
417 const char *dname;
7c2ab358
LDM
418
419 err = readdir_r(d, &ent, &entp);
420 if (err != 0) {
b7b7ac29
LDM
421 ERR(ctx, "reading entry %s\n", strerror(-err));
422 goto fail_read;
7c2ab358
LDM
423 }
424
425 if (entp == NULL)
426 break;
427
8f767e2d 428 if (conf_files_filter_out(ctx, d, path, entp->d_name))
7c2ab358
LDM
429 continue;
430
b7b7ac29
LDM
431 /* insert sorted */
432 kmod_list_foreach(l, *list) {
433 if (strcmp(entp->d_name, l->data) < 0)
434 break;
7c2ab358
LDM
435 }
436
b7b7ac29
LDM
437 dname = strdup(entp->d_name);
438 if (dname == NULL)
439 goto fail_oom;
7c2ab358 440
b7b7ac29
LDM
441 if (l == NULL)
442 tmp = kmod_list_append(*list, dname);
443 else if (l == *list)
444 tmp = kmod_list_prepend(*list, dname);
445 else
446 tmp = kmod_list_insert_before(l, dname);
7c2ab358 447
b7b7ac29
LDM
448 if (tmp == NULL)
449 goto fail_oom;
7c2ab358 450
b7b7ac29
LDM
451 if (l == NULL || l == *list)
452 *list = tmp;
453 }
7c2ab358 454
b7b7ac29 455 return d;
7c2ab358 456
b7b7ac29
LDM
457fail_oom:
458 ERR(ctx, "out of memory while scanning '%s'\n", path);
459fail_read:
460 for (; *list != NULL; *list = kmod_list_remove(*list))
461 free((*list)->data);
462 closedir(d);
463 return NULL;
7c2ab358
LDM
464}
465
c35347f1
LDM
466int kmod_config_new(struct kmod_ctx *ctx, struct kmod_config **p_config,
467 const char * const *config_paths)
7c2ab358 468{
d13e606f 469 struct kmod_config *config;
b7b7ac29 470 size_t i;
7c2ab358 471
d13e606f 472 *p_config = config = calloc(1, sizeof(struct kmod_config));
2295acc5 473 if (config == NULL)
d13e606f 474 return -ENOMEM;
2295acc5 475
d13e606f
GSB
476 config->ctx = ctx;
477
cb8d4d3e
GSB
478 for (i = 0; config_paths[i] != NULL; i++) {
479 const char *path = config_paths[i];
4782396c 480 struct kmod_list *list;
cb8d4d3e 481 struct stat st;
b7b7ac29 482 DIR *d;
7c2ab358 483
cb8d4d3e
GSB
484 if (stat(path, &st) != 0) {
485 DBG(ctx, "could not load '%s': %s\n",
486 path, strerror(errno));
487 continue;
488 }
489
490 if (S_ISREG(st.st_mode)) {
491 int fd = open(path, O_RDONLY);
492 DBG(ctx, "parsing file '%s': %d\n", path, fd);
493 if (fd >= 0)
494 kmod_config_parse(config, fd, path);
495 continue;
496 } else if (!S_ISDIR(st.st_mode)) {
497 ERR(ctx, "unsupported file mode %s: %#x\n",
498 path, st.st_mode);
499 continue;
500 }
501
502 d = conf_files_list(ctx, &list, path);
7c2ab358 503
b7b7ac29 504 for (; list != NULL; list = kmod_list_remove(list)) {
cb8d4d3e
GSB
505 int fd = openat(dirfd(d), list->data, O_RDONLY);
506 DBG(ctx, "parsing file '%s/%s': %d\n", path,
507 (const char *) list->data, fd);
b7b7ac29
LDM
508 if (fd >= 0)
509 kmod_config_parse(config, fd, list->data);
7c2ab358 510
b7b7ac29
LDM
511 free(list->data);
512 }
7c2ab358 513
b7b7ac29 514 closedir(d);
7c2ab358
LDM
515 }
516
b7b7ac29 517 return 0;
7c2ab358 518}