Allow skipping bootstrap wait with Ctrl-C

When setting up a new system with Finit it is very common to make small
logical mistakes that cause "hangs" at boot.  This is when Finit waits
for 180 sec. for run/tasks to complete before moving to the configured
runlevel.  This patch adds console input monitoring during bootstrap
wait that allows users to press Ctrl-C to skip waiting and proceed to
the configured runlevel.

When Ctrl-C is detected, all incomplete run/task/services are logged
to syslog for later troubleshooting after login.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
This commit is contained in:
Joachim Wiberg committed 2025-12-07 09:38:12 +01:00
1 parent 21f40fb95e
commit 4e18affa9b
3 files changed
+124 -2

No files matched your search

+36
View File
@@ -3092,6 +3092,42 @@ int service_completed(svc_t **svcp)
return 1;
}
/*
* service_log_incomplete - Log all incomplete run/task in current runlevel
*
* Iterates through all run/task services and logs those that have not
* yet completed. Useful for diagnosing bootstrap delays or reporting
* what was skipped when user interrupts bootstrap wait.
*/
void service_log_incomplete(void)
{
svc_t *svc, *iter = NULL;
int count = 0;
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
if (!svc_is_runtask(svc))
continue;
if (!svc_enabled(svc))
continue;
if (svc_conflicts(svc))
continue;
if (strstr(svc->cond, plugin_hook_str(HOOK_SVC_UP)) ||
strstr(svc->cond, plugin_hook_str(HOOK_SYSTEM_UP)))
continue;
if (!svc->once) {
logit(LOG_WARNING, "Incomplete: %s", svc_ident(svc, NULL, 0));
count++;
}
}
if (count == 0)
logit(LOG_WARNING, "No incomplete run/task services found");
}
/*
* Called in SM_RELOAD_WAIT_STATE to update unmodified non-native
* services' READY condition to the current generation. Similar
+1
View File
@@ -48,6 +48,7 @@ void service_step_all (int types);
void service_worker (void *unused);
int service_completed (svc_t **svc);
void service_log_incomplete (void);
void service_notify_reconf (void);
void service_init (uev_ctx_t *ctx);
+87 -2
View File
@@ -23,8 +23,10 @@
#include "config.h" /* Generated by configure script */
#include <fcntl.h>
#include <paths.h>
#include <sys/types.h>
#include <termios.h>
#include "finit.h"
#include "cond.h"
@@ -56,8 +58,80 @@ static struct {
int newlevel; /* Set to new runlevel on change, -1 otherwise */
int reload; /* Set on reload event, else 0 */
int in_reload; /* Set when waiting for all processes to be halted */
int skip_bootstrap; /* Set to skip bootstrap waiting */
} sm;
static uev_t console_watcher;
static int console_fd = -1;
/*
* Console input callback - handles Ctrl-C during bootstrap wait
*/
static void console_input_cb(uev_t *w, void *arg, int events)
{
ssize_t len;
char ch;
(void)arg;
if (UEV_ERROR == events) {
warnx("Error in console watcher");
return;
}
/* Read and check for Ctrl-C (ASCII 3) */
len = read(w->fd, &ch, 1);
if (len == 1 && ch == 3) { /* Ctrl-C */
sm.skip_bootstrap = 1;
print(2, "Bootstrap wait interrupted by Ctrl-C, resuming ...");
}
}
/*
* Stop and cleanup the console watcher
*/
static void console_watcher_stop(void)
{
if (console_fd >= 0) {
uev_io_stop(&console_watcher);
close(console_fd);
console_fd = -1;
}
}
/*
* Setup console watcher for Ctrl-C during bootstrap
*/
static void console_watcher_start(uev_ctx_t *ctx)
{
struct termios tio;
/* Open console in non-blocking mode */
console_fd = open(console(), O_RDONLY | O_NONBLOCK);
if (console_fd < 0) {
dbg("Failed opening console for input monitoring: %s", strerror(errno));
return;
}
/* Get current terminal settings */
if (tcgetattr(console_fd, &tio) == 0) {
/* Put terminal in raw mode to catch Ctrl-C */
tio.c_lflag &= ~(ICANON | ECHO | ISIG);
tio.c_cc[VMIN] = 0;
tio.c_cc[VTIME] = 0;
tcsetattr(console_fd, TCSANOW, &tio);
}
/* Register I/O watcher */
if (uev_io_init(ctx, &console_watcher, console_input_cb, NULL, console_fd, UEV_READ)) {
dbg("Failed setting up console watcher");
close(console_fd);
console_fd = -1;
} else {
dbg("Console watcher active, press Ctrl-C to skip bootstrap wait");
}
}
/*
* Wait for system bootstrap to complete, all SVC_TYPE_RUNTASK must be
@@ -76,13 +150,19 @@ static void sm_check_bootstrap(void *work)
service_step_all(SVC_TYPE_ANY);
bootstrap_done = service_completed(&svc);
if (timeout-- > 0 && !bootstrap_done) {
if (timeout-- > 0 && !bootstrap_done && !sm.skip_bootstrap) {
dbg("Not all bootstrap run/tasks have completed yet ... %d", timeout);
schedule_work(work);
return;
}
if (timeout > 0) {
/* Stop console watcher before proceeding */
console_watcher_stop();
if (sm.skip_bootstrap) {
logit(LOG_WARNING, "Ctrl-C received, skipped waiting for the following:");
service_log_incomplete();
} else if (timeout > 0) {
dbg("All run/task have completed, resuming bootstrap.");
} else {
dbg("Timeout, resuming bootstrap.");
@@ -184,6 +264,7 @@ void sm_init(void)
sm.newlevel = -1;
sm.reload = 0;
sm.in_reload = 0;
sm.skip_bootstrap = 0;
dbg("Starting bootstrap finalize timer ...");
schedule_work(&work);
@@ -206,6 +287,7 @@ restart:
service_step_all(SVC_TYPE_RUNTASK | SVC_TYPE_SERVICE | SVC_TYPE_SYSV);
sm.state = SM_BOOTSTRAP_WAIT_STATE;
console_watcher_start(ctx);
break;
/*
@@ -229,6 +311,9 @@ restart:
if (sm.newlevel == -1)
break;
/* Ensure console watcher is stopped before proceeding */
console_watcher_stop();
/* Hooks that should run at the very end */
dbg("Calling all system up hooks ...");
plugin_run_hooks(HOOK_SYSTEM_UP);