Skip to content

Commit 4e18aff

Browse files
committed
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 <[email protected]>
1 parent 21f40fb commit 4e18aff

File tree

3 files changed

+124
-2
lines changed

3 files changed

+124
-2
lines changed

src/service.c

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3092,6 +3092,42 @@ int service_completed(svc_t **svcp)
30923092
return 1;
30933093
}
30943094

3095+
/*
3096+
* service_log_incomplete - Log all incomplete run/task in current runlevel
3097+
*
3098+
* Iterates through all run/task services and logs those that have not
3099+
* yet completed. Useful for diagnosing bootstrap delays or reporting
3100+
* what was skipped when user interrupts bootstrap wait.
3101+
*/
3102+
void service_log_incomplete(void)
3103+
{
3104+
svc_t *svc, *iter = NULL;
3105+
int count = 0;
3106+
3107+
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
3108+
if (!svc_is_runtask(svc))
3109+
continue;
3110+
3111+
if (!svc_enabled(svc))
3112+
continue;
3113+
3114+
if (svc_conflicts(svc))
3115+
continue;
3116+
3117+
if (strstr(svc->cond, plugin_hook_str(HOOK_SVC_UP)) ||
3118+
strstr(svc->cond, plugin_hook_str(HOOK_SYSTEM_UP)))
3119+
continue;
3120+
3121+
if (!svc->once) {
3122+
logit(LOG_WARNING, "Incomplete: %s", svc_ident(svc, NULL, 0));
3123+
count++;
3124+
}
3125+
}
3126+
3127+
if (count == 0)
3128+
logit(LOG_WARNING, "No incomplete run/task services found");
3129+
}
3130+
30953131
/*
30963132
* Called in SM_RELOAD_WAIT_STATE to update unmodified non-native
30973133
* services' READY condition to the current generation. Similar

src/service.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ void service_step_all (int types);
4848
void service_worker (void *unused);
4949

5050
int service_completed (svc_t **svc);
51+
void service_log_incomplete (void);
5152
void service_notify_reconf (void);
5253

5354
void service_init (uev_ctx_t *ctx);

src/sm.c

Lines changed: 87 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,10 @@
2323

2424
#include "config.h" /* Generated by configure script */
2525

26+
#include <fcntl.h>
2627
#include <paths.h>
2728
#include <sys/types.h>
29+
#include <termios.h>
2830

2931
#include "finit.h"
3032
#include "cond.h"
@@ -56,8 +58,80 @@ static struct {
5658
int newlevel; /* Set to new runlevel on change, -1 otherwise */
5759
int reload; /* Set on reload event, else 0 */
5860
int in_reload; /* Set when waiting for all processes to be halted */
61+
int skip_bootstrap; /* Set to skip bootstrap waiting */
5962
} sm;
6063

64+
static uev_t console_watcher;
65+
static int console_fd = -1;
66+
67+
/*
68+
* Console input callback - handles Ctrl-C during bootstrap wait
69+
*/
70+
static void console_input_cb(uev_t *w, void *arg, int events)
71+
{
72+
ssize_t len;
73+
char ch;
74+
75+
(void)arg;
76+
77+
if (UEV_ERROR == events) {
78+
warnx("Error in console watcher");
79+
return;
80+
}
81+
82+
/* Read and check for Ctrl-C (ASCII 3) */
83+
len = read(w->fd, &ch, 1);
84+
if (len == 1 && ch == 3) { /* Ctrl-C */
85+
sm.skip_bootstrap = 1;
86+
print(2, "Bootstrap wait interrupted by Ctrl-C, resuming ...");
87+
}
88+
}
89+
90+
/*
91+
* Stop and cleanup the console watcher
92+
*/
93+
static void console_watcher_stop(void)
94+
{
95+
if (console_fd >= 0) {
96+
uev_io_stop(&console_watcher);
97+
close(console_fd);
98+
console_fd = -1;
99+
}
100+
}
101+
102+
/*
103+
* Setup console watcher for Ctrl-C during bootstrap
104+
*/
105+
static void console_watcher_start(uev_ctx_t *ctx)
106+
{
107+
struct termios tio;
108+
109+
/* Open console in non-blocking mode */
110+
console_fd = open(console(), O_RDONLY | O_NONBLOCK);
111+
if (console_fd < 0) {
112+
dbg("Failed opening console for input monitoring: %s", strerror(errno));
113+
return;
114+
}
115+
116+
/* Get current terminal settings */
117+
if (tcgetattr(console_fd, &tio) == 0) {
118+
/* Put terminal in raw mode to catch Ctrl-C */
119+
tio.c_lflag &= ~(ICANON | ECHO | ISIG);
120+
tio.c_cc[VMIN] = 0;
121+
tio.c_cc[VTIME] = 0;
122+
tcsetattr(console_fd, TCSANOW, &tio);
123+
}
124+
125+
/* Register I/O watcher */
126+
if (uev_io_init(ctx, &console_watcher, console_input_cb, NULL, console_fd, UEV_READ)) {
127+
dbg("Failed setting up console watcher");
128+
close(console_fd);
129+
console_fd = -1;
130+
} else {
131+
dbg("Console watcher active, press Ctrl-C to skip bootstrap wait");
132+
}
133+
}
134+
61135

62136
/*
63137
* Wait for system bootstrap to complete, all SVC_TYPE_RUNTASK must be
@@ -76,13 +150,19 @@ static void sm_check_bootstrap(void *work)
76150
service_step_all(SVC_TYPE_ANY);
77151

78152
bootstrap_done = service_completed(&svc);
79-
if (timeout-- > 0 && !bootstrap_done) {
153+
if (timeout-- > 0 && !bootstrap_done && !sm.skip_bootstrap) {
80154
dbg("Not all bootstrap run/tasks have completed yet ... %d", timeout);
81155
schedule_work(work);
82156
return;
83157
}
84158

85-
if (timeout > 0) {
159+
/* Stop console watcher before proceeding */
160+
console_watcher_stop();
161+
162+
if (sm.skip_bootstrap) {
163+
logit(LOG_WARNING, "Ctrl-C received, skipped waiting for the following:");
164+
service_log_incomplete();
165+
} else if (timeout > 0) {
86166
dbg("All run/task have completed, resuming bootstrap.");
87167
} else {
88168
dbg("Timeout, resuming bootstrap.");
@@ -184,6 +264,7 @@ void sm_init(void)
184264
sm.newlevel = -1;
185265
sm.reload = 0;
186266
sm.in_reload = 0;
267+
sm.skip_bootstrap = 0;
187268

188269
dbg("Starting bootstrap finalize timer ...");
189270
schedule_work(&work);
@@ -206,6 +287,7 @@ void sm_step(void)
206287
service_step_all(SVC_TYPE_RUNTASK | SVC_TYPE_SERVICE | SVC_TYPE_SYSV);
207288

208289
sm.state = SM_BOOTSTRAP_WAIT_STATE;
290+
console_watcher_start(ctx);
209291
break;
210292

211293
/*
@@ -229,6 +311,9 @@ void sm_step(void)
229311
if (sm.newlevel == -1)
230312
break;
231313

314+
/* Ensure console watcher is stopped before proceeding */
315+
console_watcher_stop();
316+
232317
/* Hooks that should run at the very end */
233318
dbg("Calling all system up hooks ...");
234319
plugin_run_hooks(HOOK_SYSTEM_UP);

0 commit comments

Comments
 (0)