Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 11 additions & 4 deletions recipes/core/mdevd/recipe.kdl
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
recipe {
name "mdevd"
version "0.1.8.1"
release 1
release 2
description "A small daemon managing kernel hotplug events, similarly to udevd"
url "http://skarnet.org/software/mdevd/"
archs "aarch64" "x86_64"
Expand Down Expand Up @@ -46,6 +46,10 @@ source "https://raw.githubusercontent.com/illiliti/libudev-zero/refs/heads/maste
blake3 "a5a0e4875dd26f438ce73177881b13530a99b6916b0b3466bf875dff9b45bea7"
}

source "service" {
blake3 "d499904ba4962d285f0e9b9db9ada186a4560be7172fcc88715833e033a9585b"
}

build {
script r#"
./configure \
Expand All @@ -68,6 +72,7 @@ install {
install -m0754 "${MERE_SOURCES_DIR}/settle-nics" "${DESTDIR}/lib/mdev/helpers/"
install -m0754 "${MERE_SOURCES_DIR}/sound-control" "${DESTDIR}/lib/mdev/helpers/"
install -m0754 "${MERE_SOURCES_DIR}/storage-device" "${DESTDIR}/lib/mdev/helpers/"
install -m0755 "${MERE_SOURCES_DIR}/service" "${DESTDIR}/lib/mdev/helpers/service"
install -m0754 uevents "${DESTDIR}/lib/mdev/helpers/"

"#
Expand All @@ -80,15 +85,17 @@ package "mdevd" {

service "mdevd" {
type "daemon"
command "/sbin/mdevd" "-D" "3"
command "/lib/mdev/helpers/service" "start"
log false
ready-notification 3
essential true
depends-on "mount-proc" "mount-rw"
depends-on "mount-proc" "mount-rw" "mount-sys" "mount-runtime"
}

service "mdevd-coldplug" {
type "oneshot"
up "if { install -d /run/.libudev-zero } mdevd-coldplug"
up "/lib/mdev/helpers/service" "coldplug"
log false
essential true
depends-on "mdevd"
}
Expand Down
12 changes: 12 additions & 0 deletions recipes/core/mdevd/service
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#!/bin/sh
set -eu
export PATH=/bin:/sbin:/usr/bin:/usr/sbin
exec > /dev/console 2>&1
case ${1:-} in
start) exec /sbin/mdevd -D 3 -O 4 ;;
coldplug)
mkdir -p /run/.libudev-zero
exec /sbin/mdevd-coldplug -O 4
;;
*) printf 'mdevd: unknown service action: %s\n' "${1:-}" >&2; exit 2 ;;
esac
96 changes: 96 additions & 0 deletions recipes/core/mere-boot/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# Mere boot services

`mere-boot` supplies the minimum local-disk boot graph for dinit. Networking,
SSH, time synchronization, D-Bus, and desktop services are optional. No
initramfs is required; the kernel must contain the root-disk/filesystem drivers,
console support, and automatic devtmpfs mounting needed to reach `/sbin/init`.

## Installation contract

Install `busybox`, `dinit` (unpatched 0.22.1 is sufficient), `mdevd` (release 2 or later),
`shadow`, and `mere-boot` into the **system** profile with
`settings { init-provider "dinit"; }` in Mere's
configuration. These are explicit installation requirements: recipe `depends`
lists build inputs, and script-invoked programs are not inferred ELF dependencies.
The image or installer must expose the active system generation's `bin`, `sbin`,
`lib`, and `usr` directories at the filesystem root. Boot the kernel with
`init=/sbin/init`; the launcher execs dinit so it remains PID 1.

Provision the root directories `/dev`, `/proc`, `/sys`, `/run`, `/tmp`, `/var`,
`/root`, and `/home` before booting a read-only root. `/usr` and the Mere store
must be accessible on the root filesystem before init starts. A separately
mounted early `/usr`, encrypted root, and remote root need additional design.
Provide an `/etc/fstab` containing the required local mounts (an empty file is
valid), account databases, a usable Shadow/PAM login configuration, and an
administrator-provisioned root credential. This package never sets a password,
unlocks an account, or creates an unauthenticated recovery shell. Its minimal
`/etc/profile` default makes both `/bin` and `/usr/bin` (and administrative paths)
available in a login shell; an existing administrator profile is preserved.

Mere generates the ordinary service descriptions in `/usr/share/dinit.d` during
system-profile activation. The `boot`, `recovery`, and `single` descriptions are
package `/etc` defaults because bundles and console attachment are not currently
expressible with service primitives. They are copied by normal activation when
absent; existing administrator files remain unchanged. Review `mere etc diff`
and merge relevant defaults on an existing installation. Do not replace the
whole machine's `/etc` merely to adopt this package.

## Graph and ordering

- `mount-proc` establishes procfs. `mount-sys`, `mount-dev`, and `mount-rw` depend
on it and establish sysfs, devtmpfs, and a writable root.
- `mount-runtime` waits for those services, establishes `/run`, devpts and shared
memory, sets the temporary-directory mode, and exposes `/dev/fd`.
- mdevd starts after those mounts and signals readiness. Its coldplug service
creates the libudev-zero runtime directory and then enumerates devices.
- `mount-fstab` waits for coldplug before mounting the local filesystems.
- `system-setup` creates log/home directories, applies optional hostname and
sysctl configuration, and enables configured swap. `sysinit` requires it.
- `tty1` waits for `sysinit` and starts BusyBox getty with Shadow login. The
default `boot` bundle requires both `sysinit` and `tty1`.

The init launcher prepares `/run` before dinit creates its control socket, so a
later tmpfs mount cannot hide `/run/dinitctl`. The graph's runtime-mount helper
recognizes this existing mount. Setup is otherwise performed by recipe service
primitives, whose helpers use POSIX shell and BusyBox utilities.

The startup mount services deliberately do not unmount filesystems when released:
a partial boot may need those filesystems for authenticated recovery. On normal
shutdown dinit stops the complete service set first; its shutdown utility then
terminates remaining processes, disables swap, syncs, and unmounts/remounts
filesystems before invoking kernel power-off or reboot. Do not add early mount
stop commands which race optional daemons during shutdown.

## Machine configuration and failure handling

`/etc/dinit.d/boot.d` is the administrator's optional-service set. For a QEMU or
headless machine with a `ttyS0` serial console, enable the supplied serial service:

```sh
mkdir -p /etc/dinit.d/boot.d
ln -s /usr/share/dinit.d/ttyS0 /etc/dinit.d/boot.d/ttyS0
```

This records next-boot intent; use `mere start ttyS0` separately to start it now.
The supplied serial speed is 115200. Other ports/speeds can use an administrator
service override. Optional-service failures do not satisfy or replace the
required foundational graph. Optional daemons with additional filesystem/device
requirements should declare those dependencies in their own recipes.

Setup diagnostics go to the kernel-selected console; dinit also writes
`/run/dinit.log`, including failures before `/var` is mounted. Daemons using
Mere's ordinary logging metadata retain their own configured log files. Log
rotation is separate work.

A required setup failure prevents normal boot completion. Dinit presents its
boot-failure menu on the console; select `e` for recovery. Recovery then starts
a console-attached Shadow login, initially requesting root. Automatic recovery
is left disabled so the menu and login never compete for console input.

The helper reopens dinit's control socket after failed startup so lifecycle and
shutdown commands remain available. Credentials and normal PAM policy still
apply; locked or missing credentials do not grant a shell. There is no automatic
reboot. Recovery does not depend on `sysinit`, so failure of that graph cannot
suppress the recovery login. An operator repairs the system and explicitly
requests reboot or power-off. `single` selects the same authenticated recovery
path directly.
4 changes: 4 additions & 0 deletions recipes/core/mere-boot/boot
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
type = internal
depends-on = sysinit
depends-on = tty1
waits-for.d = boot.d
1 change: 1 addition & 0 deletions recipes/core/mere-boot/boot.keep
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Optional boot services are enabled here by the administrator.
28 changes: 28 additions & 0 deletions recipes/core/mere-boot/getty
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#!/bin/sh
# Getty/login owns the selected tty. Keep supervising across normal logouts.
set -eu
export PATH=/bin:/sbin:/usr/bin:/usr/sbin
if [ ! -c "/dev/$1" ] || [ ! -x /sbin/getty ] || [ ! -x /usr/bin/login ]; then
printf 'mere-boot: console or login unavailable: %s\n' "$1" > /dev/console
exit 1
fi
child=
stop() {
trap - TERM INT HUP
if [ -n "$child" ]; then
kill "$child" 2>/dev/null || :
wait "$child" 2>/dev/null || :
fi
exit 0
}
trap stop TERM INT HUP
while :; do
/sbin/getty -l /usr/bin/login "${2:-38400}" "$1" &
child=$!
wait "$child" || :
child=
sleep 1 &
child=$!
wait "$child" || :
child=
done
11 changes: 11 additions & 0 deletions recipes/core/mere-boot/init
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/bin/sh
# Keep PID 1 when handing system management to dinit.
export PATH=/bin:/sbin:/usr/bin:/usr/sbin
umask 022
# The control socket must live on the final /run mount, not a hidden lower one.
if ! { mkdir -p /run && { mountpoint -q /run || mount -t tmpfs -o mode=0755,nosuid,nodev tmpfs /run; }; }; then
printf 'mere-boot: cannot prepare /run; entering authenticated recovery\n' >&2
exec /bin/dinit -d /etc/dinit.d -d /usr/share/dinit.d -t recovery
fi
exec /bin/dinit -d /etc/dinit.d -d /usr/share/dinit.d \
--log-file /run/dinit.log "$@"
2 changes: 2 additions & 0 deletions recipes/core/mere-boot/login-profile
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Standard command paths for a Mere login shell.
export PATH=/usr/local/bin:/usr/local/sbin:/bin:/sbin:/usr/bin:/usr/sbin
143 changes: 143 additions & 0 deletions recipes/core/mere-boot/recipe.kdl
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
recipe {
name "mere-boot"
version "0.1.0"
release 1
description "Foundational boot services for Mere Linux with dinit"
url "https://merelinux.org"
licenses "MIT"
archs "aarch64" "x86_64"
depends "busybox"
}

source "init" {
blake3 "916bef670194f057612fa9013e67a556b0dce8cff2669eb63b903405aa01e72a"
}

source "setup" {
blake3 "714fa6451e8ea0d12ee974fdab8fee76184188752aa97d9aaee36d8009bf705b"
}

source "getty" {
blake3 "e942324c8eab2e87c0515f208eaa260316acb60d78e0682b224648a1dfb95a99"
}

source "recovery" {
blake3 "0a8ff9019220c10d5aec8c2c4a6a297c92320d447218a10a7d49555ad06239d3"
}

source "boot" {
blake3 "5f18e95596194b75ba5c1c88bb415582fd19bca610e82d28d87739492c499ac4"
}

source "recovery.service" {
blake3 "e18e1be428ee230085d03584bab216beb477869c90a32f14eb0a04f10b1b9b5d"
}

source "single" {
blake3 "f59f08b983904c6c47544e83d7fc276d58771297af184f638c0a167623bee445"
}

source "boot.keep" {
blake3 "5d9e9021b9a8c8c781e714b68bad269e2a5509b8b9317ddd4c91dc2899ff99d5"
}

source "login-profile" {
blake3 "43bdcd99c1deb6e66e5efa3b396558a7b2297159d83eb141a1698a028173a581"
}

install {
script r#"
install -d "${DESTDIR}/sbin" "${DESTDIR}/usr/libexec/mere-boot" \
"${DESTDIR}/etc/dinit.d/boot.d"
install -m 0755 "${MERE_SOURCES_DIR}/init" "${DESTDIR}/sbin/init"
for helper in setup getty recovery; do
install -m 0755 "${MERE_SOURCES_DIR}/$helper" "${DESTDIR}/usr/libexec/mere-boot/"
done
install -m 0644 "${MERE_SOURCES_DIR}/login-profile" "${DESTDIR}/etc/profile"
install -m 0644 "${MERE_SOURCES_DIR}/boot.keep" "${DESTDIR}/etc/dinit.d/boot.d/.keep"
install -m 0644 "${MERE_SOURCES_DIR}/boot" "${DESTDIR}/etc/dinit.d/boot"
install -m 0644 "${MERE_SOURCES_DIR}/recovery.service" "${DESTDIR}/etc/dinit.d/recovery"
install -m 0644 "${MERE_SOURCES_DIR}/single" "${DESTDIR}/etc/dinit.d/single"
"#
}

package "mere-boot" {
files "sbin/" "usr/" "etc/"

service "mount-proc" {
type "oneshot"
up "/usr/libexec/mere-boot/setup" "proc"
essential true
log false
}

service "mount-sys" {
type "oneshot"
up "/usr/libexec/mere-boot/setup" "sys"
essential true
log false
depends-on "mount-proc"
}

service "mount-dev" {
type "oneshot"
up "/usr/libexec/mere-boot/setup" "dev"
essential true
log false
depends-on "mount-proc"
}

service "mount-rw" {
type "oneshot"
up "/usr/libexec/mere-boot/setup" "rw"
essential true
log false
depends-on "mount-proc"
}

service "mount-runtime" {
type "oneshot"
up "/usr/libexec/mere-boot/setup" "runtime"
essential true
log false
depends-on "mount-rw" "mount-sys" "mount-dev"
}

service "mount-fstab" {
type "oneshot"
up "/usr/libexec/mere-boot/setup" "local"
essential true
log false
depends-on "mount-runtime" "mdevd-coldplug"
}

service "system-setup" {
type "oneshot"
up "/usr/libexec/mere-boot/setup" "system"
essential true
log false
depends-on "mount-fstab"
}

service "sysinit" {
type "oneshot"
up "/bin/true"
essential true
log false
depends-on "system-setup"
}

service "tty1" {
type "daemon"
command "/usr/libexec/mere-boot/getty" "tty1" "38400"
log false
depends-on "sysinit"
}

service "ttyS0" {
type "daemon"
command "/usr/libexec/mere-boot/getty" "ttyS0" "115200"
log false
depends-on "sysinit"
}
}
9 changes: 9 additions & 0 deletions recipes/core/mere-boot/recovery
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/bin/sh
export PATH=/bin:/sbin:/usr/bin:/usr/sbin
printf '\nmere-boot: foundational startup failed. Authentication is required.\n'
printf 'Repair the system, then request reboot or power-off explicitly.\n'
# Dinit closes its control socket after the failed graph stops. Reopen it so
# an authenticated operator can use dinitctl and the shutdown utilities.
kill -s USR1 1 || printf 'mere-boot: could not reopen the dinit control socket\n' >&2
# Never use login -f or fall back to an unauthenticated shell.
exec /usr/bin/login root
4 changes: 4 additions & 0 deletions recipes/core/mere-boot/recovery.service
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
type = process
command = /usr/libexec/mere-boot/recovery
options = runs-on-console
restart = false
Loading