- Introduction
- Features
- Bootstrap
- Runlevels
- Syntax
- Rebooting & Halting
- Commands & Status
- Tips & Tricks with the kernel cmdline
- Building
- Requirements
- Origin & References
Introduction
Finit is a simple alternative to SysV init and systemd. It was reverse engineered from the EeePC fastinit over ten 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. For working examples, see the contrib/ section with tutorials for:
The screenshot shows Alpine Linux.
Example
This example /etc/finit.conf
can also be split up in multiple .conf
files in /etc/finit.d
. Available, but not yet enabled, services can
be placed in /etc/finit.d/available
and enabled by an operator using
the initctl tool.
# Fallback if /etc/hostname is missing
host default
# Runlevel to start after bootstrap, 'S', default: 2
runlevel 2
# Max file size for each log file: 100 kiB, rotate max 4 copies:
# log => log.1 => log.2.gz => log.3.gz => log.4.gz
log size=100k count=4
# Services to be monitored and respawned as needed
service [S12345] /sbin/watchdogd -L -f -- System watchdog daemon
service [S12345] /sbin/syslogd -n -b 3 -D -- System log daemon
service [S12345] /sbin/klogd -n -- Kernel log daemon
service [2345] /sbin/lldpd -d -c -M1 -H0 -i -- LLDP daemon (IEEE 802.1ab)
# The BusyBox ntpd does not use syslog when running in the foreground
# So we use this trick to redirect stdout/stderr to a log file. The
# log file is rotated with the above settings. The condition declares
# a dependency on a system default route (gateway) to be set. A single
# <!> at the beginning means ntpd does not respect SIGHUP for restart.
service [2345] log:/var/log/ntpd.log <!net/route/default> /sbin/ntpd -n -l -I eth0 -- NTP daemon
# For multiple instances of the same service, add :ID somewhere between
# the service/run/task keyword and the command.
service :1 [2345] /sbin/merecat -n -p 80 /var/www -- Web server
service :2 [2345] /sbin/merecat -n -p 8080 /var/www -- Old web server
# Alternative method instead of below runparts, can also use /etc/rc.local
#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/kbd start -- Preparing console
# Inetd services to start on demand, with alternate ports and filtering
inetd ftp/tcp nowait [2345] /sbin/in.ftpd -- FTP daemon
inetd tftp/udp wait [2345] /sbin/in.tftpd -- TFTP daemon
inetd time/udp wait [2345] internal -- UNIX rdate service
inetd time/tcp nowait [2345] internal -- UNIX rdate service
inetd 3737/tcp nowait [2345] internal.time -- UNIX rdate service
inetd telnet/tcp nowait [2345] /sbin/telnetd -i -F -- Telnet daemon
inetd 2323/tcp nowait [2345] /sbin/telnetd -i -F -- Telnet daemon
inetd 222/tcp@eth0 nowait [2345] /sbin/dropbear -i -R -F -- SSH service
inetd ssh/tcp@*,!eth0 nowait [2345] /sbin/dropbear -i -R -F -- SSH service
# Run start scripts from this directory
# runparts /etc/start.d
# Virtual consoles run BusyBox getty, keep kernel default speed
tty [12345] /sbin/getty -L 0 /dev/tty1 linux
tty [12345] /sbin/getty -L 0 /dev/tty2 linux
tty [12345] /sbin/getty -L 0 /dev/tty3 linux
# Use built-in getty for serial port and USB serial
tty [12345] /dev/ttyAMA0 noclear nowait
tty [12345] /dev/ttyUSB0 noclear
# Just give me a shell, I need to debug this embedded system!
tty [12345] @console noclear nologin
The service
stanza, as well as task
, run
, inetd
and others are
described in full in doc/config.md. Here's a quick
overview of some of the most common components needed to start a UNIX
daemon:
service [LVLS] <COND> /path/to/daemon ARGS -- Some text
^ ^ ^ ^ ^ ^
| | | | | `-- Optional description
| | | | `---------- Daemon arguments
| | | `-------------------------- Path to daemon
| | `--------------------------------- Optional conditions
| `---------------------------------------- Optional Runlevels
`------------------------------------------------ Monitored application
Some components are optional: runlevel(s), condition(s) and description, making it easy to create simple start scripts and still possible for more advanced uses as well:
service /usr/sbin/sshd -D
Dependencies are handled using conditions. One of the most common conditions is to wait for basic networking to become available:
service <net/route/default> /usr/sbin/nginx -- High performace HTTP server
Here is another example where we instruct Finit to not start BusyBox
ntpd
until syslogd
has started properly. Finit waits for syslogd
to create its PID file, by default /var/run/syslogd.pid
.
service [2345] log <svc/sbin/syslogd> /usr/sbin/ntpd -n -N -p pool.ntp.org
service [S12345] /sbin/syslogd -n -- Syslog daemon
Notice the log
keyword, BusyBox ntpd
uses stderr
for logging when
run in the foreground. With log
Finit redirects stdout
+ stderr
to the system log daemon using the command line logger(1)
tool.
A service, or task, can have multiple dependencies listed. Here we wait
for both syslogd
to have started and basic networking to be up:
service [2345] log <svc/sbin/syslogd,net/route/default> /usr/sbin/ntpd -n -N -p pool.ntp.org
If either condition fails, e.g. loss of networking, ntpd
is stopped
and as soon as it comes back up again ntpd
is restarted automatically.
Note: Make sure daemons do not fork and detach themselves from the
controlling TTY, usually an -n
or -f
flag, or -D
as in the case
of OpenSSH above. If it detaches itself, Finit cannot monitor it and
will instead try to restart it.
Features
Process Supervision
Start, monitor and restart services should they fail.
Inetd
Finit comes with a built-in inetd server. No need to maintain a separate config file for services that you want to start on demand.
All inetd services started can be filtered per port and inbound interface, reducing the need for a full blown firewall.
Built-in optional inetd services:
For more information, see doc/inetd.md.
Getty
Finit supports external getty but also comes with a limited built-in
Getty, useful for really small systems. A getty sets up the TTY and
waits for user input before handing over to /bin/login
, which is
responsible for handling the actual authentication.
tty [12345] /dev/tty1 nowait linux
tty [12345] /dev/ttyAMA0 noclear vt100
tty [12345] /sbin/getty -L /dev/ttyAMA0 vt100
Users of embedded systems may want to enable automatic serial console
with the special @console
device. This works regardless weather the
system uses ttyS0
, ttyAMA0
, ttyMXC0
, or anything else. Finit
figures it out by querying sysfs: /sys/class/tty/console/active
.
tty [12345] @console linux noclear
Notice the optional noclear
, nowait
, and nologin
flags. The
latter is for skipping the login process entirely. For more information,
see doc/config.md.
Runlevels
Support for SysV init-style runlevels is available, in the same
minimal style as everything else in Finit. The [2345]
syntax can be
applied to service, task, run, inetd, and TTY stanzas.
Reserved runlevels are 0 and 6, halt and reboot, respectively just like
SysV init. Runlevel 1 can be configured freely, but is recommended to
be kept as the system single-user runlevel since Finit will not start
networking here. The configured runlevel NUM
from /etc/finit.conf
is what Finit changes to after bootstrap, unless 'single' (or 'S') is
given on the kernel cmdline, in which case runlevel 1 is started.
All services in runlevel S(0) are started first, followed by the desired
run-time runlevel. Run tasks in runlevel S can be started in sequence
by using run [S] cmd
. Changing runlevels at runtime is done like any
other init, e.g. init 4, but also using the more advanced
intictl
tool.
Plugins
Plugins can extend the functionality of Finit and hook into the
different stages of the boot process and at runtime. Plugins are
written in C and compiled into a dynamic library loaded automatically by
finit at boot. A basic set of plugins are bundled in the plugins/
directory.
Capabilities:
- Hooks
Hook into the boot at predefined points to extend Finit - I/O
Listen to external events and control Finit behavior/services - Inetd
Extend Finit with internal inetd services, for an example, seeplugins/time.c
Extensions and functionality not purely related to what an /sbin/init
needs to start a system are available as a set of plugins that either
hook into the boot process or respond to various I/O.
For more information, see doc/plugins.md.
Progress
Finit prior to v3.0 showed the progress at boot: plugins and modules being loaded, run/tasks/services being started, etc. By default Finit is now silent, but progress can still be enabled at build-time using:
configure --enable-progress
or at boot from the kernel cmdline using the boot parameter splash
:
append="init=/sbin/finit splash"
For a really nice boot, add quiet
as well. This silences the output
from the kernel, leaving only warnings and errors. For other kernel
command line parameters, see doc/build.md.
Automatic Reload
By default, Finit monitors /etc/finit.d/
and /etc/finit.d/enabled/
registering any changes to .conf
files. To activate a change the user
must call initctl reload
, which reloads all modified files, stops any
removed services, starts new ones, and restarts any modified ones, with
SIGHUP if the process supports it.
For some use-cases the extra step of calling initctl reload
creates an
unnecessary overhead, which can be removed at build-time using:
configure --enable-auto-reload
Runparts & /etc/rc.local
At the end of the boot, when all bootstrap (S
) tasks and services have
started, but not networking, Finit calls its built-in run-parts(8)
command on any configured runparts <DIR>
directory. This happens just
before changing to the configured runlevel (default 2). (Networking is
enabled just prior to changing from single user mode.)
runparts /etc/rc.d/
Right after the runlevel change when all services have started properly,
/etc/rc.local
is called.
No configuration stanza in /etc/finit.conf
is required for rc.local
.
If it exists and is an executable shell script Finit calls it at the very
end of the boot, before calling the HOOK_SYSTEM_UP
. See more on hooks
in doc/plugins.md, and about the system bootstrap
in doc/bootstrap.md.
Runlevels
Basic support for runlevels is included in Finit from v1.8. By
default all services, tasks, run commands and TTYs listed without a set
of runlevels get a default set [234]
assigned. The default runlevel
after boot is 2.
Finit supports runlevels 0-9, and S, with 0 reserved for halt, 6 reboot and S for services to only run at bootstrap. Runlevel 1 is the single user level, where usually no networking is enabled. In Finit this is more of a policy for the user to define. Normally only runlevels 1-6 are used, and even more commonly, only the default runlevel is used.
To specify an allowed set of runlevels for a service
, run
command,
task
, or tty
, add [NNN]
to your /etc/finit.conf
, like this:
service [S12345] /sbin/syslogd -n -x -- System log daemon
run [S] /etc/init.d/acpid start -- Starting ACPI Daemon
task [S] /etc/init.d/kbd start -- Preparing console
service [S12345] /sbin/klogd -n -x -- Kernel log daemon
tty [12345] /dev/tty1
tty [2] /dev/tty2
tty [2] /dev/tty3
tty [2] /dev/tty4
tty [2] /dev/tty5
tty [2] /dev/tty6
In this example syslogd is first started, in parallel, and then acpid is called using a conventional SysV init script. It is called with the run command, meaning the following task command to start the kbd script is not called until the acpid init script has fully completed. Then the keyboard setup script is called in parallel with klogd as a monitored service.
Again, tasks and services are started in parallel, while run commands are called in the order listed and subsequent commands are not started until a run command has completed. Also, task and run commands are run in a shell, so pipes and redirects can be used.
The following examples illustrate this. Bootstrap task and run commands
are also removed when they have completed, initctl show
will not list
them.
task [S] echo "foo" | cat >/tmp/bar
run [S] echo "$HOME" >/tmp/secret
Switching between runlevels can be done by calling init with a single
argument, e.g. init 5, or using initctl runlevel 5
, both
switch to runlevel 5. When changing runlevels Finit also automatically
reloads all .conf
files in the /etc/finit.d/
directory. So if you
want to set a new system config, switch to runlevel 1, change all config
files in the system, and touch all .conf
files in /etc/finit.d
before switching back to the previous runlevel again — that way Finit
can both stop old services and start any new ones for you, without
rebooting the system.
Rebooting & Halting
Traditionally, rebooting and halting a UNIX system is done by changing
its runlevel. Finit comes with its own tooling providing: shutdown
,
reboot
, poweroff
, and suspend
, but also the traditional init
and
telinit
, as well as a more modern initctl
tool, detailed in the next
section.
For compatibility reasons Finit listens to the same set of signals as BusyBox init. This is not 100% compatible with SysV init, but clearly the more common combination for Finit. For more details, see doc/signals.md.
Finit also listens to the classic SysV init FIFO, used by telinit
.
Support for this is implemented by the initctl.so
plugin. Hence,
telinit q
will work as the UNIX beards intended.
~ # telinit -h
Usage: telinit [OPTIONS] [q | Q | 0-9]
Options:
-h, --help This help text
-V, --version Show Finit version
Commands:
0 Power-off the system, same as initctl poweroff
6 Reboot the system, same as initctl reboot
2, 3, 4, 5 Change runlevel. Starts services in new runlevel, stops any
services in prev. runlevel that are not allowed in new.
q, Q Reload *.conf in /etc/finit.d/, same as initctl reload or
sending SIGHUP to PID 1
1, s, S Enter system rescue mode, runlevel 1
Commands & Status
Finit also implements a more modern API to query status, and start/stop
services, called initctl
. Unlike telinit
the initctl
tool does
not return until the given command has fully completed.
~ $ initctl -h
Usage: initctl [OPTIONS] [COMMAND]
Options:
-v, --verbose Verbose output
-h, --help This help text
Commands:
debug Toggle Finit (daemon) debug
help This help text
version Show Finit version
list List all .conf in /etc/finit.d/
enable <CONF> Enable .conf in /etc/finit.d/available/
disable <CONF> Disable .conf in /etc/finit.d/[enabled/]
touch <CONF> Mark .conf in /etc/finit.d/ for reload
reload Reload *.conf in /etc/finit.d/ (activates changes)
cond show Show condition status
cond dump Dump all conditions and their status
log [NAME] Show ten last Finit, or NAME, messages from syslog
start <JOB|NAME>[:ID] Start service by job# or name, with optional ID
stop <JOB|NAME>[:ID] Stop/Pause a running service by job# or name
restart <JOB|NAME>[:ID] Restart (stop/start) service by job# or name
status <JOB|NAME>[:ID] Show service status, by job# or name
status | show Show status of services, default command
runlevel [0-9] Show or set runlevel: 0 halt, 6 reboot
reboot Reboot system
halt Halt system
poweroff Halt and power off system
utmp show Raw dump of UTMP/WTMP db
For services not supporting SIGHUP
the <!>
notation in the .conf
file must be used to tell Finit to stop and start it on reload
and
runlevel
changes. If <>
holds more conditions,
these will also affect how a service is maintained.
Note: even though it is possible to start services not belonging in the current runlevel these services will not be respawned automatically by Finit if they exit (crash). Hence, if the runlevel is 2, the below Dropbear SSH service will not be restarted if it is killed or exits.
~ $ initctl status -v
1 running 476 [S12345] /sbin/watchdog -T 16 -t 2 -F /dev/watchdog
2 running 477 [S12345] /sbin/syslogd -n -b 3 -D
3 running 478 [S12345] /sbin/klogd -n
4:1 inetd 0 [2345] internal time allow *:37,!eth0
4:2 inetd 0 [2345] internal time allow eth0:3737
5:1 inetd 0 [2345] /sbin/telnetd allow *:23 deny eth0,eth1
5:2 inetd 0 [2345] /sbin/telnetd allow eth0:2323,eth2:2323,eth1:2323
6:1 inetd 0 [345] /sbin/dropbear allow eth0:222
6:2 inetd 0 [345] /sbin/dropbear allow *:22 deny eth0
Requirements
Finit is capable of running on both desktop/server systems with udev and
embedded systems that usually come with BusyBox mdev. Some systems have
systemd-udev or eudev today instead of the original udev, Finit probes
for all of them at runtime and expects /dev/
to be a writable file
system using devtmpfs
. It is also possible to run on a statically set
up /dev
if needed. It is however not a good idea to have both udev
and mdev installed at the same time, this will lead to unpredictable
results.
At boot Finit calls either mdev
or udevd
to populate /dev
, this is
done slightly differently and on systems with udev you might want to add
the following one-shot task early in your /etc/finit.conf
:
run [S] /sbin/udevadm settle --timeout=120 -- Waiting for udev
Finit has a built-in Getty for TTYs, but requires a working /bin/login
or /bin/sh
, if no TTYs are configured in /etc/finit.conf
.
For a fully operational system /var
, /run
and /tmp
must be set up
properly in /etc/fstab
-- which is iterated over at boot.
The built-in Inetd requires /etc/services
and /etc/protocols
to work
with port names rather than numbers.
Origin & References
This project is based on the original finit by Claudio Matsuoka which was reverse engineered from syscalls of the EeePC fastinit — "gaps filled with frog DNA …"
Finit is developed and maintained by Joachim Nilsson at GitHub. Please file bug reports, clone it, or send pull requests for bug fixes and proposed extensions.