blob: 75fdbf15b679a22584acdf171c6c9de7729ee679 [file] [log] [blame]
Lucas De Marchi7c2ab352011-11-29 18:07:43 -02001/*
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
35static const char *config_files[] = {
36 "/run/modprobe.d",
37 "/etc/modprobe.d",
Lucas De Marchi7c2ab352011-11-29 18:07:43 -020038 "/lib/modprobe.d",
39};
40
41struct kmod_alias {
42 char *name;
Gustavo Sverzut Barbieri43c29d12011-12-04 17:32:44 -020043 char modname[];
Lucas De Marchi7c2ab352011-11-29 18:07:43 -020044};
45
Lucas De Marchib0ef19f2011-11-30 18:18:13 -020046const char *kmod_alias_get_name(const struct kmod_list *l) {
Gustavo Sverzut Barbieri1ce08a52011-12-02 20:24:07 -020047 const struct kmod_alias *alias = l->data;
Lucas De Marchib0ef19f2011-11-30 18:18:13 -020048 return alias->name;
49}
50
51const char *kmod_alias_get_modname(const struct kmod_list *l) {
Gustavo Sverzut Barbieri1ce08a52011-12-02 20:24:07 -020052 const struct kmod_alias *alias = l->data;
Lucas De Marchib0ef19f2011-11-30 18:18:13 -020053 return alias->modname;
54}
55
Gustavo Sverzut Barbierid13e6062011-12-02 21:40:22 -020056static int kmod_config_add_alias(struct kmod_config *config,
57 const char *name, const char *modname)
Lucas De Marchi7c2ab352011-11-29 18:07:43 -020058{
59 struct kmod_alias *alias;
Gustavo Sverzut Barbierid13e6062011-12-02 21:40:22 -020060 struct kmod_list *list;
Gustavo Sverzut Barbieri43c29d12011-12-04 17:32:44 -020061 size_t namelen = strlen(name) + 1, modnamelen = strlen(modname) + 1;
Lucas De Marchi7c2ab352011-11-29 18:07:43 -020062
Gustavo Sverzut Barbierid13e6062011-12-02 21:40:22 -020063 DBG(config->ctx, "name=%s modname=%s\n", name, modname);
Lucas De Marchi7c2ab352011-11-29 18:07:43 -020064
Gustavo Sverzut Barbieri43c29d12011-12-04 17:32:44 -020065 alias = malloc(sizeof(*alias) + namelen + modnamelen);
Gustavo Sverzut Barbierid13e6062011-12-02 21:40:22 -020066 if (!alias)
67 goto oom_error_init;
Gustavo Sverzut Barbieri43c29d12011-12-04 17:32:44 -020068 alias->name = sizeof(*alias) + modnamelen + (char *)alias;
69
70 memcpy(alias->modname, modname, modnamelen);
71 memcpy(alias->name, name, namelen);
Lucas De Marchi7c2ab352011-11-29 18:07:43 -020072
Gustavo Sverzut Barbierid13e6062011-12-02 21:40:22 -020073 list = kmod_list_append(config->aliases, alias);
74 if (!list)
75 goto oom_error;
76 config->aliases = list;
77 return 0;
78
79oom_error:
Gustavo Sverzut Barbierid13e6062011-12-02 21:40:22 -020080 free(alias);
81oom_error_init:
82 ERR(config->ctx, "out-of-memory name=%s modname=%s\n", name, modname);
83 return -ENOMEM;
Lucas De Marchi7c2ab352011-11-29 18:07:43 -020084}
85
Gustavo Sverzut Barbierid13e6062011-12-02 21:40:22 -020086static void kmod_config_free_alias(struct kmod_config *config, struct kmod_list *l)
Lucas De Marchi7c2ab352011-11-29 18:07:43 -020087{
88 struct kmod_alias *alias = l->data;
89
Lucas De Marchi7c2ab352011-11-29 18:07:43 -020090 free(alias);
91
Gustavo Sverzut Barbierid13e6062011-12-02 21:40:22 -020092 config->aliases = kmod_list_remove(l);
Lucas De Marchi7c2ab352011-11-29 18:07:43 -020093}
94
Gustavo Sverzut Barbierid13e6062011-12-02 21:40:22 -020095static int kmod_config_add_blacklist(struct kmod_config *config,
Lucas De Marchi81cf2062011-11-29 18:48:02 -020096 const char *modname)
97{
Lucas De Marchi81cf2062011-11-29 18:48:02 -020098 char *p;
Gustavo Sverzut Barbierid13e6062011-12-02 21:40:22 -020099 struct kmod_list *list;
Lucas De Marchi81cf2062011-11-29 18:48:02 -0200100
Gustavo Sverzut Barbierid13e6062011-12-02 21:40:22 -0200101 DBG(config->ctx, "modname=%s\n", modname);
Lucas De Marchi81cf2062011-11-29 18:48:02 -0200102
103 p = strdup(modname);
Gustavo Sverzut Barbierid13e6062011-12-02 21:40:22 -0200104 if (!p)
105 goto oom_error_init;
Lucas De Marchi81cf2062011-11-29 18:48:02 -0200106
Gustavo Sverzut Barbierid13e6062011-12-02 21:40:22 -0200107 list = kmod_list_append(config->blacklists, p);
108 if (!list)
109 goto oom_error;
110 config->blacklists = list;
111 return 0;
112
113oom_error:
114 free(p);
115oom_error_init:
116 ERR(config->ctx, "out-of-memory modname=%s\n", modname);
117 return -ENOMEM;
Lucas De Marchi81cf2062011-11-29 18:48:02 -0200118}
119
Gustavo Sverzut Barbierid13e6062011-12-02 21:40:22 -0200120static void kmod_config_free_blacklist(struct kmod_config *config,
Lucas De Marchi81cf2062011-11-29 18:48:02 -0200121 struct kmod_list *l)
122{
123 free(l->data);
Gustavo Sverzut Barbierid13e6062011-12-02 21:40:22 -0200124 config->blacklists = kmod_list_remove(l);
Lucas De Marchi81cf2062011-11-29 18:48:02 -0200125}
126
Gustavo Sverzut Barbierid13e6062011-12-02 21:40:22 -0200127static int kmod_config_parse(struct kmod_config *config, const char *filename)
Lucas De Marchi7c2ab352011-11-29 18:07:43 -0200128{
Gustavo Sverzut Barbierid13e6062011-12-02 21:40:22 -0200129 struct kmod_ctx *ctx = config->ctx;
Lucas De Marchi7c2ab352011-11-29 18:07:43 -0200130 char *line;
131 FILE *fp;
132 unsigned int linenum;
133
134 DBG(ctx, "%s\n", filename);
135
136 fp = fopen(filename, "r");
137 if (fp == NULL)
138 return errno;
139
140 while ((line = getline_wrapped(fp, &linenum)) != NULL) {
Lucas De Marchic11e62b2011-12-01 18:59:54 -0200141 char *cmd, *saveptr;
Lucas De Marchi7c2ab352011-11-29 18:07:43 -0200142
143 if (line[0] == '\0' || line[0] == '#')
144 goto done_next;
145
Lucas De Marchic11e62b2011-12-01 18:59:54 -0200146 cmd = strtok_r(line, "\t ", &saveptr);
Lucas De Marchi7c2ab352011-11-29 18:07:43 -0200147 if (cmd == NULL)
148 goto done_next;
149
150 if (!strcmp(cmd, "alias")) {
Lucas De Marchic11e62b2011-12-01 18:59:54 -0200151 char *alias = strtok_r(NULL, "\t ", &saveptr);
152 char *modname = strtok_r(NULL, "\t ", &saveptr);
Lucas De Marchi7c2ab352011-11-29 18:07:43 -0200153
154 if (alias == NULL || modname == NULL)
155 goto syntax_error;
156
Gustavo Sverzut Barbierid13e6062011-12-02 21:40:22 -0200157 kmod_config_add_alias(config,
Lucas De Marchi30be7512011-11-30 02:14:57 -0200158 underscores(ctx, alias),
159 underscores(ctx, modname));
Lucas De Marchi81cf2062011-11-29 18:48:02 -0200160 } else if (!strcmp(cmd, "blacklist")) {
Lucas De Marchic11e62b2011-12-01 18:59:54 -0200161 char *modname = strtok_r(NULL, "\t ", &saveptr);
Lucas De Marchi81cf2062011-11-29 18:48:02 -0200162
163 if (modname == NULL)
164 goto syntax_error;
165
Gustavo Sverzut Barbierid13e6062011-12-02 21:40:22 -0200166 kmod_config_add_blacklist(config,
Lucas De Marchi2295acc2011-12-03 04:05:22 -0200167 underscores(ctx, modname));
Lucas De Marchi7c2ab352011-11-29 18:07:43 -0200168 } else if (!strcmp(cmd, "include") || !strcmp(cmd, "options")
169 || !strcmp(cmd, "install")
Lucas De Marchi7c2ab352011-11-29 18:07:43 -0200170 || !strcmp(cmd, "remove")
171 || !strcmp(cmd, "softdep")
172 || !strcmp(cmd, "config")) {
Lucas De Marchi81cf2062011-11-29 18:48:02 -0200173 INFO(ctx, "%s: command %s not implemented yet\n",
174 filename, cmd);
Lucas De Marchi7c2ab352011-11-29 18:07:43 -0200175 } else {
176syntax_error:
177 ERR(ctx, "%s line %u: ignoring bad line starting with '%s'\n",
178 filename, linenum, cmd);
179 }
180
181done_next:
182 free(line);
183 }
184
185 fclose(fp);
186
187 return 0;
188}
189
Gustavo Sverzut Barbierid13e6062011-12-02 21:40:22 -0200190void kmod_config_free(struct kmod_config *config)
Lucas De Marchi7c2ab352011-11-29 18:07:43 -0200191{
192 while (config->aliases)
Gustavo Sverzut Barbierid13e6062011-12-02 21:40:22 -0200193 kmod_config_free_alias(config, config->aliases);
Lucas De Marchi81cf2062011-11-29 18:48:02 -0200194
195 while (config->blacklists)
Gustavo Sverzut Barbierid13e6062011-12-02 21:40:22 -0200196 kmod_config_free_blacklist(config, config->blacklists);
197
198 free(config);
Lucas De Marchi7c2ab352011-11-29 18:07:43 -0200199}
200
201static bool conf_files_filter(struct kmod_ctx *ctx, const char *path,
202 const char *fn)
203{
204 size_t len = strlen(fn);
205
206 if (fn[0] == '.')
207 return 1;
208
209 if (len < 6 || (strcmp(&fn[len - 5], ".conf") != 0
210 && strcmp(&fn[len - 6], ".alias"))) {
211 INFO(ctx, "All config files need .conf: %s/%s, "
212 "it will be ignored in a future release\n",
213 path, fn);
214 return 1;
215 }
216
217 return 0;
218}
219
220static int conf_files_list(struct kmod_ctx *ctx, struct kmod_list **list,
221 const char *path, size_t *n)
222{
223 struct stat st;
224 DIR *d;
225 int err;
226
227 if (stat(path, &st) < 0)
228 return -ENOENT;
229
230 if (!S_ISDIR(st.st_mode)) {
231 *list = kmod_list_append(*list, (void *)path);
232 *n += 1;
233 return 0;
234 }
235
236 d = opendir(path);
237 if (d == NULL) {
238 err = errno;
239 ERR(ctx, "%m\n");
240 return -errno;
241 }
242
243 for (;;) {
244 struct dirent ent, *entp;
245 char *p;
246
247 err = readdir_r(d, &ent, &entp);
248 if (err != 0) {
249 err = -err;
250 goto finish;
251 }
252
253 if (entp == NULL)
254 break;
255
256 if (conf_files_filter(ctx, path, entp->d_name) == 1)
257 continue;
258
259 if (asprintf(&p, "%s/%s", path, entp->d_name) < 0) {
260 err = -ENOMEM;
261 goto finish;
262 }
263
264 DBG(ctx, "%s\n", p);
265
266 *list = kmod_list_append(*list, p);
267 *n += 1;
268 }
269
270finish:
271 closedir(d);
272 return err;
273}
274
275static int base_cmp(const void *a, const void *b)
276{
277 const char *s1, *s2;
278
279 s1 = *(char * const *)a;
280 s2 = *(char * const *)b;
281
282 return strcmp(basename(s1), basename(s2));
283}
284
Gustavo Sverzut Barbierid13e6062011-12-02 21:40:22 -0200285int kmod_config_new(struct kmod_ctx *ctx, struct kmod_config **p_config)
Lucas De Marchi7c2ab352011-11-29 18:07:43 -0200286{
Gustavo Sverzut Barbierid13e6062011-12-02 21:40:22 -0200287 struct kmod_config *config;
Lucas De Marchi7c2ab352011-11-29 18:07:43 -0200288 size_t i, n = 0;
Gustavo Sverzut Barbieri1ce08a52011-12-02 20:24:07 -0200289 const char **files;
Lucas De Marchi7c2ab352011-11-29 18:07:43 -0200290 int err = 0;
291 struct kmod_list *list = NULL, *l;
292
Gustavo Sverzut Barbierid13e6062011-12-02 21:40:22 -0200293 *p_config = config = calloc(1, sizeof(struct kmod_config));
Lucas De Marchi2295acc2011-12-03 04:05:22 -0200294 if (config == NULL)
Gustavo Sverzut Barbierid13e6062011-12-02 21:40:22 -0200295 return -ENOMEM;
Lucas De Marchi2295acc2011-12-03 04:05:22 -0200296
Gustavo Sverzut Barbierid13e6062011-12-02 21:40:22 -0200297 config->ctx = ctx;
298
Lucas De Marchi7c2ab352011-11-29 18:07:43 -0200299 for (i = 0; i < ARRAY_SIZE(config_files); i++)
300 conf_files_list(ctx, &list, config_files[i], &n);
301
302 files = malloc(sizeof(char *) * n);
303 if (files == NULL) {
304 err = -ENOMEM;
305 goto finish;
306 }
307
308 i = 0;
309 kmod_list_foreach(l, list) {
310 files[i] = l->data;
311 i++;
312 }
313
314 qsort(files, n, sizeof(char *), base_cmp);
315
316 for (i = 0; i < n; i++)
Gustavo Sverzut Barbierid13e6062011-12-02 21:40:22 -0200317 kmod_config_parse(config, files[i]);
Lucas De Marchi7c2ab352011-11-29 18:07:43 -0200318
319finish:
320 free(files);
321
322 while (list) {
323 free(list->data);
324 list = kmod_list_remove(list);
325 }
326
327 return err;
328}