From 3df154b79851bb4e1f857d08ba0623281f79d00d Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Tue, 6 Dec 2022 14:01:20 +0100 Subject: [PATCH] Fix #325: allow custom modprobe command in modules-load.d plugin Signed-off-by: Joachim Wiberg --- doc/plugins.md | 19 ++++++--- plugins/modules-load.c | 94 ++++++++++++++++++++++++++++++------------ 2 files changed, 82 insertions(+), 31 deletions(-) diff --git a/doc/plugins.md b/doc/plugins.md index c7d09c67..e8c1a38d 100644 --- a/doc/plugins.md +++ b/doc/plugins.md @@ -62,7 +62,7 @@ For your convenience a set of *optional* plugins are available: * *modules-load.so*: Scans `/etc/modules-load.d/*.conf` for modules to load using `modprobe`. Each file can contain multiple lines with the name of the module to load. Any line starting with the standard UNIX - comment character, `#`, is skipped. + comment character, `#`, or `;`, is skipped. Modules are loaded when entering runlevel `[2345]` using the `task` stanza. Each module gets a unique `name:modprobe.foo`, and @@ -71,11 +71,20 @@ For your convenience a set of *optional* plugins are available: set noindex + To change the index used by the plugin: + + set index 1234 + **Note:** unlike the traditional .conf `module` directive, which load - any listed module immediately, this plugin creates standard `taks` - directives which load the module(s) in the background. As long as - the `modprobe` program is found in the path, these tasks will always - return `[ OK ]` at boot. Check the actual status using `initctl`. + any listed module immediately, this plugin creates a background `task` + which load the module(s) in the background. The program is modprobe, + `/sbin/modprobe`, which you can override per .conf file: + + set modprobe /path/to/maybe-a-modprobe-wrapper + + Since these tasks run in the background, they usually return `[ OK ]` + at boot, unless the modprobe tool does not exist. Check syslog for + warnings and the actual status of the operation using `initctl`. * *netlink.so*: Listens to Linux kernel Netlink events for gateway and interfaces. These events are then sent to the Finit service monitor diff --git a/plugins/modules-load.c b/plugins/modules-load.c index 60a792b0..93299289 100644 --- a/plugins/modules-load.c +++ b/plugins/modules-load.c @@ -62,62 +62,104 @@ #ifndef MODULES_LOAD_PATH #define MODULES_LOAD_PATH "/etc/modules-load.d" #endif +#define MODPROBE_PATH "/sbin/modprobe" #define SERVICE_LINE \ - "cgroup.init name:modprobe.%s :%d [2345] /sbin/modprobe %s %s -- Kernel module: %s" + "cgroup.init name:modprobe.%s :%d [2345] %s %s %s -- Kernel module: %s" #define SERVICE_LINE_NOINDEX \ - "cgroup.init name:modprobe.%s [2345] /sbin/modprobe %s %s -- Kernel module: %s" + "cgroup.init name:modprobe.%s [2345] %s %s %s -- Kernel module: %s" -static int load_file(const char *file, int index) +static int modules_load(const char *file, int index) { char module_path[PATH_MAX]; - char line[256]; + char *modprobe_path; int num = 0; + char *line; FILE *fp; strlcpy(module_path, MODULES_LOAD_PATH "/", sizeof(module_path)); strlcat(module_path, file, sizeof(module_path)); fp = fopen(module_path, "r"); - if (!fp) + if (!fp) { + warnx("failed opening %s for reading, skipping ...", module_path); return 0; + } - while (fgets(line, sizeof(line), fp)) { - char cmd[CMD_SIZE], *mod, *args; + modprobe_path = strdup(MODPROBE_PATH); + if (!modprobe_path) { + warnx("failed allocating memory in modules-load plugin."); + fclose(fp); + return -1; + } - chomp(line); + while ((line = fparseln(fp, NULL, NULL, NULL, 0))) { + char cmd[CMD_SIZE * 2], *mod, *args, *set; + /* + * fparseln() skips regular UNIX comments only. + * This is for modules-load.d(5) compat. + */ + if (line[0] == ';') + goto next; + + /* trim whitespace */ mod = strip_line(line); if (!mod[0]) - continue; + goto next; - if (mod[0] == '#' || mod[0] == ';') - continue; + /* Finit extension 'set foo bar' */ + if ((set = fgetval(mod, "set", "= \t"))) { + char *val = mod; - if (!strcmp(mod, "set noindex")) { - index = 0; - continue; - } - if (!strncmp(mod, "set index", 9)) { - mod += 9; - args = strchr(mod, '='); - if (args) /* optional */ - mod = args + 1; - index = atoi(mod); - continue; + if (!strcmp(set, "noindex")) { + index = 0; + free(set); + goto next; + } + + if ((val = fgetval(set, "index", "= \t"))) { + index = atoi(val); + free(set); + free(val); + goto next; + } + + if ((val = fgetval(set, "modprobe", "= \t"))) { + if (access(val, X_OK)) { + warn("%s: cannot use %s", module_path, val); + free(set); + free(val); + free(line); + goto skip; + } else { + free(modprobe_path); + modprobe_path = val; + } + free(set); + goto next; + } + + /* unknown 'set' command, ignore. */ + goto next; } mod = strtok_r(mod, " ", &args); if (!mod) - continue; + goto next; if (!index) - snprintf(cmd, sizeof(cmd), SERVICE_LINE_NOINDEX, mod, mod, args, mod); + snprintf(cmd, sizeof(cmd), SERVICE_LINE_NOINDEX, mod, modprobe_path, mod, args, mod); else - snprintf(cmd, sizeof(cmd), SERVICE_LINE, mod, index++, mod, args, mod); + snprintf(cmd, sizeof(cmd), SERVICE_LINE, mod, index++, modprobe_path, mod, args, mod); + dbg("task %s", cmd); service_register(SVC_TYPE_TASK, cmd, global_rlimit, NULL); num++; + next: + free(line); } +skip: + free(modprobe_path); fclose(fp); return num; @@ -146,7 +188,7 @@ static void load(void *arg) num = scandir(MODULES_LOAD_PATH, &dentry, module_filter, alphasort); if (num > 0) { for (i = 0; i < num; i++) { - index += load_file(dentry[i]->d_name, index); + index += modules_load(dentry[i]->d_name, index); free(dentry[i]); } free(dentry);