Compare commits

..
22 Commits
Author SHA1 Message Date
Joachim Wiberg 197c5767ea Switch from PTY to pipe for service log redirection
The PTY approach caused isatty() to return true for services using
the log directive, triggering programs like fprintd (using glib) to
emit ANSI escape codes and other TTY-specific formatting in syslog.

Using a standard pipe ensures isatty() correctly returns false, so
programs produce plain text output suitable for logging.

For services that require line-buffered output, users can wrap the
command with `stdbuf -oL` as documented in doc/config/logging.md.

Fixes #455

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-12-06 15:41:30 +01:00
Joachim Wiberg 22072e477c Change default reboot to SOC reset from WDT reset
Fixes #460

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-12-06 15:01:31 +01:00
Joachim Wiberg bda2f94e8a 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>
2025-11-27 13:09:10 +01:00
Joachim Wiberg abb4561516 Allow more initctl commands in runlevels S/0/6
Relax the constraints introduced in a39ee0b, for issue #342, a bit on
when start/stop/restart/reload service can be called.  Also, allow
'initctl reload', but ignore it when the system is in runlevels S/0/6.

This makes it possible to start manual:yes type services at botostrap,
for example, which has been a common feature request.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-11-27 11:33:39 +01:00
Joachim Wiberg 309cc7f741 Ensure mount/unmount skips 'noauto' entries
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-11-27 11:30:27 +01:00
Joachim Wiberg c23952cd9e Merge pull request #453 from liuming50/fix-rtc-restore-issues
plugins:rtc.c: drop some invalid condition checks

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-10-14 20:33:25 +02:00
Ming Liu 13a8f56b46 plugins:rtc.c: drop some invalid condition checks
There are some invalid condition checks in rtc.c, when rtc_file is
missing, it should not consider that as a error and return, otherwise,
it can lead to the case that time_set wont be called at all.

With this fix, when both RTC and file restore fail, it will fall back
to call time_set(NULL) and restore time from rtc_timestamp, this
ensures the OS has a valid time at the very first boot.

Signed-off-by: Ming Liu <liu.ming50@gmail.com>
2025-10-14 15:38:56 +02:00
Joachim Wiberg 927acdbd7e Increase MAX_ID_LEN to support longer service identifiers
Allow service IDs up to 64 characters to support SHA-256 hashes,
UUIDs, and other long unique identifiers. This increases memory
usage by ~98 bytes per service instance, which is negligible for
typical deployments.

The IDENT column width in initctl output adapts dynamically based
on actual ID lengths in use, so short IDs remain unaffected.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-10-14 11:49:57 +02:00
Joachim Wiberg 6f6267f3b3 When collecting a script or unregistering a service, cancel timeouts
Both service and sysv stanzas support a cleanup:script (since v4.10), with an
optional timeout.  These timers must be stopped when collecting a PID for an
svc_t, or when unregistering an svc_t, otherwise the timer callback will be
continuously called, reporting "spurious problem", and cause 100% CPU usage.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-08-29 16:49:34 +02:00
Joachim Wiberg dec89d4054 Minor, drop __func__ from dbg() statements
The dbg() macro already includes __func__ as the first text string in
the format string.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-08-29 16:48:38 +02:00
Joachim Wiberg 193e114596 Enable libsystemd replacement lib for tests also in distcheck stage
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-08-29 16:02:42 +02:00
Joachim Wiberg 1be67c434e Update ChangeLog and bump version for v4.14 release
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-08-29 15:07:45 +02:00
Joachim Wiberg 4f8dab75b2 Update test helper and all workflows to enble libsystemd build
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-08-29 14:43:25 +02:00
Joachim Wiberg 37d063fd3b Build --without-libsystemd by default
Many packages fail to detect that this replacement libsystemd does not
support the logging API (yet), so let's disable it by default.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-08-29 13:12:43 +02:00
Joachim Wiberg 97be3da126 Minor doc styling fixes
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-08-29 12:54:06 +02:00
Joachim Wiberg e7f97f8264 Fix state machine ordering issue
This patch fixes an ordering issue where the configuration generation is
stepped before all old/disabled services had been stopped.  Finit should
ensure disabled services are stopped before entering the next generation.

If the genation is stepped first, all conditions are put in "flux" state,
so, e.g, a podman container running as a sysv service may lock-up until
it is killed by Finit.  Found in the Infix OS project.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-08-29 12:47:43 +02:00
Joachim Wiberg 6415aeebac Ensure sysv 'stop' command has same env. as 'start'
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-08-29 10:17:19 +02:00
Mattias Barthel e69200ea43 Add exit status/signal info when a process dies
Signed-off-by: Mattias Barthel <mattiasbarthel@gmail.com>
2025-08-29 10:12:58 +02:00
Joachim Wiberg 26d5018224 Allow kill delay up to five minutes for debug
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-08-29 10:02:36 +02:00
Joachim Wiberg 1b7699ca93 Expand norespawn grasp to service_retry()
The norespawn hack pause Finit while a developer debugs the system.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-08-29 10:00:25 +02:00
Joachim Wiberg 8eab099f09 Minor, encase script + args in '' for debug help
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-08-29 09:58:10 +02:00
Joachim Wiberg c7c5426586 Fix #449: alsa plugin ftbfs
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-07-22 15:46:46 +02:00
23 changed files with 329 additions and 79 deletions
+1 -1
View File
@@ -45,7 +45,7 @@ jobs:
run: |
./configure --prefix=/usr --exec-prefix= --sysconfdir=/etc --localstatedir=/var \
--enable-x11-common-plugin --enable-testserv-plugin --with-watchdog \
--with-keventd \
--with-keventd --with-libsystemd \
CFLAGS="-fsanitize=address -ggdb"
make -j3 clean
make -j3 V=1
+2 -1
View File
@@ -66,7 +66,8 @@ jobs:
- name: Configure
run: |
./autogen.sh
./configure --prefix= --enable-x11-common-plugin --with-watchdog --with-keventd
./configure --prefix= --enable-x11-common-plugin --with-watchdog \
--with-keventd --with-libsystemd
- name: Build
run: |
export PATH=`pwd`/coverity/bin:$PATH
+4 -3
View File
@@ -39,9 +39,10 @@ jobs:
- name: Creating Makefiles ...
run: |
./autogen.sh
./configure --prefix=/usr --exec-prefix= --sysconfdir=/etc --localstatedir=/var \
--enable-x11-common-plugin --enable-testserv-plugin --with-watchdog \
--with-keventd
./configure --prefix=/usr --exec-prefix= --sysconfdir=/etc \
--localstatedir=/var --enable-x11-common-plugin \
--enable-testserv-plugin --with-watchdog \
--with-keventd --with-libsystemd
- name: Enable unprivileged userns (unshare)
run: |
sudo sysctl kernel.apparmor_restrict_unprivileged_userns=0
+1 -1
View File
@@ -43,4 +43,4 @@ release: distcheck
DISTCHECK_CONFIGURE_FLAGS = --prefix=/usr --sysconfdir=/etc --localstatedir=/var \
--enable-testserv-plugin --enable-x11-common-plugin \
--with-watchdog --with-keventd --with-fstab=/etc/fstab \
--with-bash-completion-dir=no
--with-libsystemd --with-bash-completion-dir=no
+2 -2
View File
@@ -1,4 +1,4 @@
AC_INIT([Finit], [4.13], [https://github.com/troglobit/finit/issues],
AC_INIT([Finit], [4.14], [https://github.com/troglobit/finit/issues],
[finit], [https://troglobit.com/projects/finit/])
AC_CONFIG_AUX_DIR(aux)
AM_INIT_AUTOMAKE([1.11 foreign subdir-objects])
@@ -171,7 +171,7 @@ AC_ARG_WITH(watchdog,
AC_ARG_WITH(libsystemd,
AS_HELP_STRING([--with-libsystemd], [Build replacement libsystemd library, default: yes]),
[with_libsystemd=$withval], [with_libsystemd=yes])
[with_libsystemd=$withval], [with_libsystemd=no])
AC_ARG_WITH(hook-scripts-path,
AS_HELP_STRING([--with-hook-scripts-path=DIR], [Base directory for hook scripts, default $libexecdir/finit/hook]),
+19
View File
@@ -4,6 +4,25 @@ Change Log
All relevant changes are documented in this file.
[4.14][] - 2025-08-29
---------------------
Minor bug fix release.
### Changes
- Add exit status/signal info when a process dies, by Mattias Berthel
- Extend `service kill:SEC` to support up to 300 sec.
- Extend `norespawn` debug to cover `service_retry()`
- Build *without* libsystemd replacement by default
### Fixes
- Fix #449: ALSA plugin fail-to-build-from-source
- Fix `sysv` stop command process environment, should be same as start
- Fix state machine ordering issue: enter new config generation *after*
services disabled in previous generation have been stopped
[4.13][] - 2025-07-10
---------------------
+22
View File
@@ -39,3 +39,25 @@ Log rotation is controlled using the global `log` setting.
**Example:**
service log:prio:user.warn,tag:ntpd /sbin/ntpd pool.ntp.org -- NTP daemon
Output Buffering
----------------
When using the `log` directive, Finit redirects the service's stdout and
stderr to a pipe connected to a logger process. Programs detect this as
non-interactive output (i.e., `isatty()` returns false) and typically
switch from line-buffered to fully-buffered mode.
Most well-behaved daemons explicitly flush their output or use syslog
directly, so this is rarely an issue. However, if a service's log
messages appear delayed or batched, you can force line-buffered output
by wrapping the command with `stdbuf`:
service log /usr/bin/stdbuf -oL /path/to/command -- My service
The `-oL` option forces line-buffered output, and `-o0` forces unbuffered
output. See `stdbuf(1)` for details.
> [!NOTE]
> Using `stdbuf` is rarely necessary. Only use it if you observe actual
> buffering issues with a specific service.
+19 -1
View File
@@ -165,4 +165,22 @@ sync(2) has been called, twice.
> "On Linux, sync is only guaranteed to schedule the dirty blocks for
> writing; it can actually take a short time before all the blocks are
> finally written.
> finally written.
**Syntax:** `reboot-watchdog <on|off|true|false|1|0>`
Controls whether the system should reboot via the watchdog timer (WDT)
or directly via the SoC/kernel. When enabled, Finit will:
1. Send `SIGPWR` to the registered watchdog daemon before shutdown
2. Send `SIGTERM` to the watchdog daemon and wait up to 10 seconds
for the watchdog to trigger a hardware reset
When disabled (default), Finit skips the watchdog reboot logic and
calls the kernel's `reboot(2)` syscall directly for a clean SoC reboot.
*Default:* off (reboot via SoC)
> [!NOTE]
> This setting only affects reboots. The watchdog daemon will still
> run and monitor the system during normal operation.
+11
View File
@@ -0,0 +1,11 @@
.md-header__topic:first-child {
font-weight: 500;
font-style: italic;
}
.md-header__title {
font-size: 1.1rem;
font-style: italic;
font-weight: 500;
line-height: 2.6rem;
}
+14 -7
View File
@@ -10,14 +10,21 @@ many embedded systems this is crucial to ensure all circuits on the
board are properly reset for the next boot, in effect ensuring the
system works the same after both a power-on and reboot event.
> [!NOTE]
> The watchdog reboot delegation can be enabled with the `reboot-watchdog`
> configuration option in `/etc/finit.conf`. By default this is disabled
> and the system reboots directly via the SoC using the kernel's `reboot(2)`
> syscall. See [Runlevels](config/runlevels.md) for details.
The delegation is performed at the very last steps of system shutdown,
if reboot has been selected and an elected watchdog is known, first a
`SIGPWR` is sent to advise watchdogd of the pending reboot. Then, when
the necessary steps of preparing the system for shutdown (umount etc.)
are completed, Finit sends `SIGTERM` to watchdogd and puts itself in a
10 sec timeout loop waiting for the WDT to reset the board. If a reset
is not done before the timeout, Finit falls back to`reboot(RB_AUTOBOOT)`
which tells the kernel to do the reboot.
if reboot has been selected, `reboot-watchdog` is enabled, and an elected
watchdog is known. First a `SIGPWR` is sent to advise watchdogd of the
pending reboot. Then, when the necessary steps of preparing the system
for shutdown (umount etc.) are completed, Finit sends `SIGTERM` to
watchdogd and puts itself in a 10 sec timeout loop waiting for the WDT
to reset the board. If a reset is not done before the timeout, Finit
falls back to `reboot(RB_AUTOBOOT)` which tells the kernel to do the
reboot.
An external watchdog service can also be used. The more advanced cousin
[watchdogd](https://github.com/troglobit/watchdogd/) is the recommended
+3 -1
View File
@@ -1,4 +1,4 @@
site_name: doc/
site_name: Documentation
site_description: Fast Init for Linux Systems
site_url: https://troglobit.github.io/finit/
repo_url: https://github.com/troglobit/finit
@@ -6,6 +6,8 @@ repo_name: troglobit/finit
copyright: Copyright &copy; 2008-2025 Joachim Wiberg
docs_dir: doc/
edit_uri: edit/master/doc/
extra_css:
- extra.css
exclude_docs: |
TODO.md
+1
View File
@@ -27,6 +27,7 @@
# include <lite/lite.h>
#endif
#include "conf.h"
#include "finit.h"
#include "helpers.h"
#include "plugin.h"
+21 -13
View File
@@ -170,16 +170,18 @@ static void file_save(void *arg)
static void file_restore(void *arg)
{
struct tm tm = { 0 };
int rc = 1;
int rc = 0;
FILE *fp;
char msg[120];
if (!rtc_file) {
logit(LOG_NOTICE, "System has no RTC (missing driver?), skipping restore.");
snprintf(msg, sizeof(msg), "Resetting system clock to kernel default, %s.", rtc_timestamp);
print_desc(NULL, msg);
rc = time_set(NULL);
print(rc, NULL);
return;
}
print_desc(NULL, "Restoring system clock from backup");
fp = fopen(rtc_file, "r");
if (fp) {
char buf[32];
@@ -187,18 +189,25 @@ static void file_restore(void *arg)
if (fgets(buf, sizeof(buf), fp)) {
chomp(buf);
strptime(buf, RTC_FMT, &tm);
rc = time_set(&tm);
if (!strptime(buf, RTC_FMT, &tm))
rc = 1;
}
fclose(fp);
} else
logit(LOG_WARNING, "Missing %s", rtc_file);
}
rc = 1;
if (rc) {
time_set(NULL);
rc = 2;
print_desc(NULL, "Failed to restore system clock from restore file");
print(2, NULL);
snprintf(msg, sizeof(msg), "Resetting system clock to kernel default, %s.", rtc_timestamp);
print_desc(NULL, msg);
rc = time_set(NULL);
print(rc, NULL);
} else {
print_desc(NULL, "Restoring system clock from restore file");
rc = time_set(&tm);
print(rc, NULL);
}
print(rc, NULL);
}
static int rtc_open(void)
@@ -292,8 +301,7 @@ static void rtc_restore(void *arg)
print(2, NULL);
/* Try restoring from last save game */
if (rtc_file)
file_restore(arg);
file_restore(arg);
} else
print(0, NULL);
+9 -8
View File
@@ -68,7 +68,8 @@ static int stop(svc_t *svc, void *user_data)
service_timeout_cancel(svc);
svc_stop(svc);
service_step(svc);
service_step_all(SVC_TYPE_ANY);
if (!IS_RESERVED_RUNLEVEL(runlevel))
service_step_all(SVC_TYPE_ANY);
return 0;
}
@@ -83,7 +84,8 @@ static int start(svc_t *svc, void *user_data)
service_timeout_cancel(svc);
svc_start(svc);
service_step(svc);
service_step_all(SVC_TYPE_ANY);
if (!IS_RESERVED_RUNLEVEL(runlevel))
service_step_all(SVC_TYPE_ANY);
return 0;
}
@@ -391,11 +393,6 @@ static void api_cb(uev_t *w, void *arg, int events)
}
switch (rq.cmd) {
case INIT_CMD_RELOAD:
case INIT_CMD_START_SVC:
case INIT_CMD_RESTART_SVC:
case INIT_CMD_STOP_SVC:
case INIT_CMD_RELOAD_SVC:
case INIT_CMD_REBOOT:
case INIT_CMD_HALT:
case INIT_CMD_POWEROFF:
@@ -453,6 +450,10 @@ static void api_cb(uev_t *w, void *arg, int events)
break;
case INIT_CMD_RELOAD: /* 'init q' and 'initctl reload' */
if (IS_RESERVED_RUNLEVEL(runlevel)) {
warnx("Ignoring reload in runlevel S and 6/0.");
goto done;
}
dbg("reload");
sm_reload();
break;
@@ -576,7 +577,7 @@ static void api_cb(uev_t *w, void *arg, int events)
dbg("Unsupported cmd: %d", rq.cmd);
break;
}
done:
if (result)
rq.cmd = INIT_CMD_NACK;
else
+6
View File
@@ -60,6 +60,7 @@ int single = 0; /* single user mode from kernel cmdline */
int bootstrap = 1; /* set while bootstrapping (for TTYs) */
int kerndebug = 0; /* set if /proc/sys/kernel/printk > 7 */
int syncsec = 0; /* reboot delay */
int wdtreboot = 0; /* reboot via watchdog, default: SOC */
int readiness = SVC_NOTIFY_PID;
char *finit_conf= NULL;
char *finit_rcsd= NULL;
@@ -1034,6 +1035,11 @@ static int parse_static(char *line, int is_rcsd)
return 0;
}
if (MATCH_CMD(line, "reboot-watchdog ", x)) {
wdtreboot = get_bool(strip_line(x), 0);
return 0;
}
/*
* Periodic check and instability index leveler, seconds
*/
+1
View File
@@ -37,6 +37,7 @@ extern int single;
extern int bootstrap;
extern int kerndebug;
extern int syncsec;
extern int wdtreboot;
extern int readiness;
extern char *fstab;
extern char *sdown;
+10
View File
@@ -212,6 +212,11 @@ static int fsck(int pass)
if (mnt->mnt_passno == 0 || mnt->mnt_passno != pass)
continue;
if (hasmntopt(mnt, "noauto")) {
dbg("Skipping fsck of %s, marked noauto", mnt->mnt_fsname);
continue;
}
/* Device to maybe fsck, */
dev = mnt->mnt_fsname;
@@ -425,6 +430,11 @@ static void fs_swapon(char *cmd, size_t len)
if (strcmp(mnt->mnt_type, MNTTYPE_SWAP))
continue;
if (hasmntopt(mnt, "noauto")) {
dbg("Skipping swap %s, marked noauto", mnt->mnt_fsname);
continue;
}
snprintf(cmd, len, "swapon %s", mnt->mnt_fsname);
run_interactive(cmd, "Enabling swap %s", mnt->mnt_fsname);
}
+78 -31
View File
@@ -88,7 +88,7 @@ static void service_timeout_cb(uev_t *w, void *arg, int events)
svc_t *svc = arg;
if (UEV_ERROR == events) {
dbg("%s(): spurious problem, svc %s.", __func__, svc_ident(svc, NULL, 0));
dbg("%s: spurious problem", svc_ident(svc, NULL, 0));
uev_timer_start(w);
return;
}
@@ -271,25 +271,22 @@ static int lredirect(svc_t *svc)
{
static int have_sysklogd = -1;
pid_t svc_pid = getpid();
int pipefd[2];
pid_t pid;
int fd;
/*
* Open PTY to connect to logger. A pty isn't buffered
* like a pipe, and it eats newlines so they aren't logged
* Use a pipe to connect to logger. This ensures isatty()
* returns false for the service, preventing programs from
* emitting ANSI escape codes and other TTY-specific output.
*/
fd = posix_openpt(O_RDWR);
if (fd == -1) {
dbg("Failed posix_openpt(), errno %d: %s", errno, strerror(errno));
svc->log.enabled = 0;
return -1;
}
if (grantpt(fd) == -1 || unlockpt(fd) == -1) {
dbg("Failed grantpt()|unlockpt(), errno %d: %s", errno, strerror(errno));
close(fd);
if (pipe(pipefd) == -1) {
dbg("Failed pipe(), errno %d: %s", errno, strerror(errno));
svc->log.enabled = 0;
return -1;
}
fd = pipefd[1]; /* Write end for service */
/*
* First time, check if we have sysklogd logger tool.
@@ -319,17 +316,12 @@ static int lredirect(svc_t *svc)
char *prio = "daemon.info";
char buf[MAX_IDENT_LEN];
char *tag;
int fds;
sched_yield();
fds = open(ptsname(fd), O_RDONLY);
close(fd);
if (fds == -1) {
logit(LOG_WARNING, "failed open() ptsname(%d), errno %d", fd, errno);
_exit(0);
}
dup2(fds, STDIN_FILENO);
close(pipefd[1]); /* Close write end in logger */
dup2(pipefd[0], STDIN_FILENO);
close(pipefd[0]);
/* Reset signals */
sig_unblock();
@@ -385,10 +377,11 @@ static int lredirect(svc_t *svc)
_exit(1);
}
dup2(fd, STDOUT_FILENO);
dup2(fd, STDERR_FILENO);
close(pipefd[0]); /* Close read end in service */
dup2(pipefd[1], STDOUT_FILENO);
dup2(pipefd[1], STDERR_FILENO);
return close(fd);
return close(pipefd[1]);
}
/*
@@ -1147,10 +1140,11 @@ int service_stop(svc_t *svc)
args[i++] = "stop";
args[i] = NULL;
switch (fork()) {
switch (service_fork(svc)) {
case 0:
setsid();
redirect(svc);
setsid();
sig_unblock();
exec_runtask(args[0], &args[1]);
_exit(0);
break;
@@ -1420,9 +1414,9 @@ static void parse_killdelay(svc_t *svc, char *delay)
const char *errstr;
long long sec;
sec = strtonum(delay, 1, 60, &errstr);
sec = strtonum(delay, 1, 300, &errstr);
if (errstr) {
errx(1, "%s: killdelay %s is %s (1-60)", svc_ident(svc, NULL, 0), delay, errstr);
errx(1, "%s: killdelay %s is %s (1-300)", svc_ident(svc, NULL, 0), delay, errstr);
return;
}
@@ -1464,7 +1458,7 @@ static void parse_script(svc_t *svc, char *type, char *script, int *tmo, char *b
found = which(path);
if (!found) {
logit(LOG_WARNING, "%s:%s is missing or not executable, skipping.", type, path);
logit(LOG_WARNING, "%s:'%s' is missing or not executable, skipping.", type, path);
goto err;
}
free(found);
@@ -2097,6 +2091,7 @@ void service_unregister(svc_t *svc)
return;
service_stop(svc);
service_timeout_cancel(svc);
for (c = strtok(svc->cond, ","); c; c = strtok(NULL, ","))
devmon_del_cond(c);
@@ -2136,6 +2131,10 @@ void service_monitor(pid_t lost, int status)
: (svc->state == SVC_CLEANUP_STATE
? svc->cleanup_script
: svc->pre_script)), lost, ok, sig, rc);
/* Prevent: spurious problem from timeout callback */
service_timeout_cancel(svc);
/* Kill all children in the same proess group, e.g. logit */
dbg("Killing lingering children in same process group ...");
kill(-svc->pid, SIGKILL);
@@ -2467,6 +2466,10 @@ static void service_retry(svc_t *svc)
int timeout;
service_timeout_cancel(svc);
if (is_norespawn())
return;
if (svc->respawn) {
dbg("%s crashed/exited, respawning ...", svc_ident(svc, NULL, 0));
svc_unblock(svc);
@@ -2518,8 +2521,13 @@ static void service_retry(svc_t *svc)
timeout = ((*restart_cnt) <= (svc->restart_max / 2)) ? 2000 : 5000;
/* If a longer timeout was specified in the conf, use that instead. */
svc->restart_tmo = max(svc->restart_tmo, timeout);
logit(LOG_CONSOLE|LOG_WARNING, "Service %s[%d] died, restarting (retry in %d msec) (attempt: %d/%d)",
svc_ident(svc, NULL, 0), svc->oldpid, svc->restart_tmo, *restart_cnt, svc->restart_max);
logit(LOG_CONSOLE|LOG_WARNING, "Service %s[%d] died (%s%d), restarting (retry in %d msec) (attempt: %d/%d)",
svc_ident(svc, NULL, 0), svc->oldpid,
WIFEXITED(svc->status) ? "with exit status: " : "by signal: ",
WIFEXITED(svc->status) ? WEXITSTATUS(svc->status) : WTERMSIG(svc->status),
svc->restart_tmo,
*restart_cnt,
svc->restart_max);
svc_unblock(svc);
service_step(svc);
@@ -2771,6 +2779,9 @@ restart:
if (sm_in_reload())
break;
if (is_norespawn())
break;
/* Don't start if it conflicts with something else already started */
if (svc_conflicts(svc)) {
logit(svc->nowarn ? LOG_DEBUG : LOG_INFO,
@@ -3035,6 +3046,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
@@ -3141,7 +3188,7 @@ static void service_interval_cb(uev_t *w, void *arg, int events)
(void)arg;
if (UEV_ERROR == events) {
dbg("%s(): spurious problem, restarting.", __func__);
dbg("spurious problem, restarting.");
uev_timer_start(w);
return;
}
+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);
+9 -4
View File
@@ -187,6 +187,11 @@ static void fs_swapoff(void)
if (strcmp(mnt->mnt_type, MNTTYPE_SWAP))
continue;
if (hasmntopt(mnt, "noauto")) {
dbg("Skipping swap %s, marked noauto", mnt->mnt_fsname);
continue;
}
snprintf(cmd, sizeof(cmd), "swapoff %s", mnt->mnt_fsname);
run_interactive(cmd, "Disabling swap %s", mnt->mnt_fsname);
}
@@ -353,7 +358,7 @@ void do_shutdown(shutop_t op)
return;
}
if (wdog) {
if (wdtreboot && wdog) {
print(kill(wdog->pid, SIGPWR) == 1, "Advising watchdog, system going down");
do_sleep(2);
}
@@ -400,17 +405,17 @@ void do_shutdown(shutop_t op)
/* Reboot via watchdog or kernel, or shutdown? */
if (op == SHUT_REBOOT) {
print(0, "Rebooting ...");
if (wdog && wdog->pid > 1) {
if (wdtreboot && wdog && wdog->pid > 1) {
int timeout = 10;
/* Wait here until the WDT reboots, or timeout with fallback */
print(0, "Rebooting using WDT, please wait ...");
print(kill(wdog->pid, SIGTERM) == 1, "Pending watchdog reboot");
while (timeout--)
do_sleep(1);
}
dbg("Rebooting ...");
print(0, "Rebooting ...");
reboot(RB_AUTOBOOT);
} else if (op == SHUT_OFF) {
print(0, "Powering down ...");
+92 -4
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);
@@ -359,6 +444,8 @@ restart:
break;
case SM_RELOAD_CHANGE_STATE:
sm.in_reload = 1;
/* First reload all *.conf in /etc/finit.d/ */
conf_reload();
@@ -367,10 +454,11 @@ restart:
* let all affected services move to WAITING/HALTED
*/
dbg("Stopping services not allowed after reconf ...");
sm.in_reload = 1;
cond_reload();
service_step_all(SVC_TYPE_ANY);
/* Step the generation ... */
cond_reload();
sm.state = SM_RELOAD_WAIT_STATE;
break;
+1 -1
View File
@@ -94,7 +94,7 @@ typedef enum {
SVC_NOTIFY_S6,
} svc_notify_t;
#define MAX_ID_LEN 16
#define MAX_ID_LEN 65
#define MAX_ARG_LEN 64
#define MAX_CMD_LEN 256
#define MAX_IDENT_LEN (MAX_ARG_LEN + MAX_ID_LEN + 1)
+2 -1
View File
@@ -25,7 +25,8 @@ if [ "$run_make" -eq 1 ]; then
fi
./configure --prefix=/usr --exec-prefix= --sysconfdir=/etc --localstatedir=/var \
--enable-x11-common-plugin --with-watchdog --with-keventd \
--enable-x11-common-plugin --enable-testserv-plugin --with-watchdog \
--with-keventd --with-libsystemd \
CFLAGS='-fsanitize=address -ggdb'
if [ "$run_make" -eq 1 ]; then