#include <fcntl.h>
#include <sys/stat.h>
#include <sys/wait.h>
+#include <libkmod.h>
#include "udev.h"
+static struct kmod_ctx *ctx;
+
+static int command_do(struct kmod_module *module, const char *type, const char *command, const char *cmdline_opts)
+{
+ const char *modname = kmod_module_get_name(module);
+ char *p, *cmd = NULL;
+ size_t cmdlen, cmdline_opts_len, varlen;
+ int ret = 0;
+
+ if (cmdline_opts == NULL)
+ cmdline_opts = "";
+ cmdline_opts_len = strlen(cmdline_opts);
+
+ cmd = strdup(command);
+ if (cmd == NULL)
+ return -ENOMEM;
+ cmdlen = strlen(cmd);
+ varlen = sizeof("$CMDLINE_OPTS") - 1;
+ while ((p = strstr(cmd, "$CMDLINE_OPTS")) != NULL) {
+ size_t prefixlen = p - cmd;
+ size_t suffixlen = cmdlen - prefixlen - varlen;
+ size_t slen = cmdlen - varlen + cmdline_opts_len;
+ char *suffix = p + varlen;
+ char *s = malloc(slen + 1);
+ if (s == NULL) {
+ free(cmd);
+ return -ENOMEM;
+ }
+ memcpy(s, cmd, p - cmd);
+ memcpy(s + prefixlen, cmdline_opts, cmdline_opts_len);
+ memcpy(s + prefixlen + cmdline_opts_len, suffix, suffixlen);
+ s[slen] = '\0';
+
+ free(cmd);
+ cmd = s;
+ cmdlen = slen;
+ }
+
+ setenv("MODPROBE_MODULE", modname, 1);
+ ret = system(cmd);
+ unsetenv("MODPROBE_MODULE");
+ if (ret == -1 || WEXITSTATUS(ret)) {
+ //LOG("Error running %s command for %s\n", type, modname);
+ if (ret != -1)
+ ret = -WEXITSTATUS(ret);
+ }
+
+end:
+ free(cmd);
+ return ret;
+}
+
+static int insmod_do_dependencies(struct kmod_module *parent);
+static int insmod_do_soft_dependencies(struct kmod_module *mod, struct kmod_list *deps);
+
+static int insmod_do_deps_list(struct kmod_module *parent, struct kmod_list *deps, unsigned stop_on_errors)
+{
+ struct kmod_list *d;
+ int err = 0;
+
+ kmod_list_foreach(d, deps) {
+ struct kmod_module *dm = kmod_module_get_module(d);
+ struct kmod_list *pre = NULL, *post = NULL;
+ const char *cmd, *opts, *dmname = kmod_module_get_name(dm);
+ int state;
+ int r;
+
+ r = insmod_do_dependencies(dm);
+ if (r < 0) {
+ //WRN("could not insert dependencies of '%s': %s\n", dmname, strerror(-r));
+ goto dep_error;
+ }
+
+ r = kmod_module_get_softdeps(dm, &pre, &post);
+ if (r < 0) {
+ //WRN("could not get softdeps of '%s': %s\n", dmname, strerror(-r));
+ goto dep_done;
+ }
+
+ r = insmod_do_soft_dependencies(dm, pre);
+ if (r < 0) {
+ //WRN("could not insert pre softdeps of '%s': %s\n", dmname, strerror(-r));
+ goto dep_error;
+ }
+
+ state = kmod_module_get_initstate(dm);
+ if (state == KMOD_MODULE_LIVE ||
+ state == KMOD_MODULE_COMING ||
+ state == KMOD_MODULE_BUILTIN)
+ goto dep_done;
+
+ cmd = kmod_module_get_install_commands(dm);
+ if (cmd) {
+ r = command_do(dm, "install", cmd, NULL);
+ if (r < 0) {
+ //WRN("failed to execute install command of '%s':" " %s\n", dmname, strerror(-r));
+ goto dep_error;
+ } else
+ goto dep_done;
+ }
+
+ opts = kmod_module_get_options(dm);
+
+ r = kmod_module_insert_module(dm, 0, opts);
+ if (r < 0) {
+ //WRN("could not insert '%s': %s\n", dmname, strerror(-r));
+ goto dep_error;
+ }
+
+ dep_done:
+ r = insmod_do_soft_dependencies(dm, post);
+ if (r < 0) {
+ //WRN("could not insert post softdeps of '%s': %s\n", dmname, strerror(-r));
+ goto dep_error;
+ }
+
+ kmod_module_unref_list(pre);
+ kmod_module_unref_list(post);
+ kmod_module_unref(dm);
+ continue;
+
+ dep_error:
+ err = r;
+ kmod_module_unref_list(pre);
+ kmod_module_unref_list(post);
+ kmod_module_unref(dm);
+ if (stop_on_errors)
+ break;
+ else
+ continue;
+ }
+
+ return err;
+}
+
+static int insmod_do_soft_dependencies(struct kmod_module *mod, struct kmod_list *deps)
+{
+ return insmod_do_deps_list(mod, deps, 0);
+}
+
+static int insmod_do_dependencies(struct kmod_module *parent)
+{
+ struct kmod_list *deps = kmod_module_get_dependencies(parent);
+ int err = insmod_do_deps_list(parent, deps, 1);
+ kmod_module_unref_list(deps);
+ return err;
+}
+
+static int insmod_do(struct kmod_module *mod, const char *extra_opts)
+{
+ const char *modname = kmod_module_get_name(mod);
+ const char *conf_opts = kmod_module_get_options(mod);
+ struct kmod_list *pre = NULL, *post = NULL;
+ char *opts = NULL;
+ const char *cmd;
+ int state;
+ int err;
+
+ err = kmod_module_get_softdeps(mod, &pre, &post);
+ if (err < 0) {
+ //WRN("could not get softdeps of '%s': %s\n", modname, strerror(-err));
+ return err;
+ }
+
+ err = insmod_do_soft_dependencies(mod, pre);
+ if (err < 0) {
+ //WRN("could not insert pre softdeps of '%s': %s\n", modname, strerror(-err));
+ goto error;
+ }
+
+ cmd = kmod_module_get_install_commands(mod);
+ if (cmd != NULL) {
+ err = command_do(mod, "install", cmd, extra_opts);
+ goto done;
+ }
+
+ state = kmod_module_get_initstate(mod);
+ if (state == KMOD_MODULE_BUILTIN || state == KMOD_MODULE_LIVE)
+ return 0;
+
+ /*
+ * At this point it's not possible to be a install/remove command
+ * anymore. So if we can't get module's path, it's because it was
+ * really intended to be a module and it doesn't exist
+ */
+ if (kmod_module_get_path(mod) == NULL) {
+ //LOG("Module %s not found.\n", modname);
+ return -ENOENT;
+ }
+
+ err = insmod_do_dependencies(mod);
+ if (err < 0)
+ return err;
+
+ if (conf_opts || extra_opts) {
+ if (conf_opts == NULL)
+ opts = strdup(extra_opts);
+ else if (extra_opts == NULL)
+ opts = strdup(conf_opts);
+ else if (asprintf(&opts, "%s %s", conf_opts, extra_opts) < 0)
+ opts = NULL;
+
+ if (opts == NULL) {
+ err = -ENOMEM;
+ goto error;
+ }
+ }
+
+ err = kmod_module_insert_module(mod, 0, opts);
+ if (err == -EEXIST)
+ err = 0;
+
+done:
+ err = insmod_do_soft_dependencies(mod, post);
+ if (err < 0) {
+ //WRN("could not insert post softdeps of '%s': %s\n", modname, strerror(-err));
+ goto error;
+ }
+
+error:
+ kmod_module_unref_list(pre);
+ kmod_module_unref_list(post);
+ free(opts);
+ return err;
+}
+
+static int insmod_path(struct kmod_ctx *ctx, const char *path, const char *extra_options)
+{
+ struct kmod_module *mod;
+ int err;
+
+ err = kmod_module_new_from_path(ctx, path, &mod);
+ if (err < 0) {
+ //LOG("Module %s not found.\n", path);
+ return err;
+ }
+ err = insmod_do(mod, extra_options);
+ kmod_module_unref(mod);
+ return err;
+}
+
+static int insmod_alias(struct kmod_ctx *ctx, const char *alias, const char *extra_options)
+{
+ struct kmod_list *l, *list = NULL;
+ struct kmod_list *filtered = NULL;
+ int err;
+
+ err = kmod_module_new_from_lookup(ctx, alias, &list);
+ if (err < 0)
+ return err;
+
+ if (list == NULL) {
+ //LOG("Module %s not found.\n", alias);
+ return err;
+ }
+
+ err = kmod_module_get_filtered_blacklist(ctx, list, &filtered);
+ kmod_module_unref_list(list);
+ if (err < 0) {
+ //LOG("Could not filter alias list!\n");
+ return err;
+ }
+ list = filtered;
+
+ kmod_list_foreach(l, list) {
+ struct kmod_module *mod = kmod_module_get_module(l);
+ err = insmod_do(mod, extra_options);
+ kmod_module_unref(mod);
+ if (err < 0)
+ break;
+ }
+
+ kmod_module_unref_list(list);
+ return err;
+}
+
+static int insmod(struct kmod_ctx *ctx, const char *name, const char *extra_options)
+{
+ struct stat st;
+ if (stat(name, &st) == 0)
+ return insmod_path(ctx, name, extra_options);
+ else
+ return insmod_alias(ctx, name, extra_options);
+}
+
static int builtin_kmod(struct udev_device *dev, int argc, char *argv[], bool test)
{
struct udev *udev = udev_device_get_udev(dev);
- pid_t pid;
- char *m[5];
+ int i;
+
+ if (!ctx)
+ return EXIT_FAILURE;
if (argc < 3) {
err(udev, "missing command + argument\n");
return EXIT_FAILURE;
}
- err(udev, "'%s' the module '%s' (%i)\n", argv[1], argv[2], argc);
-
- m[0] = "/sbin/modprobe";
- m[1] = "-bv";
- m[1] = argv[2];
- m[2] = argv[3];
- m[3] = NULL;
-
- pid = fork();
- switch(pid) {
- case 0:
- execv(m[0], m);
- _exit(1);
- case -1:
- return EXIT_FAILURE;
- default:
- waitpid(pid, NULL, 0);
+ for (i = 2; argv[i]; i++) {
+ info(udev, "%s '%s'\n", argv[1], argv[i]);
+ insmod(ctx, argv[i], NULL);
}
return EXIT_SUCCESS;
static int builtin_kmod_load(struct udev *udev)
{
+ kmod_unref(ctx);
+ ctx = kmod_new(NULL, NULL);
+ if (!ctx)
+ return -ENOMEM;
+
info(udev, "load module index\n");
return 0;
}
static int builtin_kmod_unload(struct udev *udev)
{
+ kmod_unref(ctx);
info(udev, "unload module index\n");
return 0;
}