From e00fda5dc3175b7d39c1a4a00017f28dac17f20d Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Sat, 22 Apr 2023 14:48:35 +0200 Subject: [PATCH] Fix #352: separate runlevel S from runlevel 0 Due to an old design decision runlevel S was encoded internally as '0', meaning it was the same as halt/poweroff. If you want to run scripts at system shutdown this was less then ideal since it meant your scripts also ran at bootstrap. This change is quite invasive. It introduces INIT_LEVEL (10) as the value for runlevel S, meaning all code that parses and/or evaluates anything for runlevel 0/S was affected and had to be reworked. Signed-off-by: Joachim Wiberg --- README.md | 32 ++++++++++++++++---------------- src/api.c | 2 +- src/conf.c | 13 +++++++------ src/finit.c | 2 +- src/finit.h | 3 ++- src/initctl.c | 34 ++++++++++++++++++++++++---------- src/private.h | 2 ++ src/service.c | 8 ++++---- src/sig.c | 8 ++++---- src/sm.c | 4 ++-- src/svc.c | 2 +- src/utmp-api.c | 4 +++- 12 files changed, 67 insertions(+), 47 deletions(-) diff --git a/README.md b/README.md index 8755cf1d..6f04343a 100644 --- a/README.md +++ b/README.md @@ -244,7 +244,7 @@ networking here. The configured `runlevel NUM` from `/etc/finit.conf` is what Finit changes to after bootstrap, unless 'single' (or 'S') is given on the kernel cmdline, in which case runlevel 1 is started. -All services in runlevel S(0) are started first, followed by the desired +All services in runlevel S) are started first, followed by the desired run-time runlevel. Run tasks in runlevel S can be started in sequence by using `run [S] cmd`. Changing runlevels at runtime is done like any other init, e.g. init 4, but also using the more advanced @@ -585,24 +585,24 @@ scripting and documentation: ``` alpine:~# initctl -p -PID IDENT STATUS RUNLEVELS DESCRIPTION +PID IDENT STATUS RUNLEVELS DESCRIPTION ====================================================================== -1506 acpid running [--2345----] ACPI daemon -1509 crond running [--2345----] Cron daemon -1489 dropbear running [--2345----] Dropbear SSH daemon -1511 klogd running [S12345----] Kernel log daemon -1512 ntpd running [--2345----] NTP daemon -1473 syslogd running [S12345----] Syslog daemon +1506 acpid running [---2345----] ACPI daemon +1509 crond running [---2345----] Cron daemon +1489 dropbear running [---2345----] Dropbear SSH daemon +1511 klogd running [S-12345----] Kernel log daemon +1512 ntpd running [---2345----] NTP daemon +1473 syslogd running [S-12345----] Syslog daemon alpine:~# initctl -pv -PID IDENT STATUS RUNLEVELS COMMAND +PID IDENT STATUS RUNLEVELS COMMAND ====================================================================== -1506 acpid running [--2345----] acpid -f -1509 crond running [--2345----] crond -f -S $CRON_OPTS -1489 dropbear running [--2345----] dropbear -R -F $DROPBEAR_OPTS -1511 klogd running [S12345----] klogd -n $KLOGD_OPTS -1512 ntpd running [--2345----] ntpd -n $NTPD_OPTS -1473 syslogd running [S12345----] syslogd -n +1506 acpid running [---2345----] acpid -f +1509 crond running [---2345----] crond -f -S $CRON_OPTS +1489 dropbear running [---2345----] dropbear -R -F $DROPBEAR_OPTS +1511 klogd running [S-12345----] klogd -n $KLOGD_OPTS +1512 ntpd running [---2345----] ntpd -n $NTPD_OPTS +1473 syslogd running [S-12345----] syslogd -n ``` The environment variables to each of the services above are read from, @@ -627,7 +627,7 @@ Condition(s): User : root Group : root Uptime : 2 hour 46 min 56 sec - Runlevels : [--2345----] + Runlevels : [---2345----] Memory : 1.2M CGroup : /system/dropbear cpu 0 [100, max] mem [--.--, max] |- 1485 dropbear -R -F diff --git a/src/api.c b/src/api.c index c8628782..b074733c 100644 --- a/src/api.c +++ b/src/api.c @@ -375,7 +375,7 @@ static void api_cb(uev_t *w, void *arg, int events) case INIT_CMD_HALT: case INIT_CMD_POWEROFF: case INIT_CMD_SUSPEND: - if (runlevel == 0 || runlevel == 6) { + if (IS_RESERVED_RUNLEVEL(runlevel)) { warnx("Unsupported command in runlevel S and 6."); goto leave; } diff --git a/src/conf.c b/src/conf.c index 88488753..cecad9ce 100644 --- a/src/conf.c +++ b/src/conf.c @@ -48,7 +48,7 @@ #include "helpers.h" #include "util.h" -#define BOOTSTRAP (runlevel == 0) +#define BOOTSTRAP (runlevel == INIT_LEVEL) int logfile_size_max = 200000; /* 200 kB */ int logfile_count_max = 5; @@ -475,7 +475,7 @@ static void kmod_load(char *mod) char module[64] = { 0 }; char cmd[CMD_SIZE]; - if (runlevel != 0) + if (runlevel != INIT_LEVEL) return; /* Strip args for progress below and kmod_exists() */ @@ -508,15 +508,16 @@ int conf_parse_runlevels(char *runlevels) break; if ('!' == lvl) { not = 1; - bitmask = 0x3FE; + bitmask = 0x7FE; continue; } if ('s' == lvl || 'S' == lvl) - lvl = '0'; + level = INIT_LEVEL; + else + level = lvl - '0'; - level = lvl - '0'; - if (level > 9 || level < 0) + if (level > INIT_LEVEL || level < 0) continue; if (not) diff --git a/src/finit.c b/src/finit.c index d9d881ed..8b33fb5c 100644 --- a/src/finit.c +++ b/src/finit.c @@ -60,7 +60,7 @@ #include "utmp-api.h" #include "schedule.h" -int runlevel = 0; /* Bootstrap 'S' */ +int runlevel = INIT_LEVEL; /* Bootstrap 'S' */ int cfglevel = RUNLEVEL; /* Fallback if no configured runlevel */ int cmdlevel = 0; /* runlevel override from cmdline */ int prevlevel = -1; diff --git a/src/finit.h b/src/finit.h index ac6ebedf..4c39027f 100644 --- a/src/finit.h +++ b/src/finit.h @@ -63,10 +63,11 @@ #define LINE_SIZE 1024 #define BUF_SIZE 4096 -/* We extend the INIT_CMD_ range for the new initctl tool. */ #define INIT_SOCKET _PATH_VARRUN "finit/socket" #define INIT_MAGIC 0x03091969 +#define INIT_LEVEL 10 +/* We extend the INIT_CMD_ range for the new initctl tool. */ #define INIT_CMD_START 0 #define INIT_CMD_RUNLVL 1 #define INIT_CMD_POWERFAIL 2 diff --git a/src/initctl.c b/src/initctl.c index 9c5ef8e6..916ff878 100644 --- a/src/initctl.c +++ b/src/initctl.c @@ -761,17 +761,18 @@ static int plugins_list(char *arg) char *runlevel_string(int currlevel, int levels) { static char lvl[21]; - int i, pos = 1; + int i = INIT_LEVEL; + int pos = 1; memset(lvl, 0, sizeof(lvl)); lvl[0] = '['; - for (i = 0; i < 10; i++) { + do { if (ISSET(levels, i)) { if (!plain && currlevel == i) pos = strlcat(lvl, "\e[1m", sizeof(lvl)); - if (i == 0) + if (i == INIT_LEVEL) lvl[pos++] = 'S'; else lvl[pos++] = '0' + i; @@ -781,7 +782,14 @@ char *runlevel_string(int currlevel, int levels) } else { lvl[pos++] = '-'; } - } + + /* XXX: ugly hack to get order right: S0123456789 */ + if (i == INIT_LEVEL) + i = 0; + else + i++; + } while (i < INIT_LEVEL); + lvl[pos++] = ']'; lvl[pos] = 0; @@ -792,18 +800,24 @@ char *runlevel_string(int currlevel, int levels) char *runlevel_arr(int levels) { static char lvl[42]; + int i = INIT_LEVEL; int p = 2, s = 0; strlcpy(lvl, "[ ", sizeof(lvl)); - for (int i = 0; i < 10; i++) { + do { if (ISSET(levels, i)) { - if (i == 0) + if (i == INIT_LEVEL) p += snprintf(&lvl[p], sizeof(lvl) - p, "\"S\""); else p += snprintf(&lvl[p], sizeof(lvl) - p, "%s%c", s ? ", " : "", '0' + i); s++; } - } + /* XXX: ugly hack to get order right: S0123456789 */ + if (i == INIT_LEVEL) + i = 0; + else + i++; + } while (i < INIT_LEVEL); strlcat(lvl, " ]", sizeof(lvl)); return lvl; @@ -1162,7 +1176,7 @@ static int show_status(char *arg) if (heading) { char title[80]; - snprintf(title, sizeof(title), "%-*s %-*s %-8s %-12s ", + snprintf(title, sizeof(title), "%-*s %-*s %-8s %-13s ", pw, "PID", iw, "IDENT", "STATUS", "RUNLEVELS"); if (!verbose) strlcat(title, "DESCRIPTION", sizeof(title)); @@ -1184,9 +1198,9 @@ static int show_status(char *arg) lvls = runlevel_string(runlevel, svc->runlevels); if (strchr(lvls, '\e')) - printf("%-20.20s ", lvls); + printf("%-21.21s ", lvls); else - printf("%-12.12s ", lvls); + printf("%-13.13s ", lvls); if (!verbose) puts(svc->desc); diff --git a/src/private.h b/src/private.h index cfba4260..2ba36583 100644 --- a/src/private.h +++ b/src/private.h @@ -32,6 +32,8 @@ #define MATCH_CMD(l, c, x) \ (!strncasecmp(l, c, strlen(c)) && (x = (l) + strlen(c))) +#define IS_RESERVED_RUNLEVEL(l) (l == 0 || l == 6 || l == INIT_LEVEL) + extern char *finit_conf; extern char *finit_rcsd; extern svc_t *wdog; diff --git a/src/service.c b/src/service.c index 2869d40b..eac2e118 100644 --- a/src/service.c +++ b/src/service.c @@ -1051,7 +1051,7 @@ static int service_restart(svc_t *svc) } /* Skip progress if desc disabled or bootstrap task */ - if (!svc->desc[0] || svc_in_runlevel(svc, 0)) + if (!svc->desc[0] || svc_in_runlevel(svc, INIT_LEVEL)) do_progress = 0; if (do_progress) @@ -1106,13 +1106,13 @@ void service_reload_dynamic(void) */ void service_runlevel(int newlevel) { - if (!rescue && runlevel <= 1 && newlevel > 1) + if (!rescue && (runlevel == 1 || runlevel == INIT_LEVEL) && !IS_RESERVED_RUNLEVEL(newlevel)) networking(1); sm_set_runlevel(&sm, newlevel); sm_step(&sm); - if (!rescue && (runlevel <= 1 || runlevel == 6)) + if (!rescue && IS_RESERVED_RUNLEVEL(runlevel)) networking(0); } @@ -1538,7 +1538,7 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file) id = ""; levels = conf_parse_runlevels(runlevels); - if (runlevel > 0 && !ISOTHER(levels, 0)) { + if (runlevel != INIT_LEVEL && !ISOTHER(levels, 0)) { dbg("Skipping %s%s%s, bootstrap is completed.", name, id[0] ? ":" : "", id[0] ? id : ""); return 0; diff --git a/src/sig.c b/src/sig.c index 142f06ea..60aaf723 100644 --- a/src/sig.c +++ b/src/sig.c @@ -429,7 +429,7 @@ static void sighup_cb(uev_t *w, void *arg, int events) return; } - if (runlevel == 0 || runlevel == 6) { + if (IS_RESERVED_RUNLEVEL(runlevel)) { warnx("SIGHUP ignored in runlevel S and 6."); return; } @@ -485,7 +485,7 @@ static void sigusr1_cb(uev_t *w, void *arg, int events) return; } - if (runlevel == 0 || runlevel == 6) { + if (IS_RESERVED_RUNLEVEL(runlevel)) { warnx("SIGUSR1 ignored in runlevel S and 6."); return; } @@ -506,7 +506,7 @@ static void sigusr2_cb(uev_t *w, void *arg, int events) return; } - if (runlevel == 0 || runlevel == 6) { + if (IS_RESERVED_RUNLEVEL(runlevel)) { warnx("SIGUSR2 ignored in runlevel S and 6."); return; } @@ -526,7 +526,7 @@ static void sigterm_cb(uev_t *w, void *arg, int events) return; } - if (runlevel == 0 || runlevel == 6) { + if (IS_RESERVED_RUNLEVEL(runlevel)) { warnx("SIGTERM ignored in runlevel S and 6."); return; } diff --git a/src/sm.c b/src/sm.c index ec0f444e..04a604e5 100644 --- a/src/sm.c +++ b/src/sm.c @@ -86,10 +86,10 @@ static char *sm_status(sm_state_t state) */ static void nologin(void) { - if (runlevel == 1 || runlevel == 0 || runlevel == 6) + if (runlevel == 1 || IS_RESERVED_RUNLEVEL(runlevel)) touch(FINIT_NOLOGIN_PATH); - if (prevlevel == 1 || prevlevel == 0 || prevlevel == 6) + if (prevlevel == 1 || IS_RESERVED_RUNLEVEL(prevlevel)) erase(FINIT_NOLOGIN_PATH); } diff --git a/src/svc.c b/src/svc.c index 174d9e3f..2c5f176a 100644 --- a/src/svc.c +++ b/src/svc.c @@ -594,7 +594,7 @@ void svc_clean_dynamic(void (*cb)(svc_t *)) */ int svc_clean_bootstrap(svc_t *svc) { - if (!ISOTHER(svc->runlevels, 0)) { + if (!ISOTHER(svc->runlevels, INIT_LEVEL)) { svc->pid = 0; svc_del(svc); return 1; diff --git a/src/utmp-api.c b/src/utmp-api.c index 77e2aa76..018551a3 100644 --- a/src/utmp-api.c +++ b/src/utmp-api.c @@ -32,6 +32,7 @@ # include #endif +#include "finit.h" #include "helpers.h" #include "util.h" #include "utmp-api.h" @@ -174,7 +175,8 @@ int utmp_set_dead(int pid) static int encode(int lvl) { - if (!lvl) return 0; + if (lvl == INIT_LEVEL) + return 0; return lvl + '0'; }