Commit Graph
4057 Commits
Author SHA1 Message Date
Joachim Wiberg 8a3d55b416 test: refuse to run against a stale sysroot binary
'make check' refreshes the sysroot through the setup-chroot rule, but
running a test script by hand does not, so the test exercises whichever
finit was installed last and reports on code that is no longer there.
Both a passing and a failing run are then meaningless, and nothing says
so.

Compare the built binary against the installed one at startup and fail
with the command that fixes it.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2026-07-30 15:21:28 +02:00
Joachim Wiberg 53cb2b7bdb service: give stop and reload scripts their own timeout
Both were bounded by killdelay, the delay between the stop signal and
SIGKILL, because service_run_script() had nothing else to reach for.
That conflates two things: how long the daemon may take to die, and
how long its stop script may run.

Give each hook a timeout of its own, defaulting to killdelay when
unset, so the existing behaviour is what you get until you ask for
something else.  parse_script() already falls back that way for the
hooks that had one.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2026-07-30 15:21:27 +02:00
Joachim Wiberg 2a5190ecff service: do not let a script timeout take PID 1 with it
A stop: or reload: script written with a timeout killed Finit at
config load:

    service stop:5,/bin/true service.sh -- Boom

parse_script() takes the timeout as a pointer and the caller decides
whether it wants one.  However, both stop: and reload: scripts so far
have no timeout, i.e., NULL.  Guard the branch that reads a leading
number.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2026-07-30 15:21:27 +02:00
Joachim Wiberg 0721b0fec2 util: one whole-file reader for all tools
Three private ones had grown: fnread() in util.c, flen() behind
pid_cmdline()/pid_cgroup() in cgutil.c, and conf_read_template() in
conf.c.  Two of them were also wrong in ways the others were not.

fnread() formatted the path into a char[256] and stat()ed it before
opening, so a longer path was silently truncated and then read from
whichever file the truncation happened to name, and the size could
change between the look and the read.  flen() existed because neither
of those approaches works on procfs at all, where stat() reports zero
and the only way to learn the size is to read to EOF.

Add fslurp() to util.[ch], which every tool already links.  It opens
first and sizes the fd it holds, treats st_size as a hint, and reads
until EOF, so procfs and regular files take the same path.  Paths are
formatted by libite's vfopenf(), which allocates to fit.  Callers that
need the byte count, /proc/PID/cmdline embeds NUL, ask for it.

fnread() keeps its signature and becomes a bounded copy out of the
result, so its one caller is unaffected.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2026-07-30 15:21:27 +02:00
Joachim Wiberg f1393ff8a1 conf: instantiate %i templates for the block format
A template in the block format registered garbage.  conf_parse_file()
routed every file with an '@' in its name straight to the legacy
parser, which read the block line by line: the section header became a
service whose command was the section title, and each key = value line
below it became an environment variable.

    service serv:%i { ... }   ->  service 'serv:eth0' with argument '{'

Substitute %i over the whole file before parsing instead, so format
detection and both parsers see finished text.  A bare name@.conf is
still skipped, it is the template rather than an instance of one.

The legacy parser no longer opens the file or substitutes per line, it
is handed the instantiated buffer, so the template convention now has
one implementation instead of two.  conf_is_template() applies
basenm(), a directory with an '@' in its name is not a template.

libconfuse cannot name a buffer it parses before 3.4, so a typo in a
template would be reported against "[buf]".  Parse through fmemopen()
with the file name preset until the floor moves.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2026-07-30 15:21:26 +02:00
Joachim Wiberg 47a18140c1 conf: cleanup and relocation of common parsing functions and helpers
- legacy.[ch]: strictly legacy .conf parser boilerplate only
 - conf.[ch]: .conf parser and generic configuration functionality

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2026-07-30 15:21:26 +02:00
Joachim Wiberg c20d64587c conf: relocate global state from legacy parser
Relocate process-wide global variables from legacy parser that ended up
there because it used to be conf.c, but which is now now frozen at the
4.x feature set.  Each variable is moved to their respective "owner".

Give cgroup_current[] and cgroup_settings_current[] named bounds.  Their
extern declarations were unsized, so sizeof() on them stopped compiling
once the definitions moved to another translation unit.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2026-07-30 15:21:25 +02:00
Joachim Wiberg 3b866c95e0 conf: add libconfuse block format alongside the one-liner format
The one-liner format has grown crowded and very wide, and every new
service option makes it worse.

Add a second, block-based format, parsed with libconfuse:

    service sshd {
        description = "OpenSSH daemon"
        runlevel    = "2345"
        command     = "/usr/sbin/sshd -D $SSHD_OPTS"
    }

Both formats keep the .conf extension and are detected per file by
content.  Try-parse strictly with libconfuse; on a parse error,
re-parse leniently to tell a block file with a typo from a one-liner
file.  Only a one-liner file reaches the legacy parser, a typo is
reported with its file and line.

Each block is translated to the canonical one-liner and registered
through the existing entry points, so the two formats cannot drift.

The one-liner parser is frozen at the 4.x feature set, new options
land only in the block schema.  libconfuse 3.3 or later is required,
CFGF_KEYSTRVAL does not exist before it.

Covers service, task, run, sysv and tty blocks, the static directives,
and the cgroup, rlimit, set and log blocks.  Templating and the
documentation rewrite are still to come.

The regression test covers translation of a service block to the
one-liner, a block-format /etc/finit.conf booting with set {} applied
at bootstrap, both formats side by side, and rejection of a typo at
block and at root level.

A rejected file must not fall through to the legacy parser, which
registers a bogus unstartable service per line.  assert_num_children
cannot see that, the bogus service has no children either, so the
check is assert_num_services.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2026-07-30 15:21:25 +02:00
Joachim Wiberg fc74f87986 conf: rename conf.c to legacy.c
The one-liner parser is about to be joined by a second, block-based
format.  Give it a name that says which of the two it implements,
before any content changes make the diff hard to follow.

No functional change.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2026-07-30 15:21:24 +02:00
Joachim Wiberg ee5038e7af test: reap the watchdog's sleep in wdkill()
Every test left a stray `sleep 300` behind, reparented to PID 1, where
it lingered for up to five minutes after the test had finished.

wdstart() runs the watchdog in a subshell, so $! is the pid of the
subshell, not of the sleep it forks.  wdkill() killed the subshell and
orphaned the sleep.

Kill the child first, killing the subshell puts the sleep beyond the
reach of pkill -P.  Neither kill is sure to match, and wdkill() runs
from the EXIT trap under set -e, so both must tolerate failure.  Also
return early when wdpid is unset, for failures before wdstart() runs.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2026-07-30 15:21:24 +02:00
Joachim Wiberg ad750a13c3 Bump version for 5.0 release cycle
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2026-07-30 15:21:23 +02:00
Joachim Wiberg b56ffc155e doc: version the published User Guide by major release
Finit 5.0 changes the .conf syntax, which has been essentially
unchanged since 1.x.  The published docs track master, so when 5.x
lands, 4.x users lose their reference.

Publish the site under a per-major directory, /4.x/ for now, with
the Material version selector to switch between them.  The selector
only needs mike's file layout -- a versions.json at the site root --
which the deploy job now generates from the version directories in
the pages repo, so mike itself is not needed.

The major comes from AC_INIT and the future 4.x maintenance branch
is already in the workflow triggers, so once 5.0 is on master, doc
fixes on the 4.x branch keep /4.x/ updated.  A root index.html
redirects to the newest version, and a 404.html rewrites
pre-versioned deep links so old bookmarks and search hits land in
the right place.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2026-07-30 12:27:27 +02:00
Joachim Wiberg 4596e27eb0 doc: simplify and add some polish to the User Guide
Both the Finit project and Infix use the same MkDocs Material setup, and
in the latter the User Guide has picked up a lot of polish that never
made it back here: a single sidebar with section indexes instead of
tabs, footnote tooltips, more pymdownx markup, image zoom tuning, and no
generator advert in the footer.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2026-07-30 12:22:17 +02:00
Joachim Wiberg a3eac3a85e Merge pull request #489 from finit-project/stale-pidfile
Handle stale pid file for crashing daemons
2026-05-12 10:40:01 +02:00
Joachim Wiberg 7d09e34b80 doc: document stale pidfile cleanup and new restart log
* src/pid.c: note the stale-pidfile-cleanup exception to the
  documented "Finit does not touch pid:! pidfiles" rule.
* doc/config/services.md: add a user-facing paragraph on the same.
* doc/ChangeLog.md: add Unreleased section covering this PR --
  stale pidfile cleanup, restart log with signal name and core
  dump flag, and the SIGUNKOWN typo fix.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2026-05-12 10:11:50 +02:00
Joachim Wiberg 3febc7d513 test: regression for stale pidfile cleanup after unclean exit
Cover the scenario fixed in "service: clean stale pidfile after
unclean daemon exit": a daemon with a pid:!/path config dies via
SIGKILL, leaving its pidfile behind, and the next instance must
still come up.

Add a 'serv -x' flag (refuse to start when the pidfile already
exists, dbus-style) so the test actually exercises the cleanup --
without it, plain 'serv' would happily overwrite the file and the
test would pass with or without the fix.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2026-05-12 10:11:49 +02:00
Joachim Wiberg 309ad1bea7 sig: spell SIGUNKNOWN correctly
The fallback for unknown signal numbers in sig_name() returned the
misspelled "SIGUNKOWN".  Now that this string surfaces in user-
facing logs ("killed by …"), fix the typo.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2026-05-11 21:09:02 +02:00
Joachim Wiberg 30f2ca3b2e service: log signal name and core dumps in death message
Replace the bare signal number ("by signal: 9") with the symbolic
name ("killed by SIGKILL") and annotate when the kernel wrote a
core:("killed by SIGSEGV, core dumped").  Makes the restart line
self-explanatory and gives operators a strong breadcrumb when a
daemon dies unexpectedly.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2026-05-11 21:09:01 +02:00
Joachim Wiberg 4a53f610cd service: clean stale pidfile after unclean daemon exit
With `pid:!/path` Finit does not manage the file -- the daemon
creates it on start and removes it on graceful exit.  If the daemon
dies before cleanup (SIGKILL, OOM, segfault, exit during startup)
the file lingers and can block the next instance from starting,
e.g. dbus-daemon refuses with EEXIST and the restart loop fails.

Remove the file when it still names the just-reaped PID and that
PID is no longer alive (the liveness check guards against reuse).
Called from service_cleanup(), and from service_monitor()'s
forking+starting branch where cleanup was previously skipped.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2026-05-11 21:08:51 +02:00
Joachim Wiberg e74dff96ba Update ChangeLog and bump version for v4.17 GA
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
4.17
2026-04-28 10:09:16 +02:00
Joachim Wiberg 9ed4407b20 Follow-up to 96f944b: fix regression in status output at boot
status() returns a pointer to a single static buffer, so calling it twice
in the same cprintf() argument list — status(3) and status(rc) — causes
one to overwrite the other before the format string is rendered.  When
status(3) wins, the line shows [ ⋯  ] instead of [ OK ].  Fix by copying
status(rc) into a local buffer before calling status(3).

Also drop the delline() calls added to print() — that macro writes \033[2K
to buffered stdout while cprintf() writes unbuffered to stderr, so the
erase sequences can arrive out of order.  The \r\e[K already present in
the cprintf format strings makes them redundant anyway.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2026-04-28 10:02:21 +02:00
Joachim Wiberg eb913e8bf2 initramfs: fix Coverity defects
Check return value of remove() in delete_cb() and log failures via
dbg(), CID 909395

Replace stat() calls with open(O_DIRECTORY)+ fstat() for newroot and "/"
checks.  Eliminates the check-then-use race and lets O_DIRECTORY do the
isdir validation atomically, CID 909394

Drop the explicit close(0/1/2) before opening /dev/console.  dup2()
closes the old targets itself, so open() returns a fd > STDERR_FILENO
that can always be closed unconditionally, removing the conditional
guard, CID 909393

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
4.17-rc1
2026-04-26 22:11:19 +02:00
Joachim Wiberg 17d04779fb Update ChangeLog and bump version for v4.17-rc1
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2026-04-26 21:54:49 +02:00
Joachim Wiberg ab661423ba Merge pull request #488 from Ollie-Gutierrez/ctty-support
service: add optional controlling TTY via tty: stanza
2026-04-23 19:41:02 +02:00
Ollie Gutierrez 8e77ea68b2 service: add optional controlling TTY via tty: stanza 2026-04-23 15:08:57 +00:00
Joachim Wiberg b0e672bc9a Merge pull request #487 from aanderse/patch-1
tmpfiles.d: update /var/tmp mode
2026-04-18 07:34:37 +02:00
Aaron Andersen ad76e15949 tmpfiles.d: update /var/tmp mode
`/var/tmp` mode should match `/tmp`
2026-04-18 09:17:10 +09:00
Joachim Wiberg b8735cc893 Merge pull request #486 from aanderse/patch-1 2026-04-14 04:57:00 +02:00
Aaron Andersen c058b3f9ce plugins/plymouth.c: fixup email address 2026-04-14 09:03:19 +09:00
Joachim Wiberg 8f6c61606a Merge pull request #485 from finit-project/console-output-fixes
Atomic print and re-print desc on status, flush before reboot
2026-04-08 17:11:57 +02:00
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 a81530754b Merge pull request #483 from aanderse/plymouth
Add Plymouth boot splash plugin
2026-03-20 14:35:53 +01:00
Aaron Andersen 1518eb9466 Add Plymouth boot splash plugin
Manage the plymouthd lifecycle across boot, switch_root, and shutdown.
Activated by the "splash" kernel command line argument.
2026-03-20 09:18:18 -04: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>
4.16
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
Joachim Wiberg cef5049bf0 Update ChangeLog and bump version for v4.16-rc1
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
4.16-rc1
2026-02-24 05:36:45 +01:00
Joachim Wiberg ec251f1ce1 Merge pull request #480 from aanderse/master 2026-02-24 05:05:05 +01:00
Aaron Andersen 98ec30d308 tmpfiles: add --exclude-prefix and -E flags
Add support for the --exclude-prefix=PATH option to skip rules whose
path starts with the specified prefix.  The option can be specified
multiple times to exclude multiple path prefixes.

The -E flag is a shortcut for:

    --exclude-prefix=/dev --exclude-prefix=/proc \
    --exclude-prefix=/run --exclude-prefix=/sys

This is useful to avoid creating files below virtual or memory-backed
file system mount points.
2026-02-23 15:39:44 +00:00
Joachim Wiberg e1fe870aaf Merge pull request #478 from mattiaswal/fix-initial-devmon
devmon: assert condition immediately if device already exists

  1. udev fires early, creates device nodes in /dev/                                                                                                           
  2. Config is parsed later, calling devmon_add_cond() for each dev/ condition
  3. At this point the device already exists but the inotify event was missed
  4. The PR's fexist() check catches this — new node is added to the TAILQ and condition is immediately asserted

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2026-02-23 08:09:12 +01:00
Mattias Walström f04fce544d devmon: assert condition immediately if device already exists
The config parsing happens after udev triggers the initial event,
make sure to assert the condition if the device node exists when adding
it from configuration.

Signed-off-by: Mattias Walström <lazzer@gmail.com>
2026-02-23 07:30:08 +01:00
Joachim Wiberg 7aa9ed5982 Merge pull request #474 from finit-project/multi-depend
Multi depend fix
2026-02-22 21:05:31 +01:00
Joachim Wiberg a1a92a04bd Update ChangeLog
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2026-02-22 20:51:07 +01:00
Joachim Wiberg 804f655d87 test: fix depserv.sh after regression in 0b182c06
A service that is reloaded should not trigger dependants to be reloaded
unless the new <~cond> is used.  Which is reserve for tightly coupled
services.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2026-02-22 20:36:36 +01:00
Joachim Wiberg 2c78e7429a Only remove managed pidfiles in service cleanup
For SysV services with pid:!/path, the pidfile belongs to the service
itself and Finit shouldn't delete it.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2026-02-22 20:36:36 +01:00