implement softdeps.
diff --git a/TODO b/TODO
index 8ace999..47a6aa8 100644
--- a/TODO
+++ b/TODO
@@ -17,8 +17,6 @@
* Add functions list all modules known by modules.dep
-* Implement soft dependencies
-
* provide 1:1 compatibility with module-init-tools's modprobe
- show modversions (needs elf manipulation)
- dump configuration
diff --git a/libkmod/libkmod-config.c b/libkmod/libkmod-config.c
index b9ef0cd..057c093 100644
--- a/libkmod/libkmod-config.c
+++ b/libkmod/libkmod-config.c
@@ -48,6 +48,14 @@
char modname[];
};
+struct kmod_softdep {
+ char *name;
+ const char **pre;
+ const char **post;
+ unsigned int n_pre;
+ unsigned int n_post;
+};
+
const char *kmod_alias_get_name(const struct kmod_list *l) {
const struct kmod_alias *alias = l->data;
return alias->name;
@@ -78,6 +86,23 @@
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;
+}
+
static int kmod_config_add_command(struct kmod_config *config,
const char *modname,
const char *command,
@@ -242,6 +267,149 @@
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;
+ 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 void kmod_config_free_softdep(struct kmod_config *config,
+ struct kmod_list *l)
+{
+ free(l->data);
+ config->softdeps = kmod_list_remove(l);
+}
+
static void kcmdline_parse_result(struct kmod_config *config, char *modname,
char *param, char *value)
{
@@ -381,8 +549,16 @@
underscores(ctx, modname),
strtok_r(NULL, "\0", &saveptr),
cmd, &config->remove_commands);
+ } else if streq(cmd, "softdep") {
+ char *modname = strtok_r(NULL, "\t ", &saveptr);
+
+ if (modname == NULL)
+ goto syntax_error;
+
+ kmod_config_add_softdep(config,
+ underscores(ctx, modname),
+ strtok_r(NULL, "\0", &saveptr));
} else if (streq(cmd, "include")
- || streq(cmd, "softdep")
|| streq(cmd, "config")) {
INFO(ctx, "%s: command %s not implemented yet\n",
filename, cmd);
@@ -422,6 +598,9 @@
&config->remove_commands);
}
+ while (config->softdeps)
+ kmod_config_free_softdep(config, config->softdeps);
+
free(config);
}
diff --git a/libkmod/libkmod-module.c b/libkmod/libkmod-module.c
index e6388f2..cb0ec3d 100644
--- a/libkmod/libkmod-module.c
+++ b/libkmod/libkmod-module.c
@@ -34,6 +34,7 @@
#include <sys/types.h>
#include <sys/mman.h>
#include <string.h>
+#include <fnmatch.h>
#include "libkmod.h"
#include "libkmod-private.h"
@@ -52,6 +53,10 @@
char *options;
const char *install_commands; /* owned by kmod_config */
const char *remove_commands; /* owned by kmod_config */
+ struct {
+ struct kmod_list *pre;
+ struct kmod_list *post;
+ } softdeps;
char *alias; /* only set if this module was created from an alias */
int n_dep;
int refcount;
@@ -60,6 +65,7 @@
bool options : 1;
bool install_commands : 1;
bool remove_commands : 1;
+ bool softdeps : 1;
} init;
};
@@ -393,6 +399,8 @@
DBG(mod->ctx, "kmod_module %p released\n", mod);
kmod_pool_del_module(mod->ctx, mod, mod->hashkey);
+ kmod_module_unref_list(mod->softdeps.pre);
+ kmod_module_unref_list(mod->softdeps.post);
kmod_module_unref_list(mod->dep);
kmod_unref(mod->ctx);
free(mod->options);
@@ -851,6 +859,111 @@
mod->install_commands = cmd;
}
+static struct kmod_list *lookup_softdep(struct kmod_ctx *ctx, const char * const * array, unsigned int count)
+{
+ struct kmod_list *ret = NULL;
+ unsigned i;
+
+ for (i = 0; i < count; i++) {
+ const char *depname = array[i];
+ struct kmod_list *lst = NULL;
+ int err;
+
+ err = kmod_module_new_from_lookup(ctx, depname, &lst);
+ if (err < 0) {
+ ERR(ctx, "failed to lookup soft dependency '%s', continuing anyway.\n", depname);
+ continue;
+ } else if (lst != NULL)
+ ret = kmod_list_append_list(ret, lst);
+ }
+ return ret;
+}
+
+/**
+ * kmod_module_get_softdeps:
+ * @mod: kmod module
+ * @pre: where to save the list of preceding soft dependencies.
+ * @post: where to save the list of post soft dependencies.
+ *
+ * Get soft dependencies for this kmod module. Soft dependencies come
+ * from configuration file and are cached in @mod. The first call
+ * to this function will search for this module in configuration and
+ * subsequent calls return the known results.
+ *
+ * Both @pre and @post are newly created list of kmod_module and
+ * should be unreferenced with kmod_module_unref_list().
+ *
+ * Returns: 0 on success or < 0 otherwise.
+ */
+KMOD_EXPORT int kmod_module_get_softdeps(const struct kmod_module *mod, struct kmod_list **pre, struct kmod_list **post)
+{
+ const struct kmod_list *l;
+ struct kmod_list *l_new;
+
+ if (mod == NULL || pre == NULL || post == NULL)
+ return -ENOENT;
+
+ assert(*pre == NULL);
+ assert(*post == NULL);
+
+ if (!mod->init.softdeps) {
+ /* lazy init */
+ struct kmod_module *m = (struct kmod_module *)mod;
+ const struct kmod_list *ctx_softdeps;
+
+ ctx_softdeps = kmod_get_softdeps(mod->ctx);
+
+ kmod_list_foreach(l, ctx_softdeps) {
+ const char *modname = kmod_softdep_get_name(l);
+ const char * const *array;
+ unsigned count;
+
+ if (fnmatch(modname, mod->name, 0) != 0)
+ continue;
+
+ array = kmod_softdep_get_pre(l, &count);
+ m->softdeps.pre = lookup_softdep(mod->ctx, array, count);
+ array = kmod_softdep_get_post(l, &count);
+ m->softdeps.post = lookup_softdep(mod->ctx, array, count);
+ /*
+ * find only the first command, as modprobe from
+ * module-init-tools does
+ */
+ break;
+ }
+
+ m->init.softdeps = true;
+ }
+
+ kmod_list_foreach(l, mod->softdeps.pre) {
+ l_new = kmod_list_append(*pre, kmod_module_ref(l->data));
+ if (l_new == NULL) {
+ kmod_module_unref(l->data);
+ goto fail;
+ }
+ *pre = l_new;
+ }
+
+ kmod_list_foreach(l, mod->softdeps.post) {
+ l_new = kmod_list_append(*post, kmod_module_ref(l->data));
+ if (l_new == NULL) {
+ kmod_module_unref(l->data);
+ goto fail;
+ }
+ *post = l_new;
+ }
+
+ return 0;
+
+fail:
+ kmod_module_unref_list(*pre);
+ *pre = NULL;
+ kmod_module_unref_list(*post);
+ *post = NULL;
+ return -ENOMEM;
+}
+
+
/**
* kmod_module_get_remove_commands:
* @mod: kmod module
diff --git a/libkmod/libkmod-private.h b/libkmod/libkmod-private.h
index b9a93d2..85cfc12 100644
--- a/libkmod/libkmod-private.h
+++ b/libkmod/libkmod-private.h
@@ -85,6 +85,7 @@
const struct kmod_list *kmod_get_options(const struct kmod_ctx *ctx) __must_check __attribute__((nonnull(1)));
const struct kmod_list *kmod_get_install_commands(const struct kmod_ctx *ctx) __must_check __attribute__((nonnull(1)));
const struct kmod_list *kmod_get_remove_commands(const struct kmod_ctx *ctx) __must_check __attribute__((nonnull(1)));
+const struct kmod_list *kmod_get_softdeps(const struct kmod_ctx *ctx) __must_check __attribute__((nonnull(1)));
/* libkmod-config.c */
@@ -95,6 +96,7 @@
struct kmod_list *options;
struct kmod_list *remove_commands;
struct kmod_list *install_commands;
+ struct kmod_list *softdeps;
};
int kmod_config_new(struct kmod_ctx *ctx, struct kmod_config **config, const char * const *config_paths) __attribute__((nonnull(1, 2,3)));
void kmod_config_free(struct kmod_config *config) __attribute__((nonnull(1)));
@@ -105,6 +107,10 @@
const char *kmod_command_get_command(const struct kmod_list *l) __attribute__((nonnull(1)));
const char *kmod_command_get_modname(const struct kmod_list *l) __attribute__((nonnull(1)));
+const char *kmod_softdep_get_name(const struct kmod_list *l) __attribute__((nonnull(1)));
+const char * const *kmod_softdep_get_pre(const struct kmod_list *l, unsigned int *count) __attribute__((nonnull(1, 2)));
+const char * const *kmod_softdep_get_post(const struct kmod_list *l, unsigned int *count);
+
/* libkmod-module.c */
int kmod_module_new_from_alias(struct kmod_ctx *ctx, const char *alias, const char *name, struct kmod_module **mod);
diff --git a/libkmod/libkmod.c b/libkmod/libkmod.c
index c87e95c..87c9a3d 100644
--- a/libkmod/libkmod.c
+++ b/libkmod/libkmod.c
@@ -754,3 +754,8 @@
{
return ctx->config->remove_commands;
}
+
+const struct kmod_list *kmod_get_softdeps(const struct kmod_ctx *ctx)
+{
+ return ctx->config->softdeps;
+}
diff --git a/libkmod/libkmod.h b/libkmod/libkmod.h
index d9169d9..194946d 100644
--- a/libkmod/libkmod.h
+++ b/libkmod/libkmod.h
@@ -131,6 +131,8 @@
const char *kmod_module_get_install_commands(const struct kmod_module *mod);
const char *kmod_module_get_remove_commands(const struct kmod_module *mod);
+int kmod_module_get_softdeps(const struct kmod_module *mod, struct kmod_list **pre, struct kmod_list **post);
+
#ifdef __cplusplus
} /* extern "C" */
#endif
diff --git a/libkmod/libkmod.sym b/libkmod/libkmod.sym
index 552c3f3..71b7ccc 100644
--- a/libkmod/libkmod.sym
+++ b/libkmod/libkmod.sym
@@ -45,6 +45,7 @@
kmod_module_get_options;
kmod_module_get_install_commands;
kmod_module_get_remove_commands;
+ kmod_module_get_softdeps;
local:
*;
};
diff --git a/test/test-lookup.c b/test/test-lookup.c
index f4501b5..e68832c 100644
--- a/test/test-lookup.c
+++ b/test/test-lookup.c
@@ -92,6 +92,7 @@
printf("Alias: '%s'\nModules matching:\n", alias);
kmod_list_foreach(l, list) {
+ struct kmod_list *d, *pre = NULL, *post = NULL;
struct kmod_module *mod = kmod_module_get_module(l);
const char *str;
@@ -105,6 +106,31 @@
str = kmod_module_get_remove_commands(mod);
if (str)
printf("\t\tremove commands: '%s'\n", str);
+
+ err = kmod_module_get_softdeps(mod, &pre, &post);
+ if (err == 0) {
+ if (pre != NULL || post != NULL)
+ puts("\t\tsoft dependencies:");
+ if (pre != NULL) {
+ fputs("\t\t\tpre:", stdout);
+ kmod_list_foreach(d, pre) {
+ struct kmod_module *dm = kmod_module_get_module(d);
+ printf(" %s", kmod_module_get_name(dm));
+ }
+ putchar('\n');
+ kmod_module_unref_list(pre);
+ }
+ if (post != NULL) {
+ fputs("\t\t\tpost:", stdout);
+ kmod_list_foreach(d, post) {
+ struct kmod_module *dm = kmod_module_get_module(d);
+ printf(" %s", kmod_module_get_name(dm));
+ }
+ putchar('\n');
+ kmod_module_unref_list(post);
+ }
+ }
+
kmod_module_unref(mod);
}