mirror of
https://github.com/AuxXxilium/kmod.git
synced 2024-11-23 23:10:53 +07:00
05828b4a6e
It has been seen that for some network mac drivers (i.e. lan78xx) the related module for the phy is loaded dynamically depending on the current hardware. In this case, the associated phy is read using mdio bus and then the associated phy module is loaded during runtime (kernel function phy_request_driver_module). However, no software dependency is defined, so the user tools will no be able to get this dependency. For example, if dracut is used and the hardware is present, lan78xx will be included but no phy module will be added, and in the next restart the device will not work from boot because no related phy will be found during initramfs stage. In order to solve this, we could define a normal 'pre' software dependency in lan78xx module with all the possible phy modules (there may be some), but proceeding in that way, all the possible phy modules would be loaded while only one is necessary. The idea is to create a new type of dependency, that we are going to call 'weak' to be used only by the user tools that need to detect this situation. In that way, for example, dracut could check the 'weak' dependency of the modules involved in order to install these dependencies in initramfs too. That is, for the commented lan78xx module, defining the 'weak' dependency with the possible phy modules list, only the necessary phy would be loaded on demand keeping the same behavior, but all the possible phy modules would be available from initramfs. A new function 'kmod_module_get_weakdeps' in libkmod will be added for this to avoid breaking the API and maintain backward compatibility. This general procedure could be useful for other similar cases (not only for dynamic phy loading). Signed-off-by: Jose Ignacio Tornos Martinez <jtornosm@redhat.com> Link: https://lore.kernel.org/r/20240327141116.97587-1-jtornosm@redhat.com
1465 lines
34 KiB
C
1465 lines
34 KiB
C
/*
|
|
* libkmod - interface to kernel module operations
|
|
*
|
|
* Copyright (C) 2011-2013 ProFUSION embedded systems
|
|
* Copyright (C) 2013 Intel Corporation. All rights reserved.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <ctype.h>
|
|
#include <dirent.h>
|
|
#include <errno.h>
|
|
#include <stdarg.h>
|
|
#include <stddef.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
|
|
#include <shared/util.h>
|
|
|
|
#include "libkmod.h"
|
|
#include "libkmod-internal.h"
|
|
|
|
struct kmod_alias {
|
|
char *name;
|
|
char modname[];
|
|
};
|
|
|
|
struct kmod_options {
|
|
char *options;
|
|
char modname[];
|
|
};
|
|
|
|
struct kmod_command {
|
|
char *command;
|
|
char modname[];
|
|
};
|
|
|
|
struct kmod_softdep {
|
|
char *name;
|
|
const char **pre;
|
|
const char **post;
|
|
unsigned int n_pre;
|
|
unsigned int n_post;
|
|
};
|
|
|
|
struct kmod_weakdep {
|
|
char *name;
|
|
const char **weak;
|
|
unsigned int n_weak;
|
|
};
|
|
|
|
const char *kmod_blacklist_get_modname(const struct kmod_list *l)
|
|
{
|
|
return l->data;
|
|
}
|
|
|
|
const char *kmod_alias_get_name(const struct kmod_list *l) {
|
|
const struct kmod_alias *alias = l->data;
|
|
return alias->name;
|
|
}
|
|
|
|
const char *kmod_alias_get_modname(const struct kmod_list *l) {
|
|
const struct kmod_alias *alias = l->data;
|
|
return alias->modname;
|
|
}
|
|
|
|
const char *kmod_option_get_options(const struct kmod_list *l) {
|
|
const struct kmod_options *alias = l->data;
|
|
return alias->options;
|
|
}
|
|
|
|
const char *kmod_option_get_modname(const struct kmod_list *l) {
|
|
const struct kmod_options *alias = l->data;
|
|
return alias->modname;
|
|
}
|
|
|
|
const char *kmod_command_get_command(const struct kmod_list *l) {
|
|
const struct kmod_command *alias = l->data;
|
|
return alias->command;
|
|
}
|
|
|
|
const char *kmod_command_get_modname(const struct kmod_list *l) {
|
|
const struct kmod_command *alias = l->data;
|
|
return alias->modname;
|
|
}
|
|
|
|
const char *kmod_softdep_get_name(const struct kmod_list *l) {
|
|
const struct kmod_softdep *dep = l->data;
|
|
return dep->name;
|
|
}
|
|
|
|
const char * const *kmod_softdep_get_pre(const struct kmod_list *l, unsigned int *count) {
|
|
const struct kmod_softdep *dep = l->data;
|
|
*count = dep->n_pre;
|
|
return dep->pre;
|
|
}
|
|
|
|
const char * const *kmod_softdep_get_post(const struct kmod_list *l, unsigned int *count) {
|
|
const struct kmod_softdep *dep = l->data;
|
|
*count = dep->n_post;
|
|
return dep->post;
|
|
}
|
|
|
|
const char *kmod_weakdep_get_name(const struct kmod_list *l) {
|
|
const struct kmod_weakdep *dep = l->data;
|
|
return dep->name;
|
|
}
|
|
|
|
const char * const *kmod_weakdep_get_weak(const struct kmod_list *l, unsigned int *count) {
|
|
const struct kmod_weakdep *dep = l->data;
|
|
*count = dep->n_weak;
|
|
return dep->weak;
|
|
}
|
|
static int kmod_config_add_command(struct kmod_config *config,
|
|
const char *modname,
|
|
const char *command,
|
|
const char *command_name,
|
|
struct kmod_list **list)
|
|
{
|
|
_cleanup_free_ struct kmod_command *cmd;
|
|
struct kmod_list *l;
|
|
size_t modnamelen = strlen(modname) + 1;
|
|
size_t commandlen = strlen(command) + 1;
|
|
|
|
DBG(config->ctx, "modname='%s' cmd='%s %s'\n", modname, command_name,
|
|
command);
|
|
|
|
cmd = malloc(sizeof(*cmd) + modnamelen + commandlen);
|
|
if (!cmd)
|
|
return -ENOMEM;
|
|
|
|
cmd->command = sizeof(*cmd) + modnamelen + (char *)cmd;
|
|
memcpy(cmd->modname, modname, modnamelen);
|
|
memcpy(cmd->command, command, commandlen);
|
|
|
|
l = kmod_list_append(*list, cmd);
|
|
if (!l)
|
|
return -ENOMEM;
|
|
|
|
*list = l;
|
|
cmd = NULL;
|
|
return 0;
|
|
}
|
|
|
|
static void kmod_config_free_command(struct kmod_config *config,
|
|
struct kmod_list *l,
|
|
struct kmod_list **list)
|
|
{
|
|
struct kmod_command *cmd = l->data;
|
|
|
|
free(cmd);
|
|
*list = kmod_list_remove(l);
|
|
}
|
|
|
|
static int kmod_config_add_options(struct kmod_config *config,
|
|
const char *modname, const char *options)
|
|
{
|
|
_cleanup_free_ struct kmod_options *opt;
|
|
struct kmod_list *list;
|
|
size_t modnamelen = strlen(modname) + 1;
|
|
size_t optionslen = strlen(options) + 1;
|
|
|
|
DBG(config->ctx, "modname='%s' options='%s'\n", modname, options);
|
|
|
|
opt = malloc(sizeof(*opt) + modnamelen + optionslen);
|
|
if (!opt)
|
|
return -ENOMEM;
|
|
|
|
opt->options = sizeof(*opt) + modnamelen + (char *)opt;
|
|
|
|
memcpy(opt->modname, modname, modnamelen);
|
|
memcpy(opt->options, options, optionslen);
|
|
strchr_replace(opt->options, '\t', ' ');
|
|
|
|
list = kmod_list_append(config->options, opt);
|
|
if (!list)
|
|
return -ENOMEM;
|
|
|
|
opt = NULL;
|
|
config->options = list;
|
|
return 0;
|
|
}
|
|
|
|
static void kmod_config_free_options(struct kmod_config *config,
|
|
struct kmod_list *l)
|
|
{
|
|
struct kmod_options *opt = l->data;
|
|
|
|
free(opt);
|
|
|
|
config->options = kmod_list_remove(l);
|
|
}
|
|
|
|
static int kmod_config_add_alias(struct kmod_config *config,
|
|
const char *name, const char *modname)
|
|
{
|
|
_cleanup_free_ struct kmod_alias *alias;
|
|
struct kmod_list *list;
|
|
size_t namelen = strlen(name) + 1, modnamelen = strlen(modname) + 1;
|
|
|
|
DBG(config->ctx, "name=%s modname=%s\n", name, modname);
|
|
|
|
alias = malloc(sizeof(*alias) + namelen + modnamelen);
|
|
if (!alias)
|
|
return -ENOMEM;
|
|
|
|
alias->name = sizeof(*alias) + modnamelen + (char *)alias;
|
|
|
|
memcpy(alias->modname, modname, modnamelen);
|
|
memcpy(alias->name, name, namelen);
|
|
|
|
list = kmod_list_append(config->aliases, alias);
|
|
if (!list)
|
|
return -ENOMEM;
|
|
|
|
alias = NULL;
|
|
config->aliases = list;
|
|
return 0;
|
|
}
|
|
|
|
static void kmod_config_free_alias(struct kmod_config *config,
|
|
struct kmod_list *l)
|
|
{
|
|
struct kmod_alias *alias = l->data;
|
|
|
|
free(alias);
|
|
|
|
config->aliases = kmod_list_remove(l);
|
|
}
|
|
|
|
static int kmod_config_add_blacklist(struct kmod_config *config,
|
|
const char *modname)
|
|
{
|
|
_cleanup_free_ char *p;
|
|
struct kmod_list *list;
|
|
|
|
DBG(config->ctx, "modname=%s\n", modname);
|
|
|
|
p = strdup(modname);
|
|
if (!p)
|
|
return -ENOMEM;
|
|
|
|
list = kmod_list_append(config->blacklists, p);
|
|
if (!list)
|
|
return -ENOMEM;
|
|
|
|
p = NULL;
|
|
config->blacklists = list;
|
|
return 0;
|
|
}
|
|
|
|
static void kmod_config_free_blacklist(struct kmod_config *config,
|
|
struct kmod_list *l)
|
|
{
|
|
free(l->data);
|
|
config->blacklists = kmod_list_remove(l);
|
|
}
|
|
|
|
static int kmod_config_add_softdep(struct kmod_config *config,
|
|
const char *modname,
|
|
const char *line)
|
|
{
|
|
struct kmod_list *list;
|
|
struct kmod_softdep *dep;
|
|
const char *s, *p;
|
|
char *itr;
|
|
unsigned int n_pre = 0, n_post = 0;
|
|
size_t modnamelen = strlen(modname) + 1;
|
|
size_t buflen = 0;
|
|
bool was_space = false;
|
|
enum { S_NONE, S_PRE, S_POST } mode = S_NONE;
|
|
|
|
DBG(config->ctx, "modname=%s\n", modname);
|
|
|
|
/* analyze and count */
|
|
for (p = s = line; ; s++) {
|
|
size_t plen;
|
|
|
|
if (*s != '\0') {
|
|
if (!isspace(*s)) {
|
|
was_space = false;
|
|
continue;
|
|
}
|
|
|
|
if (was_space) {
|
|
p = s + 1;
|
|
continue;
|
|
}
|
|
was_space = true;
|
|
|
|
if (p >= s)
|
|
continue;
|
|
}
|
|
plen = s - p;
|
|
|
|
if (plen == sizeof("pre:") - 1 &&
|
|
memcmp(p, "pre:", sizeof("pre:") - 1) == 0)
|
|
mode = S_PRE;
|
|
else if (plen == sizeof("post:") - 1 &&
|
|
memcmp(p, "post:", sizeof("post:") - 1) == 0)
|
|
mode = S_POST;
|
|
else if (*s != '\0' || (*s == '\0' && !was_space)) {
|
|
if (mode == S_PRE) {
|
|
buflen += plen + 1;
|
|
n_pre++;
|
|
} else if (mode == S_POST) {
|
|
buflen += plen + 1;
|
|
n_post++;
|
|
}
|
|
}
|
|
p = s + 1;
|
|
if (*s == '\0')
|
|
break;
|
|
}
|
|
|
|
DBG(config->ctx, "%u pre, %u post\n", n_pre, n_post);
|
|
|
|
dep = malloc(sizeof(struct kmod_softdep) + modnamelen +
|
|
n_pre * sizeof(const char *) +
|
|
n_post * sizeof(const char *) +
|
|
buflen);
|
|
if (dep == NULL) {
|
|
ERR(config->ctx, "out-of-memory modname=%s\n", modname);
|
|
return -ENOMEM;
|
|
}
|
|
dep->n_pre = n_pre;
|
|
dep->n_post = n_post;
|
|
dep->pre = (const char **)((char *)dep + sizeof(struct kmod_softdep));
|
|
dep->post = dep->pre + n_pre;
|
|
dep->name = (char *)(dep->post + n_post);
|
|
|
|
memcpy(dep->name, modname, modnamelen);
|
|
|
|
/* copy strings */
|
|
itr = dep->name + modnamelen;
|
|
n_pre = 0;
|
|
n_post = 0;
|
|
mode = S_NONE;
|
|
was_space = false;
|
|
for (p = s = line; ; s++) {
|
|
size_t plen;
|
|
|
|
if (*s != '\0') {
|
|
if (!isspace(*s)) {
|
|
was_space = false;
|
|
continue;
|
|
}
|
|
|
|
if (was_space) {
|
|
p = s + 1;
|
|
continue;
|
|
}
|
|
was_space = true;
|
|
|
|
if (p >= s)
|
|
continue;
|
|
}
|
|
plen = s - p;
|
|
|
|
if (plen == sizeof("pre:") - 1 &&
|
|
memcmp(p, "pre:", sizeof("pre:") - 1) == 0)
|
|
mode = S_PRE;
|
|
else if (plen == sizeof("post:") - 1 &&
|
|
memcmp(p, "post:", sizeof("post:") - 1) == 0)
|
|
mode = S_POST;
|
|
else if (*s != '\0' || (*s == '\0' && !was_space)) {
|
|
if (mode == S_PRE) {
|
|
dep->pre[n_pre] = itr;
|
|
memcpy(itr, p, plen);
|
|
itr[plen] = '\0';
|
|
itr += plen + 1;
|
|
n_pre++;
|
|
} else if (mode == S_POST) {
|
|
dep->post[n_post] = itr;
|
|
memcpy(itr, p, plen);
|
|
itr[plen] = '\0';
|
|
itr += plen + 1;
|
|
n_post++;
|
|
}
|
|
}
|
|
p = s + 1;
|
|
if (*s == '\0')
|
|
break;
|
|
}
|
|
|
|
list = kmod_list_append(config->softdeps, dep);
|
|
if (list == NULL) {
|
|
free(dep);
|
|
return -ENOMEM;
|
|
}
|
|
config->softdeps = list;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int kmod_config_add_weakdep(struct kmod_config *config,
|
|
const char *modname,
|
|
const char *line)
|
|
{
|
|
struct kmod_list *list;
|
|
struct kmod_weakdep *dep;
|
|
const char *s, *p;
|
|
char *itr;
|
|
unsigned int n_weak = 0;
|
|
size_t modnamelen = strlen(modname) + 1;
|
|
size_t buflen = 0;
|
|
bool was_space = false;
|
|
|
|
DBG(config->ctx, "modname=%s\n", modname);
|
|
|
|
/* analyze and count */
|
|
for (p = s = line; ; s++) {
|
|
size_t plen;
|
|
|
|
if (*s != '\0') {
|
|
if (!isspace(*s)) {
|
|
was_space = false;
|
|
continue;
|
|
}
|
|
|
|
if (was_space) {
|
|
p = s + 1;
|
|
continue;
|
|
}
|
|
was_space = true;
|
|
|
|
if (p >= s)
|
|
continue;
|
|
}
|
|
plen = s - p;
|
|
|
|
if (*s != '\0' || (*s == '\0' && !was_space)) {
|
|
buflen += plen + 1;
|
|
n_weak++;
|
|
}
|
|
p = s + 1;
|
|
if (*s == '\0')
|
|
break;
|
|
}
|
|
|
|
DBG(config->ctx, "%u weak\n", n_weak);
|
|
|
|
dep = malloc(sizeof(struct kmod_weakdep) + modnamelen +
|
|
n_weak * sizeof(const char *) +
|
|
buflen);
|
|
if (dep == NULL) {
|
|
ERR(config->ctx, "out-of-memory modname=%s\n", modname);
|
|
return -ENOMEM;
|
|
}
|
|
dep->n_weak = n_weak;
|
|
dep->weak = (const char **)((char *)dep + sizeof(struct kmod_weakdep));
|
|
dep->name = (char *)(dep->weak + n_weak);
|
|
|
|
memcpy(dep->name, modname, modnamelen);
|
|
|
|
/* copy strings */
|
|
itr = dep->name + modnamelen;
|
|
n_weak = 0;
|
|
was_space = false;
|
|
for (p = s = line; ; s++) {
|
|
size_t plen;
|
|
|
|
if (*s != '\0') {
|
|
if (!isspace(*s)) {
|
|
was_space = false;
|
|
continue;
|
|
}
|
|
|
|
if (was_space) {
|
|
p = s + 1;
|
|
continue;
|
|
}
|
|
was_space = true;
|
|
|
|
if (p >= s)
|
|
continue;
|
|
}
|
|
plen = s - p;
|
|
|
|
if (*s != '\0' || (*s == '\0' && !was_space)) {
|
|
dep->weak[n_weak] = itr;
|
|
memcpy(itr, p, plen);
|
|
itr[plen] = '\0';
|
|
itr += plen + 1;
|
|
n_weak++;
|
|
}
|
|
p = s + 1;
|
|
if (*s == '\0')
|
|
break;
|
|
}
|
|
|
|
list = kmod_list_append(config->weakdeps, dep);
|
|
if (list == NULL) {
|
|
free(dep);
|
|
return -ENOMEM;
|
|
}
|
|
config->weakdeps = list;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static char *softdep_to_char(struct kmod_softdep *dep) {
|
|
const size_t sz_preprefix = sizeof("pre: ") - 1;
|
|
const size_t sz_postprefix = sizeof("post: ") - 1;
|
|
size_t sz = 1; /* at least '\0' */
|
|
size_t sz_pre, sz_post;
|
|
const char *start, *end;
|
|
char *s, *itr;
|
|
|
|
/*
|
|
* Rely on the fact that dep->pre[] and dep->post[] are strv's that
|
|
* point to a contiguous buffer
|
|
*/
|
|
if (dep->n_pre > 0) {
|
|
start = dep->pre[0];
|
|
end = dep->pre[dep->n_pre - 1]
|
|
+ strlen(dep->pre[dep->n_pre - 1]);
|
|
sz_pre = end - start;
|
|
sz += sz_pre + sz_preprefix;
|
|
} else
|
|
sz_pre = 0;
|
|
|
|
if (dep->n_post > 0) {
|
|
start = dep->post[0];
|
|
end = dep->post[dep->n_post - 1]
|
|
+ strlen(dep->post[dep->n_post - 1]);
|
|
sz_post = end - start;
|
|
sz += sz_post + sz_postprefix;
|
|
} else
|
|
sz_post = 0;
|
|
|
|
itr = s = malloc(sz);
|
|
if (s == NULL)
|
|
return NULL;
|
|
|
|
if (sz_pre) {
|
|
char *p;
|
|
|
|
memcpy(itr, "pre: ", sz_preprefix);
|
|
itr += sz_preprefix;
|
|
|
|
/* include last '\0' */
|
|
memcpy(itr, dep->pre[0], sz_pre + 1);
|
|
for (p = itr; p < itr + sz_pre; p++) {
|
|
if (*p == '\0')
|
|
*p = ' ';
|
|
}
|
|
itr = p;
|
|
}
|
|
|
|
if (sz_post) {
|
|
char *p;
|
|
|
|
memcpy(itr, "post: ", sz_postprefix);
|
|
itr += sz_postprefix;
|
|
|
|
/* include last '\0' */
|
|
memcpy(itr, dep->post[0], sz_post + 1);
|
|
for (p = itr; p < itr + sz_post; p++) {
|
|
if (*p == '\0')
|
|
*p = ' ';
|
|
}
|
|
itr = p;
|
|
}
|
|
|
|
*itr = '\0';
|
|
|
|
return s;
|
|
}
|
|
|
|
static char *weakdep_to_char(struct kmod_weakdep *dep) {
|
|
size_t sz;
|
|
const char *start, *end;
|
|
char *s, *itr;
|
|
|
|
/*
|
|
* Rely on the fact that dep->weak[] and are strv's that point to a
|
|
* contiguous buffer
|
|
*/
|
|
if (dep->n_weak > 0) {
|
|
start = dep->weak[0];
|
|
end = dep->weak[dep->n_weak - 1]
|
|
+ strlen(dep->weak[dep->n_weak - 1]);
|
|
sz = end - start;
|
|
} else
|
|
sz = 0;
|
|
|
|
itr = s = malloc(sz);
|
|
if (s == NULL)
|
|
return NULL;
|
|
|
|
if (sz) {
|
|
char *p;
|
|
|
|
/* include last '\0' */
|
|
memcpy(itr, dep->weak[0], sz + 1);
|
|
for (p = itr; p < itr + sz; p++) {
|
|
if (*p == '\0')
|
|
*p = ' ';
|
|
}
|
|
itr = p;
|
|
}
|
|
|
|
*itr = '\0';
|
|
|
|
return s;
|
|
}
|
|
|
|
static void kmod_config_free_softdep(struct kmod_config *config,
|
|
struct kmod_list *l)
|
|
{
|
|
free(l->data);
|
|
config->softdeps = kmod_list_remove(l);
|
|
}
|
|
|
|
static void kmod_config_free_weakdep(struct kmod_config *config,
|
|
struct kmod_list *l)
|
|
{
|
|
free(l->data);
|
|
config->weakdeps = kmod_list_remove(l);
|
|
}
|
|
|
|
static void kcmdline_parse_result(struct kmod_config *config, char *modname,
|
|
char *param, char *value)
|
|
{
|
|
if (modname == NULL || param == NULL)
|
|
return;
|
|
|
|
DBG(config->ctx, "%s %s\n", modname, param);
|
|
|
|
if (streq(modname, "modprobe") && !strncmp(param, "blacklist=", 10)) {
|
|
for (;;) {
|
|
char *t = strsep(&value, ",");
|
|
if (t == NULL)
|
|
break;
|
|
|
|
kmod_config_add_blacklist(config, t);
|
|
}
|
|
} else {
|
|
if (underscores(modname) < 0) {
|
|
ERR(config->ctx, "Ignoring bad option on kernel command line while parsing module name: '%s'\n",
|
|
modname);
|
|
} else {
|
|
kmod_config_add_options(config, modname, param);
|
|
}
|
|
}
|
|
}
|
|
|
|
static int kmod_config_parse_kcmdline(struct kmod_config *config)
|
|
{
|
|
char buf[KCMD_LINE_SIZE];
|
|
int fd, err;
|
|
char *p, *p_quote_start, *modname, *param = NULL, *value = NULL;
|
|
bool is_quoted = false, iter = true;
|
|
enum state {
|
|
STATE_IGNORE,
|
|
STATE_MODNAME,
|
|
STATE_PARAM,
|
|
STATE_VALUE,
|
|
STATE_COMPLETE,
|
|
} state;
|
|
|
|
fd = open("/proc/cmdline", O_RDONLY|O_CLOEXEC);
|
|
if (fd < 0) {
|
|
err = -errno;
|
|
DBG(config->ctx, "could not open '/proc/cmdline' for reading: %m\n");
|
|
return err;
|
|
}
|
|
|
|
err = read_str_safe(fd, buf, sizeof(buf));
|
|
close(fd);
|
|
if (err < 0) {
|
|
ERR(config->ctx, "could not read from '/proc/cmdline': %s\n",
|
|
strerror(-err));
|
|
return err;
|
|
}
|
|
|
|
state = STATE_MODNAME;
|
|
p_quote_start = NULL;
|
|
for (p = buf, modname = buf; iter; p++) {
|
|
switch (*p) {
|
|
case '"':
|
|
is_quoted = !is_quoted;
|
|
|
|
/*
|
|
* only allow starting quote as first char when looking
|
|
* for a modname: anything else is considered ill-formed
|
|
*/
|
|
if (is_quoted && state == STATE_MODNAME && p == modname) {
|
|
p_quote_start = p;
|
|
modname = p + 1;
|
|
} else if (state != STATE_VALUE) {
|
|
state = STATE_IGNORE;
|
|
}
|
|
|
|
break;
|
|
case '\0':
|
|
iter = false;
|
|
/* fall-through */
|
|
case ' ':
|
|
case '\n':
|
|
case '\t':
|
|
case '\v':
|
|
case '\f':
|
|
case '\r':
|
|
if (is_quoted && state == STATE_VALUE) {
|
|
/* no state change*/;
|
|
} else if (is_quoted) {
|
|
/* spaces are only allowed in the value part */
|
|
state = STATE_IGNORE;
|
|
} else if (state == STATE_VALUE || state == STATE_PARAM) {
|
|
*p = '\0';
|
|
state = STATE_COMPLETE;
|
|
} else {
|
|
/*
|
|
* go to next option, ignoring any possible
|
|
* partial match we have
|
|
*/
|
|
modname = p + 1;
|
|
state = STATE_MODNAME;
|
|
p_quote_start = NULL;
|
|
}
|
|
break;
|
|
case '.':
|
|
if (state == STATE_MODNAME) {
|
|
*p = '\0';
|
|
param = p + 1;
|
|
state = STATE_PARAM;
|
|
} else if (state == STATE_PARAM) {
|
|
state = STATE_IGNORE;
|
|
}
|
|
break;
|
|
case '=':
|
|
if (state == STATE_PARAM) {
|
|
/*
|
|
* Don't set *p to '\0': the value var shadows
|
|
* param
|
|
*/
|
|
value = p + 1;
|
|
state = STATE_VALUE;
|
|
} else if (state == STATE_MODNAME) {
|
|
state = STATE_IGNORE;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (state == STATE_COMPLETE) {
|
|
/*
|
|
* We may need to re-quote to unmangle what the
|
|
* bootloader passed. Example: grub passes the option as
|
|
* "parport.dyndbg=file drivers/parport/ieee1284_ops.c +mpf"
|
|
* instead of
|
|
* parport.dyndbg="file drivers/parport/ieee1284_ops.c +mpf"
|
|
*/
|
|
if (p_quote_start && p_quote_start < modname) {
|
|
/*
|
|
* p_quote_start
|
|
* |
|
|
* |modname param value
|
|
* || | |
|
|
* vv v v
|
|
* "parport\0dyndbg=file drivers/parport/ieee1284_ops.c +mpf" */
|
|
memmove(p_quote_start, modname, value - modname);
|
|
value--; modname--; param--;
|
|
*value = '"';
|
|
}
|
|
kcmdline_parse_result(config, modname, param, value);
|
|
/* start over on next iteration */
|
|
modname = p + 1;
|
|
state = STATE_MODNAME;
|
|
p_quote_start = NULL;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Take an fd and own it. It will be closed on return. filename is used only
|
|
* for debug messages
|
|
*/
|
|
static int kmod_config_parse(struct kmod_config *config, int fd,
|
|
const char *filename)
|
|
{
|
|
struct kmod_ctx *ctx = config->ctx;
|
|
char *line;
|
|
FILE *fp;
|
|
unsigned int linenum = 0;
|
|
int err;
|
|
|
|
fp = fdopen(fd, "r");
|
|
if (fp == NULL) {
|
|
err = -errno;
|
|
ERR(config->ctx, "fd %d: %m\n", fd);
|
|
close(fd);
|
|
return err;
|
|
}
|
|
|
|
while ((line = freadline_wrapped(fp, &linenum)) != NULL) {
|
|
char *cmd, *saveptr;
|
|
|
|
if (line[0] == '\0' || line[0] == '#')
|
|
goto done_next;
|
|
|
|
cmd = strtok_r(line, "\t ", &saveptr);
|
|
if (cmd == NULL)
|
|
goto done_next;
|
|
|
|
if (streq(cmd, "alias")) {
|
|
char *alias = strtok_r(NULL, "\t ", &saveptr);
|
|
char *modname = strtok_r(NULL, "\t ", &saveptr);
|
|
|
|
if (underscores(alias) < 0 || underscores(modname) < 0)
|
|
goto syntax_error;
|
|
|
|
kmod_config_add_alias(config, alias, modname);
|
|
} else if (streq(cmd, "blacklist")) {
|
|
char *modname = strtok_r(NULL, "\t ", &saveptr);
|
|
|
|
if (underscores(modname) < 0)
|
|
goto syntax_error;
|
|
|
|
kmod_config_add_blacklist(config, modname);
|
|
} else if (streq(cmd, "options")) {
|
|
char *modname = strtok_r(NULL, "\t ", &saveptr);
|
|
char *options = strtok_r(NULL, "\0", &saveptr);
|
|
|
|
if (underscores(modname) < 0 || options == NULL)
|
|
goto syntax_error;
|
|
|
|
kmod_config_add_options(config, modname, options);
|
|
} else if (streq(cmd, "install")) {
|
|
char *modname = strtok_r(NULL, "\t ", &saveptr);
|
|
char *installcmd = strtok_r(NULL, "\0", &saveptr);
|
|
|
|
if (underscores(modname) < 0 || installcmd == NULL)
|
|
goto syntax_error;
|
|
|
|
kmod_config_add_command(config, modname, installcmd,
|
|
cmd, &config->install_commands);
|
|
} else if (streq(cmd, "remove")) {
|
|
char *modname = strtok_r(NULL, "\t ", &saveptr);
|
|
char *removecmd = strtok_r(NULL, "\0", &saveptr);
|
|
|
|
if (underscores(modname) < 0 || removecmd == NULL)
|
|
goto syntax_error;
|
|
|
|
kmod_config_add_command(config, modname, removecmd,
|
|
cmd, &config->remove_commands);
|
|
} else if (streq(cmd, "softdep")) {
|
|
char *modname = strtok_r(NULL, "\t ", &saveptr);
|
|
char *softdeps = strtok_r(NULL, "\0", &saveptr);
|
|
|
|
if (underscores(modname) < 0 || softdeps == NULL)
|
|
goto syntax_error;
|
|
|
|
kmod_config_add_softdep(config, modname, softdeps);
|
|
} else if (streq(cmd, "weakdep")) {
|
|
char *modname = strtok_r(NULL, "\t ", &saveptr);
|
|
char *weakdeps = strtok_r(NULL, "\0", &saveptr);
|
|
|
|
if (underscores(modname) < 0 || weakdeps == NULL)
|
|
goto syntax_error;
|
|
|
|
kmod_config_add_weakdep(config, modname, weakdeps);
|
|
} else if (streq(cmd, "include")
|
|
|| streq(cmd, "config")) {
|
|
ERR(ctx, "%s: command %s is deprecated and not parsed anymore\n",
|
|
filename, cmd);
|
|
} else {
|
|
syntax_error:
|
|
ERR(ctx, "%s line %u: ignoring bad line starting with '%s'\n",
|
|
filename, linenum, cmd);
|
|
}
|
|
|
|
done_next:
|
|
free(line);
|
|
}
|
|
|
|
fclose(fp);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void kmod_config_free(struct kmod_config *config)
|
|
{
|
|
while (config->aliases)
|
|
kmod_config_free_alias(config, config->aliases);
|
|
|
|
while (config->blacklists)
|
|
kmod_config_free_blacklist(config, config->blacklists);
|
|
|
|
while (config->options)
|
|
kmod_config_free_options(config, config->options);
|
|
|
|
while (config->install_commands) {
|
|
kmod_config_free_command(config, config->install_commands,
|
|
&config->install_commands);
|
|
}
|
|
|
|
while (config->remove_commands) {
|
|
kmod_config_free_command(config, config->remove_commands,
|
|
&config->remove_commands);
|
|
}
|
|
|
|
while (config->softdeps)
|
|
kmod_config_free_softdep(config, config->softdeps);
|
|
|
|
while (config->weakdeps)
|
|
kmod_config_free_weakdep(config, config->weakdeps);
|
|
|
|
for (; config->paths != NULL;
|
|
config->paths = kmod_list_remove(config->paths))
|
|
free(config->paths->data);
|
|
|
|
free(config);
|
|
}
|
|
|
|
static bool conf_files_filter_out(struct kmod_ctx *ctx, DIR *d,
|
|
const char *path, const char *fn)
|
|
{
|
|
size_t len = strlen(fn);
|
|
struct stat st;
|
|
|
|
if (fn[0] == '.')
|
|
return true;
|
|
|
|
if (len < 6 || (!streq(&fn[len - 5], ".conf")
|
|
&& !streq(&fn[len - 6], ".alias")))
|
|
return true;
|
|
|
|
fstatat(dirfd(d), fn, &st, 0);
|
|
|
|
if (S_ISDIR(st.st_mode)) {
|
|
ERR(ctx, "Directories inside directories are not supported: "
|
|
"%s/%s\n", path, fn);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
struct conf_file {
|
|
const char *path;
|
|
bool is_single;
|
|
char name[];
|
|
};
|
|
|
|
static int conf_files_insert_sorted(struct kmod_ctx *ctx,
|
|
struct kmod_list **list,
|
|
const char *path, const char *name)
|
|
{
|
|
struct kmod_list *lpos, *tmp;
|
|
struct conf_file *cf;
|
|
size_t namelen;
|
|
int cmp = -1;
|
|
bool is_single = false;
|
|
|
|
if (name == NULL) {
|
|
name = basename(path);
|
|
is_single = true;
|
|
}
|
|
|
|
kmod_list_foreach(lpos, *list) {
|
|
cf = lpos->data;
|
|
|
|
if ((cmp = strcmp(name, cf->name)) <= 0)
|
|
break;
|
|
}
|
|
|
|
if (cmp == 0) {
|
|
DBG(ctx, "Ignoring duplicate config file: %s/%s\n", path,
|
|
name);
|
|
return -EEXIST;
|
|
}
|
|
|
|
namelen = strlen(name);
|
|
cf = malloc(sizeof(*cf) + namelen + 1);
|
|
if (cf == NULL)
|
|
return -ENOMEM;
|
|
|
|
memcpy(cf->name, name, namelen + 1);
|
|
cf->path = path;
|
|
cf->is_single = is_single;
|
|
|
|
if (lpos == NULL)
|
|
tmp = kmod_list_append(*list, cf);
|
|
else if (lpos == *list)
|
|
tmp = kmod_list_prepend(*list, cf);
|
|
else
|
|
tmp = kmod_list_insert_before(lpos, cf);
|
|
|
|
if (tmp == NULL) {
|
|
free(cf);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
if (lpos == NULL || lpos == *list)
|
|
*list = tmp;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Insert configuration files in @list, ignoring duplicates
|
|
*/
|
|
static int conf_files_list(struct kmod_ctx *ctx, struct kmod_list **list,
|
|
const char *path,
|
|
unsigned long long *path_stamp)
|
|
{
|
|
DIR *d;
|
|
int err;
|
|
struct stat st;
|
|
struct dirent *dent;
|
|
|
|
if (stat(path, &st) != 0) {
|
|
err = -errno;
|
|
DBG(ctx, "could not stat '%s': %m\n", path);
|
|
return err;
|
|
}
|
|
|
|
*path_stamp = stat_mstamp(&st);
|
|
|
|
if (!S_ISDIR(st.st_mode)) {
|
|
conf_files_insert_sorted(ctx, list, path, NULL);
|
|
return 0;
|
|
}
|
|
|
|
d = opendir(path);
|
|
if (d == NULL) {
|
|
ERR(ctx, "opendir(%s): %m\n", path);
|
|
return -EINVAL;
|
|
}
|
|
|
|
for (dent = readdir(d); dent != NULL; dent = readdir(d)) {
|
|
if (conf_files_filter_out(ctx, d, path, dent->d_name))
|
|
continue;
|
|
|
|
conf_files_insert_sorted(ctx, list, path, dent->d_name);
|
|
}
|
|
|
|
closedir(d);
|
|
return 0;
|
|
}
|
|
|
|
int kmod_config_new(struct kmod_ctx *ctx, struct kmod_config **p_config,
|
|
const char * const *config_paths)
|
|
{
|
|
struct kmod_config *config;
|
|
struct kmod_list *list = NULL;
|
|
struct kmod_list *path_list = NULL;
|
|
size_t i;
|
|
|
|
conf_files_insert_sorted(ctx, &list, kmod_get_dirname(ctx), "modules.softdep");
|
|
conf_files_insert_sorted(ctx, &list, kmod_get_dirname(ctx), "modules.weakdep");
|
|
|
|
for (i = 0; config_paths[i] != NULL; i++) {
|
|
const char *path = config_paths[i];
|
|
unsigned long long path_stamp = 0;
|
|
size_t pathlen;
|
|
struct kmod_list *tmp;
|
|
struct kmod_config_path *cf;
|
|
|
|
if (conf_files_list(ctx, &list, path, &path_stamp) < 0)
|
|
continue;
|
|
|
|
pathlen = strlen(path) + 1;
|
|
cf = malloc(sizeof(*cf) + pathlen);
|
|
if (cf == NULL)
|
|
goto oom;
|
|
|
|
cf->stamp = path_stamp;
|
|
memcpy(cf->path, path, pathlen);
|
|
|
|
tmp = kmod_list_append(path_list, cf);
|
|
if (tmp == NULL) {
|
|
free(cf);
|
|
goto oom;
|
|
}
|
|
path_list = tmp;
|
|
}
|
|
|
|
*p_config = config = calloc(1, sizeof(struct kmod_config));
|
|
if (config == NULL)
|
|
goto oom;
|
|
|
|
config->paths = path_list;
|
|
config->ctx = ctx;
|
|
|
|
for (; list != NULL; list = kmod_list_remove(list)) {
|
|
char buf[PATH_MAX];
|
|
const char *fn = buf;
|
|
struct conf_file *cf = list->data;
|
|
int fd;
|
|
|
|
if (cf->is_single) {
|
|
fn = cf->path;
|
|
} else if (snprintf(buf, sizeof(buf), "%s/%s",
|
|
cf->path, cf->name) >= (int)sizeof(buf)) {
|
|
ERR(ctx, "Error parsing %s/%s: path too long\n",
|
|
cf->path, cf->name);
|
|
free(cf);
|
|
continue;
|
|
}
|
|
|
|
fd = open(fn, O_RDONLY|O_CLOEXEC);
|
|
DBG(ctx, "parsing file '%s' fd=%d\n", fn, fd);
|
|
|
|
if (fd >= 0)
|
|
kmod_config_parse(config, fd, fn);
|
|
|
|
free(cf);
|
|
}
|
|
|
|
kmod_config_parse_kcmdline(config);
|
|
|
|
return 0;
|
|
|
|
oom:
|
|
for (; list != NULL; list = kmod_list_remove(list))
|
|
free(list->data);
|
|
|
|
for (; path_list != NULL; path_list = kmod_list_remove(path_list))
|
|
free(path_list->data);
|
|
|
|
return -ENOMEM;
|
|
}
|
|
|
|
/**********************************************************************
|
|
* struct kmod_config_iter functions
|
|
**********************************************************************/
|
|
|
|
enum config_type {
|
|
CONFIG_TYPE_BLACKLIST = 0,
|
|
CONFIG_TYPE_INSTALL,
|
|
CONFIG_TYPE_REMOVE,
|
|
CONFIG_TYPE_ALIAS,
|
|
CONFIG_TYPE_OPTION,
|
|
CONFIG_TYPE_SOFTDEP,
|
|
CONFIG_TYPE_WEAKDEP,
|
|
};
|
|
|
|
struct kmod_config_iter {
|
|
enum config_type type;
|
|
bool intermediate;
|
|
const struct kmod_list *list;
|
|
const struct kmod_list *curr;
|
|
void *data;
|
|
const char *(*get_key)(const struct kmod_list *l);
|
|
const char *(*get_value)(const struct kmod_list *l);
|
|
};
|
|
|
|
static const char *softdep_get_plain_softdep(const struct kmod_list *l)
|
|
{
|
|
char *s = softdep_to_char(l->data);
|
|
return s;
|
|
}
|
|
|
|
static const char *weakdep_get_plain_weakdep(const struct kmod_list *l)
|
|
{
|
|
char *s = weakdep_to_char(l->data);
|
|
return s;
|
|
}
|
|
|
|
static struct kmod_config_iter *kmod_config_iter_new(const struct kmod_ctx* ctx,
|
|
enum config_type type)
|
|
{
|
|
struct kmod_config_iter *iter = calloc(1, sizeof(*iter));
|
|
const struct kmod_config *config = kmod_get_config(ctx);
|
|
|
|
if (iter == NULL)
|
|
return NULL;
|
|
|
|
iter->type = type;
|
|
|
|
switch (type) {
|
|
case CONFIG_TYPE_BLACKLIST:
|
|
iter->list = config->blacklists;
|
|
iter->get_key = kmod_blacklist_get_modname;
|
|
break;
|
|
case CONFIG_TYPE_INSTALL:
|
|
iter->list = config->install_commands;
|
|
iter->get_key = kmod_command_get_modname;
|
|
iter->get_value = kmod_command_get_command;
|
|
break;
|
|
case CONFIG_TYPE_REMOVE:
|
|
iter->list = config->remove_commands;
|
|
iter->get_key = kmod_command_get_modname;
|
|
iter->get_value = kmod_command_get_command;
|
|
break;
|
|
case CONFIG_TYPE_ALIAS:
|
|
iter->list = config->aliases;
|
|
iter->get_key = kmod_alias_get_name;
|
|
iter->get_value = kmod_alias_get_modname;
|
|
break;
|
|
case CONFIG_TYPE_OPTION:
|
|
iter->list = config->options;
|
|
iter->get_key = kmod_option_get_modname;
|
|
iter->get_value = kmod_option_get_options;
|
|
break;
|
|
case CONFIG_TYPE_SOFTDEP:
|
|
iter->list = config->softdeps;
|
|
iter->get_key = kmod_softdep_get_name;
|
|
iter->get_value = softdep_get_plain_softdep;
|
|
iter->intermediate = true;
|
|
break;
|
|
case CONFIG_TYPE_WEAKDEP:
|
|
iter->list = config->weakdeps;
|
|
iter->get_key = kmod_weakdep_get_name;
|
|
iter->get_value = weakdep_get_plain_weakdep;
|
|
iter->intermediate = true;
|
|
break;
|
|
}
|
|
|
|
return iter;
|
|
}
|
|
|
|
/**
|
|
* SECTION:libkmod-config
|
|
* @short_description: retrieve current libkmod configuration
|
|
*/
|
|
|
|
/**
|
|
* kmod_config_get_blacklists:
|
|
* @ctx: kmod library context
|
|
*
|
|
* Retrieve an iterator to deal with the blacklist maintained inside the
|
|
* library. See kmod_config_iter_get_key(), kmod_config_iter_get_value() and
|
|
* kmod_config_iter_next(). At least one call to kmod_config_iter_next() must
|
|
* be made to initialize the iterator and check if it's valid.
|
|
*
|
|
* Returns: a new iterator over the blacklists or NULL on failure. Free it
|
|
* with kmod_config_iter_free_iter().
|
|
*/
|
|
KMOD_EXPORT struct kmod_config_iter *kmod_config_get_blacklists(const struct kmod_ctx *ctx)
|
|
{
|
|
if (ctx == NULL)
|
|
return NULL;;
|
|
|
|
return kmod_config_iter_new(ctx, CONFIG_TYPE_BLACKLIST);
|
|
}
|
|
|
|
/**
|
|
* kmod_config_get_install_commands:
|
|
* @ctx: kmod library context
|
|
*
|
|
* Retrieve an iterator to deal with the install commands maintained inside the
|
|
* library. See kmod_config_iter_get_key(), kmod_config_iter_get_value() and
|
|
* kmod_config_iter_next(). At least one call to kmod_config_iter_next() must
|
|
* be made to initialize the iterator and check if it's valid.
|
|
*
|
|
* Returns: a new iterator over the install commands or NULL on failure. Free
|
|
* it with kmod_config_iter_free_iter().
|
|
*/
|
|
KMOD_EXPORT struct kmod_config_iter *kmod_config_get_install_commands(const struct kmod_ctx *ctx)
|
|
{
|
|
if (ctx == NULL)
|
|
return NULL;;
|
|
|
|
return kmod_config_iter_new(ctx, CONFIG_TYPE_INSTALL);
|
|
}
|
|
|
|
/**
|
|
* kmod_config_get_remove_commands:
|
|
* @ctx: kmod library context
|
|
*
|
|
* Retrieve an iterator to deal with the remove commands maintained inside the
|
|
* library. See kmod_config_iter_get_key(), kmod_config_iter_get_value() and
|
|
* kmod_config_iter_next(). At least one call to kmod_config_iter_next() must
|
|
* be made to initialize the iterator and check if it's valid.
|
|
*
|
|
* Returns: a new iterator over the remove commands or NULL on failure. Free
|
|
* it with kmod_config_iter_free_iter().
|
|
*/
|
|
KMOD_EXPORT struct kmod_config_iter *kmod_config_get_remove_commands(const struct kmod_ctx *ctx)
|
|
{
|
|
if (ctx == NULL)
|
|
return NULL;;
|
|
|
|
return kmod_config_iter_new(ctx, CONFIG_TYPE_REMOVE);
|
|
}
|
|
|
|
/**
|
|
* kmod_config_get_aliases:
|
|
* @ctx: kmod library context
|
|
*
|
|
* Retrieve an iterator to deal with the aliases maintained inside the
|
|
* library. See kmod_config_iter_get_key(), kmod_config_iter_get_value() and
|
|
* kmod_config_iter_next(). At least one call to kmod_config_iter_next() must
|
|
* be made to initialize the iterator and check if it's valid.
|
|
*
|
|
* Returns: a new iterator over the aliases or NULL on failure. Free it with
|
|
* kmod_config_iter_free_iter().
|
|
*/
|
|
KMOD_EXPORT struct kmod_config_iter *kmod_config_get_aliases(const struct kmod_ctx *ctx)
|
|
{
|
|
if (ctx == NULL)
|
|
return NULL;;
|
|
|
|
return kmod_config_iter_new(ctx, CONFIG_TYPE_ALIAS);
|
|
}
|
|
|
|
/**
|
|
* kmod_config_get_options:
|
|
* @ctx: kmod library context
|
|
*
|
|
* Retrieve an iterator to deal with the options maintained inside the
|
|
* library. See kmod_config_iter_get_key(), kmod_config_iter_get_value() and
|
|
* kmod_config_iter_next(). At least one call to kmod_config_iter_next() must
|
|
* be made to initialize the iterator and check if it's valid.
|
|
*
|
|
* Returns: a new iterator over the options or NULL on failure. Free it with
|
|
* kmod_config_iter_free_iter().
|
|
*/
|
|
KMOD_EXPORT struct kmod_config_iter *kmod_config_get_options(const struct kmod_ctx *ctx)
|
|
{
|
|
if (ctx == NULL)
|
|
return NULL;;
|
|
|
|
return kmod_config_iter_new(ctx, CONFIG_TYPE_OPTION);
|
|
}
|
|
|
|
/**
|
|
* kmod_config_get_softdeps:
|
|
* @ctx: kmod library context
|
|
*
|
|
* Retrieve an iterator to deal with the softdeps maintained inside the
|
|
* library. See kmod_config_iter_get_key(), kmod_config_iter_get_value() and
|
|
* kmod_config_iter_next(). At least one call to kmod_config_iter_next() must
|
|
* be made to initialize the iterator and check if it's valid.
|
|
*
|
|
* Returns: a new iterator over the softdeps or NULL on failure. Free it with
|
|
* kmod_config_iter_free_iter().
|
|
*/
|
|
KMOD_EXPORT struct kmod_config_iter *kmod_config_get_softdeps(const struct kmod_ctx *ctx)
|
|
{
|
|
if (ctx == NULL)
|
|
return NULL;;
|
|
|
|
return kmod_config_iter_new(ctx, CONFIG_TYPE_SOFTDEP);
|
|
}
|
|
|
|
/**
|
|
* kmod_config_get_weakdeps:
|
|
* @ctx: kmod library context
|
|
*
|
|
* Retrieve an iterator to deal with the weakdeps maintained inside the
|
|
* library. See kmod_config_iter_get_key(), kmod_config_iter_get_value() and
|
|
* kmod_config_iter_next(). At least one call to kmod_config_iter_next() must
|
|
* be made to initialize the iterator and check if it's valid.
|
|
*
|
|
* Returns: a new iterator over the weakdeps or NULL on failure. Free it with
|
|
* kmod_config_iter_free_iter().
|
|
*/
|
|
KMOD_EXPORT struct kmod_config_iter *kmod_config_get_weakdeps(const struct kmod_ctx *ctx)
|
|
{
|
|
if (ctx == NULL)
|
|
return NULL;;
|
|
|
|
return kmod_config_iter_new(ctx, CONFIG_TYPE_WEAKDEP);
|
|
}
|
|
|
|
/**
|
|
* kmod_config_iter_get_key:
|
|
* @iter: iterator over a certain configuration
|
|
*
|
|
* When using a new allocated iterator, user must perform a call to
|
|
* kmod_config_iter_next() to initialize iterator's position and check if it's
|
|
* valid.
|
|
*
|
|
* Returns: the key of the current configuration pointed by @iter.
|
|
*/
|
|
KMOD_EXPORT const char *kmod_config_iter_get_key(const struct kmod_config_iter *iter)
|
|
{
|
|
if (iter == NULL || iter->curr == NULL)
|
|
return NULL;
|
|
|
|
return iter->get_key(iter->curr);
|
|
}
|
|
|
|
/**
|
|
* kmod_config_iter_get_value:
|
|
* @iter: iterator over a certain configuration
|
|
*
|
|
* When using a new allocated iterator, user must perform a call to
|
|
* kmod_config_iter_next() to initialize iterator's position and check if it's
|
|
* valid.
|
|
*
|
|
* Returns: the value of the current configuration pointed by @iter.
|
|
*/
|
|
KMOD_EXPORT const char *kmod_config_iter_get_value(const struct kmod_config_iter *iter)
|
|
{
|
|
const char *s;
|
|
|
|
if (iter == NULL || iter->curr == NULL)
|
|
return NULL;
|
|
|
|
if (iter->get_value == NULL)
|
|
return NULL;
|
|
|
|
if (iter->intermediate) {
|
|
struct kmod_config_iter *i = (struct kmod_config_iter *)iter;
|
|
|
|
free(i->data);
|
|
s = i->data = (void *) iter->get_value(iter->curr);
|
|
} else
|
|
s = iter->get_value(iter->curr);
|
|
|
|
return s;
|
|
}
|
|
|
|
/**
|
|
* kmod_config_iter_next:
|
|
* @iter: iterator over a certain configuration
|
|
*
|
|
* Make @iter point to the next item of a certain configuration. It's an
|
|
* automatically recycling iterator. When it reaches the end, false is
|
|
* returned; then if user wants to iterate again, it's sufficient to call this
|
|
* function once more.
|
|
*
|
|
* Returns: true if next position of @iter is valid or false if its end is
|
|
* reached.
|
|
*/
|
|
KMOD_EXPORT bool kmod_config_iter_next(struct kmod_config_iter *iter)
|
|
{
|
|
if (iter == NULL)
|
|
return false;
|
|
|
|
if (iter->curr == NULL) {
|
|
iter->curr = iter->list;
|
|
return iter->curr != NULL;
|
|
}
|
|
|
|
iter->curr = kmod_list_next(iter->list, iter->curr);
|
|
|
|
return iter->curr != NULL;
|
|
}
|
|
|
|
/**
|
|
* kmod_config_iter_free_iter:
|
|
* @iter: iterator over a certain configuration
|
|
*
|
|
* Free resources used by the iterator.
|
|
*/
|
|
KMOD_EXPORT void kmod_config_iter_free_iter(struct kmod_config_iter *iter)
|
|
{
|
|
free(iter->data);
|
|
free(iter);
|
|
}
|