Compare commits

...
159 Commits
Author SHA1 Message Date
Joachim Wiberg c8d6ad6546 Bump version for v4.1 release
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-06-06 17:30:48 +02:00
Joachim Wiberg 3dfae3074e Revert "Travis-CI: disable clang for Coverity run"
This reverts commit 8e1845f57b.
2021-06-06 17:05:09 +02:00
Joachim Wiberg 97bebe52c9 Fix logic expression in release_heading(), found by Coverity Scan
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-06-06 16:57:52 +02:00
Joachim Wiberg 8e1845f57b Travis-CI: disable clang for Coverity run
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-06-06 16:50:51 +02:00
Joachim Wiberg 6cd6769997 Update ChangeLog and drop man page TODO
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-06-06 16:46:59 +02:00
Joachim Wiberg 03884d3566 doc: fix minor issues, drop 'simply', nothing's simple ..
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-06-06 16:35:57 +02:00
Joachim Wiberg 899925e249 man: initial manual page for finit.conf
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-06-06 16:35:37 +02:00
Joachim Wiberg efefa398ce man: initial manual page for initctl
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-06-06 12:57:21 +02:00
Joachim Wiberg 1ee24c69bc Sync with man page
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-06-06 12:50:03 +02:00
Joachim Wiberg b16a36980c man: initial manual page for finit
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-06-06 12:50:03 +02:00
Joachim Wiberg c1b52bbc04 contrib: debian: depending on the setup, a user may not need elogind
I'm writing this in X started on a system running Finit v4.1-pre that
does not have elogind installed.  It does, however, still have systemd
installed, so systemd-logind is available.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-06-06 09:05:13 +02:00
Joachim Wiberg 9461ad4d4d doc: add reminders about limitations to sys conditions
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-06-06 09:04:45 +02:00
Joachim Wiberg e2ef6e9c34 plugins: sys: drop conditions not affecting any svc_t
- Fix bug when calling sys_update_conds() when dir already exists
  and we're not being called from the scandir() handler.  I.e.,
  every time but the first for each new condition sub-family

- Drop sys conditions that don't affect any svc_t.  This may seem
  counterintuitive, and we should probably not use oneshot conds,
  but if we leave these conds asserted they may cause inadvertent
  trigger if a finit.conf is loaded which ha this sys cond.  E.g.,
  if ctrlaltdel is asserted and we enable a task in finit.conf and
  call `initctl reload`, the task would start immediately, even
  though ctrl-alt-del may have been pressed a week ago

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-06-06 00:34:11 +02:00
Joachim Wiberg cdc8873f5b Let cond_update() return if any svc_t was affected
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-06-06 00:33:36 +02:00
Joachim Wiberg b82b205d92 contrib: debian: install available python-quit.conf
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-06-05 23:35:26 +02:00
Joachim Wiberg 39d879f240 Add support for auto-detetcing OS heading for progress
This patch removes the cognitive overhead of having to manually set your
OS heading, --with-heading="Foo OS vX.YY".  As of this patch, Finit by
default extracts PRETTY_NAME from /etc/os-release.  It is now possible
to also disable the heading entirely using --without-heading

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-06-05 14:28:49 +02:00
Joachim Wiberg 9c1db9cab3 contrib: drop os marker files, check Makefile instead
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-06-05 14:28:49 +02:00
Jacques de Laval 2f5c00f2bd Also accept \n for "Please press Enter to activate ..."
Shouldn't be needed for real cases, but we have seen automated scripts send
'\n' instead of '\r', or '\r' + '\n'.

Signed-off-by: Jacques de Laval <Jacques.De.Laval@westermo.com>
2021-06-04 13:20:14 +02:00
Joachim Wiberg dc36b313ff getty: attempt to steal ctty if don't already have it
This fixes the following classic problem

    login: root
    -sh: can't access tty; job control turned off

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-06-04 08:39:48 +02:00
Joachim Wiberg 093fa0107a getty: skip 5 sec delay introduced in 94c0d1b refactor
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-06-04 08:33:58 +02:00
Joachim Wiberg e8f5ec2579 Simplify, drop SYNC_SHUTDOWN state file, not needed anymore
Similar to 6224166 (previous commit), this old code is a remnant of a
bygone era and not needed anymore.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-06-03 12:51:59 +02:00
Joachim Wiberg 62241668df Drop SIGSTOP/TSTP/CONT handlers, not needed anymore
Once upon a time, SIGSTOP was used to pause Finit during flashing (MTD)
of a system image.  This to prevent Finit from accidentally starting any
programs, i.e., reading from flash disk during or after upgrade.

This was quite intrusive, and has possible nasty side effects, e.g., any
process with root access sends SIGSTOP prevents TTY login.  So this patch
now removes the functionality and recommends using a dedcicated runlevel
for such critical tasks instead.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-06-03 12:18:08 +02:00
Jacques de Laval ee67c08b64 Reset the starting flag of a service when stopping it
When a service is started and then stopped before it has created it's pid file,
it could be left forever in the "stopping" state, if we don't reset the
starting flag.

Signed-off-by: Jacques de Laval <Jacques.De.Laval@westermo.com>
2021-06-02 17:01:01 +02:00
Joachim Wiberg 41a14391a2 Refactor sigchld_cb(), handle EINTR properly, restart waitpid()
The waitpid() function can return -1 due to EINTR (signal), so we should
restart it to make sure we collect all zombies.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-06-02 16:34:24 +02:00
Joachim Wiberg 6eccca31b9 contrib: debian: task to stop any running plymouth boot screen
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-06-02 08:57:36 +02:00
Joachim Wiberg d497942a9f contrib: debian: speling error demon vs daemon
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-06-02 08:55:29 +02:00
Joachim Wiberg 68c7352108 Update ChangeLog and bump version for 4.1-rc2
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-06-01 16:16:00 +02:00
Joachim Wiberg 0d40e43c4b Update ChangeLog with fixes for containers
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-06-01 13:33:35 +02:00
Joachim Wiberg 9867fd82df Check correct struct member to determine if / has 'ro' flag
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-06-01 12:50:48 +02:00
Joachim Wiberg 1c941fdfbb plugins: add support for disabling RTC and urandom plugins
Some systems may want to handle RTC and /dev/urandom by themselves, or
not at all as in the case of containers where the host does all this.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-06-01 11:43:58 +02:00
Joachim Wiberg 0ba67a72f8 plugins: add missing x11-common plugin to static libplug.la
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-06-01 11:43:26 +02:00
Joachim Wiberg 09b9e26b2d fs_init(): skip mounting proc/dev/sys if already mounted
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-31 17:35:07 +02:00
Joachim Wiberg d25c1d5564 logit: avoid modifying argv[] strings
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-31 17:03:10 +02:00
Joachim Wiberg d4358ed3f7 Drop nasty hide_args() hack, should not be needed anymore
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-31 16:51:08 +02:00
Joachim Wiberg cc6ffa35fc Drop parsing of /proc/cmdline by default, instead use argc + argv[]
For most use-cases the kernel will give Finit its arguments as proper
command line args in argc + argv[], like any other program.  However,
for some users, most notably Alpine Linux, there is a slightly broken
initramfs that cannot forward more than one argument using init_args,
for such systems you can re-enable the old behavior with a configure
switch --enable-kernel-cmdline -- it's not ideal but what can you do.

The main reason for removing this feature by default is to support
use-cases where Finit runs as the init for container apps that can read
/proc -- we do not want them to use the init args from the host.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-31 16:51:08 +02:00
Joachim Wiberg 55e9cb2f58 Update with info on dropped multiple consoles
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-31 13:59:21 +02:00
Joachim Wiberg 2641256254 plugins: sys: no need to compose path, name is already absolute path
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-31 13:58:30 +02:00
Joachim Wiberg 473116f603 Drop experimental multiple console output support
It has barely worked and only caused more problems than solved annoying
issues.  We have one console for output, /dev/console, which the kernel
sets up for us.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-31 10:48:37 +02:00
Joachim Wiberg e18d63df4a Always make sure to not acquire controlling TTY for output redirect
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-31 10:48:04 +02:00
Joachim Wiberg 4b1b3f6913 Update ChangeLog with latest changes
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-31 09:35:20 +02:00
Joachim Wiberg f6da5e4136 Revert "Travis-CI: disable clang temporarily for Coverity Scan run"
This reverts commit 754358113f.
2021-05-31 07:48:49 +02:00
Joachim Wiberg d1d5d39144 Fix possible NULL ptr deref, found by Coverity Scan
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-31 07:43:29 +02:00
Joachim Wiberg 754358113f Travis-CI: disable clang temporarily for Coverity Scan run
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-31 07:27:12 +02:00
Joachim Wiberg c24207516d keventd: properly zero terminate buffer, found by Coverity Scan
Also, fix default: case in error handling, must always continue back to
poll() on any recv() error.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-31 07:22:50 +02:00
Joachim Wiberg 2a23bce78d Hide progress output also on stop/shutdown for services w/o -- descr
Progress at startup has been hidden for services w/o -- description, but
for some reason this check was not added to service_stop().  This patch
rectifies the situation, finally.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-31 07:17:48 +02:00
Joachim Wiberg a19b82ea5f Allow tty's to always respawn, no max restart count
This patch adds a `respawn` flag for services and ttys, always set for
ttys, that allows bypassing the crash/restart counter and immediately
restart a 'crashing' service.

For tty type services this is the expected behavior, but for regular
services it is not.  That is why `respawn` flags is not advertised in
the docs.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-31 06:29:11 +02:00
Joachim Wiberg ed9590aef1 Fix tty default :ID => tty:S0 instead of tty:ttyS0
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-31 06:27:59 +02:00
Joachim Wiberg 25683221cc Set default envs like sysvinit and busybox does; TERM, LOGNAME, USER
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-30 13:22:50 +02:00
Joachim Wiberg 9cc9e3ddbb getty: if we cannot execute /bin/login, try sulogin before /bin/sh
Basic security measure, don't bail to shell if we cannot find/exec
login, instead try sulogin before falling back to plain shell.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-30 12:42:48 +02:00
Joachim Wiberg 654f8aeb19 getty: handle variable number of arguments, only tty required
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-30 11:55:21 +02:00
Joachim Wiberg e349abb897 stty: allow utf8 input characters
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-30 11:26:41 +02:00
Joachim Wiberg 3027a6755f Adjust logic in finding system consoles for progress/debug output
By default, Linux gives us /dev/console for output.  This is a pseudo
device that uses the same actual device as the last console=foo listed
in /proc/cmdline.  The last one listed is the main console, which is
also the *first* one listed in /sys/class/tty/console/active, so we skip
that when we check for system consoles to avoid duplicating output.

The getty code in tty.c currently has its own handling of @console,
which we keep for now.  Ideally, however, the code should be merged.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-30 11:26:41 +02:00
Joachim Wiberg 5404aa6037 Refactor run_getty() and run_getty2() into one
By moving the built-in getty to a stand-alone bundled getty we can now
refactor the old run_getty() functions into a single one.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-30 11:26:41 +02:00
Joachim Wiberg a319e32438 Clean up prepare_tty()
When TTYs became first class citizens in Finit 4.0 much of the
boilerplate setup is now down by service_start().  Also, the calls to
TIOCSCTTY, VINTR, and SIGINT ignore is not necessary to do here, it
should be done by the getty used, if any.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-30 11:26:41 +02:00
Joachim Wiberg 2eaf57fc67 stty: simplify and ignore BREAK/SIGINT
It is up to /bin/login, sulogin, or plain shells to unblock SIGINT.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-30 11:26:41 +02:00
Joachim Wiberg 88ec4a55cb Call setsid() before redirecting stdio or unblocking signals
We must detach from the controlling TTY before enabling signals and
setting up new stdio.  TTYs have their own handling of stdio.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-30 11:26:41 +02:00
Joachim Wiberg 0acbc60ec1 initctl: drop confusing 'errno 0' from "Timed out" message
errno is not set when we timeout waiting for a reply from Finit.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-30 11:26:41 +02:00
Joachim Wiberg 4362971c6a run(): Drop extra sig_unblock()
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-30 11:26:41 +02:00
Joachim Wiberg 94c0d1b833 Refactor built-in getty into a standalone getty in /libexec
This patch moves the built-in getty out of Finit into /libexec/finit/,
reducing the size of the Finit binary and simplifying the code.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-30 11:26:41 +02:00
Joachim Wiberg 1e795d4e16 Fix #175: Ensure finit does not acquire 'dev' as controlling TTY
When starting a getty Finit checks first that the configured TTY device
actually exists and is a TTY.  This patch, by Tobias Waldekranz, ensures
Finit (PID 1) doesn't acquire the TTY device as a controlling TTY.  If
that happens PID 1 will get all signals sent to the process actually
started with the device as its controlling TTY.

Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-30 00:33:31 +02:00
Joachim Wiberg 93a027ca23 plugins: netlink: fix gcc signed vs unsigned comparsion warning
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-27 11:18:06 +02:00
Joachim Wiberg 5f3958fab4 getty: max username is 32 chars, according to useradd(1)
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-27 11:18:06 +02:00
Joachim Wiberg 1351b708fe Fix parse error for detecting external getty
When an external getty is used, and an absolute path is not given, the
parser tried to use access() to determine built-in vs external getty.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-27 11:18:06 +02:00
Joachim Wiberg e867e039bb Follow-up, document SIGINT/SIGPWR in comment, add missing include
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-27 11:18:06 +02:00
Joachim Wiberg 1d1087a3b3 Merge pull request #174 from troglobit/fix-kmsg-log-level
logging: Disregard logging facility when comparing log levels
2021-05-20 19:00:08 +02:00
Jacques de Laval e91f1ea1b2 logging: Disregard logging facility when comparing log levels
If we don't extract the level (disregarding facility), statements such as
"logit(LOG_CONSOLE | LOG_NOTICE, ...)" would never reach the kernel log.

Signed-off-by: Jacques de Laval <Jacques.De.Laval@westermo.com>
2021-05-20 17:19:53 +02:00
Joachim Wiberg 55d16edadc contrib: update /etc/finit.conf examples
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-18 09:02:58 +02:00
Joachim Wiberg b79245ddb3 Add support for sys/pwr/fail and sys/key/ctrlaltdel conditions
This patch drops the default signal handlers for SIGPWR and SIGINT with
optional tasks in /etc/finit.conf, triggered by two new conditions:
sys/pwr/fail and sys/key/ctrlaltdel, respectively.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-18 08:58:11 +02:00
Joachim Wiberg e706300b6d keventd: rename fgetln() -> fgetline()
Apparently fgetln() is available in musl libc, but not glibc.  This is a
funciton that first appeared in 4.4BSD.  We take a risk that fgetline()
is free in all libc's, getline() is not.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-17 00:44:39 +02:00
Joachim Wiberg d08cbd77aa keventd: use poll.h, not sys/poll.h, fix warning on musl libc
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-17 00:41:35 +02:00
Joachim Wiberg b070ae73a4 Revert "Travis-CI: disable clang temporarily for Coverity Scan run"
This reverts commit f52c141062.
2021-05-17 00:33:30 +02:00
Joachim Wiberg 2c30eef9a1 Update changelog for v4.1 release
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-17 00:32:43 +02:00
Joachim Wiberg 31cdc113fc doc: update build instructions and examples
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-17 00:32:21 +02:00
Joachim Wiberg f6f813498b doc: add some info about keventd, what it does
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-17 00:32:11 +02:00
Joachim Wiberg 4c8baaf7c0 Make kevent a configure option, disbled by default
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-17 00:26:57 +02:00
Joachim Wiberg 57ca937f27 New function to create onshot conds without updating
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-16 23:37:41 +02:00
Joachim Wiberg d13e39e345 keventd ftbfs: add missing include path to libite
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-16 23:35:39 +02:00
Joachim Wiberg f52c141062 Travis-CI: disable clang temporarily for Coverity Scan run
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-16 23:21:31 +02:00
Joachim Wiberg 40042da8ff contrib: debian: add anacron and cron services
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-15 10:36:05 +02:00
Joachim Wiberg c251304146 Refactor sys condition plugin into a standalone keventd
This patch is a refactor of the prototype sys condition plugin.  It
moves most of the logic to monitor kernel events into a keventd that,
currently only, sets and clears the sys/pwr/ac condition.  The sys
plugin itself is now only a monitor of conditions and ensures Finit
follows them.  This is a lot more secure and moves (at least one piece
of) netlink processing out from PID 1.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-15 10:31:08 +02:00
Joachim Wiberg c047e37a79 cgroup: ensure all services in init group remain as leaf nodes
The top-level cgroup init is a leaf group and should be treated as such,
even for user setup tasks/services.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-15 10:29:29 +02:00
Joachim Wiberg d2c57447f0 initctl: no error if failing to remove non-existing usr condition
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-15 10:29:01 +02:00
Joachim Wiberg 79e9dabc11 Fix bug: re-registering a former service as task does not work
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-15 10:28:29 +02:00
Joachim Wiberg b04dc4d457 Ensure services in plugins and from finit.c belong to a cgroup
The udevd, dbus, bundled watchdog, and others were started without a
valid cgroup.  This is a workaround to ensure they are assigned one.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-15 10:27:59 +02:00
Joachim Wiberg f86e7810be sig.c: disable kernel default ctrl-alt-delete handler
This fixes a long-standanding issue with finit not controlling the
reboot when the user presses ctrl-alt-delete (PC systems).

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-14 10:34:54 +02:00
Joachim Wiberg 2018c82ea1 plugins: sys: watch uevent to prevent feedback loop
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-13 21:20:08 +02:00
Joachim Wiberg c27f3603e6 plugins: sys: use counting semaphores to handle >1 AC supply
- Track number of ac and ac online
 - Use systemd logic to assert sys/pwr/ac also when no supply

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-13 08:45:49 +02:00
Joachim Wiberg 6f82b806ad plugins: new sys condition event monitor (wip)
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-11 07:02:49 +02:00
Joachim Wiberg cbb8935660 New functions: fnread() and fngetint(), companions to fnwrite()
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-11 07:02:30 +02:00
Joachim Wiberg 21ebbb942f plugins: bootmisc: minor, whitespace
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-09 14:38:16 +02:00
Joachim Wiberg 988ecd95e3 plugins: bootmisc: only create /run/lock if missing
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-09 14:38:16 +02:00
Joachim Wiberg 3f3e70c686 Mount /dev/pts with gid of tty group and set nosuid, noexec flags
Note, this code only runs if /dev/pts was not mounted in /etc/fstab

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-09 14:38:16 +02:00
Joachim Wiberg 073b040981 Prevent user DoS by mounting /run/lock as separate tmpfs
This patch does several things related to /run and system reliability.

 - Mount /run with MAX 10% of usable RAM
 - Create and mount /run/lock as a separate tmpfs with max 5 MiB

As a spin-off, this patch also fixes permisions on /run/lock to 0777
so regular users can create lock files.

Note: none of this code runs if /run is mounted alread in /etc/fstab

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-09 14:38:16 +02:00
Joachim Wiberg b1a058ccdd doc: update TODO a bit, remember -s for initctl
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-09 14:38:16 +02:00
Joachim Wiberg cbc7b8c3c7 plugins: bootmisc: add S02sudo setup for Debian systems
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-09 14:38:16 +02:00
Joachim Wiberg 13340e4200 contrib: debian: start tty's on tty1-tty6 clear 2-6
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-09 14:38:16 +02:00
Joachim Wiberg 0076c1f218 contrib: debian: add apparmor, avahi, and bluetooth services
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-09 14:38:16 +02:00
Joachim Wiberg 6fc71b7528 Follow-up to a1af8e8: mount /tmp with perms for all users
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-09 14:38:16 +02:00
Joachim Wiberg b42f1e99e2 README: use absolute path in sshd example, it requires that
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-07 22:29:23 +02:00
Joachim Wiberg afd6ad06a6 README: drop urls from initctl help text to sync with app
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-07 22:28:50 +02:00
Joachim Wiberg ea0bfaa2f3 Revert "Travis-CI: disable clang temporarily for Coverity Scan run"
This reverts commit 40425ee9ec.
2021-05-07 21:15:37 +02:00
Joachim Wiberg 5c3ec176c2 Update changelog, issue #173
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-07 20:19:31 +02:00
Joachim Wiberg 40425ee9ec Travis-CI: disable clang temporarily for Coverity Scan run
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-07 19:45:28 +02:00
Joachim Wiberg 424db825a3 plugins: netlink: error handling fixes
Handle EAGAIN properly, for both regular and resync flow, on any error
in the regular flow (unless ENOBUFS) we want to check for nl_ifdown on
any of the successfully parsed messages.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-07 17:02:09 +02:00
Joachim Wiberg b9cc4cd629 Revert "plugins: fix #173, increase size of netlink socket receive buffer"
This reverts commit add55cfc2a.
2021-05-07 16:50:04 +02:00
Joachim Wiberg 243b8f025b plugins: netlink: refactor and reduce recv() buffer
This patch adds support for calling recv() repeatedly to get the netlink
response from the kernel.  As a result, the recv() buffer can be reduced
down to 4k again.

Both the regular flow and the resync flow now follow the exact same code
path, except for the ENOBUFS handling.  If we get ENOBUFS in resync, we
are screwed anyway.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-07 16:42:42 +02:00
Joachim Wiberg 6c60b67588 plugins: netlink: fix resync request size alignment with kernel
For RTM_GETLINK we need a `struct ifinfomsg`, not `struct rtmsg`,
otherwise the kernel will get 8 extra bytes and complain about it.

This patch makes sure to set the correct iface change mask as well, and
increases the debug logs a bit to get a fix on sizes used.  We increase
the recv() buffer 8k -> 64k to make sure we can get all data in one big
swoop.  Plan is to refactor this mess in a later commit.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-07 15:20:25 +02:00
Joachim Wiberg 910bb13711 plugins: netlink: redesign to handle ENOBUFS with kernel resync
This is a major redesign of the netlink plugin to be able to handle
ENOBUFS¹ properly.  Pending verification, this change replaces the patch
to increase socket buffer size, which in real life turned out to be
insufficient.

When nl_callback() calls recv() and it fails with ENOBUFS, we consider
our cache of the kernel state invalid and thus:

  1. deassert all net/ conditions
  2. open a new (temporary) netlink socket
  3. send RTM_GETLINK  and re-assert all interfaces using nl_link()
  4. send RTM_GETROUTE and re-assert all routes with nl_route()

Like before, the kernel will not send us a RTM_DELROUTE when it removes
the default route, so we still have to track this ourselves.  This patch
also refactors that functionality to only resync routes when the ifindex
associated previosly with the default route goes down or is removed.

The previous change that added nl_default() to recheck, has been dropped
to instead reuse the standard nl_route() callback.
___
¹ see netlink(7) for details.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-07 13:06:47 +02:00
Joachim Wiberg add55cfc2a plugins: fix #173, increase size of netlink socket receive buffer
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-04 13:19:54 +02:00
Joachim Wiberg 565820325b Follow-up to a1af8e8: nodev is a mount flag as well
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-04 07:36:41 +02:00
Joachim Wiberg 6bb4d67d92 Follow-up to a1af8e8: fix mount options vs mount flags
The nosuid, noexec, and relatime arguments are not mount options, but flags.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-04 07:34:39 +02:00
Joachim Wiberg 534d50c03a Fix diff link
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-03 16:09:27 +02:00
Joachim Wiberg 9a42883a96 Bump version to 4.1-rc1
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-03 16:09:21 +02:00
Joachim Wiberg 7c77e2c2b6 Update with major bug fixes for 4.1
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-03 15:25:03 +02:00
Joachim Wiberg ea8c46cd30 Fix #170: check for loss of default route when interfaces go down
This patch fixes the problem with Linux not sending netlink route change
notifications when interfaces for these routes goes down.  When an iface
goes down we now send a route request to the kernel and check the return
message, if no default route is found we deassert net/default/route.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-03 15:18:25 +02:00
Joachim Wiberg fead3b0671 configure.ac: drop grub Makefile.in generation
Not needed anymore.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-03 09:34:27 +02:00
Joachim Wiberg 081990b49e Update for upcoming v4.1 bug fix release
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-03 03:50:12 +02:00
Joachim Wiberg 6b89f8c6db contrib: debian: update README with new grub setup
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-03 03:39:08 +02:00
Joachim Wiberg 373097040a contrib: debian: update install legend
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-03 03:03:30 +02:00
Joachim Wiberg a4ab75172c contrib: debian: use SUPPORTED_INITS instead of custom entry
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-03 01:53:09 +02:00
Joachim Wiberg 5cf9958957 contrib: debian: read parameters from /etc/default/grub
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-02 15:43:27 +02:00
Joachim Wiberg b137e08e84 contrib: fix install script to be able to run from tarball
The file autogen.sh is not included in tarballs.  Use configure.ac
instead to detect topdir.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-02 15:30:13 +02:00
Joachim Wiberg a1af8e8687 Follow-up to 4febd28: restore mount of /dev/shm et al for Debian
This patch restores parts of 4febd28, but in a more orderly fashion, in
order to get a standard Debian system to boot properly with X and tools.

The patch adds fs_finalize() which checks if /dev/sh/, /dev/pts, /run,
and /tmp have been mounted properly from /etc/fstab.  If not, then the
function makes sure to mount tmfps (or devpts) file systems as expected
by most modern systems.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-02 15:21:50 +02:00
Joachim Wiberg 53c7ddcb22 Change SIGUSR1 to be compatible with systemd and SysV init
This patch changes the behavior of SIGUSR1.  Previously USR1 could be
used to halt a running Finit system, similar to BusyBox init.  However,
compatibility with sysvinit and systemd has been deemed more important.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-05-02 14:48:49 +02:00
Joachim Wiberg 3872077ff1 plugins: netlink: stricter interface name validation
Coverity suggests validating against only a set of known characters.
However, the kernel allows just about all characters in an interface
name.  This version of valdiate_ifname() is blatantly stolen, more
or less, from linux/net/core/dev.c

 https://code.woboq.org/linux/linux/net/core/dev.c.html#1020

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-29 17:19:19 +02:00
Joachim Wiberg 9e85c4f3d1 Bump version for v4.0 release
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-26 01:01:20 +02:00
Joachim Wiberg 2531102623 sulogin: ensure Ctrl-D returns OK(0) also for pwd login prompt
sulogin should return OK (exit code 0) when the user presses Ctrl-D,
regardless of the prompt shown.  Otherwise Finit may look for another
sulogin to run straight after this one.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-26 00:59:01 +02:00
Joachim Wiberg 33deada6f0 README: mention new rescue mode in feature list overview
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-26 00:52:40 +02:00
Joachim Wiberg 9d1506bb7e Revert "Travis-CI: disable clang for coverity scan run"
This reverts commit e011dac98b.
2021-04-26 00:45:57 +02:00
Joachim Wiberg c3f115c9ec sulogin: fix build issue
Keyboard suddenly lost CapsLock -> Ctrl remapping ...

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-26 00:39:39 +02:00
Joachim Wiberg 430d402b14 sulogin: check for Ctrl-D also if no pwd to abort and continue boot
Signed-off-by: Joachim Wiberg <joachim.nilsson@westermo.se>
2021-04-26 00:25:24 +02:00
Joachim Wiberg e011dac98b Travis-CI: disable clang for coverity scan run
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-25 23:57:41 +02:00
Joachim Wiberg 42b84d1ccb Update ChangeLog for v4.0 GA
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-25 23:57:02 +02:00
Joachim Wiberg 288105eac7 sulogin: must fflush() prompt on Alpine Linux (musl libc)
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-25 23:14:05 +02:00
Joachim Wiberg 842b522e68 Drop --disable-logit configure option, no longer in $PATH
Simplify build system.

The logit tool is, as of v4.0, installed into /libexec/finit/logit and
thus not part of the system $PATH.  If a sysadmin or distro wants to
remove it from the system it's entirely possible since Finit always
checks for logit availability before attempting to use it.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-25 22:53:34 +02:00
Joachim Wiberg e6f36974c8 Document all facets of rescue mode; traditional and fallback
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-25 22:46:43 +02:00
Joachim Wiberg 679b4df881 Fallback rescue mode handling
Before 4e5d06b the only way to go into rescue mode was to skip certain
steps and then attempt to load /lib/finit/rescue.conf.  After 4e5d06b
we keep the old handling as a fallback in case early sulogin fails.

The new tty option 'rescue' is added, which recue.conf is updated with.
This option implies notty mode and will try sulogin (again) before it
falls back to start /bin/sh as a login shell.

The reason we keep this is to be able to handle all possible use-cases
and also allow sysadmins to set up the behavior that fits their needs.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-25 22:10:42 +02:00
Joachim Wiberg 666cae6f49 service_start(): allow notty TTYs without device to start
A notty TTY can be declared with or without a device, it requires none.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-25 22:09:07 +02:00
Joachim Wiberg be2ac1d1b1 sulogin: handle no password and locked account (treated as no pwd)
This patch does two things:

 1. A system with a root account that has no password is now
    allowed to login without password, as one expects.
 2. A systems with a locked root account, is treated as (1).

The latter of the two may very well turn out to be a really bad idea.
Much angst have been poured into it, yet the end decision is to allow
access.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-25 21:43:39 +02:00
Joachim Wiberg 266e5b30eb Drop all mention of old fallback shell from comments
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-25 21:43:16 +02:00
Joachim Wiberg 4e5d06b580 Replace experimental emergency shell with actual rescue mode
Most major init systems have a way to start a rescue shell.  This is
usally started when the string `rescue` is read from the kernel's
command line.

The traditional Finit way of handling this has been to skip certain
phases of the standard boot, skip /etc/finit.conf et al, and go for a
/lib/finit/rescue.conf instead.  This pratice has been imroved over
the last few commits with a sulogin.  However, there was no way to
resume boot, like most other init systems offer.

This patch implements a very simple rescue mode, utilizing the new
sulogin, replacing the old (and experimental) emergency shell.  If
Ctrl-D is pressed at the maintenance login prompt the boot is now
resumed.  The same goes for exit/Ctrl-D from the maintenace shell.

The preferred sulogin is the bundled /libexec/finit/sulogin, but
if that isn't installed, the first one in $PATH is used.  If no
sulogin at all is found, Finit proceeds as before this patch, to
skip certains steps and go for a /lib/finit/rescue.conf.  Hence,
al steps can be controlled by an administrator.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-25 21:32:35 +02:00
Joachim Wiberg fe334135ce Fix small memory leak, use whichp() to query, not which()
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-25 19:47:15 +02:00
Joachim Wiberg aef10fb5e7 Revert "Travis-CI: disable clang for coverity scan run"
This reverts commit 550ae9c784.
2021-04-25 19:47:04 +02:00
Joachim Wiberg 86707c1f0b sulogin: minor refactor, splice out sh() as separate function
- splice out sh() as a separate function
 - make sh() session leader and set controlling TTY (TIOCSCTTY)
   to make it a proper login shell with Ctrl-C functionality
 - simplify call to execl()

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-25 19:28:03 +02:00
Joachim Wiberg a28d5b3c09 sulogin: fix minor issue found by Coverity Scan
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-25 19:27:42 +02:00
Joachim Wiberg 550ae9c784 Travis-CI: disable clang for coverity scan run
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-24 20:45:44 +02:00
Joachim Wiberg cd5b91680a sulogin: home cooked version, can be built -static
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-24 20:43:54 +02:00
Joachim Wiberg 221ab20898 tty: add basic security, call sulogin in rescue mode (notty)
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-24 20:27:26 +02:00
Joachim Wiberg 96534789db Minor, rename LOGIT_PATH -> _PATH_LOGIT for consistency
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-24 20:26:52 +02:00
Joachim Wiberg 26128dd788 plugins: skip plugin in rescue mode
These plugins should not run in rescue mode, because the system may be
in a very bad state and we do not want to make the situation any worse
than it already is.

Essentially, only services in rescue.conf should run in rescue mode.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-24 19:46:43 +02:00
Joachim Wiberg 799e992542 tty: minor, rename local variables for consistency
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-24 10:33:58 +02:00
Joachim Wiberg fa524eec60 contrib: debian: add elogind.conf and update recommendations
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-24 10:33:57 +02:00
Joachim Wiberg 6dced29b49 Add another constraint for the built-in watchdog, device exists
- Check if watchdogd *and* `WDT_DEVNODE` exists before registering the
   built-in watchdogd at boot
 - Update bootstrap.md with this additional constraint
 - Update config.md with references to bootstrap and the new constraint

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-24 10:33:57 +02:00
Joachim Wiberg 2ea613a42b Make sure built-in watchdog is disabled by default
Omitting --without-watchdog caused the watchdog to be enabled.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-24 09:43:27 +02:00
Joachim Wiberg 985df20519 cgroup: don't warn on cgroup.events EINVAL for top-level groups
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-24 07:42:31 +02:00
Joachim Wiberg f6161b9be9 doc: Add blurb on bundled vs external watchdog, handover and reset
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-24 07:41:46 +02:00
Joachim Wiberg 25c5014d3c Also check env: file for changes to detect svc->args_dirty
This patch fixes an issue where services that support SIGHUP are not
properly stop/started on changes to their command line arguments.

A change to a service's env: file, e.g. /etc/default/foo for service
foo, must also be counted as a change to the foo args.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-23 17:12:59 +02:00
Joachim Wiberg b8b7b53d96 External plugins require exporting cgroup.h, svc.h needs it
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-20 01:48:03 +02:00
Joachim Wiberg 7707c4b8e6 initctl: only show key capture in debug mode
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-04-19 15:56:39 +02:00
92 changed files with 3453 additions and 751 deletions
+1 -1
View File
@@ -45,7 +45,7 @@ script:
- ./configure --prefix= --enable-static - ./configure --prefix= --enable-static
- make clean - make clean
- make V=1 -j5 - make V=1 -j5
- ./configure --prefix= --enable-x11-common-plugin --with-watchdog - ./configure --prefix= --enable-x11-common-plugin --with-watchdog --with-keventd
- cat config.log - cat config.log
- make clean - make clean
- make V=1 -j5 - make V=1 -j5
+144 -21
View File
@@ -4,12 +4,112 @@ Change Log
All relevant changes are documented in this file. All relevant changes are documented in this file.
[4.0][UNRELEASED] - 2021-03-xx [4.1][] - 2021-06-06
------------------------------ --------------------
Bug fix release. Also disables handlers for `SIGINT` and `SIGPWR`, a
new set of `sys` conditions are instead generated which can be used to
trigger external programs.
### Changes
* Change behavior on SIGUSR1 to be compatible with sysvinit and systemd.
Previously SIGUSR1 caused Finit to halt, like BusyBox init. This had
"interesting" side effects on Debian systems when coexisting with
sysvinit (upgrading/reinstalling causes scripts to `kill -USR1 1`)
* Change how `contrib/debian/install.sh` sets up a Grub boot entry for
finit. We now modify the $SUPPORTED_INITS variable in `10_linux`
* Disable default kernel ctrl-alt-delete handler and let Finit instead
catch `SIGINT` from kernel to be able to perform a proper reboot.
There is no default command for this, you need to set up a task that
triggers on `<sys/key/ctrlaltdel>` to issue `initctl reboot`
* Added keventd to provide `<sys/pwr/ac>` condition to Finit. keventd
is currently only responsible for monitoring `/sys/class/power_supply`
for changes to active AC mains power online status. Enable keventd
with `configure --with-keventd`
* For handling power fail events (from UPS and similar) a process may
send `SIGPWR` to PID 1. Finit no longer redirects this to `SIGUSR1`
(poweroff). There is no default command for this, you need to set up
a task that triggers on `<sys/pwr/fail>` to issue `initctl poweroff`
* Built-in Finit getty is now a standalone program
* Default termios for TTYs now enable `IUTF8` on input
* If `/bin/login` is not found, Finit now tries `sulogin` before it
falls back to an unauthenticated `/bin/sh`
* Dropped (broken) support for multiple consoles. Finit now follows
the default console selected by the kernel, `/dev/console`
* Dropped signal handlers for SIGSTOP/TSTP and SIGCONT
* Added support for `\n`, in addition to `\r`, in "Please press Enter"
prompt before starting getty
* Finit no longer parses `/proc/cmdline` for its options. Instead all
options are by default now read from `argv[]`, like a normal program,
this is also what the kernel does by default. Please note, this may
not work if your systems boots with an initramfs (ymmv), for such
cases, see `configure --enable-kernel-cmdline`
* The following plugins are now possible to disable (for containers):
`rtc.so`, `urandom.so`, you may also want to disable `hotplug.so`.
They are all enabled by default, as in Finit 4.0, but may be moved
to external tools or entries in `finit.conf` in Finit 5.0
* Added support for reading `PRETTY_NAME` from `/etc/os-release` to use
as heading in progress output, unless `--with-heading=GREET` is used.
* Added manual pages for finit(8), initctl(8), and finit.conf(5)
### Fixes
* Stricter interface name validation in netlink plugin, modeled after
the kernel. Suggested by Coverity Scan
* Fix problem of re-registering a service as a task. Previously, if a
fundamental change, like type, was made to an active service/run/task
it did not take. Only possible workaround was to remove from config
* initctl: drop warning when removing a non-existing usr condition
* initctl: drop confusing `errno 0` when timing out waiting for reply
* Ensure services in plugins and from Finit main belong to a cgroup
* Ensure init top-level cgroup remains a leaf group
* Fix tty parse error for detecting use of external getty
* Fix default `name:` and `:ID` for tty's, e.g. `ttyS0` now gives
`tty:S0` as expected. This was default for built-in getty already
* Fix max username (32 chars) in bundled Finit getty
* The `contrib/*/install.sh` scripts failed to run from tarball
* Finit no longer forcibly mounts; `/dev`, `/proc`, or `/sys`, instead
it checks first if they are already mounted (devtmpfs or container)
* Fix `/etc/fstab` parser to properly check for 'ro' to not remount the
root filesystem at boot. The wrong field was read, so a root mounted
by an initramfs, or by lxc for a container, had their root remounted
* Fix SIGCHLD handler, `waitpd()` may be interrupted by a signal
* Reset `starting` flag of services being stopped. When a service
is started and then stopped before it has created its pid file,
it could be left forever in the stopping state, unless we reset
the starting flag.
* Fix #170: detect loss of default route when interfaces go down. This
emulates the missing kernel netlink message to remove the condition
net/default/route to allow stopping dependent services
* Fix #171: restore automatic mount of `/dev/shm`, `/dev/pts`, `/run`
and `/tmp`, unless mounted already by `/etc/fstab`. This is what most
desktop systems expect PID 1 to do. Here we also make sure to mount
`/run/lock` as a tmpfs as well, with write perms for regular users,
this prevents regular users from filling up `/run` and causing DoS.
* Fix #173: netlink plugin runs out of socket buffer space;
finit[1]: nl_callback():recv(): No buffer space available
Fixed by adding support for resync with kernel on `ENOBUFS`. See
netlink(7) for details. As a spin-off the plugin now supports any
number of interfaces and routes on a system. On resync, the following
message is now logged, as a warning:
finit[1]: nl_callback():busy system, resynchronizing with kernel.
* Fix #174: loss of log messages using combo of prio and facility, e.g.,
`logit(LOG_CONSOLE | LOG_NOTICE, ...)`, by Jacques de Laval, Westermo
* Fix #175: ensure Finit does not acquire a controlling TTY when checking
if a device is a TTY before starting a getty. This fixes an old bug
where Ctrl-C after logout from a shell could cause PID 1 to get SIGINT,
which in turn could lead to a system reboot
[4.0][] - 2021-04-26
--------------------
This release became v4.0, and not v3.2, because of incompatible changes This release became v4.0, and not v3.2, because of incompatible changes
to service conditions. There are other significant changes as well, so to service conditions. There are other significant changes as well, so
make sure to read the whole changelog when upgrading. make sure to read the whole change log when upgrading.
### Changes ### Changes
* The stand-alone `reboot` tool has been replaced with a symlink to * The stand-alone `reboot` tool has been replaced with a symlink to
@@ -24,28 +124,47 @@ make sure to read the whole changelog when upgrading.
* Removed built-in inetd super server. If you need this functionality, * Removed built-in inetd super server. If you need this functionality,
use an external inetd, like xinetd, instead. A pull request for a use an external inetd, like xinetd, instead. A pull request for a
stand-alone inetd, like watchdogd and getty, is most welcome! stand-alone inetd, like watchdogd and getty, is most welcome!
* Incompatible `configure` script changes, i.e., you must give proper * Incompatible `configure` script changes, i.e., no guessing `--prefix`
path arguments to the script, no more guessing just GNU defaults. and other paths. Also, many options have been changed, renamed, or
There are examples in the documentation and the `contrib/` section flipped defaults, or even dropped altogether. There are examples in
* Change service conditions from the non-obvious `<svc/path/to/foo>` to the documentation and the `contrib/` section
`<pid/foo:id>`. This to hopefully make it clear that one service's * Service conditions change from the non-obvious `<svc/path/to/foo>` to
'pid:!foo' pidfile is another service's `<pid/foo>` condition `<pid/foo:id>`. Not only does this give simpler internal semantics,
* Initial support for cgroups, every service now runs in its own cgroup it hopefully also makes it clear that one service's `pid:!foo` pidfile
https://twitter.com/b0rk/status/1214341831049252870?s=20 is another service's `<pid/foo>` condition, issue #143
* Initial support for cgroups v2:
- services runs in a cgroup named after their respective *.conf file
- top-level groups are; init, user, and system
- all top-level groups can be configured from finit *.conf files
- each service can tweak the cgroup settings
- Use `initctl [top|ps|cgroup]` commands to inspect runtime state
- https://twitter.com/b0rk/status/1214341831049252870?s=20
* Major refactor of Finit's `main()` function to be able to start the * Major refactor of Finit's `main()` function to be able to start the
event loop earlier. This also facilitated factoring out functionality event loop earlier. This also facilitated factoring out functionality
previously hard-coded in Finit, e.g., starting the bundled watchdogd, previously hard-coded in Finit, e.g., starting the bundled watchdogd,
various distro packed udevd and other hotplugging tools various distro packed udevd and other hotplugging tools
* A proper rescue mode has been added. It is started extremely early
and is protected with a bundled `suslogin`. Exiting rescue mode now
brings up the system as a normal boot, as one expects
* Support for `sysv` start/stop scripts as well as monitoring forking * Support for `sysv` start/stop scripts as well as monitoring forking
services, stared using `sysv` or `service` stanza services, stared using `sysv` or `service` stanza
* Support for custom `kill:DELAY`, default 3 sec. * Support for custom `kill:DELAY`, default 3 sec.
* Support for custom `halt:SIGNAL`, default SIGTERM * Support for custom `halt:SIGNAL`, default SIGTERM
* Support for `pre:script` and `post:script`, allows for setup and
teardown/cleanup before and after a service runs, issue #129
* Support for `env:file` in `/etc/default/foo` or `/etc/conf.d/foo`, see
the contrib section for examples that utilize this feature. Variables
expanded from env files, and the env files themselves, are tracked for
changes to see if a service .conf file is "dirty" and needs restart on
`initctl reload`
* Support for tracking custom PID files, using `pid:!/path/to/foo.pid`, * Support for tracking custom PID files, using `pid:!/path/to/foo.pid`,
useful with new `sysv` or `service` which fork to background useful with new `sysv` or `service` which fork to background
* Support starting run/task/services without absolute path, trust `$PATH` * Support starting run/task/services without absolute path, trust `$PATH`
* Add support for `--disable-doc` and `--disable-contrib` to speed up * Add support for `--disable-doc` and `--disable-contrib` to speed up
builds and work around issue with massively parallel builds builds and work around issue with massively parallel builds
* Add support for `@console` also for external getty * Support for `@console` also for external getty
* Support for `notty` option to built-in getty, for board bring-up
* Support for `rescue` option to built-in getty, for rescue shells
* Add `-b`, batch mode, for non-interactive use to `initctl` * Add `-b`, batch mode, for non-interactive use to `initctl`
* Prefer udev to handle `/dev/` if mdev is also available * Prefer udev to handle `/dev/` if mdev is also available
* Redirect dbus daemon output to syslog * Redirect dbus daemon output to syslog
@@ -95,6 +214,8 @@ make sure to read the whole changelog when upgrading.
* Always append `:ID` qualifier to conditions if set for a service * Always append `:ID` qualifier to conditions if set for a service
* The IPC socket has moved from `/run/finit.sock` to `/run/finit/socket` * The IPC socket has moved from `/run/finit.sock` to `/run/finit/socket`
officially only supported for use by the `initctl` tool officially only supported for use by the `initctl` tool
* The IPC socket now uses `SOCK_SEQPACKET` instead of `SOCK_STREAM`.
Recommend using watchdogd v3.4, or later, which support this
* Improved support for modern `/etc/network/interfaces`, which has * Improved support for modern `/etc/network/interfaces`, which has
include statements. No more native `ifup` of individual interfaces, include statements. No more native `ifup` of individual interfaces,
Finit now calls `ifup -a`, or `ifdown -a`, delegating all details to Finit now calls `ifup -a`, or `ifdown -a`, delegating all details to
@@ -112,16 +233,21 @@ make sure to read the whole changelog when upgrading.
that do not have a watchdog that do not have a watchdog
* Fix #100: Early condition handling may not work if `/var/run` does * Fix #100: Early condition handling may not work if `/var/run` does
not yet exist (symlink to `/run`). Added compat layer for access not yet exist (symlink to `/run`). Added compat layer for access
* Fix #101: Built-in inetd removed
* Fix #102: Start built-in watchdogd as a regular service
* Fix #103: Register multiple getty if `@console` resolves to >1 TTY, * Fix #103: Register multiple getty if `@console` resolves to >1 TTY,
* Fix #105: Only remove /etc/nologin when moving from runlevel 0, 1, 6 * Fix #105: Only remove /etc/nologin when moving from runlevel 0, 1, 6
Fixed by Jonas Johansson, Westermo Fixed by Jonas Johansson, Westermo
* Fix #109: Support for PID files in sub-directories to `/var/run` * Fix #109: Support for PID files in sub-directories to `/var/run`
* Handle rename of PID files, by Robert Andersson, Atlas Copco * Handle rename of PID files, by Robert Andersson, Atlas Copco
* Fix #110: automatic modprobe of RTC devices, built-in hwclock
* Fix #120: Redirect `stdin` to `/dev/null` for services by default * Fix #120: Redirect `stdin` to `/dev/null` for services by default
* Fix #122: Switch to `nanosleep()` to achieve "signal safe" sleep, * Fix #122: Switch to `nanosleep()` to achieve "signal safe" sleep,
fixed by Jacques de Laval, Westermo fixed by Jacques de Laval, Westermo
* Fix #124: Lingering processes in process group when session leader * Fix #124: Lingering processes in process group when session leader
exits. E.g., lingering `logit` processes when parent dies exits. E.g., lingering `logit` processes when parent dies
* Fix #127: Show all runparts scripts as they start, like rc.local, fixed
by Jacques de Laval, Westermo
* Fix service name matching, e.g. for condition handling, may match with * Fix service name matching, e.g. for condition handling, may match with
wrong service, by Jonas Holmberg, Westermo wrong service, by Jonas Holmberg, Westermo
* Run all run-parts scripts using `/bin/sh -c foo` just like the standard * Run all run-parts scripts using `/bin/sh -c foo` just like the standard
@@ -140,6 +266,8 @@ make sure to read the whole changelog when upgrading.
* Provide service description for built-in watchdog daemon * Provide service description for built-in watchdog daemon
* Fix #138: Handle `SIGPWR` like `SIGSUR2`, i.e., power off the system * Fix #138: Handle `SIGPWR` like `SIGSUR2`, i.e., power off the system
* Drop the '%m' GNUism, for compat with older musl libc * Drop the '%m' GNUism, for compat with older musl libc
* Fix #139: call `tzset()` on `initctl reload` to activate system
timezone changes (for logging)
[3.1][] - 2018-01-23 [3.1][] - 2018-01-23
@@ -748,14 +876,14 @@ Major bug fix release.
* Makefile fixes for installation, paths encoded wrong * Makefile fixes for installation, paths encoded wrong
* Strip binaries + .so files, support for `$(CROSS)` toolchain strip * Strip binaries + .so files, support for `$(CROSS)` toolchain strip
* Default install is now to `/sbin/finit` and `/usr/` * Default install is now to `/sbin/finit` and `/usr/`
* Note change in `$PLUGIN_DIR` environemnt variable to `$plugindir` * Note change in `$PLUGIN_DIR` environment variable to `$plugindir`
[1.2][] - 2012-09-27 [1.2][] - 2012-09-27
-------------------- --------------------
### Changes ### Changes
* Update README with section on building and enviroment variables * Update README with section on building and environment variables
### Fixes ### Fixes
* Fix installation paths encoded in finit binary * Fix installation paths encoded in finit binary
@@ -847,7 +975,8 @@ Major bug fix release.
* Initial release * Initial release
[UNRELEASED]: https://github.com/troglobit/finit/compare/3.1...HEAD [UNRELEASED]: https://github.com/troglobit/finit/compare/4.0...HEAD
[4.1]: https://github.com/troglobit/finit/compare/4.0...4.1
[4.0]: https://github.com/troglobit/finit/compare/3.1...4.0 [4.0]: https://github.com/troglobit/finit/compare/3.1...4.0
[3.1]: https://github.com/troglobit/finit/compare/3.0...3.1 [3.1]: https://github.com/troglobit/finit/compare/3.0...3.1
[3.0]: https://github.com/troglobit/finit/compare/2.4...3.0 [3.0]: https://github.com/troglobit/finit/compare/2.4...3.0
@@ -879,9 +1008,3 @@ Major bug fix release.
[0.2]: https://github.com/troglobit/finit/compare/0.1...0.2 [0.2]: https://github.com/troglobit/finit/compare/0.1...0.2
[libuEv]: https://github.com/troglobit/libuev [libuEv]: https://github.com/troglobit/libuev
[dea3ae8]: https://github.com/troglobit/finit/commit/dea3ae8 [dea3ae8]: https://github.com/troglobit/finit/commit/dea3ae8
<!--
-- Local Variables:
-- mode: markdown
-- End:
-->
+1 -1
View File
@@ -1,5 +1,5 @@
ACLOCAL_AMFLAGS = -I m4 ACLOCAL_AMFLAGS = -I m4
SUBDIRS = plugins src test SUBDIRS = man plugins src test
doc_DATA = README.md LICENSE contrib/finit.conf doc_DATA = README.md LICENSE contrib/finit.conf
EXTRA_DIST = README.md LICENSE ChangeLog.md contrib/finit.conf EXTRA_DIST = README.md LICENSE ChangeLog.md contrib/finit.conf
+9 -13
View File
@@ -22,19 +22,19 @@
Introduction Introduction
------------ ------------
Finit is a simple alternative to [SysV init][4] and [systemd][6]. It Fast init for Linux systems. Reverse engineered from the [EeePC
was reverse engineered from the [EeePC fastinit][] over ten years ago fastinit][] over ten years ago by [Claudio Matsuoka][] — "gaps filled
by [Claudio Matsuoka][] — "gaps filled with frog DNA …" with frog DNA …"
![Alpine screenshot](img/finit4-screenshot.png) ![Alpine screenshot](img/finit4-screenshot.png)
*Figure 1: Screenshot showing Finit booting [Alpine Linux](https://www.alpinelinux.org/).* *Figure 1: Screenshot showing Finit booting [Alpine Linux](https://www.alpinelinux.org/).*
Features include: Features include:
* Runlevels * Runlevels, defined per service
* One-shot tasks, daemons, or SysV init scripts * One-shot tasks, services (daemons), or [SysV init][4] start/stop scripts
* Runparts and rc.local * Runparts and `/etc/rc.local` support
* Process supervision * Process supervision similar to [systemd][]
* Sourcing environment files * Sourcing environment files
* Conditions for network/process/custom dependencies * Conditions for network/process/custom dependencies
* Pre/Post script actions * Pre/Post script actions
@@ -44,6 +44,7 @@ Features include:
* Built-in support for Debian/BusyBox `/etc/network/interfaces` * Built-in support for Debian/BusyBox `/etc/network/interfaces`
* Cgroups v2, both configuration and monitoring in `initctl top` * Cgroups v2, both configuration and monitoring in `initctl top`
* Plugin support for customization * Plugin support for customization
* Proper rescue mode with bundled `sulogin` for protected maintenance shell
Focus is on small and embedded systems, although Finit is fully usable Focus is on small and embedded systems, although Finit is fully usable
on server and desktop systems as well. For working examples, see the on server and desktop systems as well. For working examples, see the
@@ -133,7 +134,7 @@ Some components are optional: runlevel(s), condition(s) and description,
making it easy to create simple start scripts and still possible for more making it easy to create simple start scripts and still possible for more
advanced uses as well: advanced uses as well:
service sshd -D service /usr/sbin/sshd -D
Dependencies are handled using [conditions](doc/conditions.md). One of Dependencies are handled using [conditions](doc/conditions.md). One of
the most common conditions is to wait for basic networking to become the most common conditions is to wait for basic networking to become
@@ -459,9 +460,6 @@ Commands:
halt Halt system halt Halt system
poweroff Halt and power off system poweroff Halt and power off system
suspend Suspend system suspend Suspend system
Bug report address: https://github.com/troglobit/finit/issues
Project homepage: https://troglobit.com/finit.html
``` ```
For services *not* supporting `SIGHUP` the `<!>` notation in the .conf For services *not* supporting `SIGHUP` the `<!>` notation in the .conf
@@ -583,8 +581,6 @@ and proposed extensions.
[3]: http://smarden.org/runit/ [3]: http://smarden.org/runit/
[4]: http://en.wikipedia.org/wiki/Init [4]: http://en.wikipedia.org/wiki/Init
[5]: http://en.wikipedia.org/wiki/Runlevel [5]: http://en.wikipedia.org/wiki/Runlevel
[6]: https://www.freedesktop.org/wiki/Software/systemd/
[9]: https://github.com/troglobit/troglos
[RFC862]: https://tools.ietf.org/html/rfc862 [RFC862]: https://tools.ietf.org/html/rfc862
[RFC863]: https://tools.ietf.org/html/rfc863 [RFC863]: https://tools.ietf.org/html/rfc863
[RFC864]: https://tools.ietf.org/html/rfc864 [RFC864]: https://tools.ietf.org/html/rfc864
+28 -26
View File
@@ -1,4 +1,4 @@
AC_INIT(Finit, 4.0-rc4, https://github.com/troglobit/finit/issues, finit, https://troglobit.com/finit.html) AC_INIT(Finit, 4.1, https://github.com/troglobit/finit/issues, finit, https://troglobit.com/finit.html)
AC_CONFIG_AUX_DIR(aux) AC_CONFIG_AUX_DIR(aux)
AM_INIT_AUTOMAKE([1.11 foreign dist-xz subdir-objects]) AM_INIT_AUTOMAKE([1.11 foreign dist-xz subdir-objects])
AM_SILENT_RULES([yes]) AM_SILENT_RULES([yes])
@@ -8,9 +8,10 @@ AC_CONFIG_HEADER([config.h])
AC_CONFIG_FILES([Makefile AC_CONFIG_FILES([Makefile
contrib/Makefile contrib/Makefile
contrib/alpine/Makefile contrib/alpine/finit.d/Makefile contrib/alpine/finit.d/available/Makefile contrib/alpine/Makefile contrib/alpine/finit.d/Makefile contrib/alpine/finit.d/available/Makefile
contrib/debian/Makefile contrib/debian/finit.d/Makefile contrib/debian/finit.d/available/Makefile contrib/debian/grub.d/Makefile contrib/debian/Makefile contrib/debian/finit.d/Makefile contrib/debian/finit.d/available/Makefile
contrib/void/Makefile contrib/void/finit.d/Makefile contrib/void/finit.d/available/Makefile contrib/void/Makefile contrib/void/finit.d/Makefile contrib/void/finit.d/available/Makefile
doc/Makefile doc/Makefile
man/Makefile
plugins/Makefile plugins/Makefile
src/Makefile src/Makefile
test/test.env test/test.env
@@ -48,23 +49,18 @@ PKG_CHECK_MODULES([uev], [libuev >= 2.2.0])
PKG_CHECK_MODULES([lite], [libite >= 2.2.0]) PKG_CHECK_MODULES([lite], [libite >= 2.2.0])
# Check for configured Finit features # Check for configured Finit features
AC_ARG_ENABLE(emergency_shell,
AS_HELP_STRING([--enable-emergency-shell], [Experimental emergency fallback at boot.
Use only for debugging very early boot problems with Finit]),,[
enable_emergency_shell=no])
AC_ARG_ENABLE(auto_reload, AC_ARG_ENABLE(auto_reload,
AS_HELP_STRING([--enable-auto-reload], [Automatic reload on any .conf change]),,[ AS_HELP_STRING([--enable-auto-reload], [Automatic reload on any .conf change]),,[
enable_auto_reload=no]) enable_auto_reload=no])
AC_ARG_ENABLE(kernel_cmdline,
AS_HELP_STRING([--enable-kernel-cmdline], [Parse init args from /proc/cmdline (don't use!)]),,[
enable_kernel_cmdline=no])
AC_ARG_ENABLE(redirect, AC_ARG_ENABLE(redirect,
AS_HELP_STRING([--disable-redirect], [Disable redirection of service output to /dev/null]),,[ AS_HELP_STRING([--disable-redirect], [Disable redirection of service output to /dev/null]),,[
enable_redirect=yes]) enable_redirect=yes])
AC_ARG_ENABLE(logit,
AS_HELP_STRING([--disable-logit], [Disable logit, used for log redirection, default: enable]),,[
enable_logit=yes])
AC_ARG_ENABLE(logrotate, AC_ARG_ENABLE(logrotate,
AS_HELP_STRING([--disable-logrotate], [Disable built-in rotation of /var/log/wtmp, default enabled]),,[ AS_HELP_STRING([--disable-logrotate], [Disable built-in rotation of /var/log/wtmp, default enabled]),,[
enable_logrotate=yes]) enable_logrotate=yes])
@@ -86,10 +82,12 @@ AC_PLUGIN([resolvconf], [no], [Setup necessary files for resolvconf])
AC_PLUGIN([x11-common], [no], [Console setup (for X)]) AC_PLUGIN([x11-common], [no], [Console setup (for X)])
AC_PLUGIN([netlink], [yes], [Basic netlink plugin for IFUP/IFDN and GW events. Can be replaced with externally built plugin that links with libnl or similar.]) AC_PLUGIN([netlink], [yes], [Basic netlink plugin for IFUP/IFDN and GW events. Can be replaced with externally built plugin that links with libnl or similar.])
AC_PLUGIN([hotplug], [yes], [Setup and start udev or mdev hotplug daemon]) AC_PLUGIN([hotplug], [yes], [Setup and start udev or mdev hotplug daemon])
AC_PLUGIN([rtc], [yes], [Save and restore RTC using hwclock])
AC_PLUGIN([urandom], [yes], [Setup and save random seed at boot/shutdown])
# Check for extra arguments or packages # Check for extra arguments or packages
AC_ARG_WITH(heading, AC_ARG_WITH(heading,
AS_HELP_STRING([--with-heading=GREET], [Display GREET instead of Finit version at boot]), AS_HELP_STRING([--with-heading=GREET], [Heading for boot progress. Default: PRETTY_NAME from /etc/os-release, fallback to "Finit vX.YY"]),
[heading=$withval], [heading=yes]) [heading=$withval], [heading=yes])
AC_ARG_WITH(config, AC_ARG_WITH(config,
@@ -120,26 +118,26 @@ AC_ARG_WITH(random-seed,
AS_HELP_STRING([--with-random-seed=FILE], [Save a random seed for /dev/urandom across reboots, default /var/lib/misc/random-seed]), AS_HELP_STRING([--with-random-seed=FILE], [Save a random seed for /dev/urandom across reboots, default /var/lib/misc/random-seed]),
[random_seed=$withval], [random_seed=yes]) [random_seed=$withval], [random_seed=yes])
AC_ARG_WITH(keventd,
AS_HELP_STRING([--with-keventd], [Enable built-in keventd, default: no]),, [with_keventd=no])
AC_ARG_WITH(watchdog, AC_ARG_WITH(watchdog,
AS_HELP_STRING([--with-watchdog=[DEV]], [Enable built-in watchdog, default: /dev/watchdog]), AS_HELP_STRING([--with-watchdog=[DEV]], [Enable built-in watchdog, default: /dev/watchdog]),
[watchdog=$withval],[watchdog=]) [watchdog=$withval], [with_watchdog=no watchdog=])
### Enable features ########################################################################### ### Enable features ###########################################################################
# Create config.h from selected features and fallback defaults # Create config.h from selected features and fallback defaults
AS_IF([test "x$enable_emergency_shell" = "xyes"], [
AC_DEFINE(EMERGENCY_SHELL, 1, [Fork off init and let PID 1 wait and start /bin/sh if init crashes.])])
AS_IF([test "x$enable_auto_reload" = "xyes"], [ AS_IF([test "x$enable_auto_reload" = "xyes"], [
AC_DEFINE(AUTO_RELOAD, 1, [Finit automatically does `initctl reload` on any .conf change])]) AC_DEFINE(AUTO_RELOAD, 1, [Finit automatically does `initctl reload` on any .conf change])])
AS_IF([test "x$enable_kernel_cmdline" = "xyes"], [
AC_DEFINE(KERNEL_CMDLINE, 1, [Dumpster diving after init args from /proc/cmdline])])
AS_IF([test "x$enable_redirect" = "xyes"], [ AS_IF([test "x$enable_redirect" = "xyes"], [
AC_DEFINE(REDIRECT_OUTPUT, 1, [Enable redirection of service output to /dev/null])]) AC_DEFINE(REDIRECT_OUTPUT, 1, [Enable redirection of service output to /dev/null])])
### Disable features ########################################################################### ### Disable features ###########################################################################
AS_IF([test "x$enable_logit" != "xno"], [
AC_DEFINE(LOGIT_ENABLED, 1, [Enable logit tool, used for log redirection by Finit])])
AS_IF([test "x$enable_logrotate" != "xno"], [ AS_IF([test "x$enable_logrotate" != "xno"], [
AC_DEFINE(LOGROTATE_ENABLED, 1, [Enable built-in rotation of /var/log/wtmp et al.])]) AC_DEFINE(LOGROTATE_ENABLED, 1, [Enable built-in rotation of /var/log/wtmp et al.])])
@@ -173,9 +171,8 @@ AS_IF([test "x$with_sysconfig" != "xno"], [
[System environment, default /etc/default & /etc/conf.d])])]) [System environment, default /etc/default & /etc/conf.d])])])
AS_IF([test "x$with_heading" != "xno"], [ AS_IF([test "x$with_heading" != "xno"], [
AS_IF([test "x$heading" = "xyes"], [ AS_IF([test "x$heading" = "xyes"], [heading=""])
heading="$PACKAGE_NAME v$PACKAGE_VERSION"])]) AC_DEFINE_UNQUOTED(INIT_OSHEADING, "$heading", [Displayed by Finit at boot, defaults to /etc/os-release PRETTY_NAME])])
AC_DEFINE_UNQUOTED(INIT_HEADING, "$heading", [Displayed by Finit at boot])
AS_IF([test "x$with_hostname" != "xno"], [ AS_IF([test "x$with_hostname" != "xno"], [
AS_IF([test "x$hostname" = "xyes"], [ AS_IF([test "x$hostname" = "xyes"], [
@@ -194,16 +191,19 @@ AS_IF([test "x$with_random_seed" != "xno"], [
AC_EXPAND_DIR(random_path, "$random_seed") AC_EXPAND_DIR(random_path, "$random_seed")
AC_DEFINE_UNQUOTED(RANDOMSEED, "$random_path", [Improve random at boot by seeding it with sth from before.])]) AC_DEFINE_UNQUOTED(RANDOMSEED, "$random_path", [Improve random at boot by seeding it with sth from before.])])
AS_IF([test "x$with_keventd" != "xno"], [with_keventd=yes])
AS_IF([test "x$with_watchdog" != "xno"], [ AS_IF([test "x$with_watchdog" != "xno"], [
AS_IF([test "x$watchdog" = "xyes"], [ AS_IF([test "x$watchdog" = "xyes"], [
watchdog=/dev/watchdog]) watchdog=/dev/watchdog])
with_watchdog=yes with_watchdog=yes
AC_DEFINE_UNQUOTED(WDT_DEVNODE, "$watchdog", [Built-in watchdog device node])]) AC_DEFINE_UNQUOTED(WDT_DEVNODE, "$watchdog", [Built-in watchdog device node])], [
watchdog=])
# Control build with automake flags # Control build with automake flags
AM_CONDITIONAL(STATIC, [test "x$enable_static" = "xyes"]) AM_CONDITIONAL(STATIC, [test "x$enable_static" = "xyes"])
AM_CONDITIONAL(KEVENTD, [test "x$with_keventd" != "xno"])
AM_CONDITIONAL(WATCHDOGD, [test "x$with_watchdog" != "xno"]) AM_CONDITIONAL(WATCHDOGD, [test "x$with_watchdog" != "xno"])
AM_CONDITIONAL(LOGIT, [test "x$enable_logit" = "xyes"])
AM_CONDITIONAL(DOC, [test "x$enable_doc" = "xyes"]) AM_CONDITIONAL(DOC, [test "x$enable_doc" = "xyes"])
AM_CONDITIONAL(CONTRIB, [test "x$enable_contrib" = "xyes"]) AM_CONDITIONAL(CONTRIB, [test "x$enable_contrib" = "xyes"])
@@ -222,6 +222,9 @@ AC_DEFINE_UNQUOTED(RESCUE_CONF, "$rescue_conf", [Finit rescue mode .conf])
AC_EXPAND_DIR(sample_conf, "$libdir/finit/sample.conf") AC_EXPAND_DIR(sample_conf, "$libdir/finit/sample.conf")
AC_DEFINE_UNQUOTED(SAMPLE_CONF, "$sample_conf", [Finit sameple.conf for initctl create]) AC_DEFINE_UNQUOTED(SAMPLE_CONF, "$sample_conf", [Finit sameple.conf for initctl create])
AS_IF([test "$heading" = ""], [heading="<DEFAULT>"])
AS_IF([test "$heading" = "no"], [heading="<NONE>"])
# Generate all files # Generate all files
AC_OUTPUT AC_OUTPUT
@@ -249,10 +252,9 @@ Behavior:
Optional features: Optional features:
Install doc/..........: $enable_doc Install doc/..........: $enable_doc
Install contrib/......: $enable_contrib Install contrib/......: $enable_contrib
Built-in keventd......: $with_keventd
Built-in watchdogd....: $with_watchdog $watchdog Built-in watchdogd....: $with_watchdog $watchdog
Built-in logrotate....: $enable_logrotate Built-in logrotate....: $enable_logrotate
Scripting tool logit..: $enable_logit
Emergency shell.......: $enable_emergency_shell
Redirect output.......: $enable_redirect Redirect output.......: $enable_redirect
Default hostname......: $hostname Default hostname......: $hostname
Default runlevel......: $runlevel Default runlevel......: $runlevel
+1 -1
View File
@@ -1,5 +1,5 @@
SUBDIRS = finit.d SUBDIRS = finit.d
docsdir := @docdir@/contrib/alpine docsdir := @docdir@/contrib/alpine
docs_DATA = README.md finit.conf alpine-release build.sh install.sh rc.local docs_DATA = README.md finit.conf build.sh install.sh rc.local
EXTRA_DIST = $(docs_DATA) EXTRA_DIST = $(docs_DATA)
-1
View File
@@ -1 +0,0 @@
3.6.2
+1 -1
View File
@@ -18,7 +18,7 @@ PKG_CONFIG_LIBDIR=/usr/lib/pkgconfig:/usr/local/lib/pkgconfig ./configure \
--sysconfdir=/etc --localstatedir=/var \ --sysconfdir=/etc --localstatedir=/var \
--enable-dbus-plugin --enable-x11-common-plugin \ --enable-dbus-plugin --enable-x11-common-plugin \
--enable-alsa-utils-plugin --with-watchdog \ --enable-alsa-utils-plugin --with-watchdog \
--with-heading="Alpine Linux 3.13" --with-keventd
echo echo
echo "*** Building ..." echo "*** Building ..."
+17 -1
View File
@@ -5,6 +5,22 @@
# Default is 2 # Default is 2
#runlevel 2 #runlevel 2
# Top-level cgroups and their default settings. All groups mandatory
# but more can be added, max 8 groups in total currently. The cgroup
# 'root' is also available, reserved for RT processes. Settings are
# as-is, only one shorthand 'mem.' exists, other than that it's the
# cgroup v2 controller default names.
#cgroup init cpu.weight:100
#cgroup user cpu.weight:100
#cgroup system cpu.weight:9700
# Task to run if ctrl-alt-del is pressed. This condition is asserted by
# Finit upon receiving SIGINT (from the kernel).
#task <sys/key/ctrlaltdel> [12345789] initctl reboot -- Rebooting system
# Task to run if the kernel gets a power fail condition is pressed.
# Asserted by Finit upon receiving SIGPWR (from the kernel).
#task <sys/pwr/fail> [12345789] initctl poweroff -- Shutting down system
## Bootstrap services and tasks ## Bootstrap services and tasks
# Can be listed here or in /etc/finit.d/*.conf # Can be listed here or in /etc/finit.d/*.conf
+3 -2
View File
@@ -8,9 +8,10 @@ if [ "x`id -u`" != "x0" ]; then
fi fi
# Adjust base directory # Adjust base directory
if [ -e alpine-release ]; then grep -q alpine Makefile 2>/dev/null
if [ $? -eq 0 ]; then
cd ../.. cd ../..
elif [ ! -e autogen.sh ]; then elif [ ! -e configure.ac ]; then
echo "*** Please run this script from the Finit base directory." echo "*** Please run this script from the Finit base directory."
exit 1 exit 1
fi fi
+2 -2
View File
@@ -1,4 +1,4 @@
SUBDIRS = finit.d grub.d SUBDIRS = finit.d
docsdir := @docdir@/contrib/debian docsdir := @docdir@/contrib/debian
docs_DATA = README.md finit.conf os-release build.sh install.sh docs_DATA = README.md finit.conf build.sh install.sh
EXTRA_DIST = $(docs_DATA) EXTRA_DIST = $(docs_DATA)
+20 -7
View File
@@ -20,29 +20,42 @@ tools pre-systemd work as intended.
root@debian:~# apt install initscripts console-setup root@debian:~# apt install initscripts console-setup
> **Note:** as of Debian 11 (Bullseye), libuev and libite are part of
> the main section of Debian. So just install the -dev packages :)
The following script can then be used to configure, build, install and The following script can then be used to configure, build, install and
set up your system to run Finit: set up your system to run Finit:
user@debian:~/finit$ contrib/debian/build.sh user@debian:~/finit$ contrib/debian/build.sh
However, since `/sbin/init` already exists on your system, the script However, since `/sbin/init` already exists on your system, the script
creates another entry in your GRUB config, in `/etc/grub.d/40_custom`, creates another entry in your GRUB config by updating `$SUPPORTED_INITS`
where `init=/sbin/finit` is added to your kernel line. It is of course in `/etc/grub.d/10_inux` and run `update-grub`. On reboot you will find
also possible to change the default init to Finit, if you do you can a (finit) entry in the "Advanced options for Debian GNU/Linux" section.
remove the custom Grub entry: It is of course also possible to change the default init to Finit:
user@debian:~/finit# cd /sbin user@debian:~/finit# cd /sbin
user@debian:/sbin# sudo rm init user@debian:/sbin# sudo mv init oldinit
user@debian:/sbin# sudo ln -s finit init user@debian:/sbin# sudo ln -s finit init
Before rebooting, check the default [/etc/finit.conf](finit.conf) and Before rebooting, check the default [/etc/finit.conf](finit.conf) and
`/etc/finit.d/*.conf` files. The build + install script above provides `/etc/finit.d/*.conf` files. The build + install script above provides
a few sample `.conf` files. See `initctl list` after boot for a list of a few sample `.conf` files. See `initctl ls` after boot for a list of
enabled and available services. enabled and available services, you can then use `enable` and `disable`
comands to `initctl`, followed by `reload` to activate your changes.
You can also use a standard [/etc/rc.local](rc.local) for one-shot tasks You can also use a standard [/etc/rc.local](rc.local) for one-shot tasks
and initialization like keyboard language etc. and initialization like keyboard language etc.
> **NOTE:** X Window system, you may need to `sudo apt install elogind`
> (Bullseye and later), followed by `initctl reload` to activate it (it
> is enabled by default), and logout/login again. The elogind daemon
> ensures a regular non-root user can start and interact with an X
> session, otherwise keyboard and mouse won't work. When you're happy,
> you can enable the lightdm.conf, change the default runlevel to 3, and
> presto! you have a desktop again.
Have fun! Have fun!
/Joachim ツ /Joachim ツ
+2 -3
View File
@@ -25,9 +25,8 @@ echo
--prefix=/usr --exec-prefix= \ --prefix=/usr --exec-prefix= \
--sysconfdir=/etc --localstatedir=/var \ --sysconfdir=/etc --localstatedir=/var \
--enable-dbus-plugin --enable-x11-common-plugin \ --enable-dbus-plugin --enable-x11-common-plugin \
--enable-alsa-utils-plugin \ --enable-alsa-utils-plugin --with-keventd \
--with-random-seed=/var/lib/urandom/random-seed \ --with-random-seed=/var/lib/urandom/random-seed
--with-heading="Debian GNU/Linux"
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
echo echo
+17 -1
View File
@@ -5,6 +5,22 @@
# Default is 2 # Default is 2
#runlevel 2 #runlevel 2
# Top-level cgroups and their default settings. All groups mandatory
# but more can be added, max 8 groups in total currently. The cgroup
# 'root' is also available, reserved for RT processes. Settings are
# as-is, only one shorthand 'mem.' exists, other than that it's the
# cgroup v2 controller default names.
#cgroup init cpu.weight:100
#cgroup user cpu.weight:100
#cgroup system cpu.weight:9700
# Task to run if ctrl-alt-del is pressed. This condition is asserted by
# Finit upon receiving SIGINT (from the kernel).
#task <sys/key/ctrlaltdel> [12345789] initctl reboot -- Rebooting system
# Task to run if the kernel gets a power fail condition is pressed.
# Asserted by Finit upon receiving SIGPWR (from the kernel).
#task <sys/pwr/fail> [12345789] initctl poweroff -- Shutting down system
## Bootstrap services and tasks ## Bootstrap services and tasks
# Can be listed here or in /etc/finit.d/*.conf # Can be listed here or in /etc/finit.d/*.conf
+4 -2
View File
@@ -1,7 +1,9 @@
SUBDIRS = available SUBDIRS = available
docsdir := @docdir@/contrib/debian/finit.d docsdir := @docdir@/contrib/debian/finit.d
d_LINKS = console-setup.conf getty.conf keyboard-setup.conf \ d_LINKS = acpid.conf apparmor.conf avahi-daemon.conf \
modules.conf rsyslogd.conf sshd.conf bluetooth.conf console-setup.conf elogind.conf \
getty.conf keyboard-setup.conf modules.conf \
plymouth-quit.conf rsyslogd.conf sshd.conf
install-exec-hook: install-exec-hook:
@$(MKDIR_P) $(DESTDIR)$(docsdir)/available @$(MKDIR_P) $(DESTDIR)$(docsdir)/available
+6 -3
View File
@@ -1,5 +1,8 @@
docsdir := @docdir@/contrib/debian/finit.d/available docsdir := @docdir@/contrib/debian/finit.d/available
docs_DATA = acpid.conf console-setup.conf getty.conf keyboard-setup.conf \ docs_DATA = acpid.conf apparmor.conf avahi-daemon.conf bluetooth.conf \
lightdm.conf modules.conf rsyslogd.conf sntpd.conf \ console-setup.conf elogind.conf getty.conf \
sshd.conf syslogd.conf xdm.conf keyboard-setup.conf lightdm.conf modules.conf sntpd.conf \
plymouth-quit.conf rsyslogd.conf sshd.conf syslogd.conf \
xdm.conf
EXTRA_DIST = $(docs_DATA) EXTRA_DIST = $(docs_DATA)
@@ -0,0 +1 @@
task [2345] <sys/pwr/ac,pid/syslogd> halt:usr1 env:-/etc/default/anacron anacron -d -q $ANACRON_ARGS -- Anacron jobs
@@ -0,0 +1,2 @@
sysv [S] /etc/init.d/apparmor -- AppArmor initialization
@@ -0,0 +1 @@
service [2345] env:-/etc/default/avahi-daemon avahi-daemon -s $AVAHI_DAEMON_OPTS -- Avahi mDNS/DNS-SD Stack
@@ -0,0 +1,3 @@
# To ignore errors like "org.freedesktop.hostname1", update the env file with
# "--noplugin=hostname", i.e. append to BLUETOOTHD_OPTS
service [2345] log:null env:-/etc/default/bluetooth /usr/libexec/bluetooth/bluetoothd -n $BLUETOOTHD_OPTS -- Bluetooth service
@@ -0,0 +1 @@
service [2345] <sys/pwr/ac,pid/syslogd> env:-/etc/default/cron cron -f $EXTRA_OPTS -- Background job processing daemon
@@ -0,0 +1,4 @@
# On some systems, bootstrap can be really quick and cause elogind
# to not start before a user has managed to log in. To prevent
# this, add <pid/elogind> as condition to your TTYs in getty.conf
service [2345] <pid/syslogd> /lib/elogind/elogind -- Login manager
+5 -2
View File
@@ -1,4 +1,7 @@
# Consoles to start getty on when system is up # Consoles to start getty on when system is up
tty [12345] /dev/tty1 linux noclear nowait tty [12345] /dev/tty1 linux noclear nowait
tty [2345] /dev/tty2 linux noclear nowait tty [2345] /dev/tty2 linux nowait
tty [2345] /dev/tty3 linux noclear nowait tty [2345] /dev/tty3 linux nowait
tty [2345] /dev/tty4 linux nowait
tty [2345] /dev/tty5 linux nowait
tty [2345] /dev/tty6 linux nowait
@@ -0,0 +1,2 @@
# Need to stop plymouth, or it'll block stdin on tty1
task [S] /usr/bin/plymouth quit -- Stopping Plymouth Boot Screen
-14
View File
@@ -1,14 +0,0 @@
#!/bin/sh
exec tail -n +3 $0
# This file provides an easy way to add custom menu entries. Simply type the
# menu entries you want to add after this comment. Be careful not to change
# the 'exec tail' line above.
menuentry '$MENU with Finit' --class $TYPE --class gnu-linux --class gnu --class os --user {
insmod gzio
insmod part_msdos
insmod ext2
search --no-floppy --fs-uuid --set=root $BOOT
linux /vmlinuz root=UUID=$ROOT ro quiet init=/sbin/finit
initrd /initrd.img
}
-3
View File
@@ -1,3 +0,0 @@
docsdir := @docdir@/contrib/debian/grub.d
docs_DATA = 40_custom
EXTRA_DIST = $(docs_DATA)
+15 -9
View File
@@ -8,10 +8,10 @@ if [ "x`id -u`" != "x0" ]; then
fi fi
# Adjust base directory # Adjust base directory
grep -q Debian os-release 2>/dev/null grep -q debian Makefile 2>/dev/null
if [ $? -eq 0 ]; then if [ $? -eq 0 ]; then
cd ../.. cd ../..
elif [ ! -e autogen.sh ]; then elif [ ! -e configure.ac ]; then
echo "*** Please run this script from the Finit base directory." echo "*** Please run this script from the Finit base directory."
exit 1 exit 1
fi fi
@@ -23,7 +23,7 @@ echo "/sbin/finit - PID 1"
echo "/lib/finit/plugins/* - All enabled Finit plugins" echo "/lib/finit/plugins/* - All enabled Finit plugins"
echo "/etc/finit.conf - Finit configuration file" echo "/etc/finit.conf - Finit configuration file"
echo "/etc/finit.d/ - Finit services" echo "/etc/finit.d/ - Finit services"
echo "/etc/grub.d/40_custom - Add menu entry to the Grub boot loader" echo "/etc/grub.d/10_linux - Add Finit to Grub's SUPPORTED_INITS"
echo echo
read -p "Do you want to continue (y/N)? " yorn read -p "Do you want to continue (y/N)? " yorn
echo echo
@@ -38,12 +38,18 @@ if [ "x$yorn" = "xy" -o "x$yorn" = "xY" ]; then
cp -va finit.d /etc/ cp -va finit.d /etc/
echo "*** Setting up a GRUB boot entry ..." echo "*** Setting up a GRUB boot entry ..."
MENU=`grep gnu-linux /boot/grub/grub.cfg |head -1 |sed "s/menuentry '\([^']*\).*/\1/"` fn=/etc/grub.d/10_linux
TYPE=`grep gnu-linux /boot/grub/grub.cfg |head -1 |sed "s/.*' --class \([a-z]*\).*/\1/"` if [ -e $fn ]; then
BOOT=`grep set=root /boot/grub/grub.cfg |head -1 |sed 's/.* \([a-f0-9]*-[a-f0-9]*-[a-f0-9]*-[a-f0-9]*-[a-f0-9]*\).*/\1/'` if `grep SUPPORTED_INITS $fn |head -1 |grep -q finit`; then
ROOT=`grep root=UUID /boot/grub/grub.cfg |head -1 |sed 's/.*UUID=\([a-f0-9]*-[a-f0-9]*-[a-f0-9]*-[a-f0-9]*-[a-f0-9]*\).*/\1/'` echo "Already installed, done."
cat grub.d/40_custom | sed "s,\$BOOT,$BOOT,;s,\$ROOT,$ROOT,;s,\$TYPE,$TYPE,;s,\$MENU,$MENU," >/etc/grub.d/40_custom else
update-grub echo "Adding Finit to list of SUPPORTED_INITS ..."
sed -i 's/SUPPORTED_INITS="[^"]*/& finit:\/sbin\/finit/' $fn
update-grub
fi
else
echo "Cannot find $fn, you'll have to set up your bootloader on your own."
fi
echo echo
echo "*** Done" echo "*** Done"
-8
View File
@@ -1,8 +0,0 @@
PRETTY_NAME="Debian GNU/Linux 8 (jessie)"
NAME="Debian GNU/Linux"
VERSION_ID="8"
VERSION="8 (jessie)"
ID=debian
HOME_URL="http://www.debian.org/"
SUPPORT_URL="http://www.debian.org/support"
BUG_REPORT_URL="https://bugs.debian.org/"
+8
View File
@@ -35,6 +35,14 @@ tty /dev/tty1
tty /dev/tty2 tty /dev/tty2
tty /dev/tty3 tty /dev/tty3
# Task to run if ctrl-alt-del is pressed. This condition is asserted by
# Finit upon receiving SIGINT (from the kernel).
task <sys/key/ctrlaltdel> [12345789] initctl reboot -- Rebooting system
# Task to run if the kernel gets a power fail condition is pressed.
# Asserted by Finit upon receiving SIGPWR (from the kernel).
task <sys/pwr/fail> [12345789] initctl poweroff -- Shutting down system
# Alternative method instead of runparts # Alternative method instead of runparts
#task [S] /etc/init.d/keyboard-setup start -- Setting up preliminary keymap #task [S] /etc/init.d/keyboard-setup start -- Setting up preliminary keymap
#task [S] /etc/init.d/acpid start -- Starting ACPI Daemon #task [S] /etc/init.d/acpid start -- Starting ACPI Daemon
+1 -1
View File
@@ -1,4 +1,4 @@
SUBDIRS = finit.d SUBDIRS = finit.d
docsdir := @docdir@/contrib/void docsdir := @docdir@/contrib/void
docs_DATA = README.md finit.conf build.sh install.sh rc.local xbps docs_DATA = README.md finit.conf build.sh install.sh rc.local
EXTRA_DIST = $(docs_DATA) EXTRA_DIST = $(docs_DATA)
+1 -1
View File
@@ -18,7 +18,7 @@ PKG_CONFIG_LIBDIR=/usr/lib/pkgconfig:/usr/local/lib/pkgconfig ./configure \
--sysconfdir=/etc --localstatedir=/var \ --sysconfdir=/etc --localstatedir=/var \
--enable-dbus-plugin --enable-x11-common-plugin \ --enable-dbus-plugin --enable-x11-common-plugin \
--enable-alsa-utils-plugin --with-watchdog \ --enable-alsa-utils-plugin --with-watchdog \
--with-heading="Void Linux" --with-keventd
echo echo
echo "*** Building ..." echo "*** Building ..."
+17
View File
@@ -5,5 +5,22 @@
# Default is 2 # Default is 2
#runlevel 2 #runlevel 2
# Top-level cgroups and their default settings. All groups mandatory
# but more can be added, max 8 groups in total currently. The cgroup
# 'root' is also available, reserved for RT processes. Settings are
# as-is, only one shorthand 'mem.' exists, other than that it's the
# cgroup v2 controller default names.
#cgroup init cpu.weight:100
#cgroup user cpu.weight:100
#cgroup system cpu.weight:9700
# Task to run if ctrl-alt-del is pressed. This condition is asserted by
# Finit upon receiving SIGINT (from the kernel).
#task <sys/key/ctrlaltdel> [12345789] initctl reboot -- Rebooting system
# Task to run if the kernel gets a power fail condition is pressed.
# Asserted by Finit upon receiving SIGPWR (from the kernel).
#task <sys/pwr/fail> [12345789] initctl poweroff -- Shutting down system
## Bootstrap services and tasks ## Bootstrap services and tasks
# Can be listed here or in /etc/finit.d/*.conf # Can be listed here or in /etc/finit.d/*.conf
+3 -2
View File
@@ -8,9 +8,10 @@ if [ "x`id -u`" != "x0" ]; then
fi fi
# Adjust base directory # Adjust base directory
if [ -e xbps ]; then grep -q void Makefile 2>/dev/null
if [ $? -eq 0 ]; then
cd ../.. cd ../..
elif [ ! -e autogen.sh ]; then elif [ ! -e configure.ac ]; then
echo "*** Please run this script from the Finit base directory." echo "*** Please run this script from the Finit base directory."
exit 1 exit 1
fi fi
View File
+14 -2
View File
@@ -21,17 +21,29 @@ Some of these are also registered in the issue tracker on GitHub.
* Allow running as non-pid1 => read .conf and RCSD form cmdline * Allow running as non-pid1 => read .conf and RCSD form cmdline
* Add `finit.conf` support for UPS notification (SIGPWR) to start a task * Add `finit.conf` support for UPS notification (SIGPWR) to start a task
using, e.g. <sys/power/{ok,fail,low}> conditions. More info in sig.c using, e.g. <sys/power/{ok,fail,low}> conditions. More info in sig.c
As of Finit v4.1 SIGPWR generates <sys/power/fail> condition and it
is up to a task to check why and take appropriate action.
* Add `finit.conf` support for ctrl-alt-delete (SIGINT) and kbrequest, * Add `finit.conf` support for ctrl-alt-delete (SIGINT) and kbrequest,
i.e. KeyboardSignal, (SIGWINCH) behavior. Using conditions to a task, i.e. KeyboardSignal, (SIGWINCH) behavior. Using conditions to a task,
e.g, <sys/key/ctrlaltdel> and <sys/key/signal> like SIGPWR handling. e.g, <sys/key/ctrlaltdel> and <sys/key/signal> like SIGPWR handling.
* Write man pages for finit and `finit.conf`, steal from the excellent
`pimd` man pages ... As of Finit v4.1 SIGINT generates <sys/key/ctrlaltdel>, but there is
no check if the signal actually originates from the kernel. To get
this verification, a new libuEv release is needed. As of June 6 -21
that functionality is not yet released.
Initctl
-------
* Add support for timed shutdown in Finit, including cancelled shutdown: * Add support for timed shutdown in Finit, including cancelled shutdown:
shutdown -h 03:35 "foobar was here" shutdown -h 03:35 "foobar was here"
* Add support for JSON output, or similar, from `initctl show`, e.g. * Add support for JSON output, or similar, from `initctl show`, e.g.
`initctl show --json` `initctl show --json`
* Add support for `-s` to redirect all output to syslog, for scripting
Init Init
+5 -3
View File
@@ -9,9 +9,11 @@ Bootstrap
6. Load all `.so` plugins 6. Load all `.so` plugins
7. Call `fsck` on file systems listed in `/etc/fstab` 7. Call `fsck` on file systems listed in `/etc/fstab`
8. Populate `/dev` using either udev or mdev, depending on system type 8. Populate `/dev` using either udev or mdev, depending on system type
9. Parse `/etc/finit.conf` and all `/etc/finit.d/*.conf` files 9. Parse `/etc/finit.conf`
10. Start built-in watchdog, if enabled 10. Start built-in watchdog, if enabled and `WDT_DEVNODE` exists. This
11. Set hostname means any WDT that requires a kernel module need to be either
compiled into the kernel, or insmod'ed in `/etc/finit.conf`
11. Load all `/etc/finit.d/*.conf` files and set hostname
12. Remount `/` read-write if `/` is listed in `/etc/fstab` without `ro` 12. Remount `/` read-write if `/` is listed in `/etc/fstab` without `ro`
13. Call 1st level hooks, `HOOK_ROOTFS_UP` 13. Call 1st level hooks, `HOOK_ROOTFS_UP`
14. Mount all file systems listed in `/etc/fstab` and swap, if available 14. Mount all file systems listed in `/etc/fstab` and swap, if available
+12 -8
View File
@@ -53,6 +53,10 @@ Below are a few of the main switches to configure:
built-ins (.o files) and all external libraries, except the C library built-ins (.o files) and all external libraries, except the C library
will be linked statically. will be linked statically.
* `--enable-kernel-cmdline`: Enable Finit pre-4.1 parsing of init args from
`/proc/cmdline`, this is not recommended since Finit may be running as the
init for container apps that can see the host's `/proc` filesystem
* `--enable-alsa-utils-plugin`: Enable the optional `alsa-utils.so` sound plugin. * `--enable-alsa-utils-plugin`: Enable the optional `alsa-utils.so` sound plugin.
* `--enable-dbus-plugin`: Enable the optional D-Bus `dbus.so` plugin. * `--enable-dbus-plugin`: Enable the optional D-Bus `dbus.so` plugin.
@@ -70,16 +74,16 @@ Example
First, unpack the archive: First, unpack the archive:
```shell ```shell
$ tar xf finit-3.0.tar.xz $ tar xf finit-4.1.tar.gz
$ cd finit-3.0/ $ cd finit-4.1/
``` ```
Then configure, build and install: Then configure, build and install:
```shell ```shell
$ ./configure --prefix=/usr --exec-prefix= \ $ ./configure --prefix=/usr --exec-prefix= \
--sysconfdir=/etc --localstatedir=/var \ --sysconfdir=/etc --localstatedir=/var \
--with-heading="Alpine Linux 3.13" --with-hostname=alpine --with-keventd --with-watchdog
$ make $ make
. .
. .
@@ -87,13 +91,13 @@ $ make
$ DESTDIR=/tmp/finit make install $ DESTDIR=/tmp/finit make install
``` ```
In this example the [finit-3.0.tar.xz][1] archive is unpacked to the In this example the [finit-4.1.tar.gz][1] archive is unpacked to the
user's home directory, configured, built and installed to a temporary user's home directory, configured, built and installed to a temporary
staging directory. The environment variable `DESTDIR` controls the staging directory. The environment variable `DESTDIR` controls the
destination directory when installing, very useful for building binary destination directory when installing, very useful for building binary
standalone packages. standalone packages.
Finit 3.0 and later can detect if it runs on an embedded system, or a Finit 4.1 and later can detect if it runs on an embedded system, or a
system that use BusyBox tools instead of udev & C:o. On such systems system that use BusyBox tools instead of udev & C:o. On such systems
`mdev` instead of `udev` is used. However, remember to also change the `mdev` instead of `udev` is used. However, remember to also change the
Linux config to: Linux config to:
@@ -171,6 +175,6 @@ it only for debugging start up issues when Finit crashes.
systems since they can potentially give a user root access. systems since they can potentially give a user root access.
[1]: ftp://troglobit.com/finit/finit-3.0.tar.xz [1]: ftp://troglobit.com/finit/finit-4.1.tar.gz
[libuEv]: https://github.com/troglobit/libuev [libuEv]: https://github.com/troglobit/libuev
[libite]: https://github.com/troglobit/libite [libite]: https://github.com/troglobit/libite
+9 -5
View File
@@ -6,6 +6,8 @@ passed on the Linux kernel command line. Not limited to Finit.
The `bool` setting is one of `on, off, true false, 1, 0`. The `bool` setting is one of `on, off, true false, 1, 0`.
> **NOTE:** remember to use `--` to separate kernel parameters from
> parameters to init. E.g., `init=/sbin/finit -- finit.debug rescue`
* `debug`: Enable kernel debug. Debug messages are printed to the * `debug`: Enable kernel debug. Debug messages are printed to the
console until Finit starts up. console until Finit starts up.
@@ -30,10 +32,12 @@ The `bool` setting is one of `on, off, true false, 1, 0`.
* `quiet`: Suppress kernel logging to console, except for warnings and * `quiet`: Suppress kernel logging to console, except for warnings and
errors. errors.
* `rescue`: Start Finit rescue mode; no network, no `fsck` of file * `rescue`: Start rescue/maintenance mode. If your system comes with
systems in `/etc/fstab`, no `/etc/rc.local`, no `runparts`. The the bundled `sulogin` program (Finit, or from util-linux/Busybox),
configuration is read from `/lib/finit/rescue.conf`, and can be you will be given a root login to a maintenance shell. However, if
modified by the operator. `sulogin` is missing, the file `/lib/finit/rescue.conf` is read and
the system booted in a limited fallback mode. See [config.md][]
for more information.
* `single`, or `S`: Single user mode, runlevel 1, in Finit. Useful to * `single`, or `S`: Single user mode, runlevel 1, in Finit. Useful to
debug problems with the regular runlevel. All services and TTYs in debug problems with the regular runlevel. All services and TTYs in
@@ -42,5 +46,5 @@ The `bool` setting is one of `on, off, true false, 1, 0`.
For more on kernel boot paramaters, see the man page [bootparam(7)][]. For more on kernel boot paramaters, see the man page [bootparam(7)][].
[config.md][]: config.md#rescue-mode
[bootparam(7)]: https://www.man7.org/linux/man-pages/man7/bootparam.7.html [bootparam(7)]: https://www.man7.org/linux/man-pages/man7/bootparam.7.html
+13 -6
View File
@@ -87,12 +87,15 @@ Static (one-shot) conditions, like `usr/`, never enter the `flux` state.
Built-in Conditions Built-in Conditions
------------------- -------------------
Finit comes with a `pidfile` and `netlink` plugin. When enabled, the Finit comes with a set of plugins for conditions: `pidfile`, `netlink`,
`pidfile` plugin watches `/var/run/` (recursively) for PID files created `sys`, and `usr`. The `pidfile` plugin (recursively) watches `/run/`
by the monitored services, and sets a corresponding condition in the (recursively) for PID files created by the monitored services, and sets
`pid/` namespace. Similarily, the `netlink` plugin provides basic a corresponding condition in the `pid/` namespace. Similarily, the
conditions for when an interface is brought up/down and when a default `netlink` plugin provides basic conditions for when an interface is
route (gateway) is set, in the `net/` namespace. brought up/down and when a default route (gateway) is set, in the `net/`
namespace. The `sys` and `usr` plugins monitor are passive condition
monitors where the action is provided by `keventd`, signal handlers,
and in the case of `usr`, the end-user via the `initctl` tool
With the example listed above, finit does not start the `/sbin/netd` With the example listed above, finit does not start the `/sbin/netd`
daemon until `setupd` and `zebra` has started *and* created their PID daemon until `setupd` and `zebra` has started *and* created their PID
@@ -111,6 +114,10 @@ Built-in conditions:
- `net/<IFNAME>/exist` - `net/<IFNAME>/exist`
- `net/<IFNAME>/up` - `net/<IFNAME>/up`
- `net/<IFNAME>/running` - `net/<IFNAME>/running`
- `sys/pwr/ac`
- `sys/pwr/fail`
- `sys/key/ctrlaltdel`
- `usr/foo`
**Note:** `up` means administratively up, the interface flag `IFF_UP`. **Note:** `up` means administratively up, the interface flag `IFF_UP`.
`running` is the `IFF_RUNNING` flag, meaning operatively up. The `running` is the `IFF_RUNNING` flag, meaning operatively up. The
+159 -38
View File
@@ -21,9 +21,12 @@ Configuration
* [TTYs and Consoles](#ttys-and-consoles) * [TTYs and Consoles](#ttys-and-consoles)
* [Non-privileged Services](#non-privileged-services) * [Non-privileged Services](#non-privileged-services)
* [Redirecting Output](#redirecting-output) * [Redirecting Output](#redirecting-output)
* [Rescue Mode](#rescue-mode)
* [Limitations](#limitations) * [Limitations](#limitations)
* [/etc/finit.conf](#etcfinitconf) * [/etc/finit.conf](#etcfinitconf)
* [/etc/finit.d](#etcfinitd) * [/etc/finit.d](#etcfinitd)
* [Watchdog](#watchdog)
* [keventd](#keventd)
Introduction Introduction
@@ -37,16 +40,16 @@ Linux distributions -- each package can provide its own "script" file.
- `/etc/finit.d/*.conf`: snippets, usually one service per file - `/etc/finit.d/*.conf`: snippets, usually one service per file
Not all configuration directives are available in `/etc/finit.d/*.conf` Not all configuration directives are available in `/etc/finit.d/*.conf`
and some directives are only available at bootstrap, runlevel `S`, see and some directives are only available at [bootstrap][], runlevel `S`,
the section [Limitations](#limitations) below for details. see the section [Limitations](#limitations) below for details.
To add a new service, simply drop a `.conf` file in `/etc/finit.d` and To add a new service, drop a `.conf` file in `/etc/finit.d` and run
run `initctl reload`. (It is also possible to `SIGHUP` to PID 1, or `initctl reload`. (It is also possible to `SIGHUP` to PID 1, or call
call `finit q`, but that has been deprecated with the `initctl` tool). `finit q`, but that has been deprecated with the `initctl` tool). Finit
Finit monitors all known active `.conf` files, so if you want to force monitors all known active `.conf` files, so if you want to force a
a restart of any service you can simply touch its corresponding `.conf` restart of any service you can touch its corresponding `.conf` file in
file in `/etc/finit.d` and call `initctl reload`. Finit handle any `/etc/finit.d` and call `initctl reload`. Finit handle any and all
and all conditions and dependencies between services automatically. conditions and dependencies between services automatically.
It is also possible to drop `.conf` files in `/etc/finit.d/available/` It is also possible to drop `.conf` files in `/etc/finit.d/available/`
and use `initctl enable` to enable a service `.conf` file. This may be and use `initctl enable` to enable a service `.conf` file. This may be
@@ -151,7 +154,7 @@ Top-level group configuration.
cgroup system cpu.weight:9800 cgroup system cpu.weight:9800
Adding an extra cgroup `maint/` will require you to adjust the weight of Adding an extra cgroup `maint/` will require you to adjust the weight of
the above three. We leave `init/` and `user /` as-is reducing weight of the above three. We leave `init/` and `user/` as-is reducing weight of
`system/` to 9700. `system/` to 9700.
cgroup system cpu.weight:9700 cgroup system cpu.weight:9700
@@ -181,7 +184,7 @@ The latter form also allows per-stanza limits on the form:
Notice the comma separation and the `mem.` exception to the rule: every Notice the comma separation and the `mem.` exception to the rule: every
cgroup setting maps directly to cgroup v2 syntax. I.e., `cpu.max` maps cgroup setting maps directly to cgroup v2 syntax. I.e., `cpu.max` maps
to the file `/sys/fs/cgroup/maint/foo/cpu.max`. There is not filtering, to the file `/sys/fs/cgroup/maint/foo/cpu.max`. There is no filtering,
except for expanding the shorthand `mem.` to `memory.`, if the file is except for expanding the shorthand `mem.` to `memory.`, if the file is
not available, either the cgroup controller is not available in your not available, either the cgroup controller is not available in your
Linux kernel, or the name is misspelled. Linux kernel, or the name is misspelled.
@@ -203,7 +206,7 @@ the contents of that file is used.
Deprecated. We recommend using `/etc/hostname` instead. Deprecated. We recommend using `/etc/hostname` instead.
> **Note:** only read and executed in runlevel S (bootstrap). > **Note:** only read and executed in runlevel S ([bootstrap][]).
### Kernel Modules ### Kernel Modules
@@ -221,7 +224,7 @@ BusyBox mdev tool, add to `/etc/mdev.conf`:
$MODALIAS=.* root:root 0660 @modprobe -b "$MODALIAS" $MODALIAS=.* root:root 0660 @modprobe -b "$MODALIAS"
> **Note:** only read and executed in runlevel S (bootstrap). > **Note:** only read and executed in runlevel S ([bootstrap][]).
### Networking ### Networking
@@ -234,7 +237,7 @@ Deprecated. We recommend using dedicated task/run stanzas per runlevel,
or `/etc/network/interfaces` if you have a system with `ifupdown`, like or `/etc/network/interfaces` if you have a system with `ifupdown`, like
Debian, Ubuntu, Linux Mint, or an embedded BusyBox system. Debian, Ubuntu, Linux Mint, or an embedded BusyBox system.
> **Note:** only read and executed in runlevel S (bootstrap). > **Note:** only read and executed in runlevel S ([bootstrap][]).
### Resource Limits ### Resource Limits
@@ -268,13 +271,13 @@ stanzas can share the same rlimits if they are in the same .conf.
**Syntax:** `runlevel <N>` **Syntax:** `runlevel <N>`
Defines the system runlevel to go to after bootstrap (S) has completed. The system runlevel to go to after [bootstrap][] (S) has completed. `N`
N is the runlevel number 0-9, where 6 is reserved for reboot and 0 for is the runlevel number 0-9, where 6 is reserved for reboot and 0 for
halt. halt.
Default: 2 Default: 2
> **Note:** only read and executed in runlevel S (bootstrap). > **Note:** only read and executed in runlevel S ([bootstrap][]).
### One-shot Commands (sequence) ### One-shot Commands (sequence)
@@ -426,7 +429,7 @@ SIGKILLed, this can be adjusted using the above `kill:SEC` syntax.
Call [run-parts(8)][] on `DIR` to run start scripts. All executable Call [run-parts(8)][] on `DIR` to run start scripts. All executable
files, or scripts, in the directory are called, in alphabetic order. files, or scripts, in the directory are called, in alphabetic order.
The scripts in this directory are executed at the very end of runlevel The scripts in this directory are executed at the very end of runlevel
`S`, bootstrap. `S`, [bootstrap][].
It can be beneficial to use `S01name`, `S02othername`, etc. if there It can be beneficial to use `S01name`, `S02othername`, etc. if there
is a dependency order between the scripts. Symlinks to existing is a dependency order between the scripts. Symlinks to existing
@@ -436,7 +439,9 @@ Similar to the `/etc/rc.local` shell script, make sure that all your
services and programs either terminate or start in the background or services and programs either terminate or start in the background or
you will block Finit. you will block Finit.
> **Note:** only read and executed in runlevel S (bootstrap). > **Note:** only read and executed in runlevel S ([bootstrap][]).
[run-parts(8)]: http://manpages.debian.org/cgi-bin/man.cgi?query=run-parts
### Including Finit Configs ### Including Finit Configs
@@ -464,11 +469,13 @@ size limit is reached.
### TTYs and Consoles ### TTYs and Consoles
**Syntax:** `tty [LVLS] <COND> DEV [BAUD] [noclear] [nowait] [nologin] [TERM]` **Syntax:** `tty [LVLS] <COND> DEV [BAUD] [noclear] [nowait] [nologin] [TERM]`
`tty [LVLS] <COND> CMD <ARGS> [noclear] [nowait]` `tty [LVLS] <COND> CMD <ARGS> [noclear] [nowait]`
`tty [LVLS] <COND> [notty] [rescue]`
The first variant of this option uses the built-in getty on the given The first variant of this option uses the built-in getty on the given
TTY device DEV, in the given runlevels. The DEV may be the special TTY device DEV, in the given runlevels. DEV may be the special keyword
keyword `@console`, or `console`, useful on embedded systems. `@console`, which is expanded from `/sys/class/tty/console/active`,
useful on embedded systems.
The default baud rate is 0, i.e., keep kernel default. The default baud rate is 0, i.e., keep kernel default.
@@ -483,9 +490,18 @@ The default baud rate is 0, i.e., keep kernel default.
The second `tty` syntax variant is for using an external getty, like The second `tty` syntax variant is for using an external getty, like
agetty or the BusyBox getty. agetty or the BusyBox getty.
By default both variants *clear* the TTY and *wait* for the user to The third variant is for board bringup and the `rescue` boot mode. No
press enter before starting getty. device node is required in this variant, the same output that the kernel
uses is reused for stdio. If the `rescue` option is omitted, a shell is
started (`nologin`, `noclear`, and `nowait` are implied), if the rescue
option is set the bundled `/libexec/finit/sulogin` is started to present
a bare-bones root login prompt. If the root (uid:0, gid:0) user does
not have a password set, no rescue is possible. For more information,
see the [Rescue Mode](#rescue-mode) section.
By default, the first two syntax variants *clear* the TTY and *wait* for
the user to press enter before starting getty.
**Example:** **Example:**
@@ -522,10 +538,11 @@ can be omitted to keep the kernel default.
tty [12345] @console noclear vt220 tty [12345] @console noclear vt220
On really bare bones systems Finit can give you a shell prompt as On really bare bones systems, or for board bringup, Finit can give you a
soon as bootstrap is done, without opening any device node: shell prompt as soon as [bootstrap][] is done, without opening any
device node:
tty [12345789] notty noclear tty [12345789] notty
This should of course not be enabled on production systems. Because it This should of course not be enabled on production systems. Because it
may give a user root access without having to log in. However, for may give a user root access without having to log in. However, for
@@ -539,8 +556,8 @@ One can also use the `service` stanza to start a stand-alone shell:
### Non-privileged Services ### Non-privileged Services
Every `run`, `task`, or `service` can also list the privileges the Every `run`, `task`, or `service` can also list the privileges the
`/path/to/cmd` should be executed with. Simply prefix the path with `/path/to/cmd` should be executed with. Prefix the command with
`[@USR[:GRP]]` like this: `@USR[:GRP]`, group is optional, like this:
run [2345] @joe:users logger "Hello world" run [2345] @joe:users logger "Hello world"
@@ -581,24 +598,128 @@ Log rotation is controlled using the global `log` setting.
service log:prio:user.warn,tag:ntpd /sbin/ntpd pool.ntp.org -- NTP daemon service log:prio:user.warn,tag:ntpd /sbin/ntpd pool.ntp.org -- NTP daemon
Rescue Mode
-----------
Finit supports a rescue mode which is activated by the `rescue` option
on the kernel command line. See [cmdline docs](cmdline.md) for how to
activate it.
The rescue mode comes in two flavors; *traditional* and *fallback*.
### Traditional
This is what most users expect. A very early maintenance login prompt,
served by the bundled `/libexec/finit/sulogin` program, or the standard
`sulogin` from util-linux or BusyBox is searched for in the UNIX default
`$PATH`. If a successful login is made, or the user exits (Ctrl-D), the
rescue mode is ended and the system boots up normally.
> **Note:** if the user (UID 0 and GID 0) does not have a password, or
> __the account is locked__, the user is presented with a password-less
> `"Press enter to enter maintenance mode."`, prompt which opens up a
> root shell.
### Fallback
If no `sulogin` program is found, Finit tries to bring up as much of its
own functionality as possible, yet limiting many aspects, meaning; no
network, no `fsck` of file systems in `/etc/fstab`, no `/etc/rc.local`,
no `runparts`, and most plugins are skipped (except those that provide
functionality for the condition subsystem).
Instead of reading `/etc/finit.conf` et al, system configuration is read
from `/lib/finit/rescue.conf`, which can be freely modified by the
system administrator.
The bundled default `rescue.conf` contains nothing more than:
runlevel 1
tty [12345] rescue
The `tty` has the `rescue` option set, which works similar to the board
bring-up tty option `notty`. The major difference being that `sulogin`
is started to query for root/admin password. If `sulogin` is not found,
`rescue` behaves like `notty` and gives a plain root shell prompt.
If Finit cannot find `/lib/finit/rescue.conf` it defaults to:
tty [12345] rescue
There is no way to exit the *fallback* rescue mode.
Limitations Limitations
----------- -----------
As of Finit v4 there are no limitations to where `.conf` settings can be As of Finit v4 there are no limitations to where `.conf` settings can be
placed. Except for the system/global `rlimit`, which can only be set placed. Except for the system/global `rlimit` and `cgroup` top-level
from `/etc/finit.conf`, since it is the first `.conf` file Finit reads. group declarations, which can only be set from `/etc/finit.conf`, since
it is the first `.conf` file Finit reads.
Originally, `/etc/finit.conf` was the only way to set up a Finit system. Originally, `/etc/finit.conf` was the only way to set up a Finit system.
Today it is mainly used for bootstrap settings like system hostname, Today it is mainly used for bootstrap settings like system hostname,
network bringup and system shutdown. These can now also be set in any early module loading for watchdogd, network bringup and system shutdown.
`.conf` file in `/etc/finit.d`. These can now also be set in any `.conf` file in `/etc/finit.d`.
There is, however, nothing preventing you from having all configuration There is, however, nothing preventing you from having all configuration
settings in `/etc/finit.conf`. settings in `/etc/finit.conf`.
> **Note:** The `/etc/finit.d` directory was previously the default > **Note:** The `/etc/finit.d` directory was previously the default
> Finit `runparts` directory. Finit no longer has a default > Finit [runparts](#run-parts-scripts) directory. Finit no
> `runparts`, make sure to update your setup, or the finit > longer has a default `runparts`, make sure to update your
> configuration, accordingly. > setup, or the finit configuration, accordingly.
[run-parts(8)]: http://manpages.debian.org/cgi-bin/man.cgi?query=run-parts
Watchdog
--------
When built `--with-watchdog` a separate service is built and installed
in `/libexec/finit/watchdogd`. If this exists at runtime, and the WDT
device node exists, Finit will start it and treat it as the elected
watchdog service to delegate its reboot to. See [bootstrap][] for
details. This delegation is to ensure that the system is rebooted by a
hardware watchdog timer -- on 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.
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.
An external watchdog service can also be used. The more advanced cousin
[watchdogd](https://github.com/troglobit/watchdogd/) is the recommended
option here. It can register itself with Finit using the same IPC as
`initctl`. If the bundled watchdogd is running a hand-over takes place,
so it's safe to have both services installed on a system. For the
hand-over to work it requires that the WDT driver supports the safe exit
functionality where `"V"` is written to the device before closing the
device descriptor. If the kernel driver has been built without this,
the only option is to remove `/libexec/finit/watchdogd` or build without
it at configure time.
[bootstrap]: bootstrap.md
keventd
-------
The kernel event daemon bundled with Finit is a simple uevent monitor
for `/sys/class/power_supply`. It provides the `sys/pwr/ac` condition,
which can be useful to prevent power hungry services like anacron to run
when a laptop is only running on battery, for instance.
Since keventd is not an integral part of Finit yet it is not enabled by
default. Enable it using `./configuure --with-keventd`. The bundled
contrib build scripts for Debian, Alpine, and Void have this enabled.
This daemon is planned to be extended with monitoring of other uevents,
patches and ideas are welcome in the issue tracker.
+17 -9
View File
@@ -12,28 +12,36 @@ have been adopted:
Same effect as `finit q`, `init q`, or `initctl reload`, reloads all Same effect as `finit q`, `init q`, or `initctl reload`, reloads all
*.conf files in `/etc/finit.d/` *.conf files in `/etc/finit.d/`
This also restarts the API (initctl) socket, like SysV init and systemd
does on USR1 with their FIFO/D-Bus.
`SIGUSR1` `SIGUSR1`
--------- ---------
Calls shutdown hooks, including `HOOK_SHUTDOWN`, stopping all running Since Finit 4.1 this signal causes Finit to restart its API (initctl)
processes, and unmounts all file systems. Then tells kernel to halt. socket, like SysV init and systemd does on USR1 with their FIFO/D-Bus.
Most people these days want `SIGUSR2` though.
Finit <= 4.0 performed a system halt (like USR2 without power-off), but
this caused compatibility problems with systemd and sysvinit on desktop
systems. Hence, since Finit 4.1 it is no longer possible to halt a
system with a signal.
SysV init and systemd use this to re-open their FIFO/D-Bus.
`SIGUSR2` `SIGUSR2`
--------- ---------
Like SIGUSR1, but tell kernel to power-off the system, if ACPI or Calls shutdown hooks, including `HOOK_SHUTDOWN`, stopping all running
similar exists to actually do this. If the kernel fails power-off, processes, and unmounts all file systems. Then tells kernel to power
Finit falls back to halt. off the system, if ACPI or similar exists to actually do this. If the
kernel fails power-off, Finit falls back to halt.
SysV init N/A, systemd dumps its internal state to log. SysV init N/A, systemd dumps its internal state to log.
`SIGTERM` `SIGTERM`
--------- ---------
Like `SIGUSR1`, but tell kernel to reboot the system when done. Like `SIGUSR2`, but tell kernel to reboot the system when done.
SysV init N/A, systemd rexecutes itself. SysV init N/A, systemd rexecutes itself.
+2
View File
@@ -0,0 +1,2 @@
Makefile
Makefile.in
+2
View File
@@ -0,0 +1,2 @@
dist_man8_MANS = finit.8 initctl.8
dist_man5_MANS = finit.conf.5
+178
View File
@@ -0,0 +1,178 @@
.\" Hey, EMACS: -*- nroff -*-
.\" First parameter, NAME, should be all caps
.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
.\" other parameters are allowed: see man(7), man(1)
.Dd June 6, 2021
.\" Please adjust this date whenever revising the manpage.
.Dt FINIT 8 SMM
.Os Linux
.Sh NAME
.Nm finit
.Nd Fast init for Linux systems
.Sh SYNOPSIS
.Nm /sbin/finit
.Op finit.status_style=classic
.Op finit.show_status=[on,off]
.Op finit.debug
.Op rescue | recover
.Op single | S
.Nm /sbin/telinit
.Op Fl hv
.Op q | Q | 0-9
.Sh DESCRIPTION
Fast init for Linux systems. Reverse engineered from the EeePC
fastinit, 10+ years ago by Claudio Matsuoka, "gaps filled with frog
DNA ...". Focus is on small and embedded systems, although Finit is
fully usable on server and desktop systems as well.
.Pp
Features include:
.Bl -bullet -width 1n -compact
.It
Runlevels, defined per service
.It
One-shot tasks, services (daemons), or SysV init start/stop scripts
.It
Runparts and
.Pa /etc/rc.local
support
.It
Process supervision similar to
.Xr systemd 8
.It
Sourcing environment files
.It
Conditions for network/process/custom dependencies
.It
Pre/Post script actions
.It
Tooling to enable/disable services
.It
Built-in getty
.It
Built-in watchdog, with support for hand-over to
.Lk https://troglobit.com/watchdogd.html watchdogd
.It
Built-in support for Debian/BusyBox
.Pa /etc/network/interfaces
.It
Cgroups v2, both configuration and monitoring in `initctl top`
.It
Plugin support for customization
.It
Proper rescue mode with bundled `sulogin` for protected maintenance shell
.El
.Sh BOOTING
.Pp
Before laucnhing services, the system needs to be bootstrapped. This
involves mounting all filesystems (not already mounted by an initramfs)
in
.Pa /etc/fstab ,
ensuring that necessary filesystems like:
.Pa /dev ,
.Pa /proc ,
.Pa /sys ,
and
.Pa /run
are available and can provide the system with the services required.
.Pp
With that out of the way, a set of plugins take over:
.Pp
.Bl -tag -width bootmisc.so -offset indent
.It Cm bootmisc.so
Cleans up stale files from previous boots and default directories are
created in tmpfs filesystems
.It Cm modprobe.so
Probes kernel modules required by the system
.It Cm procps.so
Set kernel variables from
.Pa /etc/sysctl.conf
et al
.El
.Pp
When the plugins are done, finit reads
.Pa /etc/finit.conf
and all other enabled services in
.Pa /etc/finit.d/* .
First all services, run/tasks in runlevel S (bootStrap) are started,
When all run/tasks have completed, Finit changes to the configured
runlevel (default 2), Processes are started in parallel, in dependency
order according to their conditions.
.Pp
When all services and run/tasks have been started, the console progress
is disabled and all configured getty services are started.
.Sh CONDITIONS
The condition subysystem in Finit was initially created to track PID
files of daemons. If service/task B requires service A to have started,
Finit waits for the PID file of service A to be created/updated before
it starts service B. Conversely, if service A is stopped and its PID
file removed, service B is also stopped.
.Pp
The following condition families are availble today:
.Pp
.Bl -tag -width pid -offset indent
.It Cm net
Linux netlink events, e.g. net/route/default, net/eth0/up, and
net/et0/running
.It Cm pid
PID files basd on the service declaration
.Cm name:id ,
gives the condition pid/name:id
.It Cm sys
System conditions, e.g. sys/key/ctrlaltdel and sys/pwr/fail
.It Cm usr
User conditions, assert and deassert using
.Cm initctl cond [set|clr] foo
.El
.Sh SIGNALS
PID 1 is the only process that by default blocks all signals. Finit
allows for the following signals to control operation, but recommends
instead using the
.Xr initctl 8
tool.
.Pp
.Bl -tag -width TERM
.It HUP
Tell
.Nm
to reload its configuration file(s), same as
.Cm initctl reload
.It USR1
Restart API
.Cm ( initctl )
socket.
.It USR2
Tell
.Nm
to perform a system shutdown, same as
.Cm initctl poweroff
.It TERM
Like SIGUSR2 but performs a reboot, same as
.Cm initctl reboot
.It INT
Sent from kernel when Ctrl-Alt-Del is pressed (on the console), this
asserts the sys/key/ctrlaltdel condition, which can be used to start
tasks or services
.It PWR
Sent from a a power daemon on changes to UPS status, Finit asserts
the sys/pwr/fail condition
.El
.Sh FILES
.Bl -tag -width /etc/finit.d/available/*.conf -compact
.It Pa /etc/finit.conf
Main configuration file
.It Pa /etc/finit.d/*.conf
Static service definitions
.It Pa /etc/finit.d/available/*.conf
Available services
.It Pa /etc/finit.d/enabled/*.conf
Symlinks from available
.It Pa /run/finit/
Runtime files, including the condition subsystem
.El
.Sh SEE ALSO
.Xr finit.conf 5
.Xr initctl 8
.Sh AUTHORS
.Nm
was conceived and reverse engineered by Claudio Matsuoka. Since v1.0,
maintained by Joachim Wiberg, with contributions by many others.
+814
View File
@@ -0,0 +1,814 @@
.\" Hey, EMACS: -*- nroff -*-
.\" First parameter, NAME, should be all caps
.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
.\" other parameters are allowed: see man(7), man(1)
.Dd Oct 17, 2020
.Dt FINIT.CONF 5 SMM
.Os Linux
.Sh NAME
.Nm finit.conf
.Nd Finit configuration file format
.Sh SYNOPSIS
.Nm /etc/finit.conf
.Nm /etc/finit.d/*.conf
.Nm /etc/finit.d/available/*.conf
.Sh DESCRIPTION
.Nm finit
can be configured using only the original
.Pa /etc/finit.conf
file or in combination with
.Pa /etc/finit.d/*.conf .
Useful for package-based Linux distributions \(em each package can
provide its own "script" file.
.Pp
.Bl -tag -width /etc/finit.d/*.conf -offset indent -compact
.It Pa /etc/finit.conf
Main configuration file
.It Pa /etc/finit.d/*.conf
Snippets, usually one service per file
.El
.Pp
Not all configuration directives are available in
.Pa /etc/finit.d/*.conf
and some directives are only available at bootstrap, runlevel
.Cm S ,
see the section
.Sx Limitations
below for details.
.Sh FILES
.Bl -tag -width /etc/finit.d/available/*.conf -compact
.It Pa /etc/finit.conf
Main configuration file
.It Pa /etc/finit.d/*.conf
Static service definitions
.It Pa /etc/finit.d/available/*.conf
Available services
.It Pa /etc/finit.d/enabled/*.conf
Enabled services, symlinks from available
.El
.Sh FILE FORMAT
Lines starting with `#' are ignored. Note that end-of-line comments are
NOT supported, comments must be on a line of their own.
.Pp
A configuration stanza must be on a single (long) line and
.Em cannot
be broken up in multiple lines by use of a backslash as the last
character.
.Sh DIRECTIVES
This section lists all supported configuration directives. There also exist
deprecated directives, see the Markdown documentation for details on these.
.Pp
.Bl -tag -width 1n
.It Cm rlimit Oo hard|soft Oc Ar RESOURCE Aq LIMIT | unlimited
.Pp
Set the hard or soft limit for a resource, or both if that argument is
omitted.
.Ar RESOURCE
is the lower-case
.Cm RLIMIT_
string constants from
.Xr setrlimit 2 ,
without prefix. E.g. to set
.Cm RLIMIT_CPU ,
use
.Cm cpu .
.Pp
.Ar LIMIT
is an integer that depends on the resource being modified, see
the man page, or the kernel
.Pa /proc/PID/limits
file, for details.
Finit versions before v3.1 used
.Cm infinity
for
.Cm unlimited ,
which is still supported, albeit deprecated.
.Bd -unfilled -offset indent
# No process is allowed more than 8MB of address space
rlimit hard as 8388608
# Core dumps may be arbitrarily large
rlimit soft core infinity
# CPU limit for all services, soft & hard = 10 sec
rlimit cpu 10
.Ed
.Pp
.Cm rlimit
can be set globally, in
.Pa /etc/finit.conf ,
or locally per each
.Pa /etc/finit.d/*.conf
read. I.e., a set of task/run/service stanzas can share the same
rlimits if they are in the same .conf.
.It Cm runlevel Aq N
The system runlevel to go to after bootstrap (S) has completed.
.Cm N
is the runlevel number 0-9, where 6 is reserved for reboot and 0 for
halt. All other can be used by operating system administrators.
Default: 2
.Pp
.Sy Note:
only read and executed in runlevel S (bootstrap).
.It Cm run Oo LVLS Oc Ao COND Ac Ar /path/to/cmd ARGS Op -- Optional description
One-shot command to run in sequence when entering a runlevel, with
optional arguments and description.
.Pp
.Cm run
commands are guaranteed to be completed before running the next
command. Highly useful if true serialization is needed. Usually
only used in the bootstrap (S) runlevel.
.Pp
.Cm Aq COND
conditions are described in
.Xr finit 8 ,
see also the
.Sx Examples
section below.
.It Cm task Oo LVLS Oc Ao COND Ac Ar /path/to/cmd ARGS Op -- Optional description
One-shot like
.Cm run ,
but starts in parallel with the next command.
.Pp
Both
.Cm run
and
.Cm task
commands are run in a shell, so pipes and redirects can be freely used:
.Bd -unfilled -offset indent
task [s] echo "foo" | cat >/tmp/bar
.Ed
.It Cm sysv Oo LVLS Oc Ao COND Ac Ar /path/to/script ARGS Op -- Optional description
Similar to
.Cm task
is the
.Cm sysv
stanza, which can be used to call SysV style start/stop scripts. The
primary intention for this command is to be able to re-use much of
existing setup and init scripts in Linux distributions.
.Pp
When entering an allowed runlevel, Finit calls
.Cm init-script start ,
when entering a disallowed runlevel, Finit calls
.Cm init-script stop ,
and if the Finit .conf, where the
.Cm sysv
stanza is declared, is modified, Finit calls
.Cm init-script restart
on
.Cm initctl reload .
Similar to how
.Cm service
stanzas work.
.Pp
Forking services started with
.Cm sysv
scripts can be monitored by Finit by declaring the PID file to look for:
.Bd -unfilled -offset indent
sysv pid:!/path/to/pidfile.pid /path/to/script ...
.Ed
.Pp
The leading '!' is to prevent Finit from managing the PID file, which is
the default behavior for the
.Cm pid:
command modifier.
.It Cm service Oo LVLS Oc Ao COND Ac Ar /path/to/daemon ARGS Op -- Optional description
Service, or daemon, to be monitored and automatically restarted if it
exits prematurely. Finit tries to restart services that die 10 times
before giving up, then you have to restart it manually:
.Cm initctl restart NAME
.Pp
For daemons that support it, we recommend appending
.Cm --foreground , --no-background , -n , -F ,
or similar command line argument to prevent them from forking off a
sub-process in the background. This is the most reliable way to monitor
a service.
.Pp
However, not all daemons support running in the foreground, or they may
start logging to the foreground as well, these are called forking
services and are supported using the same syntax as forking
.Cm sysv
services, using the
.Cm pid:!/path/to/pidfile.pid
command modifier syntax.
.Pp
.Sy Example: in the case of
.Cm ospfd
(below), we omit the
.Cm -d
flag (daemonize) to prevent it from forking to the background:
.Bd -unfilled -offset indent
service [2345] <pid/zebra> /sbin/ospfd -- OSPF daemon
.Ed
.Cm [2345]
denote the runlevels
.Cm ospfd
is allowed to run in, they are optional and default to runlevel 2-5 if
omitted.
.Pp
.Cm <pid/zebra>
is the condition for starting
.Cm ospfd .
In this example Finit waits for another service,
.Cm zebra ,
to have created its PID file in
.Pa /var/run/quagga/zebra.pid
before starting
.Cm ospfd .
Finit watches *all* files in
.Pa /var/run ,
for each file named
.Cm *.pid ,
.Cm */pid ,
Finit opens it and find the matching
.Cm NAME:ID
using the PID.
.Pp
Some services do not maintain a PID file and rather than patching each
application Finit provides a workaround. A
.Cm pid
modifier keyword can be set to have Finit automatically create (when
starting) and later remove (when stopping) the PID file. The file is
created in the
.Pa /var/run
directory using the
.Xr basename 3
of the service. The full syntax of the
.Cm pid
modifier is:
.Bd -unfilled -offset indent
pid[:[!][/path/to/]filename[.pid]]
.Ed
.Pp
For example, by adding
.Cm pid:/run/foo.pid
to the service
.Cm /sbin/bar ,
that PID file will, not only be created and removed automatically, but
also be used by the Finit condition subsystem. So a service/run/task
can depend on the
.Cm <pid/bar>
condition.
.Pp
If a service should not be automatically started, it can be configured
as manual with the
.Cm manual:yes
command modifier. The service can then be started at any time by
running
.Cm initctl start NAME
.Pp
The name of a service, shown by the
.Cm initctl
tool, defaults to the basename of the service executable. It can be
changed with the
.Cm name:foo
command modifier.
.Pp
When stopping a service (run/task/sysv/service), either manually or when
moving to another runlevel, Finit starts by sending SIGTERM, to allow
the process to shut down gracefully. If the process has not been
collected within 3 seconds, Finit sends SIGKILL. To halt the process
using a different signal, use the command modifier
.Cm halt:SIGNAL ,
e.g.,
.Cm halt:SIGPWR .
To change the delay between your halt signal and KILL, use the command
modifier
.Cm kill:SEC ,
e.g.,
.Cm kill:10
to wait 10 seconds before sending SIGKILL.
.Pp
Services support the
.Cm pre:script
and
.Cm post:script
command actions as well. These run as the same
.Cm @USER:GROUP
as the service itself, with any
.Cm env:file
sourced. The scripts must use an absolute path, but are executed from
the
.Cm $HOME
of the given user. The scripts are not called with any argument
(currently), but both get the
.Cm SERVICE_IDENT=foo
environment variable set. Here
.Cm foo
denotes the identity of the service, which if there are multiple
services named
.Cm foo ,
may be
.Cm foo:1 ,
or any unique identifier specified in the .conf file. The
.cm post:script
is called with an additional set of environment variables:
.Bl -tag -offset indent -width 1n
.It Cm EXIT_CODE=[exited,signal]
set to one of
.Cm exited
or
.Cm signal
.It Cm EXIT_STATUS=[num,SIGNAME]
set to one of exit status code from the program, if it exited normally,
or the signal name (HUP, TERM, etc.) if it exited due to signal
.El
.Pp
The scripts have a default execution time of 3 seconds before they are
SIGKILLed, this can be adjusted using the
.Cm kill:SEC
modifier syntax.
.Pp
.Sy Note:
the
.Cm pre:script
.Em must
be idempotent, because a service can transition between READY and HALTED
states any number of times before going to RUNNING.
.It Cm runparts Aq DIR
Call
.Xr run-parts 8
on
.Cm DIR
to run start scripts. All executable files, or scripts, in the
directory are called, in alphabetic order. The scripts in this
directory are executed at the very end of bootstrap, runlevel S.
.Pp
It can be beneficial to use
.Cm S01name ,
.Cm S02othername ,
etc. if there is a dependency order between the scripts. Symlinks to
existing daemons can talso be used, but make sure they daemonize by
default.
.Pp
Similar to the
.Pa /etc/rc.local
shell script, make sure that all your services and programs either
terminate or start in the background or you will block Finit.
.Sy Note:
only read and executed in runlevel S (bootstrap).
.It Cm include Aq CONF
Include another configuration file. Absolute path required.
.It Cm log size:BYTES count:NUM
Log rotation for run/task/services using the
.Cm log
command modifier with redirection to a log file. Global setting,
applies to all services.
.Pp
The size can be given as bytes, without a specifier, or in `k`, `M`,
or `G`, e.g.
.Cm size:10M ,
or
.Cm size:3G .
A value of
.Cm size:0
disables log rotation. The default is
.Cm size:200k .
.Pp
The count value is recommended to be between 1-5, with a default 5.
Setting count to 0 means the logfile will be truncated when the MAX
size limit is reached.
.It Cm tty Oo LVLS Oc Ao COND Ac Ar DEV Oo BAUD Oc Oo noclear Oc Oo nowait Oc Oo nologin Oc Oo TERM Oc
This form of the
.Cm tty
stanza uses the built-in getty on the given TTY device
.Ar DEV ,
in the given runlevels.
.Ar DEV may be the special keyword
.Cm @console ,
or `console`, which is expanded from `/sys/class/tty/console/active`,
useful on embedded systems.
.Pp
The default baud rate is 0, i.e., keep kernel default.
.Pp
The `tty` stanza inherits runlevel, condition (and other feature)
parsing from the `service` stanza. So TTYs can run in one or many
runlevels and depend on any condition supported by Finit. This is
useful e.g. to depend on `<pid/elogind>` before starting a TTY.
.Bd -unfilled -offset indent
tty [12345] /dev/ttyAMA0 115200 noclear vt220
.Ed
.It Cm tty Oo LVLS Oc Ao COND Ac Ar CMD DEV Oo noclear Oc Oo nowait Oc
This form of the
.Cm tty
stanza is for using an external getty, like agetty or the BusyBox getty.
.Pp
By default, these first two syntax variants
.Em clear
the TTY and
.Em wait
for the user to press enter before starting getty.
.Bd -unfilled -offset indent
tty [12345] /sbin/getty -L 115200 /dev/ttyAMA0 vt100
tty [12345] /sbin/agetty -L ttyAMA0 115200 vt100 nowait
.Ed
.Pp
The
.Cm noclear
option disables clearing the TTY after each session. Clearing the TTY
when a user logs out is usually preferable.
.Pp
The
.Cm nowait
option disables the
.Cm Please press Enter to activate console
message before actually starting the getty program. On small and
embedded systems running multiple unused getty wastes both memory and
CPU cycles, so `wait` is the preferred default.
.Pp
The
.Cm nologin
option disables getty and
.Pa /bin/login ,
and gives the user a root (login) shell on the given TTY
.Cm DEV
immediately. Needless to say, this is a rather insecure option, but can
be very useful for developer builds, during board bringup, or similar.
.Pp
Notice the ordering, the
.Cm TERM
option to the built-in getty must be the last argument.
.Pp
Embedded systems may want to enable automatic `DEV` by supplying the
special
.Cm @console
device. This works regardless weather the system uses
.Cm ttyS0 , ttyAMA0 , ttyMXC0 ,
or anything else. Finit figures it out by querying sysfs:
.Pa /sys/class/tty/console/active .
The speed can be omitted to keep the kernel default.
.Pp
Most systems get by fine by just using `console`, which will evaluate
to
.Pa /dev/console .
If you have to use
.Cm @console
to get any output, you may have some issue with your kernel config.
.Bd -unfilled -offset indent
tty [12345] @console noclear vt220
.Ed
.Pp
On really bare bones systems, or for board bringup, Finit can give you a
shell prompt as soon as bootstrap is done, without opening any device
node:
.Bd -unfilled -offset indent
tty [12345789] notty
.Ed
.Pp
This should of course not be enabled on production systems. Because it
may give a user root access without having to log in. However, for
board bringup and system debugging it can come in handy.
.Pp
One can also use the
.Cm service
stanza to start a stand-alone shell:
.Bd -unfilled -offset indent
service [12345] /bin/sh -l
.Ed
.Pp
.It Cm tty Oo LVLS Oc Ao COND Ac Oo notty Oc Oo rescue Oc
The third
.Cm tty
form is for board bringup and the
.Cm rescue
boot mode. No device node is required in this variant, the same output
that the kernel uses is reused for stdio. If the
.Cm rescue
option is omitted, a shell is started. The flags
.Cm nologin , noclear ,
and
.Cm nowait
are implied. If the
.Cm rescue
option is set the bundled
.Pa /libexec/finit/sulogin
is started to present a bare-bones root login prompt. If the root
(uid:0, gid:0) user does not have a password set, no rescue is possible.
.El
.Sh COMMAND MODIFIERS
The run/task/tty/service/sysv stanzas take modifiers, or options, to
control their behavior. This section lists them with their limitations.
All modifiers must be placed between the stanza and its command.
.Bl -tag -width 1n
.It Cm @user:group
Every
.Cm run , task ,
or
.Cm service
can also list the privileges the
.Cm /path/to/cmd
should be executed with. Prefix the command with
.Cm @USR[:GRP] ,
group is optional, like this:
.Bd -unfilled -offset indent
run [2345] @joe:users logger "Hello world"
.Ed
.Pp
For multiple instances of the same command, e.g. a DHCP client or
multiple web servers, add
.Cm :ID
somewhere between the
.Cm run , task , service
keyword and the command, like this:
.Bd -unfilled -offset indent
service :80 [2345] httpd -f -h /http -p 80 -- Web server
service :8080[2345] httpd -f -h /http -p 8080 -- Old web server
.Ed
.Pp
Without the
.Cm :ID
to the service the latter will overwrite the former and only the old web
server would be started and supervised.
.It Cm log:/path/to/file
Redirect stdout/stderr of a command to the given log file. See the
global log directive, above, for details on log rotation.
.It Cm log:console
Redirect stdout/stderr of a command to
.Pa /dev/console ,
only use this for debugging or bringup.
.It Cm log:null
Redirect stdout/stderr of a command to
.Pa /dev/null .
.It Cm log:prio:facility.level,tag:ident
Redirect stdout/stderr of a command to syslog using the given priority
and tag identity.
.Bd -unfilled -offset indent
service log:prio:user.warn,tag:ntpd /sbin/ntpd pool.ntp.org -- NTP daemon
.Ed
.It Cm log
Default
.Cm prio
is
.Cm daemon.info
and the default
.Cm tag
identity is the basename of the service or run/task command.
.El
.Sh RESCUE MODE
Finit supports a rescue mode which is activated by the
.Cm rescue
option on the kernel command line. The rescue mode comes in two
flavors:
.Em traditional
and
.Em fallback .
.Ss Traditional
This is what most users expect. A very early maintenance login prompt,
served by the bundled
.Pa /libexec/finit/sulogin
program, or the standard
.Cm sulogin
from util-linux or BusyBox is searched for in the UNIX default
.Cm $PATH .
If a successful login is made, or the user exits (Ctrl-D), the rescue
mode is ended and the system boots up normally.
.Pp
.Sy Note:
if the user (UID 0 and GID 0) does not have a password, or
.Em the account is locked ,
the user is presented with a password-less prompt:
.Cm "Press enter to enter maintenance mode." ,
which opens up a root shell.
.Ss Fallback
If no
.Cm sulogin
program is found, Finit tries to bring up as much of its own
functionality as possible, yet limiting many aspects, meaning; no
network, no`fsck` of file systems in
.Pa /etc/fstab ,
no
.Pa /etc/rc.local ,
no
.Cm runparts ,
and most plugins are skipped (except those that provide functionality
for the condition subsystem).
.Pp
Instead of reading
.Pa /etc/finit.conf
et al, system configuration is read from
.Pa /lib/finit/rescue.conf ,
which can be freely modified by the system administrator.
.Pp
The bundled default `rescue.conf` contains nothing more than:
.Bd -unfilled -offset indent
runlevel 1
tty [12345] rescue
.Ed
.Pp
The
.Cm tty
has the
.Cm rescue
option set, which works similar to the board bring-up tty option
.Cm notty .
The major difference being that `sulogin` is started to query for
root/admin password. If
.Cm sulogin
is not found,
.Cm rescue
behaves like
.Cm notty
and gives a plain root shell prompt.
.Pp
If Finit cannot find
.Pa /lib/finit/rescue.conf
it defaults to:
.Bd -unfilled -offset indent
tty [12345] rescue
.Ed
.Pp
There is no way to exit the
.Em fallback
rescue mode.
.Sh SERVICE ENVIRONMENT
Finit supports sourcing environment variables from
.Pa /etc/default/* ,
or similar. This is a common pattern from SysV init scripts, where the
start/stop script is a generic script for the given service,
.Cm foo ,
and the options for the service are sourced from the file
.Pa /etc/default/foo .
Like this:
.Bd -unfilled -offset indent
/etc/default/foo:
FOO_OPTIONS=--extra-arg="bar" -s -x
/etc/finit.conf:
service [2345] env:-/etc/default/foo foo -n $FOO_OPTIONS -- Example foo daemon
.Ed
.Pp
Here the service
.Cm foo
is started with
.Op Fl -n ,
to make sure it runs in the foreground, and the with the options found
in the environment file. With the
.Cm ps
command we can see that the process is started with:
.Bd -unfilled -offset indent
foo -n --extra-arg=bar -s -x
.Ed
.Pp
.Sy Note:
the leading `-` determines if Finit should treat a missing environment
file as blocking the start of the service or not. When `-` is used, a
missing environment file does
.Em not
block the start.
.Sh SERVICE WRAPPER SCRIPTS
If your service requires to run additional commands, executed before the
service is actually started, like the systemd `ExecStartPre`, you can
use a wrapper shell script to start your service.
.Pp
The Finit service
.Cm .conf
file can be put into
.Pa /etc/finit.d/available ,
so you can control the service using
.Cm initctl .
Then use the path to the wrapper script in the Finit
.Cm .conf
service stanza. The following example employs a wrapper script in
.Pa /etc/start.d .
.Bd -unfilled
/etc/finit.d/available/program.conf:
service [235] <!> /etc/start.d/program -- Example Program
/etc/start.d/program:
#!/bin/sh
# Prepare the command line options
OPTIONS="-u $(cat /etc/username)"
# Execute the program
exec /usr/bin/program $OPTIONS
.Ed
.Pp
.Sy Note:
the example sets
.Cm <!>
to denote that it doesn't support SIGHUP. That way Finit will
stop/start the service instead of sending SIGHUP at restart/reload
events.
.Sh CGROUPS
There are three major cgroup configuration directives:
.Pp
.Bl -enum -offset indent -compact
.It
Global top-level group: init, system, user, or a custom group
.It
Selecting a top-level group for a set of run/task/services
.It
Per run/task/service limits
.El
.Pp
Top-level group configuration.
.Bd -unfilled -offset indent
# Top-level cgroups and their default settings. All groups mandatory
# but more can be added, max 8 groups in total currently. The cgroup
# 'root' is also available, reserved for RT processes. Settings are
# as-is, only one shorthand 'mem.' exists, other than that it's the
# cgroup v2 controller default names.
cgroup init cpu.weight:100
cgroup user cpu.weight:100
cgroup system cpu.weight:9800
.Ed
.Pp
Adding an extra cgroup
.Cm maint/
will require you to adjust the weight of the above three. We leave
.Cm init/
and
.Cm user/
as-is reducing weight of
.Cm system/
to 9700.
.Bd -unfilled -offset indent
cgroup system cpu.weight:9700
# Example extra cgroup 'maint'
cgroup maint cpu.weight:100
.Ed
.Pp
By default, the
.Cm system/
cgroup is selected for almost everything. The
.Cm init/
cgroup is reserved for PID 1 itself and its closest relatives. The
.Cm user/
cgroup is for local TTY logins spawned by getty.
.Pp
To select a different top-level cgroup, e.g.
.Cm maint/ ,
one can either define it for a group of run/task/service directives in a
.Cm .conf
or per each stanza:
.Bd -unfilled -offset indent
cgroup.maint
service [...] <...> /path/to/foo args -- description
service [...] <...> /path/to/bar args -- description
.Ed
.Pp
or
.Bd -unfilled -offset indent
service [...] <...> cgroup.maint /path/to/foo args -- description
.Ed
.Pp
The latter form also allows per-stanza limits on the form:
.Bd -unfilled -offset indent
service [...] <...> cgroup.maint:cpu.max:10000,mem.max:655360 /path/to/foo args -- description
.Ed
.Pp
Notice the comma separation and the
.Cm mem.
exception to the rule: every cgroup setting maps directly to cgroup v2
syntax. I.e.,
.Cm cpu.max
maps to the file
.Pm /sys/fs/cgroup/maint/foo/cpu.max .
There is no filtering, except for expanding the shorthand
.Cm mem.
to
.Cm memory. ,
if the file is not available, either the cgroup controller is not
available in your Linux kernel, or the name is misspelled.
.Pp
Linux cgroups and details surrounding values are not explained in the
Finit documentation. The Linux admin-guide cover this well:
.Lk https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html
.Sh LIMITATIONS
As of Finit v4 there are no limitations to where
.Cm .conf
settings can be placed. Except for the system/global
.Cm rlimit
and
.Cm cgroup
top-level group declarations, which can only be set from
.Pa /etc/finit.conf ,
since it is the first
.Cm .conf
file Finit reads.
.Pp
Originally,
.Pp /etc/finit.conf
was the only way to set up a Finit system. Today it is mainly used for
bootstrap settings like system hostname, early module loading for
watchdogd, network bringup and system shutdown. These can now also be
set in any
.Cm .conf
file in
.Pa /etc/finit.d .
.Pp
There is, however, nothing preventing you from having all configuration
settings in
.Pa /etc/finit.conf .
.Sh SEE ALSO
.Xr finit 8 ,
.Xr initctl 8
.Sh AUTHORS
.Nm finit
was conceived and reverse engineered by Claudio Matsuoka. Since v1.0,
maintained by Joachim Wiberg, with contributions by many others.
+175
View File
@@ -0,0 +1,175 @@
.\" Hey, EMACS: -*- nroff -*-
.\" First parameter, NAME, should be all caps
.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
.\" other parameters are allowed: see man(7), man(1)
.Dd June 6, 2021
.\" Please adjust this date whenever revising the manpage.
.Dt INITCTL 8 SMM
.Os Linux
.Sh NAME
.Nm initctl
.Nd Control tool for Finit
.Sh SYNOPSIS
.Nm /sbin/initctl
.Op Fl bcfhlpqtv
.Op COMMAND
.Sh DESCRIPTION
.Nm
is the official tool for interacting with
.Xr finit 8 .
It comes with functions for showing status, querying health of services,
as well as editing, enabling and disabling services.
.Sh OPTIONS
This program follows the usual UNIX command line syntax, with long
options starting with two dashes (`-'). The options are as follows:
.Bl -tag -width Ds
.It Fl b, -batch
Batch mode, no screen size probing.
.It Fl c, -create
Create missing paths (and files) as needed. Useful with the edit command.
.It Fl f, -force
Ignore missing files and arguments, never prompt.
.It Fl h, -help
Show built-in help text.
.It Fl 1, -once
Only one lap in commands like top.
.It Fl p, -plain
Use plain table headings, no ANSI control characters.
.It Fl q, -quiet
Silent, only return status of command.
.It Fl t, -no-heading
Skip table headings.
.It Fl v, -verbose
Verbose output, where applicable.
.El
.Sh COMMANDS
.Bl -tag -width Ds
.It Nm Ar debug
Toggle finit (daemon) debug to
.Pa /dev/console
.It Nm Ar help
Show built-in help text
.It Nm Ar version
Show Finit (daemon) version
.It Nm Ar ls | list
List all
.Cm *.conf
files in
.Pa /etc/finit.d
.It Nm Ar create Ar CONF
Create
.Cm .conf
in
.Pa /etc/finit.d/available
.It Nm Ar delete Ar CONF
Delete
.Cm .conf
in
.Pa /etc/finit.d/available
.It Nm Ar show Ar CONF
Show
.Cm .conf
in
.Pa /etc/finit.d/available
.It Nm Ar edit Ar CONF
Edit
.Cm .conf
in
.Pa /etc/finit.d/available
.It Nm Ar touch Ar CONF
Change (mark as modified)
.Cm .conf
in
.Pa /etc/finit.d/available ,
like
.Ar edit
this tells
.Nm finit
that a configuration has been changed and is a candidate to be restarted
(or SIGHUP'ed) on
.Cm initctl reload
.It Nm Ar enable Ar CONF
Enable
.Cm .conf
in
.Pa /etc/finit.d/available ,
i.e., add symlink in
.Pa /etc/finit.d/enabled
.It Nm Ar disable Ar CONF
Disable
.Cm .conf
in
.Pa /etc/finit.d/enabled ,
i.e., removes symlink
.It Nm Ar reload
Reload
.Cm *.conf in
.Pa /etc/finit.d ,
i.e., activates changes
.It Nm Ar cond set Ar COND
Set (assert) user-defined condition,
.Cm +usr/COND
.It Nm Ar cond clr | clear Ar COND
Clear (deassert) user-defined condition,
.Cm -usr/COND
.It Nm Ar cond status
Show condition status, default
.Cm cond
command.
.It Nm Ar cond dump
Dump all conditions and their status
.It Nm Ar log Op Cm NAME
Show ten last Finit, or
.Cm NAME ,
messages from syslog
.It Nm Ar start Cm NAME[:ID]
Start service by name, with optional ID, e.g.,
.Cm initctl start tty:1
.It Nm Ar stop Cm NAME[:ID]
Stop/Pause a running service by name
.It Nm Ar reload Cm NAME[:ID]
Reload service by name (SIGHUP or restart)
.It Nm Ar restart Cm NAME[:ID]
Restart (stop/start) service by name
.It Nm Ar status Cm NAME[:ID]
Show service status, by name
.It Nm Ar status
Show status of all services, default command
.It Nm Ar cgroup
List cgroup config overview
.It Nm Ar ps
List processes based on cgroups
.It Nm Ar top
Show top-like listing based on cgroups
.It Nm Ar runlevel Op Ar 0-9
Show or set runlevel: 0 halt, 6 reboot
.It Nm Ar reboot
Reboot system, default if
.Cm reboot
is symlinked to
.Nm
.It Nm Ar halt
Halt system, default if
.Cm halt
is symlinked to
.Nm
.It Nm Ar poweroff
Power-off system, default if
.Cm poweroff
is symlinked to
.Nm
.It Nm Ar suspend
Suspend system, default if
.Cm suspend
is symlinked to
.Nm
.It Nm Ar utmp show
Raw dump of UTMP/WTMP db
.El
.Sh SEE ALSO
.Xr finit.conf 5
.Xr finit 8
.Sh AUTHORS
.Nm finit
was conceived and reverse engineered by Claudio Matsuoka. Since v1.0,
maintained by Joachim Wiberg, with contributions by many others.
+22 -2
View File
@@ -7,7 +7,7 @@ AM_CPPFLAGS += $(lite_CFLAGS)
if STATIC if STATIC
noinst_LTLIBRARIES = libplug.la noinst_LTLIBRARIES = libplug.la
libplug_la_SOURCES = bootmisc.c modprobe.c rtc.c pidfile.c procps.c tty.c urandom.c usr.c libplug_la_SOURCES = bootmisc.c modprobe.c pidfile.c procps.c sys.c tty.c usr.c
if BUILD_ALSA_UTILS_PLUGIN if BUILD_ALSA_UTILS_PLUGIN
libplug_la_SOURCES += alsa-utils.c libplug_la_SOURCES += alsa-utils.c
@@ -33,8 +33,20 @@ if BUILD_RESOLVCONF_PLUGIN
libplug_la_SOURCES += resolvconf.c libplug_la_SOURCES += resolvconf.c
endif endif
if BUILD_RTC_PLUGIN
libplug_la_SOURCES += rtc.c
endif
if BUILD_URANDOM_PLUGIN
libplug_la_SOURCES += urandom.c
endif
if BUILD_X11_COMMON_PLUGIN
libplug_la_SOURCES += x11-common.c
endif
else else
pkglib_LTLIBRARIES = bootmisc.la modprobe.la rtc.la pidfile.la procps.la tty.la urandom.la usr.la pkglib_LTLIBRARIES = bootmisc.la modprobe.la pidfile.la procps.la sys.la tty.la usr.la
if BUILD_ALSA_UTILS_PLUGIN if BUILD_ALSA_UTILS_PLUGIN
pkglib_LTLIBRARIES += alsa-utils.la pkglib_LTLIBRARIES += alsa-utils.la
@@ -60,6 +72,14 @@ if BUILD_RESOLVCONF_PLUGIN
pkglib_LTLIBRARIES += resolvconf.la pkglib_LTLIBRARIES += resolvconf.la
endif endif
if BUILD_RTC_PLUGIN
pkglib_LTLIBRARIES += rtc.la
endif
if BUILD_URANDOM_PLUGIN
pkglib_LTLIBRARIES += urandom.la
endif
if BUILD_X11_COMMON_PLUGIN if BUILD_X11_COMMON_PLUGIN
pkglib_LTLIBRARIES += x11-common.la pkglib_LTLIBRARIES += x11-common.la
endif endif
+10
View File
@@ -30,6 +30,11 @@
static void save(void *arg) static void save(void *arg)
{ {
if (rescue) {
_d("Skipping %s plugin in rescue mode.", __FILE__);
return;
}
if (whichp(ALSACTL)) { if (whichp(ALSACTL)) {
_d("Saving sound settings ..."); _d("Saving sound settings ...");
run_interactive(ALSACTL " -g store", "Saving sound settings"); run_interactive(ALSACTL " -g store", "Saving sound settings");
@@ -38,6 +43,11 @@ static void save(void *arg)
static void restore(void *arg) static void restore(void *arg)
{ {
if (rescue) {
_d("Skipping %s plugin in rescue mode.", __FILE__);
return;
}
if (whichp(ALSACTL)) { if (whichp(ALSACTL)) {
_d("Restoring sound settings ..."); _d("Restoring sound settings ...");
run_interactive(ALSACTL " -g restore", "Restoring sound settings"); run_interactive(ALSACTL " -g restore", "Restoring sound settings");
+12 -5
View File
@@ -119,15 +119,16 @@ static void setup(void *arg)
makedir("/var/lib/urandom",0755); makedir("/var/lib/urandom",0755);
if (fisdir("/run")) { if (fisdir("/run")) {
_d("System with new /run tmpfs ..."); _d("System with new /run tmpfs ...");
makedir("/run/lock", 1777); if (!fisdir("/run/lock"))
makedir("/run/lock", 1777);
symlink("/run/lock", "/var/lock"); symlink("/run/lock", "/var/lock");
symlink("/dev/shm", "/run/shm"); symlink("/dev/shm", "/run/shm");
/* compat only, should really be set up by OS/dist */ /* compat only, should really be set up by OS/dist */
symlink("/run", "/var/run"); symlink("/run", "/var/run");
} else { } else {
makedir("/var/lock", 1777); makedir("/var/lock", 1777);
makedir("/var/run", 0755); makedir("/var/run", 0755);
} }
makedir("/var/log", 0755); makedir("/var/log", 0755);
makedir("/var/mail", 0755); makedir("/var/mail", 0755);
@@ -183,6 +184,12 @@ static void setup(void *arg)
/* Void Linux has a uuidd that runs as uuid:uuid and needs /run/uuid */ /* Void Linux has a uuidd that runs as uuid:uuid and needs /run/uuid */
mksubsys("/var/run/uuidd", 0755, "uuidd", "uuidd"); mksubsys("/var/run/uuidd", 0755, "uuidd", "uuidd");
/* Debian has /run/sudo, ensure correct perms and SELinux label */
mksubsys("/var/run/sudo", 0711, "root", "root");
mksubsys("/var/run/sudo/ts", 0700, "root", "root");
if (whichp("restorecon"))
run("restorecon /var/run/sudo /var/run/sudo/ts");
umask(prev); umask(prev);
} }
+6 -1
View File
@@ -43,6 +43,11 @@ static void setup(void *arg)
mode_t prev; mode_t prev;
char *cmd; char *cmd;
if (rescue) {
_d("Skipping %s plugin in rescue mode.", __FILE__);
return;
}
cmd = which(DAEMON); cmd = which(DAEMON);
if (!cmd) { if (!cmd) {
_d("Skipping plugin, %s is not installed.", DAEMON); _d("Skipping plugin, %s is not installed.", DAEMON);
@@ -59,7 +64,7 @@ static void setup(void *arg)
remove("/var/run/dbus/pid"); remove("/var/run/dbus/pid");
/* Register service with Finit */ /* Register service with Finit */
snprintf(line, sizeof(line), "[S12345789] @%s:%s %s %s -- %s", snprintf(line, sizeof(line), "[S12345789] cgroup.system @%s:%s %s %s -- %s",
DAEMONUSER, DAEMONUSER, cmd, ARGS, DESC); DAEMONUSER, DAEMONUSER, cmd, ARGS, DESC);
if (service_register(SVC_TYPE_SERVICE, line, global_rlimit, NULL)) if (service_register(SVC_TYPE_SERVICE, line, global_rlimit, NULL))
_pe("Failed registering %s", DAEMON); _pe("Failed registering %s", DAEMON);
+8 -3
View File
@@ -34,6 +34,11 @@ static void setup(void *arg)
char cmd[256]; char cmd[256];
char *path; char *path;
if (rescue) {
_d("Skipping %s plugin in rescue mode.", __FILE__);
return;
}
/* /*
* Populate /dev and prepare for runtime events from kernel. * Populate /dev and prepare for runtime events from kernel.
* Prefer udev if mdev is also available on the system. * Prefer udev if mdev is also available on the system.
@@ -43,17 +48,17 @@ static void setup(void *arg)
path = which("/lib/systemd/systemd-udevd"); path = which("/lib/systemd/systemd-udevd");
if (path) { if (path) {
/* Register udevd as a monitored service */ /* Register udevd as a monitored service */
snprintf(cmd, sizeof(cmd), "[S12345789] pid:udevd name:udevd log %s " snprintf(cmd, sizeof(cmd), "[S12345789] cgroup.system pid:udevd name:udevd log %s "
"-- Device event managing daemon", path); "-- Device event managing daemon", path);
if (service_register(SVC_TYPE_SERVICE, cmd, global_rlimit, NULL)) { if (service_register(SVC_TYPE_SERVICE, cmd, global_rlimit, NULL)) {
_pe("Failed registering %s", path); _pe("Failed registering %s", path);
} else { } else {
snprintf(cmd, sizeof(cmd), ":1 [S] <pid/udevd> log " snprintf(cmd, sizeof(cmd), "cgroup.init :1 [S] <pid/udevd> log "
"udevadm trigger -c add -t devices " "udevadm trigger -c add -t devices "
"-- Requesting device events"); "-- Requesting device events");
service_register(SVC_TYPE_RUN, cmd, global_rlimit, NULL); service_register(SVC_TYPE_RUN, cmd, global_rlimit, NULL);
snprintf(cmd, sizeof(cmd), ":2 [S] <pid/udevd> log " snprintf(cmd, sizeof(cmd), "cgroup.init :2 [S] <pid/udevd> log "
"udevadm trigger -c add -t subsystems " "udevadm trigger -c add -t subsystems "
"-- Requesting subsystem events"); "-- Requesting subsystem events");
service_register(SVC_TYPE_RUN, cmd, global_rlimit, NULL); service_register(SVC_TYPE_RUN, cmd, global_rlimit, NULL);
+2 -3
View File
@@ -36,7 +36,7 @@
#define MODULES_LOAD_PATH "/etc/modules-load.d" #define MODULES_LOAD_PATH "/etc/modules-load.d"
#endif #endif
#define SERVICE_LINE \ #define SERVICE_LINE \
":%d name:modprobe.%s [2345] /sbin/modprobe %s %s -- Kernel module: %s" "cgroup.init :%d name:modprobe.%s [2345] /sbin/modprobe %s %s -- Kernel module: %s"
static void load(void *arg) static void load(void *arg)
{ {
@@ -71,8 +71,7 @@ static void load(void *arg)
mod = strtok_r(mod, " ", &args); mod = strtok_r(mod, " ", &args);
snprintf(cmd, sizeof(cmd), SERVICE_LINE, snprintf(cmd, sizeof(cmd), SERVICE_LINE, index, mod, mod, args, mod);
index, mod, mod, args, mod);
service_register(SVC_TYPE_TASK, cmd, global_rlimit, NULL); service_register(SVC_TYPE_TASK, cmd, global_rlimit, NULL);
index++; index++;
+228 -42
View File
@@ -30,31 +30,35 @@
#include "cond.h" #include "cond.h"
#include "helpers.h" #include "helpers.h"
#include "plugin.h" #include "plugin.h"
#include "service.h"
static int nlmsg_validate(struct nlmsghdr *nh, size_t len) #define NL_BUFSZ 4096
{
if (!NLMSG_OK(nh, len))
return 1;
if (nh->nlmsg_type == NLMSG_DONE) { struct nl_request {
_d("Done with netlink messages."); struct nlmsghdr nh;
return 1; union {
} struct rtmsg rtm;
struct ifinfomsg ifi;
};
};
if (nh->nlmsg_type == NLMSG_ERROR) { static int nl_defidx;
_d("Netlink reports error."); static int nl_ifdown;
return 1; static char *nl_buf;
}
return 0;
}
static void nl_route(struct nlmsghdr *nlmsg, ssize_t len) static void nl_route(struct nlmsghdr *nlmsg, ssize_t len)
{ {
char daddr[INET_ADDRSTRLEN];
char gaddr[INET_ADDRSTRLEN];
struct in_addr ind, ing;
struct rtmsg *r; struct rtmsg *r;
struct rtattr *a; struct rtattr *a;
int plen = 0;
int dst = 0;
int idx = 0;
int gw = 0;
int la; int la;
int gw = 0, dst = 0, mask = 0, idx = 0;
if (nlmsg->nlmsg_len < NLMSG_LENGTH(sizeof(struct rtmsg))) { if (nlmsg->nlmsg_len < NLMSG_LENGTH(sizeof(struct rtmsg))) {
_e("Packet too small or truncated!"); _e("Packet too small or truncated!");
@@ -80,8 +84,8 @@ static void nl_route(struct nlmsghdr *nlmsg, ssize_t len)
case RTA_DST: case RTA_DST:
dst = *((int *)data); dst = *((int *)data);
mask = r->rtm_dst_len; plen = r->rtm_dst_len;
//_d("MASK: 0x%04x", mask); //_d("Prefix LEN: 0x%04x", plen);
break; break;
case RTA_OIF: case RTA_OIF:
@@ -93,11 +97,20 @@ static void nl_route(struct nlmsghdr *nlmsg, ssize_t len)
a = RTA_NEXT(a, la); a = RTA_NEXT(a, la);
} }
if ((!dst && !mask) && (gw || idx)) { ind.s_addr = dst;
if (nlmsg->nlmsg_type == RTM_DELROUTE) ing.s_addr = gw;
inet_ntop(AF_INET, &ind, daddr, sizeof(daddr));
inet_ntop(AF_INET, &ing, gaddr, sizeof(gaddr));
_d("Got gw %s dst/len %s/%d ifindex %d", gaddr, daddr, plen, idx);
if ((!dst && !plen) && (gw || idx)) {
if (nlmsg->nlmsg_type == RTM_DELROUTE) {
cond_clear("net/route/default"); cond_clear("net/route/default");
else nl_defidx = 0;
} else {
cond_set("net/route/default"); cond_set("net/route/default");
nl_defidx = idx;
}
} }
} }
@@ -117,14 +130,14 @@ static int validate_ifname(const char *ifname)
if (!ifname || !ifname[0]) if (!ifname || !ifname[0])
return 1; return 1;
if (strlen(ifname) >= IFNAMSIZ) if (strnlen(ifname, IFNAMSIZ) == IFNAMSIZ)
return 1; return 1;
if (strstr(ifname, "..")) if (!strcmp(ifname, ".") || !strcmp(ifname, ".."))
return 1; return 1;
while (*ifname) { while (*ifname) {
if (*ifname == '/' || isspace(*ifname)) if (*ifname == '/' || *ifname == ':' || isspace(*ifname))
return 1; return 1;
ifname++; ifname++;
} }
@@ -132,12 +145,25 @@ static int validate_ifname(const char *ifname)
return 0; return 0;
} }
/*
* Check if this interface was associated with the default route
* previously, or if it's been removed. If so, trigger a recheck
* of the system default route.
*/
static void nl_check_default(char *ifname)
{
int idx = (int)if_nametoindex(ifname);
if ((nl_defidx > 0 && nl_defidx == idx) || (idx == 0 && errno == ENODEV))
nl_ifdown = 1;
}
static void nl_link(struct nlmsghdr *nlmsg, ssize_t len) static void nl_link(struct nlmsghdr *nlmsg, ssize_t len)
{ {
int la;
char ifname[IFNAMSIZ + 1]; char ifname[IFNAMSIZ + 1];
struct rtattr *a;
struct ifinfomsg *i; struct ifinfomsg *i;
struct rtattr *a;
int la;
if (nlmsg->nlmsg_len < NLMSG_LENGTH(sizeof(struct ifinfomsg))) { if (nlmsg->nlmsg_len < NLMSG_LENGTH(sizeof(struct ifinfomsg))) {
_e("Packet too small or truncated!"); _e("Packet too small or truncated!");
@@ -172,6 +198,8 @@ static void nl_link(struct nlmsghdr *nlmsg, ssize_t len)
net_cond_set(ifname, "exist", 1); net_cond_set(ifname, "exist", 1);
net_cond_set(ifname, "up", i->ifi_flags & IFF_UP); net_cond_set(ifname, "up", i->ifi_flags & IFF_UP);
net_cond_set(ifname, "running", i->ifi_flags & IFF_RUNNING); net_cond_set(ifname, "running", i->ifi_flags & IFF_RUNNING);
if (!(i->ifi_flags & IFF_UP) || !(i->ifi_flags & IFF_RUNNING))
nl_check_default(ifname);
break; break;
case RTM_DELLINK: case RTM_DELLINK:
@@ -180,6 +208,7 @@ static void nl_link(struct nlmsghdr *nlmsg, ssize_t len)
net_cond_set(ifname, "exist", 0); net_cond_set(ifname, "exist", 0);
net_cond_set(ifname, "up", 0); net_cond_set(ifname, "up", 0);
net_cond_set(ifname, "running", 0); net_cond_set(ifname, "running", 0);
nl_check_default(ifname);
break; break;
case RTM_NEWADDR: case RTM_NEWADDR:
@@ -197,26 +226,176 @@ static void nl_link(struct nlmsghdr *nlmsg, ssize_t len)
} }
} }
static void nl_callback(void *arg, int sd, int events) static int nl_parse(int sd)
{ {
static char buf[4096]; while (1) {
struct nlmsghdr *nh; struct nlmsghdr *nh;
ssize_t len; ssize_t len;
size_t l;
memset(buf, 0, sizeof(buf)); while ((len = recv(sd, nl_buf, NL_BUFSZ, 0)) < 0) {
len = recv(sd, buf, sizeof(buf), 0); switch (errno) {
if (len < 0) { case EAGAIN: /* Nothing more right now. */
if (errno != EINTR) /* Signal */ return 0;
_pe("recv()");
case EINTR: /* Signal */
continue;
case ENOBUFS: /* netlink(7) */
break;
default:
_pe("recv()");
break;
}
return -1;
}
_d("recv %lld bytes", len);
l = (size_t)len;
for (nh = (struct nlmsghdr *)nl_buf; NLMSG_OK(nh, l); nh = NLMSG_NEXT(nh, l)) {
struct nlmsgerr *nle;
switch (nh->nlmsg_type) {
case NLMSG_DONE:
_d("Done with netlink messages.");
return 0;
case NLMSG_ERROR:
_d("Kernel netlink comm. error.");
nle = NLMSG_DATA(nh);
if (nle) {
errno = -nle->error;
_pe("Kernel netlink error %d", errno);
}
return -1;
case RTM_NEWROUTE:
case RTM_DELROUTE:
_d("Netlink route ...");
nl_route(nh, len);
break;
case RTM_NEWLINK:
case RTM_DELLINK:
_d("Netlink link ...");
nl_link(nh, len);
break;
default:
_w("unhandled netlink message, type %d", nh->nlmsg_type);
break;
}
}
}
}
static int nl_request(int sd, unsigned int seq, int type)
{
struct nl_request *nlr = (struct nl_request *)nl_buf;
memset(nlr, 0, sizeof(struct nl_request));
nlr->nh.nlmsg_type = type;
nlr->nh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST;
nlr->nh.nlmsg_seq = seq;
nlr->nh.nlmsg_pid = 1;
switch (type) {
case RTM_GETROUTE:
_d("RTM_GETROUTE");
nlr->rtm.rtm_family = AF_INET;
nlr->rtm.rtm_table = RT_TABLE_MAIN;
nlr->nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
break;
case RTM_GETLINK:
_d("RTM_GETLINK");
nlr->ifi.ifi_family = AF_UNSPEC;
nlr->ifi.ifi_change = 0xFFFFFFFF;
nlr->nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
break;
default:
_w("Cannot resync, unhandled message type %d", type);
return -1;
}
if (send(sd, nlr, nlr->nh.nlmsg_len, 0) < 0)
return 1;
return nl_parse(sd);
}
static void nl_resync_routes(int sd, unsigned int seq)
{
if (nl_request(sd, seq, RTM_GETROUTE))
_pe("Failed netlink route request");
}
static void nl_resync_ifaces(int sd, unsigned int seq)
{
if (nl_request(sd, seq, RTM_GETLINK))
_pe("Failed netlink link request");
}
/*
* We've potentially lost netlink events, let's resync with kernel.
*/
static void nl_resync(int all)
{
unsigned int seq = 0;
int sd;
sd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
if (sd < 0) {
_pe("netlink socket");
return; return;
} }
for (nh = (struct nlmsghdr *)buf; !nlmsg_validate(nh, len); nh = NLMSG_NEXT(nh, len)) { if (all) {
//_d("Well formed netlink message received. type %d ...", nh->nlmsg_type); _d("============================ RESYNC =================================");
if (nh->nlmsg_type == RTM_NEWROUTE || nh->nlmsg_type == RTM_DELROUTE) /* this doesn't update condtions, and thus does not stop services */
nl_route(nh, len); cond_deassert("net/");
else
nl_link(nh, len); nl_resync_ifaces(sd, seq++);
nl_resync_routes(sd, seq++);
/* delayed update after we've corrected things */
service_step_all(SVC_TYPE_ANY);
_d("=========================== RESYNCED ================================");
} else
nl_resync_routes(sd, seq++);
close(sd);
}
static void nl_callback(void *arg, int sd, int events)
{
if (nl_parse(sd) < 0) {
if (errno == ENOBUFS) { /* netlink(7) */
_w("busy system, resynchronizing with kernel.");
nl_resync(1);
return;
}
}
/*
* Linux doesn't send route changes when interfaces go down, so
* we need to check ourselves, e.g. for loss of default route.
*/
if (nl_ifdown) {
_d("interface down, checking default route.");
if (nl_defidx > 0) {
nl_defidx = 0;
nl_resync(0);
if (nl_defidx <= 0) {
cond_clear("net/route/default");
nl_defidx = 0;
}
}
nl_ifdown = 0;
} }
} }
@@ -236,8 +415,8 @@ static plugin_t plugin = {
PLUGIN_INIT(plugin_init) PLUGIN_INIT(plugin_init)
{ {
int sd;
struct sockaddr_nl sa; struct sockaddr_nl sa;
int sd;
sd = socket(AF_NETLINK, SOCK_RAW | SOCK_NONBLOCK | SOCK_CLOEXEC, NETLINK_ROUTE); sd = socket(AF_NETLINK, SOCK_RAW | SOCK_NONBLOCK | SOCK_CLOEXEC, NETLINK_ROUTE);
if (sd < 0) { if (sd < 0) {
@@ -247,7 +426,7 @@ PLUGIN_INIT(plugin_init)
memset(&sa, 0, sizeof(sa)); memset(&sa, 0, sizeof(sa));
sa.nl_family = AF_NETLINK; sa.nl_family = AF_NETLINK;
sa.nl_groups = RTMGRP_IPV4_ROUTE | RTMGRP_LINK; // | RTMGRP_NOTIFY | RTMGRP_IPV4_IFADDR; sa.nl_groups = RTMGRP_IPV4_ROUTE | RTMGRP_LINK;
sa.nl_pid = getpid(); sa.nl_pid = getpid();
if (bind(sd, (struct sockaddr *)&sa, sizeof(sa)) < 0) { if (bind(sd, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
@@ -256,6 +435,13 @@ PLUGIN_INIT(plugin_init)
return; return;
} }
nl_buf = malloc(NL_BUFSZ);
if (!nl_buf) {
_pe("malloc()");
close(sd);
return;
}
plugin.io.fd = sd; plugin.io.fd = sd;
plugin_register(&plugin); plugin_register(&plugin);
} }
+8
View File
@@ -23,6 +23,9 @@
#include <glob.h> #include <glob.h>
#include <lite/lite.h> #include <lite/lite.h>
#include "finit.h"
#include "helpers.h"
#include "plugin.h" #include "plugin.h"
static void setup(void *arg) static void setup(void *arg)
@@ -31,6 +34,11 @@ static void setup(void *arg)
glob_t gl; glob_t gl;
char buf[160]; char buf[160];
if (rescue) {
_d("Skipping %s plugin in rescue mode.", __FILE__);
return;
}
glob("/run/sysctl.d/*.conf", 0, NULL, &gl); glob("/run/sysctl.d/*.conf", 0, NULL, &gl);
glob("/etc/sysctl.d/*.conf", GLOB_APPEND, NULL, &gl); glob("/etc/sysctl.d/*.conf", GLOB_APPEND, NULL, &gl);
glob("/usr/local/lib/sysctl.d/*.conf", GLOB_APPEND, NULL, &gl); glob("/usr/local/lib/sysctl.d/*.conf", GLOB_APPEND, NULL, &gl);
+10
View File
@@ -84,6 +84,11 @@ static void rtc_save(void *arg)
int fd, rc = 0; int fd, rc = 0;
char tz[128]; char tz[128];
if (rescue) {
_d("Skipping %s plugin in rescue mode.", __FILE__);
return;
}
fd = rtc_open(); fd = rtc_open();
if (fd < 0) if (fd < 0)
return; return;
@@ -121,6 +126,11 @@ static void rtc_restore(void *arg)
int fd, rc = 0; int fd, rc = 0;
char tz[128]; char tz[128];
if (rescue) {
_d("Skipping %s plugin in rescue mode.", __FILE__);
return;
}
fd = rtc_open(); fd = rtc_open();
if (fd < 0) if (fd < 0)
return; return;
+209
View File
@@ -0,0 +1,209 @@
/* System condtion event monitor for the Finit condition engine
*
* Copyright (c) 2021 Joachim Wiberg <troglobit@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <dirent.h>
#include <fnmatch.h>
#include <glob.h>
#include <limits.h>
#include <paths.h>
#include "finit.h"
#include "cond.h"
#include "pid.h"
#include "plugin.h"
#include "iwatch.h"
static struct iwatch iw_sys;
static int sys_add_path(struct iwatch *iw, char *path)
{
return iwatch_add(iw, path, IN_ONLYDIR | IN_CLOSE_WRITE);
}
/*
* unlink conditions with no active task/service, otherwise they may
* trigger inadvertently when calling `initctl reload` to activate
* a new configuration.
*/
static void sys_update_conds(char *dir, char *name, uint32_t mask)
{
char path[256];
char *cond;
char *ptr;
ptr = strrchr(name, '/');
if (ptr)
snprintf(path, sizeof(path), "%s/%s", dir, ++ptr);
else
snprintf(path, sizeof(path), "%s/%s", dir, name);
cond = strstr(path, COND_BASE);
if (!cond)
return;
cond += strlen(COND_BASE) + 1;
_d("cond: %s set: %d", cond, mask & IN_CREATE ? 1 : 0);
if (!cond_update(cond))
unlink(path);
}
/* synthesize events in case of new run dirs */
static void sys_scandir(struct iwatch *iw, char *dir, int len)
{
glob_t gl;
size_t i;
char path[len + 6];
int rc;
snprintf(path, sizeof(path), "%s/*", dir);
rc = glob(path, GLOB_NOSORT, NULL, &gl);
if (rc && rc != GLOB_NOMATCH)
return;
for (i = 0; i < gl.gl_pathc; i++) {
_d("scan found %s", gl.gl_pathv[i]);
sys_update_conds(dir, gl.gl_pathv[i], IN_CREATE);
}
globfree(&gl);
}
/*
* create/remove sub-directory in monitored directory
*/
static void sys_handle_dir(struct iwatch *iw, char *dir, char *name, int mask)
{
char path[strlen(dir) + strlen(name) + 2];
struct iwatch_path *iwp;
paste(path, sizeof(path), dir, name);
_d("path: %s", path);
iwp = iwatch_find_by_path(iw, path);
if (mask & IN_CREATE) {
if (!iwp) {
if (!sys_add_path(iw, path))
sys_scandir(iw, path, sizeof(path));
}
} else if (mask & IN_DELETE) {
if (iwp)
iwatch_del(&iw_sys, iwp);
}
}
static void sys_callback(void *arg, int fd, int events)
{
static char ev_buf[8 *(sizeof(struct inotify_event) + NAME_MAX + 1) + 1];
struct inotify_event *ev;
ssize_t sz;
size_t off;
sz = read(fd, ev_buf, sizeof(ev_buf) - 1);
if (sz <= 0) {
_pe("invalid inotify event");
return;
}
ev_buf[sz] = 0;
for (off = 0; off < (size_t)sz; off += sizeof(*ev) + ev->len) {
struct iwatch_path *iwp;
if (off + sizeof(*ev) > (size_t)sz)
break;
ev = (struct inotify_event *)&ev_buf[off];
if (off + sizeof(*ev) + ev->len > (size_t)sz)
break;
_d("name %s, event: 0x%08x", ev->name, ev->mask);
if (!ev->mask)
continue;
/* Find base path for this event */
iwp = iwatch_find_by_wd(&iw_sys, ev->wd);
if (!iwp)
continue;
if (ev->mask & IN_ISDIR) {
sys_handle_dir(&iw_sys, iwp->path, ev->name, ev->mask);
continue;
}
if (ev->mask & (IN_CREATE | IN_DELETE))
sys_update_conds(iwp->path, ev->name, ev->mask);
}
}
static void sys_init(void *arg)
{
char sysdir[MAX_ARG_LEN];
char *path;
if (mkpath(pid_runpath(_PATH_CONDSYS, sysdir, sizeof(sysdir)), 0755) && errno != EEXIST) {
_pe("Failed creating %s condition directory, %s", COND_SYS, _PATH_CONDSYS);
return;
}
path = realpath(_PATH_CONDSYS, NULL);
if (!path) {
_pe("Cannot figure out real path to %s, aborting", _PATH_CONDSYS);
return;
}
if (iwatch_add(&iw_sys, path, IN_ONLYDIR))
iwatch_exit(&iw_sys);
}
static plugin_t plugin = {
.name = __FILE__,
.hook[HOOK_BASEFS_UP] = { .cb = sys_init },
};
PLUGIN_INIT(plugin_init)
{
int fd;
fd = iwatch_init(&iw_sys);
if (fd < 0)
return;
plugin.io.fd = fd;
plugin.io.cb = sys_callback;
plugin.io.flags = PLUGIN_IO_READ;
plugin_register(&plugin);
}
PLUGIN_EXIT(plugin_exit)
{
iwatch_exit(&iw_sys);
plugin_unregister(&plugin);
}
/**
* Local Variables:
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+11 -1
View File
@@ -1,4 +1,4 @@
/* Setup random seed /* Setup and save random seed at boot/shutdown
* *
* Copyright (c) 2012-2021 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2012-2021 Joachim Wiberg <troglobit@gmail.com>
* *
@@ -35,6 +35,11 @@
static void setup(void *arg) static void setup(void *arg)
{ {
#ifdef RANDOMSEED #ifdef RANDOMSEED
if (rescue) {
_d("Skipping %s plugin in rescue mode.", __FILE__);
return;
}
if (!fexist(RANDOMSEED)) { if (!fexist(RANDOMSEED)) {
int ret = 1; int ret = 1;
mode_t prev; mode_t prev;
@@ -71,6 +76,11 @@ static void save(void *arg)
#ifdef RANDOMSEED #ifdef RANDOMSEED
mode_t prev; mode_t prev;
if (rescue) {
_d("Skipping %s plugin in rescue mode.", __FILE__);
return;
}
prev = umask(077); prev = umask(077);
print_desc("Saving random seed", NULL); print_desc("Saving random seed", NULL);
+5
View File
@@ -39,6 +39,11 @@ static void setup(void *arg)
int fd; int fd;
#endif #endif
if (rescue) {
_d("Skipping %s plugin in rescue mode.", __FILE__);
return;
}
prev = umask(0); prev = umask(0);
makedir("/var/run/console", 01777); makedir("/var/run/console", 01777);
+3
View File
@@ -1,8 +1,11 @@
.deps/* .deps/*
finit finit
getty
initctl initctl
keventd
logit logit
reboot reboot
sulogin
watchdogd watchdogd
Makefile Makefile
Makefile.in Makefile.in
+23 -9
View File
@@ -8,27 +8,41 @@ AM_LDFLAGS = -export-dynamic
endif endif
sbin_PROGRAMS = finit initctl sbin_PROGRAMS = finit initctl
pkglibexec_PROGRAMS = pkglibexec_PROGRAMS = getty logit sulogin
finit_pkglibdir = $(pkglibdir) if KEVENTD
finit_pkglib_SCRIPTS = rescue.conf sample.conf pkglibexec_PROGRAMS += keventd
endif
if WATCHDOGD if WATCHDOGD
pkglibexec_PROGRAMS += watchdogd pkglibexec_PROGRAMS += watchdogd
endif endif
if LOGIT finit_pkglibdir = $(pkglibdir)
pkglibexec_PROGRAMS += logit finit_pkglib_SCRIPTS = rescue.conf sample.conf
getty_SOURCES = finit.h getty.c helpers.h logrotate.c stty.c utmp-api.c utmp-api.h
getty_CFLAGS = -W -Wall -Wextra -std=gnu99
getty_CFLAGS += $(lite_CFLAGS)
getty_LDADD = $(lite_LIBS)
keventd_SOURCES = keventd.c iwatch.c iwatch.h util.c util.h
keventd_CFLAGS = -W -Wall -Wextra -std=gnu99
keventd_CFLAGS += $(lite_CFLAGS)
keventd_LDADD = $(lite_LIBS)
sulogin_SOURCES = sulogin.c
sulogin_CFLAGS = -W -Wall -Wextra -std=gnu99
sulogin_LDADD = -lcrypt
logit_SOURCES = logit.c logrotate.c logit_SOURCES = logit.c logrotate.c
logit_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99 logit_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
logit_CFLAGS += $(lite_CFLAGS) logit_CFLAGS += $(lite_CFLAGS)
logit_LDADD = $(lite_LIBS) logit_LDADD = $(lite_LIBS)
endif
finit_SOURCES = api.c cgroup.c cgroup.h \ finit_SOURCES = api.c cgroup.c cgroup.h \
cond.c cond-w.c cond.h \ cond.c cond-w.c cond.h \
conf.c conf.h \ conf.c conf.h \
exec.c finit.c finit.h \ exec.c finit.c finit.h \
getty.c stty.c \ stty.c \
helpers.c helpers.h \ helpers.c helpers.h \
iwatch.c iwatch.h \ iwatch.c iwatch.h \
log.c log.h \ log.c log.h \
@@ -47,7 +61,7 @@ if LOGROTATE
finit_SOURCES += logrotate.c finit_SOURCES += logrotate.c
endif endif
pkginclude_HEADERS = cond.h finit.h helpers.h log.h plugin.h svc.h pkginclude_HEADERS = cgroup.h cond.h finit.h helpers.h log.h plugin.h svc.h
finit_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99 finit_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
finit_CFLAGS += $(lite_CFLAGS) $(uev_CFLAGS) finit_CFLAGS += $(lite_CFLAGS) $(uev_CFLAGS)
+3
View File
@@ -172,6 +172,9 @@ int cgroup_service(char *name, int pid, struct cgroup *cg)
if (!strcmp(cg->name, "root")) if (!strcmp(cg->name, "root"))
return fnwrite(str("%d", pid), FINIT_CGPATH "/cgroup.procs"); return fnwrite(str("%d", pid), FINIT_CGPATH "/cgroup.procs");
if (!strcmp(cg->name, "init"))
return fnwrite(str("%d", pid), FINIT_CGPATH "/init/cgroup.procs");
snprintf(path, sizeof(path), "/sys/fs/cgroup/%s", cg->name); snprintf(path, sizeof(path), "/sys/fs/cgroup/%s", cg->name);
if (fisdir(path)) if (fisdir(path))
group = cg->name; group = cg->name;
+1 -1
View File
@@ -580,7 +580,7 @@ static void key(uev_t *w, void *arg, int events)
break; break;
default: default:
warnx("Got char 0x%02x", ch); dbg("Got char 0x%02x", ch);
break; break;
} }
} }
+6 -2
View File
@@ -67,6 +67,7 @@ int client_send(struct init_request *rq, ssize_t len)
{ {
struct pollfd pfd = { 0 }; struct pollfd pfd = { 0 };
int sd, result = 255; int sd, result = 255;
int rc;
sd = client_connect(); sd = client_connect();
if (-1 == sd) if (-1 == sd)
@@ -86,8 +87,11 @@ int client_send(struct init_request *rq, ssize_t len)
pfd.fd = sd; pfd.fd = sd;
pfd.events = POLLIN; pfd.events = POLLIN;
if (poll(&pfd, 1, 2000) <= 0) { if ((rc = poll(&pfd, 1, 2000)) <= 0) {
warn("Timed out waiting for reply from Finit, errno %d", errno); if (rc)
warn("poll(), errno %d", errno);
else
warnx("Timed out waiting for reply from Finit.");
goto exit; goto exit;
} }
+77 -21
View File
@@ -163,58 +163,92 @@ int cond_set_path(const char *path, enum cond_state next)
return next != prev; return next != prev;
} }
/* Should only be used by cond_set*(), cond_clear(), and usr plugin! */ /* Should only be used by cond_set*(), cond_clear(), and usr/sys plugins! */
void cond_update(const char *name) int cond_update(const char *name)
{ {
svc_t *svc, *iter = NULL; svc_t *svc, *iter = NULL;
int affects = 0;
_d("%s", name); _d("%s", name);
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) { for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
if (!svc_has_cond(svc) || !cond_affects(name, svc->cond)) if (!svc_has_cond(svc) || !cond_affects(name, svc->cond))
continue; continue;
affects++;
_d("%s: match <%s> %s(%s)", name ?: "nil", svc->cond, svc->desc, svc->cmd); _d("%s: match <%s> %s(%s)", name ?: "nil", svc->cond, svc->desc, svc->cmd);
service_step(svc); service_step(svc);
} }
return affects;
}
int cond_set_noupdate(const char *name)
{
_d("%s", name);
if (string_compare(name, "nop"))
return 1;
if (!cond_set_path(cond_path(name), COND_ON))
return 1;
return 0;
} }
void cond_set(const char *name) void cond_set(const char *name)
{ {
_d("%s", name); _d("%s", name);
if (string_compare(name, "nop")) if (cond_set_noupdate(name))
return;
if (!cond_set_path(cond_path(name), COND_ON))
return; return;
cond_update(name); cond_update(name);
} }
void cond_set_oneshot(const char *name) int cond_set_oneshot_noupdate(const char *name)
{ {
const char *path; const char *path;
if (string_compare(name, "nop")) if (string_compare(name, "nop"))
return; return 1;
path = cond_path(name); path = cond_path(name);
_d("%s => %s", name, path); _d("%s => %s", name, path);
if (cond_checkpath(path)) if (cond_checkpath(path))
return 1;
if (symlink(_PATH_RECONF, path) && errno != EEXIST) {
_pe("Failed creating onshot cond %s", name);
return 1;
}
return 0;
}
void cond_set_oneshot(const char *name)
{
_d("%s", name);
if (cond_set_oneshot_noupdate(name))
return; return;
if (symlink(_PATH_RECONF, path) && errno != EEXIST)
_pe("Failed creating onshot cond %s", name);
cond_update(name); cond_update(name);
} }
int cond_clear_noupdate(const char *name)
{
_d("%s", name);
if (string_compare(name, "nop"))
return 1;
if (!cond_set_path(cond_path(name), COND_OFF))
return 1;
return 0;
}
void cond_clear(const char *name) void cond_clear(const char *name)
{ {
_d("%s", name); _d("%s", name);
if (string_compare(name, "nop")) if (cond_clear_noupdate(name))
return;
if (!cond_set_path(cond_path(name), COND_OFF))
return; return;
cond_update(name); cond_update(name);
@@ -227,7 +261,7 @@ void cond_reload(void)
cond_bump_reconf(); cond_bump_reconf();
} }
static int reassert(const char *fpath, const struct stat *sb, int tflg, struct FTW *ftw) static int do_assert(const char *fpath, const struct stat *sb, int tflg, struct FTW *ftw, int set)
{ {
char *nm; char *nm;
@@ -239,25 +273,47 @@ static int reassert(const char *fpath, const struct stat *sb, int tflg, struct F
nm = strstr((char *)fpath, COND_BASE); nm = strstr((char *)fpath, COND_BASE);
if (!nm) { if (!nm) {
_e("Incorrect condtion path %s, cannot reassert", fpath); _e("Incorrect condtion path %s, cannot %sassert", fpath, set ? "re" : "de");
return 1; return 1;
} }
nm += strlen(COND_BASE); nm += strlen(COND_BASE);
_d("Reasserting %s => %s", fpath, nm); _d("%sasserting %s => %s", set ? "Re" : "De", fpath, nm);
cond_set(nm); if (set)
cond_set(nm);
else
cond_clear_noupdate(nm); /* important, see netlink plugin! */
return 0; return 0;
} }
static int reassert(const char *fpath, const struct stat *sb, int tflg, struct FTW *ftw)
{
return do_assert(fpath, sb, tflg, ftw, 1);
}
static int deassert(const char *fpath, const struct stat *sb, int tflg, struct FTW *ftw)
{
return do_assert(fpath, sb, tflg, ftw, 0);
}
/* /*
* Used only by netlink plugin atm. * Used only by netlink plugin atm.
* type: is a one of pid/, net/, etc. * type: is a one of pid/, net/, etc.
*/ */
void cond_reassert(const char *type) void cond_reassert(const char *pat)
{ {
_d("%s", type); _d("%s", pat);
nftw(cond_path(type), reassert, 20, FTW_DEPTH); nftw(cond_path(pat), reassert, 20, FTW_DEPTH);
}
/*
* Used only by netlink plugin atm.
*/
void cond_deassert(const char *pat)
{
_d("%s", pat);
nftw(cond_path(pat), deassert, 20, FTW_DEPTH);
} }
void cond_init(void) void cond_init(void)
+12 -3
View File
@@ -5,12 +5,14 @@
#include <svc.h> #include <svc.h>
#define COND_BASE "finit/cond" #define COND_BASE "finit/cond"
#define COND_USR "usr/"
#define COND_PID "pid/" #define COND_PID "pid/"
#define COND_SYS "sys/"
#define COND_USR "usr/"
#define _PATH_COND _PATH_VARRUN COND_BASE "/" #define _PATH_COND _PATH_VARRUN COND_BASE "/"
#define _PATH_CONDUSR _PATH_COND COND_USR
#define _PATH_CONDPID _PATH_COND COND_PID #define _PATH_CONDPID _PATH_COND COND_PID
#define _PATH_CONDSYS _PATH_COND COND_SYS
#define _PATH_CONDUSR _PATH_COND COND_USR
#define _PATH_RECONF _PATH_COND "reconf" #define _PATH_RECONF _PATH_COND "reconf"
typedef enum cond_state { typedef enum cond_state {
@@ -28,13 +30,20 @@ enum cond_state cond_get (const char *name);
enum cond_state cond_get_agg (const char *names); enum cond_state cond_get_agg (const char *names);
int cond_affects (const char *name, const char *names); int cond_affects (const char *name, const char *names);
void cond_update (const char *name); int cond_update (const char *name);
int cond_set_path (const char *path, enum cond_state new); int cond_set_path (const char *path, enum cond_state new);
void cond_set (const char *name); void cond_set (const char *name);
void cond_set_oneshot (const char *name); void cond_set_oneshot (const char *name);
void cond_clear (const char *name); void cond_clear (const char *name);
void cond_reload (void); void cond_reload (void);
int cond_set_noupdate (const char *name);
int cond_set_oneshot_noupdate(const char *name);
int cond_clear_noupdate(const char *name);
void cond_reassert (const char *pat); void cond_reassert (const char *pat);
void cond_deassert (const char *pat);
void cond_init (void); void cond_init (void);
#endif /* FINIT_COND_H_ */ #endif /* FINIT_COND_H_ */
+30 -28
View File
@@ -66,22 +66,6 @@ static TAILQ_HEAD(head, conf_change) conf_change_list = TAILQ_HEAD_INITIALIZER(c
static int parse_conf(char *file, int is_rcsd); static int parse_conf(char *file, int is_rcsd);
static void drop_changes(void); static void drop_changes(void);
static void hide_args(int argc, char *argv[])
{
/*
* Hide command line arguments from ps (in particular for
* forked children that don't execv()). This is an ugly
* hack that only works on Linux.
* https://web.archive.org/web/20110227041321/http://netsplit.com/2007/01/10/hiding-arguments-from-ps/
*/
if (argc > 1) {
char *arg_end;
arg_end = argv[argc-1] + strlen (argv[argc-1]);
*arg_end = ' ';
}
}
int get_bool(char *arg, int default_value) int get_bool(char *arg, int default_value)
{ {
if (!arg) if (!arg)
@@ -145,24 +129,27 @@ static void parse_arg(char *arg)
single = 1; single = 1;
} }
void conf_parse_cmdline(int argc, char *argv[]) #ifdef KERNEL_CMDLINE
/*
* Parse /proc/cmdline to find args for init. Don't use this!
*
* Instead, rely on the kernel to give Finit its arguments as
* regular argc + argv[]. Only use this if the system you run
* on has a broken initramfs system that cannot forward args
* to Finit properly.
*/
static void parse_kernel_cmdline(void)
{ {
int dbg = 0;
FILE *fp;
char line[LINE_SIZE], *cmdline, *tok; char line[LINE_SIZE], *cmdline, *tok;
FILE *fp;
for (int i = 1; i < argc; i++)
parse_arg(argv[i]);
hide_args(argc, argv);
fp = fopen("/proc/cmdline", "r"); fp = fopen("/proc/cmdline", "r");
if (!fp) if (!fp)
goto done; return;
if (!fgets(line, sizeof(line), fp)) { if (!fgets(line, sizeof(line), fp)) {
fclose(fp); fclose(fp);
goto done; return;
} }
cmdline = chomp(line); cmdline = chomp(line);
@@ -173,8 +160,23 @@ void conf_parse_cmdline(int argc, char *argv[])
parse_arg(tok); parse_arg(tok);
} }
fclose(fp); fclose(fp);
}
#else
#define parse_kernel_cmdline()
#endif
/*
* Kernel gives us all non-kernel options on our cmdline
*/
void conf_parse_cmdline(int argc, char *argv[])
{
int dbg = 0;
for (int i = 1; i < argc; i++)
parse_arg(argv[i]);
parse_kernel_cmdline();
done:
log_init(dbg); log_init(dbg);
} }
@@ -672,7 +674,7 @@ int conf_reload(void)
if (rescue) { if (rescue) {
int rc; int rc;
char line[80] = "tty [12345] @console noclear nologin"; char line[80] = "tty [12345] rescue";
/* If rescue.conf is missing, fall back to a root shell */ /* If rescue.conf is missing, fall back to a root shell */
rc = parse_conf(RESCUE_CONF, 0); rc = parse_conf(RESCUE_CONF, 0);
+54 -82
View File
@@ -26,6 +26,7 @@
#include <ctype.h> /* isdigit() */ #include <ctype.h> /* isdigit() */
#include <dirent.h> #include <dirent.h>
#include <err.h>
#include <stdarg.h> #include <stdarg.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <sys/prctl.h> #include <sys/prctl.h>
@@ -33,6 +34,7 @@
#include <sys/ttydefaults.h> /* Not included by default in musl libc */ #include <sys/ttydefaults.h> /* Not included by default in musl libc */
#include <termios.h> #include <termios.h>
#include <lite/lite.h> #include <lite/lite.h>
#include <linux/vt.h>
#include "finit.h" #include "finit.h"
#include "cgroup.h" #include "cgroup.h"
@@ -106,8 +108,6 @@ int run(char *cmd)
if (0 == pid) { if (0 == pid) {
FILE *fp; FILE *fp;
/* Reset signal handlers that were set by the parent process */
sig_unblock();
setsid(); setsid();
/* Always redirect stdio for run() */ /* Always redirect stdio for run() */
@@ -240,10 +240,8 @@ int exec_runtask(char *cmd, char *args[])
static void prepare_tty(char *tty, speed_t speed, char *procname, struct rlimit rlimit[]) static void prepare_tty(char *tty, speed_t speed, char *procname, struct rlimit rlimit[])
{ {
struct sigaction sa;
struct termios term;
char name[80]; char name[80];
int fd; int fd, dummy;
fd = open(tty, O_RDWR); fd = open(tty, O_RDWR);
if (fd < 0) { if (fd < 0) {
@@ -254,44 +252,18 @@ static void prepare_tty(char *tty, speed_t speed, char *procname, struct rlimit
dup2(fd, STDIN_FILENO); dup2(fd, STDIN_FILENO);
dup2(fd, STDOUT_FILENO); dup2(fd, STDOUT_FILENO);
dup2(fd, STDERR_FILENO); dup2(fd, STDERR_FILENO);
close(fd);
/* /* Set default TERM, run_getty() below allows override */
* Become session leader and set controlling TTY if (ioctl(fd, VT_OPENQRY, &dummy) == -1)
* to enable Ctrl-C and job control in shell. setenv("TERM", "vt102", 1); /* likely a serial line */
*/ else
setsid(); setenv("TERM", "linux", 1);
if (ioctl(STDIN_FILENO, TIOCSCTTY, 1) < 0)
logit(LOG_WARNING, "Failed TIOCSCTTY on %s: %s", tty, strerror(errno));
/* /*
* Reset to sane defaults in case of messup from prev. session * Reset to sane defaults in case of messup from prev. session
*/ */
stty(STDIN_FILENO, speed); stty(fd, speed);
close(fd);
/*
* Disable ISIG (INTR, QUIT, SUSP) before handing over to getty.
* It is up to the getty process to allow them again.
*/
if (!tcgetattr(STDIN_FILENO, &term)) {
term.c_lflag &= ~ISIG;
term.c_cc[VEOF] = _POSIX_VDISABLE;
term.c_cc[VINTR] = _POSIX_VDISABLE;
tcsetattr(STDIN_FILENO, TCSAFLUSH, &term);
}
/* Reset signal handlers that were set by the parent process */
sig_unblock();
/*
* Ignore a few signals, needed to prevent Ctrl-C at login:
* prompt and to prevent QUIT from dumping core.
*/
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
sa.sa_handler = SIG_IGN;
sigaction(SIGINT, &sa, NULL);
sigaction(SIGQUIT, &sa, NULL);
/* Set configured limits */ /* Set configured limits */
for (int i = 0; i < RLIMIT_NLIMITS; i++) { for (int i = 0; i < RLIMIT_NLIMITS; i++) {
@@ -309,8 +281,12 @@ static void prepare_tty(char *tty, speed_t speed, char *procname, struct rlimit
static int activate_console(int noclear, int nowait) static int activate_console(int noclear, int nowait)
{ {
static const char msg[] = "\nPlease press Enter to activate this console.";
static const char clr[] = "\r\e[2K";
static const char cup[] = "\e[A";
struct termios orig; struct termios orig;
int ret = 0; char c;
int rc;
if (nowait || rescue) if (nowait || rescue)
return 1; return 1;
@@ -329,36 +305,24 @@ static int activate_console(int noclear, int nowait)
tcsetattr(STDIN_FILENO, TCSAFLUSH, &c); tcsetattr(STDIN_FILENO, TCSAFLUSH, &c);
} }
while (!fexist(SYNC_SHUTDOWN)) { dprint(STDERR_FILENO, clr, strlen(clr));
char c; dprint(STDERR_FILENO, msg, strlen(msg));
static const char clr[] = "\r\e[2K"; while ((rc = read(STDIN_FILENO, &c, 1)) > 0 && c != '\r' && c != '\n')
static const char cup[] = "\e[A"; continue;
static const char msg[] = "\nPlease press Enter to activate this console.";
if (fexist(SYNC_STOPPED)) { /* On any error (likely EINTR), we avoid starting getty */
sleep(5); if (rc == -1)
continue; rc = 0;
}
dprint(STDERR_FILENO, clr, strlen(clr)); /* Clear msg and move cursor up for next message/login: */
dprint(STDERR_FILENO, msg, strlen(msg)); dprint(STDERR_FILENO, clr, strlen(clr));
while (read(STDIN_FILENO, &c, 1) == 1 && c != '\r') dprint(STDERR_FILENO, cup, strlen(cup));
continue;
if (fexist(SYNC_STOPPED))
continue;
dprint(STDERR_FILENO, clr, strlen(clr));
dprint(STDERR_FILENO, cup, strlen(cup));
ret = 1;
break;
}
/* Restore TTY */ /* Restore TTY */
if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &orig) == -1) if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &orig) == -1)
ret = 0; rc = 0; /* TTY in bad shape, restart */
return ret; return rc;
} }
/* /*
@@ -380,25 +344,7 @@ static int activate_console(int noclear, int nowait)
* since /bin/login usually only disables ECHO until a password line has * since /bin/login usually only disables ECHO until a password line has
* been entered. Upon starting the user's $SHELL the ISIG flag is reset * been entered. Upon starting the user's $SHELL the ISIG flag is reset
*/ */
pid_t run_getty(char *tty, char *baud, char *term, int noclear, int nowait, struct rlimit rlimit[]) pid_t run_getty(char *tty, char *cmd, char *args[], int noclear, int nowait, struct rlimit rlimit[])
{
speed_t speed;
int rc = 1;
speed = stty_parse_speed(baud);
prepare_tty(tty, speed, "tty", rlimit);
if (activate_console(noclear, nowait)) {
logit(LOG_INFO, "Starting built-in getty on %s, speed %u", tty, speed);
rc = getty(tty, speed, term, NULL);
}
return rc;
}
/*
* Start external getty as defined by user in .conf file
*/
pid_t run_getty2(char *tty, char *cmd, char *args[], int noclear, int nowait, struct rlimit rlimit[])
{ {
int rc = 1; int rc = 1;
@@ -414,6 +360,32 @@ pid_t run_getty2(char *tty, char *cmd, char *args[], int noclear, int nowait, st
return rc; return rc;
} }
int sh(char *tty)
{
char *args[2] = {
NULL,
NULL
};
char *arg0;
size_t len;
/* The getty process is usually responsible for the UTMP login record */
utmp_set_login(tty, NULL);
/* Start /bin/sh as a login shell, i.e. with a prefix '-' */
len = strlen(_PATH_BSHELL) + 2;
arg0 = malloc(len);
if (!arg0)
err(1, "Failed allocating memory");
snprintf(arg0, len, "-%s", _PATH_BSHELL);
args[0] = arg0;
/* Unblock signals inherited from parent */
sig_unblock();
return execv(_PATH_BSHELL, args);
}
pid_t run_sh(char *tty, int noclear, int nowait, struct rlimit rlimit[]) pid_t run_sh(char *tty, int noclear, int nowait, struct rlimit rlimit[])
{ {
int rc = 1; int rc = 1;
+131 -51
View File
@@ -52,6 +52,7 @@
#include "util.h" #include "util.h"
#include "utmp-api.h" #include "utmp-api.h"
#include "schedule.h" #include "schedule.h"
#include "watchdog.h"
int runlevel = 0; /* Bootstrap 'S' */ int runlevel = 0; /* Bootstrap 'S' */
int cfglevel = RUNLEVEL; /* Fallback if no configured runlevel */ int cfglevel = RUNLEVEL; /* Fallback if no configured runlevel */
@@ -65,6 +66,7 @@ char *network = NULL;
char *hostname = NULL; char *hostname = NULL;
char *rcsd = FINIT_RCSD; char *rcsd = FINIT_RCSD;
char *runparts = NULL; char *runparts = NULL;
char *osheading = NULL;
uev_ctx_t *ctx = NULL; /* Main loop context */ uev_ctx_t *ctx = NULL; /* Main loop context */
svc_t *wdog = NULL; /* No watchdog by default */ svc_t *wdog = NULL; /* No watchdog by default */
@@ -89,7 +91,14 @@ static void banner(void)
*/ */
plugin_run_hooks(HOOK_BANNER); plugin_run_hooks(HOOK_BANNER);
print_banner(INIT_HEADING); #ifdef INIT_OSHEADING
osheading = INIT_OSHEADING;
if (osheading) {
if (!osheading[0])
osheading = release_heading();
print_banner(osheading);
}
#endif
} }
/* /*
@@ -148,6 +157,25 @@ static int fsck_all(void)
} }
#ifndef SYSROOT #ifndef SYSROOT
/* If / is not listed in fstab, or listed as 'ro', leave it alone */
static int fs_readonly_root(struct fstab *fs)
{
char *tok, *str;
if (!fs)
return 1;
str = fs->fs_mntops;
while ((tok = strtok(str, ","))) {
if (!strcmp(tok, "ro"))
return 1;
str = NULL;
}
return 0;
}
static void fs_remount_root(int fsckerr) static void fs_remount_root(int fsckerr)
{ {
struct fstab *fs; struct fstab *fs;
@@ -160,8 +188,7 @@ static void fs_remount_root(int fsckerr)
break; break;
} }
/* If / is not listed in fstab, or listed as 'ro', leave it alone */ if (fs_readonly_root(fs))
if (!fs || !strcmp(fs->fs_type, "ro"))
goto out; goto out;
if (fsckerr) if (fsckerr)
@@ -185,6 +212,69 @@ static void fs_remount_root(int fsckerr)
} }
#endif /* SYSROOT */ #endif /* SYSROOT */
/*
* Opinionated file system setup. Checks for critical mount points and
* mounts them as most users expect. All file systems are checked with
* /proc/mounts before mounting.
*
* Embedded systems, and other people who want full control, can set up
* their system with /etc/fstab, which is handled before this function
* is called. For systems like Debian/Ubuntu, who only have / and swap
* in their /etc/fstab, this function does all the magic necessary.
*/
static void fs_finalize(void)
{
/*
* Some systems rely on us to both create /dev/shm and, to mount
* a tmpfs there. Any system with dbus needs shared memory, so
* mount it, unless its already mounted, but not if listed in
* the /etc/fstab file already.
*/
if (!fismnt("/dev/shm")) {
makedir("/dev/shm", 0777);
mount("shm", "/dev/shm", "tmpfs", 0, "mode=0777");
}
/* Modern systems use /dev/pts */
if (!fismnt("/dev/pts")) {
char opts[32];
int mode;
int gid;
gid = getgroup("tty");
if (gid == -1)
gid = 0;
/* 0600 is default on Debian, use 0620 to get mesg y by default */
mode = 0620;
snprintf(opts, sizeof(opts), "gid=%d,mode=%d,ptmxmode=0666", gid, mode);
makedir("/dev/pts", 0755);
mount("devpts", "/dev/pts", "devpts", MS_NOSUID | MS_NOEXEC, opts);
}
/*
* Modern systems use tmpfs for /run. Fallback to /var/run if
* /run doesn't exist is handled by the bootmisc plugin. It
* also sets up compat symlinks.
*
* The unconditional mount of /run/lock is for DoS prevention.
* To override any of this behavior, add entries to /etc/fstab
* for /run (and optionally /run/lock).
*/
if (fisdir("/run") && !fismnt("/run")) {
mount("tmpfs", "/run", "tmpfs", MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_RELATIME, "mode=0755,size=10%");
/* This prevents user DoS of /run by filling /run/lock at the expense of another tmpfs, max 5MiB */
makedir("/run/lock", 1777);
mount("tmpfs", "/run/lock", "tmpfs", MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_RELATIME, "mode=0777,size=5252880");
}
/* Modern systems use tmpfs for /tmp */
if (!fismnt("/tmp"))
mount("tmpfs", "/tmp", "tmpfs", MS_NOSUID | MS_NODEV, "mode=1777");
}
static void fs_mount(void) static void fs_mount(void)
{ {
if (!rescue) if (!rescue)
@@ -200,6 +290,9 @@ static void fs_mount(void)
plugin_run_hooks(HOOK_MOUNT_POST); plugin_run_hooks(HOOK_MOUNT_POST);
run("swapon -ea"); run("swapon -ea");
_d("Finalize, ensure common file systems are available ...");
fs_finalize();
} }
/* /*
@@ -228,54 +321,21 @@ static void fs_init(void)
for (i = 0; i < NELEMS(fs); i++) { for (i = 0; i < NELEMS(fs); i++) {
int rc; int rc;
/*
* Check if already mounted, we may be running in a
* container, or an initramfs ran before us. The
* function fismnt() reliles on /proc/mounts being
* unique for each chroot/container.
*/
if (fismnt(fs[i].file))
continue;
rc = mount(fs[i].spec, fs[i].file, fs[i].type, 0, NULL); rc = mount(fs[i].spec, fs[i].file, fs[i].type, 0, NULL);
if (rc && errno != EBUSY) if (rc && errno != EBUSY)
_pe("Failed mounting %s on %s", fs[i].spec, fs[i].file); _pe("Failed mounting %s on %s", fs[i].spec, fs[i].file);
} }
} }
/*
* If everything goes south we can use this to give the operator an
* emergency shell to debug the problem -- Finit should not crash!
*
* Note: Only use this for debugging a new Finit setup, don't use
* this in production since it gives a root shell to anyone
* if Finit crashes.
*
* This emergency shell steps in to prevent "Aieee, PID 1 crashed"
* messages from the kernel, which usually results in a reboot, so
* that the operator instead can debug the problem.
*/
static void emergency_shell(void)
{
#ifdef EMERGENCY_SHELL
pid_t pid;
pid = fork();
if (pid) {
while (1) {
pid_t id;
/* Reap 'em (prevents Zombies) */
id = waitpid(-1, NULL, WNOHANG);
if (id == pid)
break;
}
fprintf(stderr, "\n=> Embarrassingly, Finit has crashed. Check /dev/kmsg for details.\n");
fprintf(stderr, "=> To debug, add 'debug' to the kernel command line.\n\n");
/*
* Become session leader and set controlling TTY
* to enable Ctrl-C and job control in shell.
*/
setsid();
ioctl(STDIN_FILENO, TIOCSCTTY, 1);
execl(_PATH_BSHELL, _PATH_BSHELL, NULL);
}
#endif /* EMERGENCY_SHELL */
}
/* /*
* Handle bootstrap transition to configured runlevel, start TTYs * Handle bootstrap transition to configured runlevel, start TTYs
@@ -500,17 +560,30 @@ int main(int argc, char *argv[])
*/ */
setenv("PATH", _PATH_STDPATH, 1); setenv("PATH", _PATH_STDPATH, 1);
setenv("SHELL", _PATH_BSHELL, 1); setenv("SHELL", _PATH_BSHELL, 1);
setenv("LOGNAME", "root", 1);
setenv("USER", "root", 1);
if (chdir("/")) if (chdir("/"))
_pe("Failed cd /"); _pe("Failed cd /");
/* Allow progress, if enabled */
enable_progress(1);
/* /*
* In case of emergency. * In case of emergency.
*/ */
emergency_shell(); if (rescue) {
char *sulogin[] = {
_PATH_SULOGIN,
"sulogin",
};
size_t i;
for (i = 0; i < NELEMS(sulogin); i++) {
if (systemf(sulogin[i]))
continue;
rescue = 0;
break;
}
}
/* /*
* Load plugins early, the first hook is in banner(), so we * Load plugins early, the first hook is in banner(), so we
@@ -521,6 +594,7 @@ int main(int argc, char *argv[])
/* /*
* Hello world. * Hello world.
*/ */
enable_progress(1); /* Allow progress, if enabled */
banner(); banner();
/* /*
@@ -552,11 +626,17 @@ int main(int argc, char *argv[])
/* /*
* Start built-in watchdogd as soon as possible, if enabled * Start built-in watchdogd as soon as possible, if enabled
*/ */
if (which(FINIT_LIBPATH_ "/watchdogd")) { if (whichp(FINIT_LIBPATH_ "/watchdogd") && fexist(WDT_DEVNODE)) {
service_register(SVC_TYPE_SERVICE, "[123456789] name:watchdog :finit " FINIT_LIBPATH_ "/watchdogd -- Finit watchdog daemon", global_rlimit, NULL); service_register(SVC_TYPE_SERVICE, "[123456789] cgroup.init name:watchdog :finit " FINIT_LIBPATH_ "/watchdogd -- Finit watchdog daemon", global_rlimit, NULL);
wdog = svc_find_by_nameid("watchdog", "finit"); wdog = svc_find_by_nameid("watchdog", "finit");
} }
/*
* Start kernel event daemon as soon as possible, if enabled
*/
if (whichp(FINIT_LIBPATH_ "/keventd"))
service_register(SVC_TYPE_SERVICE, "[123456789] cgroup.init " FINIT_LIBPATH_ "/keventd -- Finit kernel event daemon", global_rlimit, NULL);
/* Base FS up, enable standard SysV init signals */ /* Base FS up, enable standard SysV init signals */
sig_setup(&loop); sig_setup(&loop);
+5 -1
View File
@@ -50,7 +50,10 @@
#define FINIT_CGPATH "/sys/fs/cgroup" #define FINIT_CGPATH "/sys/fs/cgroup"
#endif #endif
#define LOGIT_PATH FINIT_LIBPATH_ "/logit" /* To use these, include config.h, or define FINIT_LIBPATH_ */
#define _PATH_LOGIT FINIT_LIBPATH_ "/logit"
#define _PATH_SULOGIN FINIT_LIBPATH_ "/sulogin"
#define _PATH_GETTY FINIT_LIBPATH_ "/getty"
#define CMD_SIZE 256 #define CMD_SIZE 256
#define LINE_SIZE 1024 #define LINE_SIZE 1024
@@ -112,6 +115,7 @@ extern char *sdown;
extern char *network; extern char *network;
extern char *hostname; extern char *hostname;
extern char *runparts; extern char *runparts;
extern char *osheading;
extern uev_ctx_t *ctx; extern uev_ctx_t *ctx;
#endif /* FINIT_H_ */ #endif /* FINIT_H_ */
+66 -67
View File
@@ -21,20 +21,21 @@
* THE SOFTWARE. * THE SOFTWARE.
*/ */
#include "config.h"
#include <err.h> #include <err.h>
#include <errno.h> #include <errno.h>
#include <paths.h> #include <paths.h>
#include <signal.h> #include <signal.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <sys/utsname.h> #include <sys/utsname.h>
#include <sys/ttydefaults.h> /* Not included by default in musl libc */ #include <sys/ttydefaults.h> /* Not included by default in musl libc */
#include <termios.h> #include <termios.h>
#include "finit.h" #include "finit.h"
#include "cgroup.h"
#include "helpers.h" #include "helpers.h"
#include "sig.h"
#include "utmp-api.h" #include "utmp-api.h"
#ifndef _PATH_LOGIN #ifndef _PATH_LOGIN
@@ -44,19 +45,14 @@
/* /*
* Read one character from stdin. * Read one character from stdin.
*/ */
static int readch(char *tty) static int readch(void)
{ {
int st;
char ch1; char ch1;
int st;
st = read(STDIN_FILENO, &ch1, 1); st = read(STDIN_FILENO, &ch1, 1);
if (st == 0) { if (st <= 0)
dprint(STDOUT_FILENO, "\n", 0); return -1;
_exit(0);
}
if (st < 0)
errx(1, "getty: %s: read error", tty);
return ch1 & 0xFF; return ch1 & 0xFF;
} }
@@ -141,10 +137,10 @@ static void do_issue(char *tty)
/* /*
* Handle the process of a GETTY. * Handle the process of a GETTY.
*/ */
static void do_getty(char *tty, char *name, size_t len) static int get_logname(char *tty, char *name, size_t len)
{ {
int ch;
char *np; char *np;
int ch;
/* /*
* Display prompt. * Display prompt.
@@ -155,14 +151,9 @@ static void do_getty(char *tty, char *name, size_t len)
do_issue(tty); do_issue(tty);
np = name; np = name;
while ((ch = readch(tty)) != '\n') { while ((ch = readch()) != '\n') {
if (ch == CTRL('U')) { if (ch < 0)
while (np > name) { return 1;
dprint(STDOUT_FILENO, "\b \b", 3);
np--;
}
continue;
}
if (np < name + len) if (np < name + len)
*np++ = ch; *np++ = ch;
@@ -174,6 +165,7 @@ static void do_getty(char *tty, char *name, size_t len)
} }
name[len - 1] = 0; name[len - 1] = 0;
return 0;
} }
/* /*
@@ -185,26 +177,28 @@ static int do_login(char *name)
{ {
struct stat st; struct stat st;
cgroup_user(name, 0);
execl(_PATH_LOGIN, _PATH_LOGIN, name, NULL); execl(_PATH_LOGIN, _PATH_LOGIN, name, NULL);
/* /*
* Failed to exec login, should be impossible. Try a starting a * Failed to exec login, should not happen on normal systems.
* fallback shell instead. * Try a starting a rescue shell instead.
*
* Note: Add /etc/securetty handling.
*/ */
warnx("Failed exec %s, attempting fallback to %s ...", _PATH_LOGIN, _PATH_BSHELL); if (fstat(0, &st) == 0 && S_ISCHR(st.st_mode)) {
if (fstat(0, &st) == 0 && S_ISCHR(st.st_mode)) warnx("Failed exec %s, attempting fallback to %s ...", _PATH_LOGIN, _PATH_SULOGIN);
execl(_PATH_SULOGIN, _PATH_SULOGIN, NULL);
warnx("Failed exec %s, attempting fallback to %s ...", _PATH_SULOGIN, _PATH_BSHELL);
execl(_PATH_BSHELL, _PATH_BSHELL, NULL); execl(_PATH_BSHELL, _PATH_BSHELL, NULL);
}
return 1; /* We shouldn't get here ... */ return 1; /* We shouldn't get here ... */
} }
int getty(char *tty, speed_t speed, char *term, char *user) static int getty(char *tty, speed_t speed, char *term, char *user)
{ {
const char cln[] = "\r\e[2K\n"; const char cln[] = "\r\e[2K\n";
char name[30]; char name[33]; /* useradd(1) limit at 32 chars */
pid_t sid;
/* /*
* Clean up tty name. * Clean up tty name.
@@ -212,59 +206,64 @@ int getty(char *tty, speed_t speed, char *term, char *user)
if (!strncmp(tty, _PATH_DEV, strlen(_PATH_DEV))) if (!strncmp(tty, _PATH_DEV, strlen(_PATH_DEV)))
tty += 5; tty += 5;
/* Set up TTY, re-enabling ISIG et al. */
stty(STDIN_FILENO, speed);
dprint(STDERR_FILENO, cln, strlen(cln));
/* The getty process is responsible for the UTMP login record */ /* The getty process is responsible for the UTMP login record */
utmp_set_login(tty, NULL); utmp_set_login(tty, NULL);
if (!user)
do_getty(tty, name, sizeof(name)); /* Replace "Please press enter ..." with login: */
else dprint(STDERR_FILENO, cln, strlen(cln));
restart:
stty(STDIN_FILENO, speed);
if (!user) {
if (get_logname(tty, name, sizeof(name)))
goto restart;
} else
strlcpy(name, user, sizeof(name)); strlcpy(name, user, sizeof(name));
/* check current session associated with our tty */
sid = tcgetsid(0);
if (sid < 0 || getpid() != sid) {
if (ioctl(STDIN_FILENO, TIOCSCTTY, 1) == -1)
err(1, "failed stealing controlling TTY");
}
if (term && term[0]) if (term && term[0])
setenv("TERM", term, 1); setenv("TERM", term, 1);
return do_login(name); return do_login(name);
} }
int sh(char *tty) static int usage(int rc)
{ {
struct termios term; warnx("usage: getty [-h?] tty [speed [term]]");
char *args[2] = { return rc;
NULL, }
NULL
};
char *arg0;
size_t len;
/* The getty process is usually responsible for the UTMP login record */ int main(int argc, char *argv[])
utmp_set_login(tty, NULL); {
char *tty, *speed = "0", *term = NULL;
int c;
/* Set up TTY, re-enabling ISIG et al. */ while ((c = getopt(argc, argv, "h?")) != EOF) {
stty(STDIN_FILENO, B0); switch(c) {
case 'h':
/* Start /bin/sh as a login shell, i.e. with a prefix '-' */ case '?':
len = strlen(_PATH_BSHELL) + 2; return usage(0);
arg0 = malloc(len); default:
if (!arg0) return usage(1);
err(1, "Failed allocating memory"); }
snprintf(arg0, len, "-%s", _PATH_BSHELL);
args[0] = arg0;
/* Reenable Ctrl-D and Ctrl-C, and ... */
if (!tcgetattr(STDIN_FILENO, &term)) {
term.c_lflag |= ISIG;
term.c_cc[VEOF] = CEOF;
term.c_cc[VINTR] = CINTR;
tcsetattr(STDIN_FILENO, TCSAFLUSH, &term);
} }
/* ... unblock signals in general */ if (optind >= argc)
sig_unblock(); return usage(1);
return execv(_PATH_BSHELL, args); tty = argv[optind++];
if (optind < argc)
speed = argv[optind++];
if (optind < argc)
term = argv[optind++];
return getty(tty, atoi(speed), term, NULL);
} }
/** /**
+45 -83
View File
@@ -68,92 +68,13 @@ static const char *status2[] = STATUS_CLASS;
#define CHOOSE(x,y,z) z #define CHOOSE(x,y,z) z
static const char *color[] = STATUS_CLASS; static const char *color[] = STATUS_CLASS;
/*
* System consoles, can be given multiple times on the kernel cmdline.
* We support only three currently. If none are given on the cmdline
* we probe /sys/class/tty/console/active and use the first (default).
*/
#define MAX_CONS 3
static char *consoles[MAX_CONS];
static int fds[MAX_CONS];
static int num_cons = 0;
/*
* System default console is always the first
*/
char *console(void) char *console(void)
{ {
return consoles[0]; return _PATH_CONSOLE;
}
static void add_console(char *cons)
{
char path[80];
char *ptr;
int i;
if (num_cons == NELEMS(fds)) {
_e("too many consoles, max %d supported", NELEMS(fds));
return;
}
/* drop any options */
ptr = strchr(cons, ',');
if (ptr)
*ptr = 0;
if (!strncmp(cons, "/dev/", 5))
cons += 5;
snprintf(path, sizeof(path), "/dev/%s", cons);
/* check for and skip duplicates */
for (i = 0; i < num_cons; i++) {
if (string_compare(path, consoles[i]))
return;
}
/* this requires we have /dev mounted && a device there */
fds[num_cons] = open(path, O_WRONLY);
if (fds[num_cons] < 0) {
_pe("Failed opening console %s", path);
return;
}
consoles[num_cons] = strdup(path);
num_cons++;
}
static void find_active_consoles(void)
{
char *cons, *ptr, *tok;
char buf[512];
FILE *fp;
fp = fopen("/sys/class/tty/console/active", "r");
if (!fp) {
fallback:
_d("Using fallback /dev/console");
add_console("console");
return;
}
ptr = fgets(buf, sizeof(buf), fp);
fclose(fp);
if (!ptr)
goto fallback;
cons = chomp(ptr);
_d("Using these system consoles: %s", cons);
while ((tok = strtok(cons, " \t"))) {
cons = NULL;
add_console(tok);
}
} }
void console_init(void) void console_init(void)
{ {
find_active_consoles();
ttinit(); ttinit();
} }
@@ -163,14 +84,12 @@ ssize_t cprintf(const char *fmt, ...)
char buf[len < 256 ? 256 : len]; char buf[len < 256 ? 256 : len];
size_t size; size_t size;
va_list ap; va_list ap;
int i;
va_start(ap, fmt); va_start(ap, fmt);
size = vsnprintf(buf, sizeof(buf), fmt, ap); size = vsnprintf(buf, sizeof(buf), fmt, ap);
va_end(ap); va_end(ap);
for (i = 0; i < num_cons; i++) dprint(STDERR_FILENO, buf, size);
dprint(fds[i], buf, size);
return size; return size;
} }
@@ -205,6 +124,49 @@ void show_progress(pstyle_t style)
progress_onoff = style; progress_onoff = style;
} }
/*
* Find PRETTY_NAME in /etc/os-release to use as heading at boot
* and on runlevel changes. Fallback: "Finit vX.YY"
*/
char *release_heading(void)
{
char *name = NULL;
char buf[256];
FILE *fp;
fp = fopen("/etc/os-release", "r");
if (fp) {
while (fgets(buf, sizeof(buf), fp)) {
char *ptr = &buf[12];
if (strncmp(buf, "PRETTY_NAME=", 12))
continue;
if (*ptr == '"' || *ptr == '\'') {
char *end = &ptr[strlen(ptr) - 1];
char q = *ptr;
while (end > ptr && isspace(*end))
*end-- = 0;
if (*end == q) {
*end = 0;
ptr++;
}
}
name = strdup(ptr);
break;
}
fclose(fp);
}
if (!name)
name = strdup(PACKAGE_NAME " v" PACKAGE_VERSION);
return name;
}
/* /*
* Return screen length of string, not counting escape chars, and * Return screen length of string, not counting escape chars, and
* accounting for unicode characters as only one screen byte wide * accounting for unicode characters as only one screen byte wide
+3 -5
View File
@@ -68,11 +68,10 @@ char *strip_line (char *line);
void enable_progress (int onoff); void enable_progress (int onoff);
void show_progress (pstyle_t style); void show_progress (pstyle_t style);
int getty (char *tty, speed_t speed, char *term, char *user); char *release_heading (void);
int sh (char *tty);
void stty (int fd, speed_t speed);
speed_t stty_parse_speed(char *baud); speed_t stty_parse_speed(char *baud);
void stty (int fd, speed_t speed);
void print_banner (const char *heading); void print_banner (const char *heading);
void printv (const char *fmt, va_list ap); void printv (const char *fmt, va_list ap);
@@ -95,8 +94,7 @@ int complete (char *cmd, int pid);
int run (char *cmd); int run (char *cmd);
int run_interactive (char *cmd, char *fmt, ...); int run_interactive (char *cmd, char *fmt, ...);
int exec_runtask (char *cmd, char *args[]); int exec_runtask (char *cmd, char *args[]);
pid_t run_getty (char *tty, char *baud, char *term, int noclear, int nowait, struct rlimit rlimit[]); pid_t run_getty (char *tty, char *cmd, char *args[], int noclear, int nowait, struct rlimit rlimit[]);
pid_t run_getty2 (char *tty, char *cmd, char *args[], int noclear, int nowait, struct rlimit rlimit[]);
pid_t run_sh (char *tty, int noclear, int nowait, struct rlimit rlimit[]); pid_t run_sh (char *tty, int noclear, int nowait, struct rlimit rlimit[]);
int run_parts (char *dir, char *cmd); int run_parts (char *dir, char *cmd);
+2 -1
View File
@@ -41,6 +41,7 @@
#include "service.h" #include "service.h"
#include "cgutil.h" #include "cgutil.h"
#include "util.h" #include "util.h"
#include "utmp-api.h"
struct cmd { struct cmd {
char *cmd; char *cmd;
@@ -345,7 +346,7 @@ static int do_cond_act(char *arg, int creat)
if (symlink(_PATH_RECONF, oneshot) && errno != EEXIST) if (symlink(_PATH_RECONF, oneshot) && errno != EEXIST)
err(1, "Failed asserting condition <%s>", &oneshot[off]); err(1, "Failed asserting condition <%s>", &oneshot[off]);
} else { } else {
if (erase(oneshot)) if (erase(oneshot) && errno != ENOENT)
err(1, "Failed deasserting condition <%s>", &oneshot[off]); err(1, "Failed deasserting condition <%s>", &oneshot[off]);
} }
+17 -1
View File
@@ -94,8 +94,24 @@ int iwatch_add(struct iwatch *iw, char *file, uint32_t mask)
wd = inotify_add_watch(iw->fd, path, IWATCH_MASK | mask); wd = inotify_add_watch(iw->fd, path, IWATCH_MASK | mask);
if (wd < 0) { if (wd < 0) {
if (errno != EEXIST) switch (errno) {
case EEXIST:
break;
case EINVAL:
/*
* some older kernels, notably 4.19, don't like
* inotify on top-level cgroups in cgroup v2
*/
if (!strncmp(path, "/sys/fs/cgroup", 14))
break; /* ignore error */
/* fallthrough */
default:
_pe("Failed adding watcher for %s", path); _pe("Failed adding watcher for %s", path);
break;
}
free(path); free(path);
return -1; return -1;
} }
+279
View File
@@ -0,0 +1,279 @@
/* Listens to kernel events like AC power status and manages sys/ conditions
*
* Copyright (c) 2021 Joachim Wiberg <troglobit@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <errno.h>
#include <dirent.h>
#include <poll.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <linux/types.h>
#include <linux/netlink.h>
#include "lite/lite.h"
#include "cond.h"
#include "pid.h"
#include "util.h"
#define _PATH_SYSFS_PWR "/sys/class/power_supply"
static int num_ac_online;
static int num_ac;
int debug; /* debug in other modules as well */
void logit(int prio, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
if (!debug)
vsyslog(prio, fmt, ap);
else {
vfprintf(stderr, fmt, ap);
fprintf(stderr, "\n");
}
va_end(ap);
}
#define panic(fmt, args...) { logit(LOG_CRIT, fmt ":%s", ##args, strerror(errno)); exit(1); }
#define warn(fmt, args...) { logit(LOG_WARNING, fmt ":%s", ##args, strerror(errno)); }
static void sys_cond(char *cond, int set)
{
char oneshot[256];
snprintf(oneshot, sizeof(oneshot), "%s/%s", _PATH_CONDSYS, cond);
if (set) {
if (symlink(_PATH_RECONF, oneshot) && errno != EEXIST)
warn("failed asserting sys/%s", cond);
} else {
if (erase(oneshot) && errno != ENOENT)
warn("failed asserting sys/%s", cond);
}
}
static int fgetline(char *path, char *buf, size_t len)
{
FILE *fp;
fp = fopen(path, "r");
if (!fp)
return -1;
if (!fgets(buf, len, fp)) {
fclose(fp);
return -1;
}
chomp(buf);
fclose(fp);
return 0;
}
static int check_online(char *online)
{
int val;
if (!online)
return 0;
val = atoi(online);
logit(LOG_INFO, "AC %s", val ? "connected" : "disconnected");
return val;
}
static int is_ac(char *type)
{
char *types[] = {
"Mains",
"USB",
"BrickID",
"Wireless",
NULL
};
int i;
for (i = 0; types[i]; i++) {
if (!strncmp(type, types[i], strlen(types[i])))
return 1;
}
return 0;
}
static void init(void)
{
struct dirent **d = NULL;
char *cond_dirs[] = {
_PATH_CONDSYS,
_PATH_CONDSYS "/pwr",
};
char path[384];
int i, n;
for (i = 0; i < (int)NELEMS(cond_dirs); i++) {
if (mkpath(cond_dirs[i], 0755) && errno != EEXIST) {
_pe("Failed creating %s condition directory, %s", COND_SYS,
cond_dirs[i]);
return;
}
}
n = scandir(_PATH_SYSFS_PWR, &d, NULL, alphasort);
for (i = 0; i < n; i++) {
char *nm = d[i]->d_name;
char buf[10];
snprintf(path, sizeof(path), "%s/%s/type", _PATH_SYSFS_PWR, nm);
if (!fgetline(path, buf, sizeof(buf)) && is_ac(buf)) {
num_ac++;
snprintf(path, sizeof(path), "%s/%s/online", _PATH_SYSFS_PWR, nm);
if (!fgetline(path, buf, sizeof(buf))) {
if (check_online(buf))
num_ac_online++;
}
}
free(d[i]);
}
if (n > 0)
free(d);
/* if any power_supply is online, or none can be found */
if (num_ac == 0 || num_ac_online > 0)
sys_cond("pwr/ac", 1);
}
/*
* Started by Finit as soon as possible when base filesystem is up,
* modules have been probed, or insmodded from /etc/finit.conf, so by
* now we should have /sys/class/power_supply/ available for probing.
* If none is found we assert /sys/pwr/ac condition anyway, this is what
* systemd does (ConditionACPower) and also makes most sense.
*/
int main(int argc, char *argv[])
{
struct sockaddr_nl nls = { 0 };
struct pollfd pfd;
char buf[1024];
if (argc > 1) {
if (!strcmp(argv[1], "-d"))
debug = 1;
}
if (!debug) {
openlog("keventd", LOG_CONS | LOG_PID, LOG_DAEMON);
setlogmask(LOG_UPTO(LOG_NOTICE));
}
init();
pfd.events = POLLIN;
pfd.fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
if (pfd.fd == -1)
panic("failed creating netlink socket");
nls.nl_family = AF_NETLINK;
nls.nl_pid = 0;
nls.nl_groups = -1;
if (bind(pfd.fd, (void *)&nls, sizeof(struct sockaddr_nl)))
panic("bind failed");
logit(LOG_DEBUG, "Waiting for events ...");
while (-1 != poll(&pfd, 1, -1)) {
char *path = NULL;
int ac = 0;
int i, len;
len = recv(pfd.fd, buf, sizeof(buf), MSG_DONTWAIT);
if (len == -1) {
switch (errno) {
case EINTR:
continue;
case ENOBUFS:
warn("lost events");
continue;
default:
panic("unhandled");
continue;
}
}
buf[len] = 0;
/* skip libusb events, focus on kernel events/changes */
if (strncmp(buf, "change@", 7))
continue;
/* XXX: currently limited to monitoring this subsystem */
if (!strstr(buf, "power_supply"))
continue;
i = 0;
while (i < len) {
char *line = buf + i;
logit(LOG_DEBUG, "%s", line);
if (!strncmp(line, "DEVPATH=", 8)) {
path = &line[8];
logit(LOG_DEBUG, "Got path %s", path);
} else if (!strncmp(line, "POWER_SUPPLY_TYPE=", 18)) {
ac = is_ac(&line[18]);
} else if (!strncmp(line, "POWER_SUPPLY_ONLINE=", 20) && ac) {
if (check_online(&line[20])) {
if (!num_ac_online)
sys_cond("pwr/ac", 1);
num_ac_online++;
} else {
if (num_ac_online > 0)
num_ac_online--;
if (!num_ac_online)
sys_cond("pwr/ac", 0);
}
}
i += strlen(line) + 1;
}
}
close(pfd.fd);
return 0;
}
/**
* Local Variables:
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+1 -1
View File
@@ -104,7 +104,7 @@ void logit(int prio, const char *fmt, ...)
goto done; goto done;
} }
if (prio > loglevel) if (LOG_PRI(prio) > loglevel)
goto done; goto done;
fp = fopen("/dev/kmsg", "w"); fp = fopen("/dev/kmsg", "w");
+14 -5
View File
@@ -98,29 +98,38 @@ static int logit(int level, char *buf, size_t len)
static int parse_prio(char *arg, int *f, int *l) static int parse_prio(char *arg, int *f, int *l)
{ {
char *ptr; char *duparg = strdup(arg);
char *ptr, *prio;
ptr = strchr(arg, '.'); if (!duparg)
prio = arg;
else
prio = duparg;
ptr = strchr(prio, '.');
if (ptr) { if (ptr) {
*ptr++ = 0; *ptr++ = 0;
for (int i = 0; facilitynames[i].c_name; i++) { for (int i = 0; facilitynames[i].c_name; i++) {
if (!strcmp(facilitynames[i].c_name, arg)) { if (!strcmp(facilitynames[i].c_name, prio)) {
*f = facilitynames[i].c_val; *f = facilitynames[i].c_val;
break; break;
} }
} }
arg = ptr; prio = ptr;
} }
for (int i = 0; prioritynames[i].c_name; i++) { for (int i = 0; prioritynames[i].c_name; i++) {
if (!strcmp(prioritynames[i].c_name, arg)) { if (!strcmp(prioritynames[i].c_name, prio)) {
*l = prioritynames[i].c_val; *l = prioritynames[i].c_val;
break; break;
} }
} }
if (duparg != arg)
free(duparg);
return 0; return 0;
} }
+1 -1
View File
@@ -206,7 +206,7 @@ int reboot_main(int argc, char *argv[])
case CMD_HALT: case CMD_HALT:
if (do_halt(NULL)) if (do_halt(NULL))
do_kill(SIGUSR1, msg); do_kill(SIGUSR2, msg);
break; break;
case CMD_POWEROFF: case CMD_POWEROFF:
+1 -2
View File
@@ -1,3 +1,2 @@
runlevel 1 runlevel 1
hostname rescue tty [12345] rescue
tty [12345] @console noclear nologin
+57 -23
View File
@@ -138,7 +138,7 @@ static int fredirect(const char *file)
{ {
int fd; int fd;
fd = open(file, O_WRONLY | O_APPEND); fd = open(file, O_WRONLY | O_APPEND | O_NOCTTY);
if (-1 != fd) { if (-1 != fd) {
dup2(fd, STDOUT_FILENO); dup2(fd, STDOUT_FILENO);
dup2(fd, STDERR_FILENO); dup2(fd, STDERR_FILENO);
@@ -206,8 +206,8 @@ static int lredirect(svc_t *svc)
/* Reset signals */ /* Reset signals */
sig_unblock(); sig_unblock();
if (!whichp(LOGIT_PATH)) { if (!whichp(_PATH_LOGIT)) {
logit(LOG_INFO, LOGIT_PATH " missing, using syslog for %s instead", svc->name); logit(LOG_INFO, _PATH_LOGIT " missing, using syslog for %s instead", svc->name);
fallback_logger(tag, prio); fallback_logger(tag, prio);
_exit(0); _exit(0);
} }
@@ -218,7 +218,7 @@ static int lredirect(svc_t *svc)
snprintf(sz, sizeof(sz), "%d", logfile_size_max); snprintf(sz, sizeof(sz), "%d", logfile_size_max);
snprintf(num, sizeof(num), "%d", logfile_count_max); snprintf(num, sizeof(num), "%d", logfile_count_max);
execlp(LOGIT_PATH, "logit", "-f", svc->log.file, "-n", sz, "-r", num, NULL); execlp(_PATH_LOGIT, "logit", "-f", svc->log.file, "-n", sz, "-r", num, NULL);
_exit(1); _exit(1);
} }
@@ -227,7 +227,7 @@ static int lredirect(svc_t *svc)
if (svc->log.prio[0]) if (svc->log.prio[0])
prio = svc->log.prio; prio = svc->log.prio;
execlp(LOGIT_PATH, "logit", "-t", tag, "-p", prio, NULL); execlp(_PATH_LOGIT, "logit", "-t", tag, "-p", prio, NULL);
_exit(1); _exit(1);
} }
@@ -347,8 +347,7 @@ static void source_env(svc_t *svc)
static int is_norespawn(void) static int is_norespawn(void)
{ {
return sig_stopped() || return fexist("/mnt/norespawn") ||
fexist("/mnt/norespawn") ||
fexist("/tmp/norespawn"); fexist("/tmp/norespawn");
} }
@@ -466,7 +465,7 @@ static int service_start(svc_t *svc)
return 1; return 1;
} }
if (svc_is_tty(svc)) { if (svc_is_tty(svc) && !svc->notty) {
char *dev = tty_canonicalize(svc->dev); char *dev = tty_canonicalize(svc->dev);
if (!dev || !tty_exists(dev)) { if (!dev || !tty_exists(dev)) {
@@ -564,10 +563,6 @@ static int service_start(svc_t *svc)
} }
args[i] = NULL; args[i] = NULL;
if (!svc_is_tty(svc))
redirect(svc);
sig_unblock();
/* /*
* The setsid() call takes care to detach the process * The setsid() call takes care to detach the process
* from its controlling terminal, preventing daemons * from its controlling terminal, preventing daemons
@@ -579,7 +574,13 @@ static int service_start(svc_t *svc)
* to the console at boot, for debugging or similar, * to the console at boot, for debugging or similar,
* have a look at redirect() and log.console instead. * have a look at redirect() and log.console instead.
*/ */
setsid(); pid = setsid();
if (pid < 1)
logit(LOG_ERR, "failed setsid(), pid %d: %s", pid, strerror(errno));
if (!svc_is_tty(svc))
redirect(svc);
sig_unblock();
if (svc_is_runtask(svc)) if (svc_is_runtask(svc))
status = exec_runtask(args[0], &args[1]); status = exec_runtask(args[0], &args[1]);
@@ -700,6 +701,7 @@ static void service_cleanup(svc_t *svc)
*/ */
static int service_stop(svc_t *svc) static int service_stop(svc_t *svc)
{ {
int do_progress = 1;
int rc = 0; int rc = 0;
if (!svc) if (!svc)
@@ -722,9 +724,19 @@ static int service_stop(svc_t *svc)
logit(LOG_CONSOLE | LOG_NOTICE, "Calling '%s stop' ...", svc->cmd); logit(LOG_CONSOLE | LOG_NOTICE, "Calling '%s stop' ...", svc->cmd);
} }
/*
* Make sure we are no longer considering the service to be starting (if
* that was the case). service_monitor() might get confused otherwise, and
* leave the service in a lingering, "stopping", state.
*/
svc_started(svc);
svc_set_state(svc, SVC_STOPPING_STATE); svc_set_state(svc, SVC_STOPPING_STATE);
if (runlevel != 1) if (!svc->desc[0])
do_progress = 0;
if (runlevel != 1 && do_progress)
print_desc("Stopping ", svc->desc); print_desc("Stopping ", svc->desc);
if (!svc_is_sysv(svc)) { if (!svc_is_sysv(svc)) {
@@ -744,6 +756,7 @@ static int service_stop(svc_t *svc)
pid = fork(); pid = fork();
switch (pid) { switch (pid) {
case 0: case 0:
setsid();
redirect(svc); redirect(svc);
exec_runtask(svc->cmd, args); exec_runtask(svc->cmd, args);
_exit(0); _exit(0);
@@ -1044,6 +1057,12 @@ static void parse_cmdline_args(svc_t *svc, char *cmd)
} }
} }
/*
* Check also for changes to /etc/default/foo, because this
* also constitutes changes to command line args.
*/
diff += conf_changed(svc_getenv(svc));
if (diff) if (diff)
_d("Modified args for %s detected", cmd); _d("Modified args for %s detected", cmd);
svc->args_dirty = (diff > 0); svc->args_dirty = (diff > 0);
@@ -1114,6 +1133,7 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
char *pre_script = NULL, *post_script = NULL; char *pre_script = NULL, *post_script = NULL;
struct tty tty = { 0 }; struct tty tty = { 0 };
char *dev = NULL; char *dev = NULL;
int respawn = 0;
int levels = 0; int levels = 0;
int manual = 0; int manual = 0;
char *line; char *line;
@@ -1173,6 +1193,8 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
name = cmd; name = cmd;
else if (!strncasecmp(cmd, "manual:yes", 10)) else if (!strncasecmp(cmd, "manual:yes", 10))
manual = 1; manual = 1;
else if (!strncasecmp(cmd, "respawn", 7))
respawn = 1;
else if (!strncasecmp(cmd, "halt:", 5)) else if (!strncasecmp(cmd, "halt:", 5))
halt = &cmd[5]; halt = &cmd[5];
else if (!strncasecmp(cmd, "kill:", 5)) else if (!strncasecmp(cmd, "kill:", 5))
@@ -1215,7 +1237,6 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
len += strlen(tty.cmd); len += strlen(tty.cmd);
else else
len += 3; len += 3;
len += tty.num + 1; len += tty.num + 1;
for (i = 0; i < tty.num; i++) for (i = 0; i < tty.num; i++)
len += strlen(tty.args[i]) + 1; len += strlen(tty.args[i]) + 1;
@@ -1224,7 +1245,7 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
if (!line) if (!line)
return errno; return errno;
snprintf(line, len, "%s", tty.cmd ?: "tty"); snprintf(line, len, "%s", tty.cmd ? tty.cmd : "tty");
for (i = 0; i < tty.num; i++) { for (i = 0; i < tty.num; i++) {
strlcat(line, " ", len); strlcat(line, " ", len);
strlcat(line, tty.args[i], len); strlcat(line, tty.args[i], len);
@@ -1238,6 +1259,9 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
dev = tty_atcon(); dev = tty_atcon();
else else
dev = tty.dev; dev = tty.dev;
/* tty's always respawn, never incr. restart_cnt */
respawn = 1;
next: next:
svc = svc_find_by_tty(dev); svc = svc_find_by_tty(dev);
} else } else
@@ -1254,6 +1278,9 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
if (type == SVC_TYPE_SERVICE && manual) if (type == SVC_TYPE_SERVICE && manual)
svc_stop(svc); svc_stop(svc);
} else { } else {
/* update type, may have changed from service -> task */
svc->type = type;
/* e.g., if missing cmd or env before */ /* e.g., if missing cmd or env before */
svc_unblock(svc); svc_unblock(svc);
} }
@@ -1295,18 +1322,19 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
svc->nowait = tty.nowait; svc->nowait = tty.nowait;
svc->nologin = tty.nologin; svc->nologin = tty.nologin;
svc->notty = tty.notty; svc->notty = tty.notty;
svc->rescue = tty.rescue;
/* TTYs cannot be redirected */ /* TTYs cannot be redirected */
log = NULL; log = NULL;
/* Create name:id tuple for identiy, e.g., tty:S0 */ /* Create name:id tuple for identiy, e.g., tty:S0 */
ptr = strrchr(svc->dev, '/'); ptr = strrchr(svc->dev, '/');
if (ptr) { if (ptr)
ptr++; ptr++;
if (!strncmp(ptr, "tty", 3)) else
ptr += 3;
} else
ptr = svc->dev; ptr = svc->dev;
if (!strncmp(ptr, "tty", 3))
ptr += 3;
if (!id || id[0] == 0) if (!id || id[0] == 0)
id = ptr; id = ptr;
strlcpy(svc->name, "tty", sizeof(svc->name)); strlcpy(svc->name, "tty", sizeof(svc->name));
@@ -1340,6 +1368,8 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
parse_env(svc, env); parse_env(svc, env);
if (file) if (file)
strlcpy(svc->file, file, sizeof(svc->file)); strlcpy(svc->file, file, sizeof(svc->file));
if (respawn)
svc->respawn = 1;
/* Set configured limits */ /* Set configured limits */
memcpy(svc->rlimit, rlimit, sizeof(svc->rlimit)); memcpy(svc->rlimit, rlimit, sizeof(svc->rlimit));
@@ -1392,7 +1422,7 @@ void service_monitor(pid_t lost, int status)
{ {
svc_t *svc; svc_t *svc;
if (fexist(SYNC_SHUTDOWN) || lost <= 1) if (lost <= 1)
return; return;
svc = svc_find_by_pid(lost); svc = svc_find_by_pid(lost);
@@ -1560,11 +1590,15 @@ static void service_retry(svc_t *svc)
char *restart_cnt = (char *)&svc->restart_cnt; char *restart_cnt = (char *)&svc->restart_cnt;
service_timeout_cancel(svc); service_timeout_cancel(svc);
if (svc->respawn) {
_d("%s crashed, respawning ...", svc->cmd);
svc_unblock(svc);
service_step(svc);
return;
}
if (svc->state != SVC_HALTED_STATE || if (svc->state != SVC_HALTED_STATE ||
svc->block != SVC_BLOCK_RESTARTING) { svc->block != SVC_BLOCK_RESTARTING) {
if (!svc_is_tty(svc))
_d("%s not crashing anymore", svc->cmd);
*restart_cnt = 0; *restart_cnt = 0;
return; return;
} }
+52 -71
View File
@@ -31,28 +31,31 @@
* does on USR1 with their FIFO/D-Bus. * does on USR1 with their FIFO/D-Bus.
* *
* SIGUSR1 * SIGUSR1
* Calls shutdown hooks, including HOOK_SHUTDOWN, stops all running * Restarts API (initctl) socket, like SysV init and systemd does
* processes, and unmounts all file systems. Then tells kernel to * on USR1 with their FIFO/D-Bus.
* halt. Most people these days want SIGUSR2 though.
* *
* (SysV init and systemd use this to re-open their FIFO/D-Bus.) * Finit <= 4.0 used to perform a system halt on USR1. This caused
* some quite nasty problems on systems with both systemd/sysvinit
* installed alongside finit. For compatibility reasons Finit 4.1
* changed to partial SIGHUP (api_exit/init).
* *
* SIGUSR2 * SIGUSR2
* Like SIGUSR1, but tell kernel to power-off the system, if ACPI * Calls shutdown hooks, including HOOK_SHUTDOWN, stops all running
* or similar exists to actually do this. If the kernel fails * processes, and unmounts all file systems. Then tells kernel to
* power-off, Finit falls back to halt. * power-off the system, if ACPI or similar exists to actually do
* this. If the kernel fails power-off, Finit falls back to halt.
* *
* (SysV init N/A, systemd dumps its internal state to log.) * (SysV init N/A, systemd dumps its internal state to log.)
* *
* SIGTERM * SIGTERM
* Like SIGUSR1, but tell kernel to reboot the system when done. * Like SIGUSR2, but tell kernel to reboot the system when done.
* *
* (SysV init N/A, systemd rexecutes itself.) * (SysV init N/A, systemd rexecutes itself.)
* *
* SIGINT * SIGINT
* Sent from kernel when the CTRL-ALT-DEL key combo is pressed. * Sent from kernel when the CTRL-ALT-DEL key combo is pressed.
* SysV init and systemd default to reboot with `shutdown -r`. * SysV init and systemd default to reboot with `shutdown -r`.
* Finit currently forwards this to SIGTERM. * Finit defaults to nothing but sets sys/key/ctrlaltdel condition.
* *
* SIGWINCH * SIGWINCH
* Sent from kernel when the KeyboardSignal key is pressed. Some * Sent from kernel when the KeyboardSignal key is pressed. Some
@@ -63,7 +66,7 @@
* Sent from a power daemon, like powstatd(8), on changes to the * Sent from a power daemon, like powstatd(8), on changes to the
* UPS status. Traditionally SysV init read /etc/powerstatus and * UPS status. Traditionally SysV init read /etc/powerstatus and
* acted on "OK", "FAIL", or "LOW" and then removed the file. * acted on "OK", "FAIL", or "LOW" and then removed the file.
* Finit currently forwards this to SIGUSR2. * Finit defaults to nothing but sets sys/pwr/fail condition.
*/ */
#include <dirent.h> #include <dirent.h>
@@ -73,6 +76,7 @@
#include <lite/lite.h> #include <lite/lite.h>
#include "finit.h" #include "finit.h"
#include "cond.h"
#include "conf.h" #include "conf.h"
#include "config.h" #include "config.h"
#include "helpers.h" #include "helpers.h"
@@ -92,11 +96,9 @@ extern svc_t *wdog;
*/ */
shutop_t halt = SHUT_DEFAULT; shutop_t halt = SHUT_DEFAULT;
static int stopped = 0;
static uev_t sigterm_watcher, sigusr1_watcher, sigusr2_watcher; static uev_t sigterm_watcher, sigusr1_watcher, sigusr2_watcher;
static uev_t sighup_watcher, sigint_watcher, sigpwr_watcher; static uev_t sighup_watcher, sigint_watcher, sigpwr_watcher;
static uev_t sigchld_watcher; static uev_t sigchld_watcher;
static uev_t sigstop_watcher, sigtstp_watcher, sigcont_watcher;
static struct sigmap { static struct sigmap {
int num; int num;
@@ -207,8 +209,6 @@ void do_kill(int signo)
void do_shutdown(shutop_t op) void do_shutdown(shutop_t op)
{ {
touch(SYNC_SHUTDOWN);
if (sdown) if (sdown)
run_interactive(sdown, "Calling shutdown hook: %s", sdown); run_interactive(sdown, "Calling shutdown hook: %s", sdown);
@@ -308,7 +308,9 @@ static void sighup_cb(uev_t *w, void *arg, int events)
} }
/* /*
* SIGINT: Should generate <sys/key/ctrlaltdel> condition, for now reboot * SIGINT: generates <sys/key/ctrlaltdel> condition, which the sys.so
* plugin picks up and tells Finit to start any service(s) or
* task(s) associated with the condition.
*/ */
static void sigint_cb(uev_t *w, void *arg, int events) static void sigint_cb(uev_t *w, void *arg, int events)
{ {
@@ -318,12 +320,27 @@ static void sigint_cb(uev_t *w, void *arg, int events)
return; return;
} }
halt = SHUT_REBOOT; cond_set_oneshot_noupdate("sys/key/ctrlaltdel");
service_runlevel(6);
} }
/* /*
* SIGUSR1: BusyBox style halt * SIGPWR: generates <sys/pwr/fail> condition, which the sys.so plugin
* picks up and tells Finit to start any service(s) or task(s)
* associated with the condition.
*/
static void sigpwr_cb(uev_t *w, void *arg, int events)
{
_d("...");
if (UEV_ERROR == events) {
_e("Unrecoverable error in signal watcher");
return;
}
cond_set_oneshot_noupdate("sys/pwr/fail");
}
/*
* SIGUSR1: SysV init/systemd API socket restart
*/ */
static void sigusr1_cb(uev_t *w, void *arg, int events) static void sigusr1_cb(uev_t *w, void *arg, int events)
{ {
@@ -333,8 +350,9 @@ static void sigusr1_cb(uev_t *w, void *arg, int events)
return; return;
} }
halt = SHUT_HALT; /* Restart initctl API domain socket, similar to systemd/SysV init */
service_runlevel(0); api_exit();
api_init(w->ctx);
} }
/* /*
@@ -381,49 +399,17 @@ static void sigchld_cb(uev_t *w, void *arg, int events)
} }
/* Reap all the children! */ /* Reap all the children! */
do { while (1) {
pid = waitpid(-1, &status, WNOHANG); pid = waitpid(-1, &status, WNOHANG);
if (pid > 0) { if (pid <= 0) {
_d("Collected child %d", pid); if (pid == -1 && errno == EINTR)
service_monitor(pid, status); continue;
break;
} }
} while (pid > 0);
}
/* _d("Collected child %d", pid);
* SIGSTOP/SIGTSTP: Paused by user or netflash service_monitor(pid, status);
*/
static void sigstop_cb(uev_t *w, void *arg, int events)
{
if (UEV_ERROR == events) {
_e("Unrecoverable error in signal watcher");
return;
} }
touch(SYNC_STOPPED);
stopped++;
}
/*
* SIGCONT: Restart service monitor
*/
static void sigcont_cb(uev_t *w, void *arg, int events)
{
if (UEV_ERROR == events) {
_e("Unrecoverable error in signal watcher");
return;
}
stopped = 0;
erase(SYNC_STOPPED);
}
/*
* Is SIGSTOP asserted?
*/
int sig_stopped(void)
{
return stopped;
} }
/* /*
@@ -513,17 +499,17 @@ void sig_setup(uev_ctx_t *ctx)
_d("Setup signals"); _d("Setup signals");
/* Cleanup any stale finit control files */ /*
erase(SYNC_SHUTDOWN); * Standard SysV init calls ctrl-alt-delete handler
erase(SYNC_STOPPED); * We need to disable kernel default so it sends us SIGINT
*/
/* Standard SysV init calls ctrl-alt-delete handler */ reboot(RB_DISABLE_CAD);
uev_signal_init(ctx, &sigint_watcher, sigint_cb, NULL, SIGINT); uev_signal_init(ctx, &sigint_watcher, sigint_cb, NULL, SIGINT);
/* BusyBox init style signals for halt, power-off and reboot. */ /* BusyBox/SysV init style signals for halt, power-off and reboot. */
uev_signal_init(ctx, &sigusr1_watcher, sigusr1_cb, NULL, SIGUSR1); uev_signal_init(ctx, &sigusr1_watcher, sigusr1_cb, NULL, SIGUSR1);
uev_signal_init(ctx, &sigusr2_watcher, sigusr2_cb, NULL, SIGUSR2); uev_signal_init(ctx, &sigusr2_watcher, sigusr2_cb, NULL, SIGUSR2);
uev_signal_init(ctx, &sigpwr_watcher, sigusr2_cb, NULL, SIGPWR); uev_signal_init(ctx, &sigpwr_watcher, sigpwr_cb, NULL, SIGPWR);
uev_signal_init(ctx, &sigterm_watcher, sigterm_cb, NULL, SIGTERM); uev_signal_init(ctx, &sigterm_watcher, sigterm_cb, NULL, SIGTERM);
/* Some C APIs may need SIGALRM for implementing timers. */ /* Some C APIs may need SIGALRM for implementing timers. */
@@ -535,11 +521,6 @@ void sig_setup(uev_ctx_t *ctx)
/* After initial bootstrap of Finit we call the service monitor to reap children */ /* After initial bootstrap of Finit we call the service monitor to reap children */
uev_signal_init(ctx, &sigchld_watcher, sigchld_cb, NULL, SIGCHLD); uev_signal_init(ctx, &sigchld_watcher, sigchld_cb, NULL, SIGCHLD);
/* Stopping init is a bit tricky. */
uev_signal_init(ctx, &sigstop_watcher, sigstop_cb, NULL, SIGSTOP);
uev_signal_init(ctx, &sigtstp_watcher, sigstop_cb, NULL, SIGTSTP);
uev_signal_init(ctx, &sigcont_watcher, sigcont_cb, NULL, SIGCONT);
setsid(); setsid();
} }
-4
View File
@@ -28,9 +28,6 @@
#include <signal.h> #include <signal.h>
#include <uev/uev.h> #include <uev/uev.h>
#define SYNC_SHUTDOWN "/var/run/finit/.shutdown"
#define SYNC_STOPPED "/var/run/finit/.stopped"
#define SETSIG(sa, sig, fun, flags) \ #define SETSIG(sa, sig, fun, flags) \
do { \ do { \
sa.sa_sigaction = fun; \ sa.sa_sigaction = fun; \
@@ -69,7 +66,6 @@ typedef enum {
extern shutop_t halt; extern shutop_t halt;
void do_shutdown (shutop_t op); void do_shutdown (shutop_t op);
int sig_stopped (void);
int sig_num (const char *name); int sig_num (const char *name);
void sig_init (void); void sig_init (void);
void sig_unblock (void); void sig_unblock (void);
+4 -1
View File
@@ -150,7 +150,10 @@ restart:
} }
_d("Setting new runlevel --> %d <-- previous %d", runlevel, prevlevel); _d("Setting new runlevel --> %d <-- previous %d", runlevel, prevlevel);
logit(LOG_CONSOLE | LOG_NOTICE, "%s, entering runlevel %d", INIT_HEADING, runlevel); if (osheading)
logit(LOG_CONSOLE | LOG_NOTICE, "%s, entering runlevel %d", osheading, runlevel);
else
logit(LOG_CONSOLE | LOG_NOTICE, "Entering runlevel %d", runlevel);
runlevel_set(prevlevel, runlevel); runlevel_set(prevlevel, runlevel);
/* Disable login in single-user mode as well as shutdown/reboot */ /* Disable login in single-user mode as well as shutdown/reboot */
+7 -5
View File
@@ -27,7 +27,8 @@
#include <sys/ttydefaults.h> /* Not included by default in musl libc */ #include <sys/ttydefaults.h> /* Not included by default in musl libc */
#include <termios.h> #include <termios.h>
#include <helpers.h> #include "helpers.h"
#include "sig.h"
/* B0 means; keep kernel default */ /* B0 means; keep kernel default */
speed_t stty_parse_speed(char *baud) speed_t stty_parse_speed(char *baud)
@@ -90,6 +91,10 @@ speed_t stty_parse_speed(char *baud)
void stty(int fd, speed_t speed) void stty(int fd, speed_t speed)
{ {
struct termios term; struct termios term;
struct sigaction sa;
/* Ignore SIGINT during login phase, /bin/login resets */
IGNSIG(sa, SIGINT, SA_RESTART);
tcdrain(fd); tcdrain(fd);
if (tcgetattr(fd, &term)) if (tcgetattr(fd, &term))
@@ -98,22 +103,19 @@ void stty(int fd, speed_t speed)
if (speed != B0) { if (speed != B0) {
cfsetispeed(&term, speed); cfsetispeed(&term, speed);
cfsetospeed(&term, speed); cfsetospeed(&term, speed);
tcsetattr(fd, TCSAFLUSH, &term);
} }
/* Modem specific control flags */ /* Modem specific control flags */
term.c_cflag &= ~(PARENB|PARODD|CSTOPB|CRTSCTS); term.c_cflag &= ~(PARENB|PARODD|CSTOPB|CRTSCTS);
term.c_cflag |= CS8|HUPCL|CREAD|CLOCAL; term.c_cflag |= CS8|HUPCL|CREAD|CLOCAL;
term.c_line = 0; term.c_line = 0;
tcsetattr(fd, TCSAFLUSH, &term);
/* Timeouts, minimum chars and default flags */ /* Timeouts, minimum chars and default flags */
term.c_cc[VTIME] = 0; term.c_cc[VTIME] = 0;
term.c_cc[VMIN] = 1; term.c_cc[VMIN] = 1;
term.c_iflag = ICRNL|IXON|IXOFF; term.c_iflag = ICRNL|IXON|IXOFF|IUTF8;
term.c_oflag = OPOST|ONLCR; term.c_oflag = OPOST|ONLCR;
term.c_lflag |= ICANON|ISIG|ECHO|ECHOE|ECHOK|ECHOKE; term.c_lflag |= ICANON|ISIG|ECHO|ECHOE|ECHOK|ECHOKE;
tcsetattr(fd, TCSAFLUSH, &term);
/* Reset special characters to defaults */ /* Reset special characters to defaults */
term.c_cc[VINTR] = CINTR; term.c_cc[VINTR] = CINTR;
+213
View File
@@ -0,0 +1,213 @@
/* poor man's sulogin, may be statically linked
*
* Copyright (c) 2021 Joachim Wiberg <troglobit@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <crypt.h>
#include <err.h>
#include <paths.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <sys/ioctl.h>
/* getpwnam() cannot be used when statically linked */
static int get_passwd(struct passwd *pw)
{
char *ptr, *tmp;
char buf[256];
FILE *fp;
fp = fopen("/etc/passwd", "r");
if (!fp)
return 1;
while ((tmp = fgets(buf, sizeof(buf), fp))) {
if (!strstr(buf, ":0:0:"))
continue; /* not uid 0 */
ptr = strsep(&tmp, ":");
if (!ptr)
break;
pw->pw_name = strdup(ptr);
ptr = strsep(&tmp, ":");
if (!ptr)
break;
/* x : password in /etc/shadow, see passwd(5) */
if (ptr[0] != 'x')
pw->pw_passwd = strdup(ptr);
break; /* done, check it */
}
fclose(fp);
if (!pw->pw_name)
return 1; /* no uid 0 here */
if (pw->pw_passwd)
return 0; /* passwd in passwd */
fp = fopen("/etc/shadow", "r");
if (!fp) {
if (pw->pw_name)
free(pw->pw_name);
return 1;
}
while ((tmp = fgets(buf, sizeof(buf), fp))) {
ptr = strsep(&tmp, ":");
if (!ptr)
break;
if (!strcmp(tmp, pw->pw_name))
continue;
ptr = strsep(&tmp, ":");
if (!ptr)
break;
pw->pw_passwd = strdup(ptr);
break;
}
fclose(fp);
if (!pw->pw_passwd) {
if (pw->pw_name)
free(pw->pw_name);
return 1; /* nopass => nologin */
}
return 0; /* passwd in shadow */
}
static void freepw(struct passwd *pw)
{
if (pw->pw_passwd) {
free(pw->pw_passwd);
pw->pw_passwd = NULL;
}
free(pw->pw_name);
}
static int sh(void)
{
/*
* Become session leader and set controlling TTY
* to enable Ctrl-C and job control in shell.
*/
setsid();
ioctl(STDIN_FILENO, TIOCSCTTY, 1);
setenv("PS1", "# ", 1);
return execl(_PATH_BSHELL, "-sh", NULL);
}
int main(void)
{
struct termios old, raw;
struct passwd pw = { 0 };
int rc = 1, ch;
if (get_passwd(&pw))
err(1, "reading credentials for UID 0");
switch (pw.pw_passwd[0]) {
case '*': case '!': /* login disabled, allow */
printf("The %s account is locked, allowing entry.\n", pw.pw_name);
/* fallthrough */
case 0: /* no password at all? */
printf("Press enter for maintenance (Ctrl-D to continue) ");
fflush(stdout);
ch = getchar();
if (ch == 4 || ch == EOF) { /* Ctrl-D */
rc = 0;
goto done;
}
goto nopass;
case '$': /* md5/shaN or similar, original DES not supported */
break;
default:
break;
}
if (tcgetattr(1, &old) < 0)
goto done; /* failed reading tty */
raw = old;
raw.c_lflag &= ~ICANON;
raw.c_lflag &= ~ECHO;
raw.c_cc[VMIN] = 1;
raw.c_cc[VTIME] = 0;
if (tcsetattr(1, TCSANOW, &raw) < 0) {
tcsetattr(1, TCSANOW, &old);
goto done; /* failed setting raw/pw mode */
}
while (1) {
char pwd[256]; /* reasonable max? */
size_t i = 0;
char *passwd;
printf("Give %s password for maintenance (Ctrl-D to continue): ", pw.pw_name);
fflush(stdout);
do {
ch = getchar();
if (ch == '\n' || ch == '\r')
break;
if (ch == 4 || ch == EOF) { /* Ctrl-D */
rc = 0;
goto exit;
}
pwd[i++] = ch;
} while (i + 1 < sizeof(pwd));
pwd[i] = 0;
puts("");
passwd = crypt(pwd, pw.pw_passwd);
if (passwd && !strcmp(passwd, pw.pw_passwd)) {
tcsetattr(1, TCSANOW, &old); /* restore tty */
nopass:
freepw(&pw);
return sh();
}
}
exit:
tcsetattr(1, TCSANOW, &old); /* restore tty */
puts("");
done:
freepw(&pw);
return rc;
}
/**
* Local Variables:
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+1 -1
View File
@@ -433,7 +433,7 @@ svc_t *svc_find_by_tty(char *dev)
{ {
svc_t *svc, *iter = NULL; svc_t *svc, *iter = NULL;
/* this is valid for fallback (notty) shells */ /* rescue (notty) shells have no device node */
if (!dev) if (!dev)
return NULL; return NULL;
+2
View File
@@ -127,6 +127,7 @@ typedef struct svc {
/* Counters */ /* Counters */
char once; /* run/task, (at least) once per runlevel */ char once; /* run/task, (at least) once per runlevel */
char respawn; /* ttys, or services with `respawn`, never increment restart_cnt */
const char restart_cnt; /* Incremented for each restart by service monitor. */ const char restart_cnt; /* Incremented for each restart by service monitor. */
union { union {
@@ -149,6 +150,7 @@ typedef struct svc {
char nowait; char nowait;
char nologin; char nologin;
char notty; char notty;
char rescue;
}; };
}; };
+38 -20
View File
@@ -158,8 +158,10 @@ int tty_parse_args(char *cmd, struct tty *tty)
else if (!strcmp(cmd, "nologin")) else if (!strcmp(cmd, "nologin"))
tty->nologin = 1; tty->nologin = 1;
else if (!strcmp(cmd, "notty")) else if (!strcmp(cmd, "notty"))
tty->notty = 1; /* for fallback shell */ tty->notty = 1; /* for board bringup */
else if (!access(cmd, X_OK)) else if (!strcmp(cmd, "rescue"))
tty->rescue = 1; /* for rescue shells */
else if (whichp(cmd)) /* in $PATH? */
tty->cmd = cmd; tty->cmd = cmd;
else else
tty->args[tty->num++] = cmd; tty->args[tty->num++] = cmd;
@@ -167,6 +169,10 @@ int tty_parse_args(char *cmd, struct tty *tty)
cmd = strtok(NULL, " \t"); cmd = strtok(NULL, " \t");
} while (cmd && tty->num < NELEMS(tty->args)); } while (cmd && tty->num < NELEMS(tty->args));
/* rescue shells are always notty */
if (tty->rescue)
tty->notty = 1;
/* skip /dev probe, we just want a bríngup shell */ /* skip /dev probe, we just want a bríngup shell */
if (tty->notty) if (tty->notty)
return 0; return 0;
@@ -215,32 +221,47 @@ int tty_parse_args(char *cmd, struct tty *tty)
_d("Registering %s getty on TTY %s at %s baud with term %s", tty->cmd ? "external" : "built-in", _d("Registering %s getty on TTY %s at %s baud with term %s", tty->cmd ? "external" : "built-in",
tty->dev, tty->baud ?: "0", tty->term ?: "N/A"); tty->dev, tty->baud ?: "0", tty->term ?: "N/A");
/* Built-in getty now comes as standalone program */
if (!tty->cmd)
tty->cmd = _PATH_GETTY;
return 0; return 0;
} }
int tty_exists(char *dev) int tty_exists(char *dev)
{ {
int fd, result;
struct termios c; struct termios c;
int fd, rc;
fd = open(dev, O_RDWR); fd = open(dev, O_RDWR | O_NOCTTY);
if (-1 == fd) if (-1 == fd)
return 0; return 0;
/* XXX: Add check for errno == EIO? */ /* XXX: Add check for errno == EIO? */
result = tcgetattr(fd, &c); rc = tcgetattr(fd, &c);
close(fd); close(fd);
return result == 0; return rc == 0;
} }
int tty_exec(svc_t *tty) int tty_exec(svc_t *svc)
{ {
char *args[MAX_NUM_SVC_ARGS]; char *args[MAX_NUM_SVC_ARGS];
char *dev; char *dev;
int i, j; int i, j;
if (tty->notty) { /* try to protect system with sulogin, fall back to root shell */
if (svc->notty) {
if (svc->rescue) {
/* check if bundled one is available */
if (whichp(_PATH_SULOGIN))
return execl(_PATH_SULOGIN, _PATH_SULOGIN, NULL);
/* util-linux or busybox, no args for compat */
if (whichp("sulogin"))
return execlp("sulogin", "sulogin", NULL);
}
/* /*
* Become session leader and set controlling TTY * Become session leader and set controlling TTY
* to enable Ctrl-C and job control in shell. * to enable Ctrl-C and job control in shell.
@@ -249,12 +270,12 @@ int tty_exec(svc_t *tty)
ioctl(STDIN_FILENO, TIOCSCTTY, 1); ioctl(STDIN_FILENO, TIOCSCTTY, 1);
prctl(PR_SET_NAME, "finitsh", 0, 0, 0); prctl(PR_SET_NAME, "finitsh", 0, 0, 0);
return execl(_PATH_BSHELL, _PATH_BSHELL, NULL); return execl(_PATH_BSHELL, "-sh", NULL);
} }
dev = tty_canonicalize(tty->dev); dev = tty_canonicalize(svc->dev);
if (!dev) { if (!dev) {
_d("%s: Cannot find TTY device: %s", tty->dev, strerror(errno)); _d("%s: Cannot find TTY device: %s", svc->dev, strerror(errno));
return EX_CONFIG; return EX_CONFIG;
} }
@@ -263,24 +284,21 @@ int tty_exec(svc_t *tty)
return EX_OSFILE; return EX_OSFILE;
} }
if (tty->nologin) { if (svc->nologin) {
_d("%s: Starting /bin/sh ...", dev); _d("%s: Starting /bin/sh ...", dev);
return run_sh(dev, tty->noclear, tty->nowait, tty->rlimit); return run_sh(dev, svc->noclear, svc->nowait, svc->rlimit);
} }
_d("%s: Starting %sgetty ...", dev, !tty->cmd ? "built-in " : ""); _d("%s: Starting %s ...", dev, svc->cmd);
if (!strcmp(tty->cmd, "tty"))
return run_getty(dev, tty->baud, tty->term, tty->noclear, tty->nowait, tty->rlimit);
for (i = 1, j = 0; i < MAX_NUM_SVC_ARGS; i++) { for (i = 1, j = 0; i < MAX_NUM_SVC_ARGS; i++) {
if (!tty->args[i][0]) if (!svc->args[i][0])
break; break;
args[j++] = tty->args[i]; args[j++] = svc->args[i];
} }
args[j++] = NULL; args[j++] = NULL;
return run_getty2(dev, tty->cmd, args, tty->noclear, tty->nowait, tty->rlimit); return run_getty(dev, svc->cmd, args, svc->noclear, svc->nowait, svc->rlimit);
} }
/** /**
+1
View File
@@ -40,6 +40,7 @@ struct tty {
char nowait; char nowait;
char nologin; char nologin;
char notty; char notty;
char rescue;
}; };
char *tty_canonicalize (char *dev); char *tty_canonicalize (char *dev);
+41 -7
View File
@@ -154,6 +154,35 @@ char *str(char *fmt, ...)
return buf; return buf;
} }
int fnread(char *buf, size_t len, char *fmt, ...)
{
char path[256];
va_list ap;
FILE *fp;
va_start(ap, fmt);
vsnprintf(path, sizeof(path), fmt, ap);
va_end(ap);
if (!buf || !len) {
struct stat st;
if (stat(path, &st))
return -1;
return (ssize_t)st.st_size;
}
fp = fopen(path, "r");
if (!fp)
return -1;
len = fread(buf, sizeof(char), len, fp);
fclose(fp);
return (int)len;
}
int fnwrite(char *value, char *fmt, ...) int fnwrite(char *value, char *fmt, ...)
{ {
char path[256]; char path[256];
@@ -185,14 +214,19 @@ int fnwrite(char *value, char *fmt, ...)
return rc; return rc;
} }
/* int fngetint(char *path, int *val)
* musl libc default to /dev/null/utmp and /dev/null/wtmp, respectively.
* See https://www.openwall.com/lists/musl/2012/03/04/4 for reasoning.
* Also, there's no __MUSL__, so we cannot make a libc-specific #ifdef
*/
int has_utmp(void)
{ {
return strncmp(_PATH_UTMP, "/dev/null", 9); char buf[10];
if (fnread(buf, sizeof(buf), path) <= 0)
return -1;
errno = 0;
*val = strtoul(chomp(buf), NULL, 0);
if (errno)
return -1;
return 0;
} }
int strtobytes(char *arg) int strtobytes(char *arg)
+2 -1
View File
@@ -50,9 +50,10 @@ extern char *prognm;
char *progname (char *arg0); char *progname (char *arg0);
char *str (char *fmt, ...); char *str (char *fmt, ...);
int fnread (char *buf, size_t len, char *fmt, ...);
int fnwrite (char *value, char *fmt, ...); int fnwrite (char *value, char *fmt, ...);
int fngetint (char *path, int *val);
int has_utmp (void);
int strtobytes (char *arg); int strtobytes (char *arg);
char *sig2str (int sig); char *sig2str (int sig);
char *code2str (int code); char *code2str (int code);
+10
View File
@@ -42,6 +42,16 @@ int utmp_show (char *file);
void runlevel_set (int pre, int now); void runlevel_set (int pre, int now);
/*
* musl libc default to /dev/null/utmp and /dev/null/wtmp, respectively.
* See https://www.openwall.com/lists/musl/2012/03/04/4 for reasoning.
* Also, there's no __MUSL__, so we cannot make a libc-specific #ifdef
*/
static inline int has_utmp(void)
{
return strncmp(_PATH_UTMP, "/dev/null", 9);
}
#endif /* FINIT_UTMP_API_H_ */ #endif /* FINIT_UTMP_API_H_ */
/** /**