From 2f91ab5eac593bcfeb2e431f1a29706850c3be90 Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Thu, 10 Nov 2022 17:21:33 +0100 Subject: [PATCH] Fix #235: support for overriding /etc/finit.conf and /etc/finit.d This change adds support for a new command line option finit.config=PATH which can be used to tell Finit to start with /etc/factory.conf instead of /etc/finit.conf. For the complete experience a new top-level configuration file directive `rcsd PATH` has aslo been added. It in turn can be used by factory.conf as follows to override /etc/finit.d: rcsd /etc/factory.d Manually verified in myLinux Signed-off-by: Joachim Wiberg --- doc/cmdline.md | 10 ++++++ doc/config.md | 29 ++++++++++++++++++ src/conf.c | 77 +++++++++++++++++++++++++++++++++++++--------- src/conf.h | 1 + src/finit.c | 9 ++++-- src/finit.h | 1 - src/initctl.c | 83 +++++++++++++++++++++++++++++++++++++++++++++----- src/initctl.h | 3 ++ src/private.h | 3 ++ src/serv.c | 42 ++++++++++++------------- src/sm.c | 2 +- 11 files changed, 212 insertions(+), 48 deletions(-) diff --git a/doc/cmdline.md b/doc/cmdline.md index ebfff8a0..01477ff0 100644 --- a/doc/cmdline.md +++ b/doc/cmdline.md @@ -23,6 +23,16 @@ The `bool` setting is one of `on, off, true false, 1, 0`. > Note: `` conditions cannot be cleared with `initctl`! +* `finit.config=/path/to/alternative/finit.conf`: override the + compile-time bootstrap config file, default: + + ./configure --with-config=/etc/finit.conf + + Useful when starting up in various rescue mode, factory, or + production test setups. Use the top-level configuration file + directive `rcsd /path/to/finit.d` to override the default + rcS.d directory. + * `finit.debug[=bool]`: Enable finit debug. This is operated independently of the kernel `debug` setting. New as of Finit v4. diff --git a/doc/config.md b/doc/config.md index a8224de6..f89a7ac7 100644 --- a/doc/config.md +++ b/doc/config.md @@ -10,6 +10,7 @@ Configuration * [Hostname](#hostname) * [Kernel Modules](#kernel-modules) * [Networking](#networking) + * [Alternate rcS.d/](#alternate-rcs-d-) * [Resource Limits](#resource-limits) * [Runlevels](#runlevels) * [One-shot Commands (sequence)](#one-shot-commands--sequence-) @@ -40,6 +41,13 @@ Linux distributions -- each package can provide its own "script" file. - `/etc/finit.conf`: main configuration file - `/etc/finit.d/*.conf`: snippets, usually one service per file +> **Note:** the above .conf file name and rcS.d directory name are the +> defaults. They can be changed at compile-time with two `configure` +> options: `--with-config=FOO` and `--with-rcsd=BAR`. +> +> They can also be overridden from the command line `finit.config=BAZ` +> and the top-level configuration directive `rcsd /path/to/finit.d`. + Not all configuration directives are available in `/etc/finit.d/*.conf` and some directives are only available at [bootstrap][], runlevel `S`, see the section [Limitations](#limitations) below for details. @@ -332,6 +340,27 @@ Debian, Ubuntu, Linux Mint, or an embedded BusyBox system. > **Note:** only read and executed in runlevel S ([bootstrap][]). +### Alternate finit.d/ + +**Syntax:** `rcsd /path/to/finit.d` + +The Finit rcS.d directory is set at compile time with: + + ./configure --with-rcsd=/etc/finit.d + +A system with multiple use-cases may be bootstrapped with different +configurations, starting with the kernel command line option: + + -- finit.config=/etc/factory.conf + +This file in turn can use the `rcsd` directive to tell Finit to use +another set of .conf files, e.g.: + + rcsd /etc/factory.d + +> **Note:** this directive is only available from the top-level +> bootstrap .conf file. + ### Resource Limits **Syntax:** `rlimit [hard|soft] RESOURCE ` diff --git a/src/conf.c b/src/conf.c index de65871e..4098c38d 100644 --- a/src/conf.c +++ b/src/conf.c @@ -90,6 +90,7 @@ fallback: } /* + * finit.config = /path/to/etc/alt-finit.conf * finit.debug = [on,off] * finit.fstab = /path/to/etc/fstab.aternative * finit.status = [on,off] (compat finit.show_status) @@ -103,18 +104,27 @@ static void parse_finit_opts(char *opt) if (arg) *arg++ = 0; + if (string_compare(opt, "cond")) { + cond_boot_parse(arg); + return; + } + + if (string_compare(opt, "config")) { + if (finit_conf) + free(finit_conf); + finit_conf = strdup(arg); + return; + } + if (string_compare(opt, "debug")) { debug = get_bool(arg, 1); return; } if (string_compare(opt, "fstab")) { - fstab = arg; - return; - } - - if (string_compare(opt, "cond")) { - cond_boot_parse(arg); + if (fstab) + free(fstab); + fstab = strdup(arg); return; } @@ -250,6 +260,11 @@ static void parse_kernel_loglevel(void) */ void conf_parse_cmdline(int argc, char *argv[]) { + /* Set up defaults */ + fstab = strdup(FINIT_FSTAB); + finit_conf = strdup(FINIT_CONF); + finit_rcsd = strdup(FINIT_RCSD); + for (int i = 1; i < argc; i++) parse_arg(argv[i]); @@ -280,6 +295,28 @@ void conf_reset_env(void) setenv("USER", "root", 1); } +/* + * Sourced mainly by initctl and other Finit helper tools + */ +void conf_saverc(void) +{ + FILE *fp; + + fp = fopen(_PATH_VARRUN "finit/.initrc", "w"); + if (!fp) { + err(1, "failed creating .finitrc"); + return; + } + + fprintf(fp, "FINIT_CONF=%s\n", finit_conf); + fprintf(fp, "FINIT_RCSD=%s\n", finit_rcsd); + fprintf(fp, "FINIT_CGPATH=%s\n", FINIT_CGPATH); + fprintf(fp, "INIT_SOCKET=%s\n", INIT_SOCKET); + fprintf(fp, "INIT_MAGIC=0x%08x\n", INIT_MAGIC); + + fclose(fp); +} + /* * Sets, and makes a note of, all KEY=VALUE lines in a given .conf line * from finit.conf, or other .conf file. Note, PATH is always reset in @@ -654,6 +691,12 @@ static int parse_static(char *line, int is_rcsd) return 0; } + if (BOOTSTRAP && MATCH_CMD(line, "rcsd ", x)) { + if (finit_rcsd) free(finit_rcsd); + finit_rcsd = strdup(strip_line(x)); + return 0; + } + if (BOOTSTRAP && MATCH_CMD(line, "runparts ", x)) { if (runparts) free(runparts); runparts = strdup(strip_line(x)); @@ -845,6 +888,7 @@ static int parse_conf(char *file, int is_rcsd) */ int conf_reload(void) { + char path[strlen(finit_rcsd) + 16]; size_t i; glob_t gl; @@ -877,7 +921,7 @@ int conf_reload(void) } /* First, read /etc/finit.conf */ - parse_conf(FINIT_CONF, 0); + parse_conf(finit_conf, 0); /* Set global limits */ for (int i = 0; i < RLIMIT_NLIMITS; i++) { @@ -887,8 +931,10 @@ int conf_reload(void) } /* Next, read all *.conf in /etc/finit.d/ */ - glob(FINIT_RCSD "/*.conf", 0, NULL, &gl); - glob(FINIT_RCSD "/enabled/*.conf", GLOB_APPEND, NULL, &gl); + snprintf(path, sizeof(path), "%s/*.conf", finit_rcsd); + glob(path, 0, NULL, &gl); + snprintf(path, sizeof(path), "%s/enabled/*.conf", finit_rcsd); + glob(path, GLOB_APPEND, NULL, &gl); for (i = 0; i < gl.gl_pathc; i++) { char *path = gl.gl_pathv[i]; @@ -1099,6 +1145,7 @@ static void conf_cb(uev_t *w, void *arg, int events) */ int conf_monitor(void) { + char path[strlen(finit_rcsd) + 16]; int rc = 0; /* @@ -1107,10 +1154,12 @@ int conf_monitor(void) * have or not have symlinks in place. We need to monitor for * changes to either symlink or target. */ - rc += iwatch_add(&iw_conf, FINIT_RCSD, IN_ONLYDIR); - rc += iwatch_add(&iw_conf, FINIT_RCSD "/available/", IN_ONLYDIR | IN_DONT_FOLLOW); - rc += iwatch_add(&iw_conf, FINIT_RCSD "/enabled/", IN_ONLYDIR | IN_DONT_FOLLOW); - rc += iwatch_add(&iw_conf, FINIT_CONF, 0); + rc += iwatch_add(&iw_conf, finit_rcsd, IN_ONLYDIR); + snprintf(path, sizeof(path), "%s/available/", finit_rcsd); + rc += iwatch_add(&iw_conf, path, IN_ONLYDIR | IN_DONT_FOLLOW); + snprintf(path, sizeof(path), "%s/enabled/", finit_rcsd); + rc += iwatch_add(&iw_conf, path, IN_ONLYDIR | IN_DONT_FOLLOW); + rc += iwatch_add(&iw_conf, finit_conf, 0); /* * Systems with /etc/default, /etc/conf.d, or similar, can also @@ -1151,7 +1200,7 @@ int conf_init(uev_ctx_t *ctx) memcpy(global_rlimit, initial_rlimit, sizeof(global_rlimit)); /* Read global rlimits and global cgroup setup from /etc/finit.conf */ - parse_conf(FINIT_CONF, 0); + parse_conf(finit_conf, 0); /* prepare /etc watcher */ fd = iwatch_init(&iw_conf); diff --git a/src/conf.h b/src/conf.h index 4fc81874..2e4d3b50 100644 --- a/src/conf.h +++ b/src/conf.h @@ -43,6 +43,7 @@ int conf_changed (char *file); int conf_monitor (void); void conf_reset_env (void); +void conf_saverc (void); void conf_parse_cmdline (int argc, char *argv[]); int conf_parse_runlevels (char *runlevels); void conf_parse_cond (svc_t *svc, char *cond); diff --git a/src/finit.c b/src/finit.c index 7a48ac39..8fe8e8f1 100644 --- a/src/finit.c +++ b/src/finit.c @@ -69,11 +69,12 @@ int rescue = 0; /* rescue mode from kernel cmdline */ int single = 0; /* single user mode from kernel cmdline */ int bootstrap = 1; /* set while bootrapping (for TTYs) */ int kerndebug = 0; /* set if /proc/sys/kernel/printk > 7 */ -char *fstab = FINIT_FSTAB; +char *finit_conf= NULL; +char *finit_rcsd= NULL; +char *fstab = NULL; char *sdown = NULL; char *network = NULL; char *hostname = NULL; -char *rcsd = FINIT_RCSD; char *runparts = NULL; char *osheading = NULL; @@ -808,9 +809,11 @@ int main(int argc, char *argv[]) cond_set_oneshot(plugin_hook_str(HOOK_ROOTFS_UP)); /* - * Initialize .conf system and load static /etc/finit.conf. + * Initialize .conf system and load static /etc/finit.conf then + * tell the world what we used. */ conf_init(&loop); + conf_saverc(); /* * Start built-in watchdogd as soon as possible, if enabled diff --git a/src/finit.h b/src/finit.h index d7aa7de4..090f4d98 100644 --- a/src/finit.h +++ b/src/finit.h @@ -122,7 +122,6 @@ extern char *fstab; extern char *sdown; extern char *network; extern char *hostname; -extern char *rcsd; extern char *runparts; extern char *osheading; extern uev_ctx_t *ctx; diff --git a/src/initctl.c b/src/initctl.c index 0b8c6bae..89363ddd 100644 --- a/src/initctl.c +++ b/src/initctl.c @@ -56,6 +56,9 @@ struct cmd { int (*cb_multiarg)(int argc, char **argv); }; +char *finit_conf = NULL; +char *finit_rcsd = NULL; + int icreate = 0; int iforce = 0; int ionce = 0; @@ -1187,9 +1190,9 @@ static int transform(char *nm) static int has_conf(char *path, size_t len, char *name) { - paste(path, len, FINIT_RCSD, name); + paste(path, len, finit_rcsd, name); if (!fisdir(path)) { - strlcpy(path, FINIT_RCSD, len); + strlcpy(path, finit_rcsd, len); return 0; } @@ -1198,7 +1201,7 @@ static int has_conf(char *path, size_t len, char *name) static int usage(int rc) { - int has_rcsd = fisdir(FINIT_RCSD); + int has_rcsd = fisdir(finit_rcsd); int has_ena; char avail[256]; char ena[256]; @@ -1230,16 +1233,16 @@ static int usage(int rc) if (has_rcsd) fprintf(stderr, - " ls | list List all .conf in " FINIT_RCSD "\n" + " ls | list List all .conf in %s\n" " create Create .conf in %s\n" " delete Delete .conf in %s\n" " show Show .conf in %s\n" " edit Edit .conf in %s\n" " touch Change .conf in %s\n", - avail, avail, avail, avail, avail); + finit_rcsd, avail, avail, avail, avail, avail); else fprintf(stderr, - " show Show %s\n", FINIT_CONF); + " show Show %s\n", finit_conf); if (has_ena) fprintf(stderr, @@ -1249,10 +1252,10 @@ static int usage(int rc) " disable Disable .conf in %s\n", ena); if (has_rcsd) fprintf(stderr, - " reload Reload *.conf in " FINIT_RCSD " (activate changes)\n"); + " reload Reload *.conf in %s (activate changes)\n", finit_rcsd); else fprintf(stderr, - " reload Reload " FINIT_CONF " (activate changes)\n"); + " reload Reload %s (activate changes)\n", finit_conf); fprintf(stderr, "\n" @@ -1356,6 +1359,69 @@ static int cmd_parse(int argc, char *argv[], struct cmd *command) return command[0].cb(NULL); /* default cmd */ } +static char *fgetval(char *line, const char *key, char *sep) +{ + char *ptr, *str, *copy; + size_t len; + + str = copy = strdup(line); + if (!copy) + return NULL; + + ptr = strsep(&str, sep); + if (ptr && strcmp(ptr, key)) { + fail: + free(copy); + return NULL; + } + + ptr = strsep(&str, sep); + if (!ptr) + goto fail; + + len = strlen(ptr) + 1; + memmove(copy, ptr, len); + + return realloc(copy, len); +} + +static void cleanup(void) +{ + if (finit_conf) + free(finit_conf); + if (finit_rcsd) + free(finit_rcsd); +} + +static void sourcerc(void) +{ + char *line; + FILE *fp; + + fp = fopen(_PATH_VARRUN "finit/.initrc", "r"); + if (!fp) + goto fallback; + + while ((line = fparseln(fp, NULL, NULL, NULL, 0))) { + char *val; + + if ((val = fgetval(line, "FINIT_CONF", "="))) + finit_conf = val; + if ((val = fgetval(line, "FINIT_RCSD", "="))) + finit_rcsd = val; + + free(line); + } + +fallback: + if (!finit_conf) + finit_conf = strdup(FINIT_CONF); + if (!finit_rcsd) + finit_rcsd = strdup(FINIT_RCSD); + + atexit(cleanup); +} + int main(int argc, char *argv[]) { struct option long_options[] = { @@ -1431,6 +1497,7 @@ int main(int argc, char *argv[]) return reboot_main(argc, argv); /* Enable functionality depending on system capabilities */ + sourcerc(); cgrp = cgroup_avail(); utmp = has_utmp(); diff --git a/src/initctl.h b/src/initctl.h index 3d7a1ee9..a7e28298 100644 --- a/src/initctl.h +++ b/src/initctl.h @@ -29,6 +29,9 @@ #include "finit.h" #include "util.h" +extern char *finit_conf; +extern char *finit_rcsd; + extern int icreate; /* initctl -c */ extern int iforce; /* initctl -f */ extern int ionce; /* initctl -1 */ diff --git a/src/private.h b/src/private.h index c42903dc..8cc7750d 100644 --- a/src/private.h +++ b/src/private.h @@ -32,6 +32,9 @@ #define MATCH_CMD(l, c, x) \ (!strncasecmp(l, c, strlen(c)) && (x = (l) + strlen(c))) +extern char *finit_conf; +extern char *finit_rcsd; + extern int service_interval; int api_init (uev_ctx_t *ctx); diff --git a/src/serv.c b/src/serv.c index 74112ed0..1cb88f38 100644 --- a/src/serv.c +++ b/src/serv.c @@ -153,7 +153,7 @@ int serv_list(char *arg) char path[256]; if (arg && arg[0]) { - paste(path, sizeof(path), FINIT_RCSD, arg); + paste(path, sizeof(path), finit_rcsd, arg); if (fisdir(path)) { do_list(path); return 0; @@ -161,19 +161,19 @@ int serv_list(char *arg) /* fall back to list all */ } - paste(path, sizeof(path), FINIT_RCSD, "available"); + paste(path, sizeof(path), finit_rcsd, "available"); if (fisdir(path)) do_list(path); - paste(path, sizeof(path), FINIT_RCSD, "enabled"); + paste(path, sizeof(path), finit_rcsd, "enabled"); if (fisdir(path)) do_list(path); - if (fisdir(FINIT_RCSD)) - do_list(FINIT_RCSD); + if (fisdir(finit_rcsd)) + do_list(finit_rcsd); - if (fexist(FINIT_CONF)) - do_list(FINIT_CONF); + if (fexist(finit_conf)) + do_list(finit_conf); return 0; } @@ -197,7 +197,7 @@ static char *conf(char *path, size_t len, char *name, int creat) char corr[40]; if (!name || !name[0] || !strcmp(name, "finit") || !strcmp(name, "finit.conf")) { - strlcpy(path, FINIT_CONF, len); + strlcpy(path, finit_conf, len); return path; } @@ -206,21 +206,21 @@ static char *conf(char *path, size_t len, char *name, int creat) name = corr; } - if (!fisdir(FINIT_RCSD)) + if (!fisdir(finit_rcsd)) return NULL; - paste(path, len, FINIT_RCSD, "available/"); + paste(path, len, finit_rcsd, "available/"); if (!fisdir(path)) { if (creat && mkdir(path, 0755) && errno != EEXIST) return NULL; else - paste(path, len, FINIT_RCSD, name); + paste(path, len, finit_rcsd, name); } else strlcat(path, name, len); /* fall back to static service unless edit/create */ if (!creat && !fexist(path)) - paste(path, len, FINIT_RCSD, name); + paste(path, len, finit_rcsd, name); return path; } @@ -241,11 +241,11 @@ int serv_enable(char *arg) arg = corr; } - if (chdir(FINIT_RCSD)) - ERR(72, "failed cd %s", FINIT_RCSD); + if (chdir(finit_rcsd)) + ERR(72, "failed cd %s", finit_rcsd); if (icreate && mkdir("enabled", 0755) && EEXIST != errno) - ERR(73, "failed creating %s/enabled directory", FINIT_RCSD); + ERR(73, "failed creating %s/enabled directory", finit_rcsd); ena = !chdir("enabled"); /* System *may* have enabled/ dir. */ snprintf(path, sizeof(path), "%savailable/%s", ena ? "../" : "", arg); @@ -273,10 +273,10 @@ int do_disable(char *arg, int check) arg = corr; } - if (chdir(FINIT_RCSD)) - ERR(72, "failed cd %s", FINIT_RCSD); + if (chdir(finit_rcsd)) + ERR(72, "failed cd %s", finit_rcsd); if (chdir("enabled")) /* System *may* have enabled/ dir. */ - dbg("Failed changing to %s/enabled/: %s", FINIT_RCSD, strerror(errno)); + dbg("Failed changing to %s/enabled/: %s", finit_rcsd, strerror(errno)); if (check && stat(arg, &st)) ERRX(6, "%s not (an) enabled (service).", arg); @@ -309,7 +309,7 @@ int serv_touch(char *arg) if (is_builtin(arg)) ERRX(4, "%s is a built-in service.", arg); - strlcpy(path, FINIT_CONF, sizeof(path)); + strlcpy(path, finit_conf, sizeof(path)); fn = path; } @@ -391,7 +391,7 @@ static int do_edit(char *arg, int creat) int serv_edit(char *arg) { if (!arg || !arg[0]) { - if (!yorn("Do you want to edit %s (y/N)? ", FINIT_CONF)) + if (!yorn("Do you want to edit %s (y/N)? ", finit_conf)) return 0; arg = ""; } @@ -449,7 +449,7 @@ int serv_delete(char *arg) if (!fn) { if (is_builtin(arg)) ERRX(4, "%s is a built-in service.", arg); - ERRX(72, FINIT_RCSD " missing on system."); + ERRX(72, "%s missing on system.", finit_rcsd); } if (!fexist(fn)) diff --git a/src/sm.c b/src/sm.c index 6322db38..1e6cabe6 100644 --- a/src/sm.c +++ b/src/sm.c @@ -121,7 +121,7 @@ restart: switch (sm->state) { case SM_BOOTSTRAP_STATE: - dbg("Bootstrapping all services in runlevel S from %s", FINIT_CONF); + dbg("Bootstrapping all services in runlevel S from %s", finit_conf); service_step_all(SVC_TYPE_RUNTASK | SVC_TYPE_SERVICE); sm->state = SM_RUNNING_STATE; break;