From 1c52260048792561160a75a2ce62217e98cd381a Mon Sep 17 00:00:00 2001 From: Gustavo Sverzut Barbieri Date: Fri, 16 Dec 2011 21:18:10 -0200 Subject: [PATCH] implement softdeps. --- TODO | 2 - libkmod/libkmod-config.c | 181 +++++++++++++++++++++++++++++++++++++- libkmod/libkmod-module.c | 113 ++++++++++++++++++++++++ libkmod/libkmod-private.h | 6 ++ libkmod/libkmod.c | 5 ++ libkmod/libkmod.h | 2 + libkmod/libkmod.sym | 1 + test/test-lookup.c | 26 ++++++ 8 files changed, 333 insertions(+), 3 deletions(-) diff --git a/TODO b/TODO index 8ace999..47a6aa8 100644 --- a/TODO +++ b/TODO @@ -17,8 +17,6 @@ Features: * 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 @@ struct kmod_command { 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 @@ const char *kmod_command_get_modname(const struct kmod_list *l) { 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 @@ static void kmod_config_free_blacklist(struct kmod_config *config, 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 @@ static int kmod_config_parse(struct kmod_config *config, int fd, 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 @@ void kmod_config_free(struct kmod_config *config) &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 #include #include +#include #include "libkmod.h" #include "libkmod-private.h" @@ -52,6 +53,10 @@ struct kmod_module { 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 @@ struct kmod_module { bool options : 1; bool install_commands : 1; bool remove_commands : 1; + bool softdeps : 1; } init; }; @@ -393,6 +399,8 @@ KMOD_EXPORT struct kmod_module *kmod_module_unref(struct kmod_module *mod) 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 @@ void kmod_module_set_install_commands(struct kmod_module *mod, const char *cmd) 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 @@ void kmod_pool_del_module(struct kmod_ctx *ctx, struct kmod_module *mod, const c 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_config { 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_option_get_modname(const struct kmod_list *l) __attribute__((no 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 @@ const struct kmod_list *kmod_get_remove_commands(const struct kmod_ctx *ctx) { 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_options(const struct kmod_module *mod); 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 @@ global: 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 @@ int main(int argc, char *argv[]) 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 @@ int main(int argc, char *argv[]) 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); } -- 2.39.2