Compare commits

...
Author SHA1 Message Date
Joachim Wiberg 96f944b816 Atomic print and re-print desc on status, flush before reboot
Refactor print() to emit description + final status in a single call to
cprintf(), preventing kernel messages from splitting the two parts.

For two-phase print(-1,...) + print_result() sequences used by, e.g.,
run_interactive, save the last description and re-print it before the
[ OK ] / [FAIL] output so the status is never left stranded on a blank
line when command output or kernel messages have scrolled away the
original description.

Finally, add print_exit() which drains the console output buffer with
tcdrain(2) and resets ANSI SGR attributes + cursor visibility before the
kernel takes back the console on reboot/halt, preventing escape code
leakage into bootloader or early-kernel output.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2026-04-07 11:36:19 +02:00
Joachim Wiberg 50b9e46e19 Merge pull request #484 from finit-project/rdep-stop-cond
Clear condition before stopping rdeps on reload
2026-03-19 06:53:40 +01:00
Joachim Wiberg c01faef99b service: clear condition before stopping rdeps on reload
When a service without SIGHUP reload support (noreload) is touched and
'initctl reload' is called, service_update_rdeps() correctly identifies
its reverse dependencies but only marks them dirty.  It does not clear
the service's condition, so when service_step_all() runs:

 - rdeps supporting SIGHUP hit the sm_in_reload() guard and break early,
   left running while their dependency is being killed.
 - rdeps without SIGHUP support may receive SIGTERM too late, after the
   dependency has already died and broken their connection, causing them
   to exit from RUNNING state and have their restart counter incremented.

Fix by calling cond_clear() on the service's condition immediately in
service_update_rdeps(), before service_step_all() runs.  cond_clear()
calls cond_update() which calls service_step() inline on all affected
services, which see COND_OFF and transition to STOPPING_STATE — all
before SIGTERM is ever sent to the dependency itself.

This mirrors the pattern already used in api.c:do_reload() for direct
'initctl reload <svc>' calls.

Fixes: avahi/mdns stop causing mdns-alias restart counter increment

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2026-03-19 06:37:50 +01:00
Joachim Wiberg 4867b7268b Merge pull request #482 from vazub/plugfix
Use explicit plugin names to prevent subtle macro processing bugs
2026-03-16 07:31:34 +01:00
bazub 662293e194 Use explicit plugin names to prevent subtle macro processing bugs 2026-03-06 20:36:23 +00:00
Joachim Wiberg 4ce810cc1f Merge pull request #481 from vazub/pathfix
Remove redundant global path var and fix memory corruption
2026-03-04 15:05:36 +01:00
bazub 184c079c08 Remove redundant global path var and fix memory corruption 2026-03-03 20:31:33 +00:00
Joachim Wiberg 17c6791c70 Update ChangeLog and bump version for v4.16 release
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2026-02-27 10:35:32 +01:00
Joachim Wiberg 42f24f5af5 Silence shutdown logging by default
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2026-02-27 10:34:38 +01:00
24 changed files with 112 additions and 46 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
AC_INIT([Finit], [4.16-rc1], [https://github.com/finit-project/finit/issues],
AC_INIT([Finit], [4.16], [https://github.com/finit-project/finit/issues],
[finit], [https://http://finit-project.github.io/])
AC_CONFIG_AUX_DIR(aux)
AM_INIT_AUTOMAKE([1.11 foreign subdir-objects])
+1 -1
View File
@@ -3,7 +3,7 @@ Change Log
All relevant changes are documented in this file.
[4.16][] - 2026-02-28
[4.16][] - 2026-02-27
---------------------
### Changes
+3 -3
View File
@@ -38,7 +38,7 @@
static void save(void *arg)
{
if (rescue) {
dbg("Skipping %s plugin in rescue mode.", __FILE__);
dbg("Skipping %s plugin in rescue mode.", "alsa-utils");
return;
}
@@ -51,7 +51,7 @@ static void save(void *arg)
static void restore(void *arg)
{
if (rescue) {
dbg("Skipping %s plugin in rescue mode.", __FILE__);
dbg("Skipping %s plugin in rescue mode.", "alsa-utils");
return;
}
@@ -62,7 +62,7 @@ static void restore(void *arg)
}
static plugin_t plugin = {
.name = __FILE__,
.name = "alsa-utils",
.hook[HOOK_BASEFS_UP] = { .cb = restore },
.hook[HOOK_SHUTDOWN] = { .cb = save }
};
+1 -1
View File
@@ -172,7 +172,7 @@ static void setup(void *arg)
}
static plugin_t plugin = {
.name = __FILE__,
.name = "bootmisc",
.hook[HOOK_MOUNT_POST] = { .cb = clean },
.hook[HOOK_BASEFS_UP] = { .cb = setup },
.depends = { "pidfile" },
+2 -2
View File
@@ -106,7 +106,7 @@ static void setup(void *arg)
char *cmd;
if (rescue) {
dbg("Skipping %s plugin in rescue mode.", __FILE__);
dbg("Skipping %s plugin in rescue mode.", "dbus");
return;
}
@@ -164,7 +164,7 @@ static void setup(void *arg)
}
static plugin_t plugin = {
.name = __FILE__,
.name = "dbus",
.hook[HOOK_SVC_PLUGIN] = { .cb = setup },
};
+1 -1
View File
@@ -79,7 +79,7 @@ static void hscript_shutdown(void *arg)
}
static plugin_t plugin = {
.name = __FILE__,
.name = "hook-scripts",
.hook[HOOK_BANNER] = { .cb = hscript_banner },
.hook[HOOK_ROOTFS_UP] = { .cb = hscript_rootfs_up },
.hook[HOOK_MOUNT_ERROR] = { .cb = hscript_mount_error },
+1 -1
View File
@@ -228,7 +228,7 @@ static void coldplug(void *arg)
}
static plugin_t plugin = {
.name = __FILE__,
.name = "modprobe",
.hook[HOOK_BASEFS_UP] = { .cb = coldplug },
.depends = { "bootmisc", }
};
+1 -1
View File
@@ -213,7 +213,7 @@ static void load(void *arg)
}
static plugin_t plugin = {
.name = __FILE__,
.name = "modules-load",
.hook[HOOK_SVC_PLUGIN] = { .cb = load },
};
+1 -1
View File
@@ -426,7 +426,7 @@ static void nl_enumerate(void *arg)
}
static plugin_t plugin = {
.name = __FILE__,
.name = "netlink",
.hook[HOOK_SVC_RECONF] = { .cb = nl_reconf },
.hook[HOOK_SVC_PLUGIN] = { .cb = nl_enumerate },
.io = {
+1 -1
View File
@@ -335,7 +335,7 @@ static void pidfile_init(void *arg)
* SIGSTP:ed (in state PAUSED) waiting for <net/iface/lo>.
*/
static plugin_t plugin = {
.name = __FILE__,
.name = "pidfile",
.hook[HOOK_BASEFS_UP] = { .cb = pidfile_init },
.hook[HOOK_SVC_RECONF] = { .cb = pidfile_reconf },
.depends = { "netlink" }, /* bootmisc depends on us */
+2 -2
View File
@@ -43,7 +43,7 @@ static void setup(void *arg)
glob_t gl;
if (rescue) {
dbg("Skipping %s plugin in rescue mode.", __FILE__);
dbg("Skipping %s plugin in rescue mode.", "procps");
return;
}
@@ -69,7 +69,7 @@ static void setup(void *arg)
}
static plugin_t plugin = {
.name = __FILE__,
.name = "procps",
.hook[HOOK_BASEFS_UP] = {
.cb = setup
},
+1 -1
View File
@@ -51,7 +51,7 @@ static void setup(void *arg)
}
static plugin_t plugin = {
.name = __FILE__,
.name = "resolvconf",
.hook[HOOK_BASEFS_UP] = {
.cb = setup
},
+3 -3
View File
@@ -236,7 +236,7 @@ static void rtc_save(void *arg)
int fd, rc = 0;
if (rescue) {
dbg("Skipping %s plugin in rescue mode.", __FILE__);
dbg("Skipping %s plugin in rescue mode.", "rtc");
return;
}
@@ -266,7 +266,7 @@ static void rtc_restore(void *arg)
int fd, rc = 0;
if (rescue) {
dbg("Skipping %s plugin in rescue mode.", __FILE__);
dbg("Skipping %s plugin in rescue mode.", "rtc");
return;
}
@@ -321,7 +321,7 @@ static void update(uev_t *w, void *arg, int events)
static plugin_t plugin = {
.name = __FILE__,
.name = "rtc",
.hook[HOOK_BASEFS_UP] = {
.cb = rtc_restore
},
+1 -1
View File
@@ -180,7 +180,7 @@ static void sys_init(void *arg)
}
static plugin_t plugin = {
.name = __FILE__,
.name = "sys",
.hook[HOOK_BASEFS_UP] = { .cb = sys_init },
.depends = { "bootmisc", },
};
+1
View File
@@ -43,6 +43,7 @@
static void tty_watcher(void *arg, int fd, int events);
static plugin_t plugin = {
.name = "tty",
.io = {
.cb = tty_watcher,
.flags = PLUGIN_IO_READ,
+3 -3
View File
@@ -87,7 +87,7 @@ static void setup(void *arg)
int fd, err;
if (rescue) {
dbg("Skipping %s plugin in rescue mode.", __FILE__);
dbg("Skipping %s plugin in rescue mode.", "urandom");
return;
}
@@ -188,7 +188,7 @@ static void save(void *arg)
mode_t prev;
if (rescue) {
dbg("Skipping %s plugin in rescue mode.", __FILE__);
dbg("Skipping %s plugin in rescue mode.", "urandom");
return;
}
@@ -202,7 +202,7 @@ static void save(void *arg)
}
static plugin_t plugin = {
.name = __FILE__,
.name = "urandom",
.hook[HOOK_BASEFS_UP] = { .cb = setup },
.hook[HOOK_SHUTDOWN] = { .cb = save },
.depends = { "bootmisc", }
+1 -1
View File
@@ -104,7 +104,7 @@ static void usr_init(void *arg)
}
static plugin_t plugin = {
.name = __FILE__,
.name = "usr",
.hook[HOOK_BASEFS_UP] = { .cb = usr_init },
.depends = { "bootmisc", },
};
+2 -2
View File
@@ -39,7 +39,7 @@
static void setup(void *arg)
{
if (rescue) {
dbg("Skipping %s plugin in rescue mode.", __FILE__);
dbg("Skipping %s plugin in rescue mode.", "x11-common");
return;
}
@@ -48,7 +48,7 @@ static void setup(void *arg)
}
static plugin_t plugin = {
.name = __FILE__,
.name = "x11-common",
.hook[HOOK_SVC_PLUGIN] = { .cb = setup },
};
+2 -11
View File
@@ -121,9 +121,6 @@ static uev_t etcw;
static TAILQ_HEAD(, conf_change) conf_change_list = TAILQ_HEAD_INITIALIZER(conf_change_list);
static char *path;
static char *shell;
static int parse_conf(char *file, int is_rcsd);
static void drop_changes(void);
@@ -377,8 +374,6 @@ void conf_parse_cmdline(int argc, char *argv[])
fstab = strdup(ptr);
finit_conf = strdup(FINIT_CONF);
finit_rcsd = strdup(FINIT_RCSD);
path = getenv("PATH");
shell = getenv("SHELL");
for (int i = 1; i < argc; i++)
parse_arg(argv[i]);
@@ -404,13 +399,9 @@ void conf_reset_env(void)
free(node);
}
if (path)
setenv("PATH", path, 1);
else
if (!getenv("PATH"))
setenv("PATH", _PATH_STDPATH, 1);
if (shell)
setenv("SHELL", shell, 1);
else
if (!getenv("SHELL"))
setenv("SHELL", _PATH_BSHELL, 1);
setenv("LOGNAME", "root", 1);
setenv("USER", "root", 1);
+62 -6
View File
@@ -50,6 +50,7 @@
static pstyle_t progress_onoff = PROGRESS_DEFAULT;
static pstyle_t progress_style = PROGRESS_DEFAULT;
static char last_desc[512]; /* saved description for re-print after interruption */
#ifndef HOSTNAME_PATH
#define HOSTNAME_PATH "/etc/hostname"
@@ -336,6 +337,8 @@ void printv(const char *fmt, va_list ap)
len = print_timestamp(buf, sizeof(buf));
vsnprintf(&buf[len], sizeof(buf) - len, fmt, ap);
strlcpy(last_desc, &buf[len], sizeof(last_desc)); /* save for re-print */
if (progress_style == PROGRESS_CLASSIC)
cprintf("\r%s ", pad(buf, sizeof(buf), ".", sizeof(buf)));
else
@@ -344,24 +347,65 @@ void printv(const char *fmt, va_list ap)
void print(int rc, const char *fmt, ...)
{
char buf[ttcols];
size_t len;
if (progress_style == PROGRESS_SILENT)
return;
buf[0] = 0;
len = print_timestamp(buf, sizeof(buf));
if (fmt) {
va_list ap;
va_start(ap, fmt);
printv(fmt, ap);
vsnprintf(&buf[len], sizeof(buf) - len, fmt, ap);
va_end(ap);
strlcpy(last_desc, &buf[len], sizeof(last_desc));
} else if (rc >= 0 && last_desc[0]) {
/*
* No new description, but we have one saved from an earlier
* print(-1, ...) call. Re-print it so the final status is not
* left stranded if command output or a kernel message scrolled
* away the original description line.
*/
strlcpy(&buf[len], last_desc, sizeof(buf) - len);
} else {
buf[0] = 0;
}
if (rc < 0)
if (rc < 0) {
/* Pending state: show description with spinner, no final status yet */
if (!buf[0])
return;
delline();
if (progress_style == PROGRESS_CLASSIC)
cprintf("\r%s ", pad(buf, sizeof(buf), ".", sizeof(buf)));
else
cprintf("\r\e[K%s%s", status(3), buf);
return;
}
if (progress_style == PROGRESS_CLASSIC)
cprintf("%s\n", status(rc));
else
cprintf("\r%s\n", status(rc));
/*
* Final status. Emit description + status in a single cprintf() so
* both reach the console in one write(), preventing kernel messages or
* command output from splitting the description from its [ OK ]/[FAIL].
*/
last_desc[0] = 0;
if (buf[0]) {
delline();
if (progress_style == PROGRESS_CLASSIC)
cprintf("\r%s %s\n", pad(buf, sizeof(buf), ".", sizeof(buf)), status(rc));
else
cprintf("\r\e[K%s%s\r%s\n", status(3), buf, status(rc));
} else {
if (progress_style == PROGRESS_CLASSIC)
cprintf("%s\n", status(rc));
else
cprintf("\r%s\n", status(rc));
}
}
void print_desc(char *action, char *desc)
@@ -375,6 +419,18 @@ int print_result(int fail)
return fail;
}
/*
* Reset console state and drain the output buffer before kernel takeover.
* Called just before reboot()/halt() to prevent ANSI escape codes from
* leaking into bootloader or early-kernel output.
*/
void print_exit(void)
{
tcdrain(STDERR_FILENO);
dprint(STDERR_FILENO, "\e[0m\e[?25h", 10); /* reset SGR, show cursor */
tcdrain(STDERR_FILENO);
}
void set_hostname(char **hostname)
{
FILE *fp;
+1
View File
@@ -68,6 +68,7 @@ void printv (const char *fmt, va_list ap);
void print (int action, const char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
void print_desc (char *action, char *desc);
int print_result (int fail);
void print_exit (void);
void set_hostname (char **hostname);
void networking (int updown);
+15 -1
View File
@@ -2418,7 +2418,21 @@ void service_update_rdeps(void)
if (!svc_is_noreload(svc))
continue; /* Yup, no need to stop start rdeps */
svc_mark_affected(mkcond(svc, cond, sizeof(cond)));
/*
* Clear the condition immediately, before service_step_all()
* runs. cond_clear() calls cond_update() which calls
* service_step() on all affected services right now. Those
* services see COND_OFF and get service_stop() called,
* transitioning to STOPPING_STATE before we ever send SIGTERM
* to this service. Without this, the condition is only cleared
* after the service dies, by which time reverse-dependencies
* may have already crashed due to the lost connection.
* See also: api.c do_reload() which does the same for direct
* 'initctl reload <svc>' calls.
*/
mkcond(svc, cond, sizeof(cond));
cond_clear(cond);
svc_mark_affected(cond);
}
}
+3
View File
@@ -424,14 +424,17 @@ void do_shutdown(shutop_t op)
}
print(0, "Rebooting ...");
print_exit();
reboot(RB_AUTOBOOT);
} else if (op == SHUT_OFF) {
print(0, "Powering down ...");
print_exit();
reboot(RB_POWER_OFF);
}
/* Also fallback if any of the other two fails */
print(0, "Halting ...");
print_exit();
reboot(RB_HALT_SYSTEM);
}
+2 -2
View File
@@ -24,9 +24,9 @@
#ifndef FINIT_SM_H_
#define FINIT_SM_H_
/* Set to 1 to enable shutdown trace logging (independent of 'initctl debug') */
/* Set to 1 to trace shutdown (independent of 'initctl debug') */
#ifndef SHUTDOWN_DEBUG
#define SHUTDOWN_DEBUG 1
#define SHUTDOWN_DEBUG 0
#endif
void sm_init (void);