This is the core services suite for dinit as used by Chimera.
It provides an expansive collection of service files, scripts and helpers to aid early boot, more suitable for a practical deployment than the example collection that comes with upstream. Patches for third party distro adaptations are welcome, provided they are not disruptive.
Currently the documentation for the suite is lacking, which is also to be done.
- dinit (0.18.0 or newer)
- Linux kernel 5.10 or newer
- POSIX shell
- POSIX core utilities
- We test chimerautils
- Others are supported (GNU,
busybox, etc.); issues should be reported
mount,umount- Implementation must support
-a
- Implementation must support
sulogin(any implementation, e.g.shadow,util-linux,busybox)- sd-tools (particularly
sd-tmpfiles) - libkmod
The distribution should provide the following helpers (the paths are the defaults, they may be altered with meson options):
/usr/libexec/dinit-console- Perform console and keyboard setup; optional
/usr/libexec/dinit-cryptdisks- Perform encrypted drive setup; optional
/usr/libexec/dinit-devd- Perform device initialization; mandatory
The dinit-console may look like this when using console-setup:
#!/bin/sh
if [ "$1" = "keyboard" ]; then
set -- "-k"
else
set --
fi
exec setupcon "$@"
The dinit-cryptdisks may look like this when using Debian cryptsetup scripts:
#!/bin/sh
[ -r /usr/lib/cryptsetup/cryptdisks-functions ] || exit 0
[ -r /etc/crypttab ] || exit 0
. /usr/lib/cryptsetup/cryptdisks-functions
INITSTATE="$1"
case "$2" in
start) do_start ;;
stop) do_stop ;;
*) exit 1 ;;
esac
It is passed two arguments, the first one is either early or remaining
while the second one is either start or stop.
The dinit-devd may look like this when using udev:
#!/bin/sh
case "$1" in
start) exec /usr/libexec/udevd --daemon ;;
stop) /usr/bin/udevadm control -e; exit 0 ;;
settle) exec /usr/bin/udevadm settle ;;
trigger) exec /usr/bin/udevadm trigger --action=add ;;
esac
echo "unknown action: $1"
exit 1
Note that currently the behaviors are subject to change. Adopters should watch out for such changes and adjust their scripts accordingly.
Not having these dependencies will allow the boot to proceed, but specific functionality will not work. Generally the affected oneshots will simply exit with success if the tools aren't located.
fsck- Without it, early file system checks won't be available
- Tested with
util-linux, others may work
- mdadm
- dmraid
- LVM2
- Btrfs
- ZFS
- makedumpfile
- For kernel crashdump support
- kexec-tools
- For kernel crashdump support
This suite implements a variety of kernel command line parameters that you can use for debugging and other purposes.
dinit_auto_recovery=1- passes--auto-recoverydinit_quiet=1- passes--quietdinit_log_file=LOGFILE- passes--log-file LOGFILEdinit_log_level=LOGLEVEL- passes--log-level LOGLEVELdinit_console_level=LOGLEVEL- passes--console-level LOGLEVEL
These are notably useful for early boot debugging. There are a lot of
early services, and if a very early service fails, the real error very
quickly scrolls past the standard verbose output as services get stopped.
Previously this required unreliable workarounds like slow-motion screen
recording; now you can edit your kernel command line and add something
like dinit_quiet=1 dinit_console_level=warn to supress the "started"
and "stopped" messages.
These are all unset so they will not make it into the activation environment.
Additionally, there are more parameters that are purely for the purpose
of boot debugging and are implemented by dinit-chimera itself:
dinit_early_debug=1- enables early debugging, causing each early service to echo a message before it performs its action; the following parameters only take effect if this is setdinit_early_debug_slow=N- sleepsNseconds after the echo and before performing the action, intentionally slowing down the boot process for better claritydinit_early_debug_log=LOGFILE- instead of the console, all output will be redirected to theLOGFILE; note that you have to ensure the location of the file is writable
The debug parameters are subject to change if necessary. They become a part of the global activation environment.
fastbootorfsck.mode=skip- skips filesystem checksforcefsckorfsck.mode=force- passes-ftofsckfsckfixorfsck.repair=yes- passes-ytofsck(do not ask questions)fsck.repair=no- passes-ntofsck
These only apply if the optional kdump service is installed.
nokdump- do not save kernel dump even if/proc/vmcoreexists
dinit.runsize=Norinitramfs.runsize=N- thesize=parameter to use when mounting/runand/run/user; they are equivalent and the former is specific todinit, while the latter exists for compatibility withinitramfs-tools(as the initramfs will mount/runalready and thendinit-chimerawill not). Defaults to10%.
dinit_early_root_remount=VALthe extraremountparameters to use for early root remount; the default isro,rshared- this can be used to prevent read-only remount of the root filesystem, e.g. for debugging. Note that this variable makes it into the global activation environment.dinit_skip_volumesskip ZFS pools, LVM, as well as btrfs scan on early boot; particularly useful for e.g. live images, where doing this automatically is counterproductive and may even break things (e.g. for root ZFS pools).
The dinit-chimera suite allows services to depend on devices.
To facilitate this, it needs a suitable device monitor, such as the
udev-based one available here.
Dummy monitor/client are provided by default. You can replace them when installing a proper one.
The capabilities depend on the device monitor implementation.
Example service that will not come up unless /dev/sda1 is around, and will
shut down if /dev/sda1 disappears:
type = process
command = /usr/bin/foo
depends-on: local.target
depends-on: device@/dev/sda1
See the documentation for your device monitor for further capabilities.
This suite supports management of zram devices on Linux.
The following configuration files are checked:
/etc/dinit-zram.d/*.conf
/run/dinit-zram.d/*.conf
/usr/local/lib/dinit-zram.d/*.conf
/usr/lib/dinit-zram.d/*.conf
/etc/dinit-zram.conf
The directory snippet paths are checked in that order and the first directory
to contain a config snippet of that name is prioritized (i.e. every file name
is only loaded once). The /etc/dinit-zram.conf configuration file is loaded
last and always (if it exists).
The syntax is like this:
; a comment
set! ratio=`echo 2`
set! ratio=2
# also a comment
[zram0]
size = (/ ram ratio)
algorithm = zstd
format = mkswap -U clear %0
Fields that are specified later override those that are specified earlier, so you can have e.g. a config file defining a zram device and then a later one defining more details for it.
Directives (set!) may exist outside sections, and are accessible in variables
taking byte values such as size. If value is given directly, it is considered
an expression and is evaluated as is. If value is given in backticks, the
contents are run in the shell, a single line of output is read, stripped of
leading and trailing whitespace, and evaluated as an expression.
Setting a variable multiple times will override it (it can access its former value).
Several variables are builtin and cannot be redefined or overridden. These
are ram (MemTotal from /proc/meminfo in bytes) and several constants
(pi, e).
There are also functions, which follow S-expression syntax like (func args...).
These are:
+ v...
- v...
* v...
/ v...
% v...
^ v...
== v...
!= v...
< v...
<= v...
> v...
>= v...
&& v...
|| v...
? cond a b
int v
floor v
ceil v
round v
abs v
sign v
min v...
max v...
sin v
cos v
tan v
asin v
acos v
atan v
sinh v
cosh v
tanh v
asinh v
acosh v
atanh v
log v base
The v means taking a single argument, while v... means any number of
arguments. The sin, cos, tan functions take radians. The log function
has an optional base, with the default of e (natural logarithm).
Arithmetic and logical operator functions take any number of arguments and
chain. The logical operators evaluate to one of the operands. The comparison
operators evaluate to 1 or 0. Everything works in terms of double precision
floating point arithmetic.
Numeric literals can take integer and decimal forms. Any form supported by
strtod works, which means most formats supported by C should work.
Memory suffixes are supported as 1024 multiples, with K for 1024,
M for 1024^2, G for 1024^3, and T for 1024^4. SI suffixes
are not supported.
The above fields are currently the only supported ones (more will be added
later as well as more syntax, and more exist but are considered unstable).
All but size are optional. The format field specifies a command to use
to format the device once set up and the default is the one above, to set up
swap space. You can set custom commands for e.g. zram ramdisks with real
filesystems on them.
Once you have a configuration file, you can activate the device by enabling
the zram-device@zramN service.
Note this is experimental and subject to changes.
This suite supports mount services, which are service-driven supervised mounts. You can define a mount service like this:
# /etc/dinit.d/usb-stick.mount
type = process
command = /usr/bin/dinit-mount-supervise \
--from PARTLABEL=usbstick \
--to /media/usb \
--type ext4 \
--ready 4
restart = false
ready-notification = pipefd:4
depends-on: device@PARTLABEL=usbstick
depends-on: early-fs-local.target
Starting this service will ensure that /dev/sda1 will remain mounted for
as long as the device exists. Stopping the service will cleanly unmount
it. The restart = false ensures manually unmounting the device will not
remount it; restart = true will make sure it's always mounted, unless
stopped explicitly.
Readiness notification (so that the supervised mount can have reliable
dependents) can be done either via the pipefd or pipevar mechanisms,
with the argument to --ready determining that (number is for pipefd,
while a name is for pipevar).
It is also possible to do a pure monitor service. Such service will watch for a mount to appear (and signal readiness then) and will terminate when it disappears again, not unmounting anything.
type = process
command = /usr/bin/dinit-mount-supervise \
--to /media/usb \
--ready MOUNT_READY \
--no-mount
restart = false
ready-notification = pipevar:MOUNT_READY
depends-on: device@PARTLABEL=usbstick
depends-on: early-fs-local.target
The --from argument can still be used for more accurate monitoring and
will match the first column in /proc/self/mounts in that case, but is
not mandatory.
Additionally, --no-umount can be specified to avoid unmounting the volume
upon exit (--no-mount implies it).
It can also be used for general one-shot mounting with the --no-supervise
option like:
# dinit-mount-supervise --no-supervise --from /dev/sda1 --to /mnt --type ext4
In this case, the process will exit (with an appropriate exit code) as soon as the mount has appeared (or the procedure has failed).
The helper can also use external commands for the mounting (and unmounting)
specified with --mount-command and --umount-command. Both are specified
as strings to be executed with the shell (sh -c string). The mount command
receives the device as the first argument ($1) which may be an empty string
if not provided, the mount point as the second argument ($2), and if any
options were given, the option string as the third argument. The umount command
receives the mount point as its sole argument. If mount command is given and
umount command is not, the regular builtin logic is used.
The mount/umount command strings are useful when you want to mount and
supervise some mount point but the path is not mountable using standard
tools, for instance things mountable without superuser privileges (for
example, sshfs).
Basic example that just uses mount(8):
# dinit-mount-supervise --from /dev/sda1 --to /mnt --mount-command 'mount "$1" "$2"' --umount-command 'umount "$1"'
Note that readiness notification and so on are entirely independent of the commands and rely purely on polling the mount table.
The collection provides special "target" services, suffixed with .target,
which can be used as dependencies for third party service files as well as
for ordering.
Until better documentation is in place, here is the list, roughly in bootup
order. The actual order may vary somewhat because of parallel startup. In
general your services should specify dependency links and ordering links
for every target that is relevant to your functionality (i.e. you should
not rely on transitive dependencies excessively). This does not apply
to very early oneshots that are guaranteed to have run, i.e. in most cases
services should not have to depend on early-prepare.target and so on.
early-prepare.target- early pseudo-filesystems have been mountedearly-modules.target- kernel modules from/etc/moduleshave been loadedearly-devices.target- device events have been processed- This means
/devis fully populated with quirks applied and so on.
- This means
early-keyboard.target- console keymap has been setearly-fs-pre.target- filesystems are ready to be checked and mounted- This means encrypted disks, RAID, LVM and so on is up.
early-root-rw.target- root filesystem has been re-mounted read/write.- That is, unless
fstabexplicitly specifies it should be read-only.
- That is, unless
early-fs-fstab.target- non-network filesystems infstabhave been mountedearly-fs-local.target- non-network filesystems have finished mounting- This includes the above plus non-
fstabfilesystems such as ZFS.
- This includes the above plus non-
early-console.target- follow-up toearly-keyboard.target(console font, etc.)pre-local.target- most important early oneshots have run.- Temporary/volatile files/dirs managed with
tmpfiles.dare not guaranteed yet. - Most services should prefer
local.targetas their sentinel. - Typically only for services that should guarantee being up before
rc.localis run. - All targets above this one are guaranteed to have been reached.
- Temporary/volatile files/dirs managed with
local.target-/etc/rc.localhas run and temp/volatile files/dirs are created- Implies
pre-local.target. - Most regular services should depend on at least this one (or
pre-local.target).
- Implies
pre-network.target- networking daemons may start.- This means things such as firewall have been brought up.
network.target- networking daemons have started.- Networking daemons should use this as
before. - Things depending on network being up should use this as a dependency.
- Networking daemons should use this as
login.target- the system is ready to run gettys, launch display manager, etc.- Typically to be used as a
beforesentinel for things that must be up before login.
- Typically to be used as a
time-sync.target- system date/time should be set by now.- Things such as NTP implementations should wait and use this as
before. - Things requiring date/time to be set should use this as a dependency.
- This may take a while, so pre-login services depending on this may stall the boot.
- Things such as NTP implementations should wait and use this as