mirror of
https://github.com/troglobit/finit.git
synced 2026-10-01 13:33:09 +07:00
The block conversion changed the bodies of the reference sections but left every "**Syntax:**" header spelling the line-based format, so each page opened by teaching the format it then stopped using. Six files were missed entirely: runparts, files, capabilities, requirements, runlevels, and switchroot. runparts had no block spelling written down anywhere, though the parser has read `runparts`, `runparts-progress`, and `runparts-sysv` all along. tty gains a table per variant. Its three syntax lines carried nine positional fields between them, which no longer describes anything the parser accepts. Fixes #148 Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
194 lines
4.6 KiB
Markdown
194 lines
4.6 KiB
Markdown
Switch Root
|
|
===========
|
|
|
|
Finit supports switching from an initramfs to a real root filesystem
|
|
using the `initctl switch-root` command. This is useful for systems
|
|
that use an initramfs for early boot (LUKS, LVM, network boot, etc.)
|
|
and need to transition to the real root before starting services.
|
|
|
|
|
|
Usage
|
|
-----
|
|
|
|
```sh
|
|
initctl switch-root NEWROOT [INIT]
|
|
```
|
|
|
|
- `NEWROOT`: Path to the mounted new root filesystem (e.g., `/mnt/root`)
|
|
- `INIT`: Optional path to init on the new root (default: `/sbin/init`)
|
|
|
|
|
|
Requirements
|
|
------------
|
|
|
|
1. Must be run during runlevel S (bootstrap) or runlevel 1
|
|
2. `NEWROOT` must be a mount point (different device than /)
|
|
3. `INIT` must exist and be executable on the new root
|
|
4. Finit must be running as PID 1 (in initramfs)
|
|
|
|
|
|
How It Works
|
|
------------
|
|
|
|
1. Runs `HOOK_SWITCH_ROOT` for any cleanup scripts/plugins
|
|
2. Runs `HOOK_SHUTDOWN` to notify plugins
|
|
3. Stops all services and kills remaining processes
|
|
4. Exits all plugins gracefully
|
|
5. Moves `/dev`, `/proc`, `/sys`, `/run` to new root
|
|
6. Deletes initramfs contents (if on tmpfs/ramfs) to free memory
|
|
7. Moves new root mount to `/`
|
|
8. Chroots to new root
|
|
9. Reopens `/dev/console` for stdin/stdout/stderr
|
|
10. Execs new init as PID 1
|
|
|
|
|
|
Example: Initramfs finit.conf
|
|
-----------------------------
|
|
|
|
Configuration file `/etc/finit.conf` in the initramfs:
|
|
|
|
```
|
|
# /etc/finit.conf in initramfs
|
|
|
|
# Mount the real root filesystem
|
|
run mount-root {
|
|
description = "Mounting root filesystem"
|
|
runlevel = "S"
|
|
command = "/bin/mount /dev/sda1 /mnt/root"
|
|
}
|
|
|
|
# Switch to real root after mount completes
|
|
run switch-root {
|
|
description = "Switching to real root"
|
|
runlevel = "S"
|
|
command = "/sbin/initctl switch-root /mnt/root"
|
|
}
|
|
```
|
|
|
|
For more complex setups (LUKS, LVM, etc.):
|
|
|
|
```
|
|
# Unlock LUKS volume
|
|
# The tty setting is required so cryptsetup can prompt for a passphrase
|
|
run cryptsetup {
|
|
description = "Unlocking encrypted root"
|
|
runlevel = "S"
|
|
tty = "@console"
|
|
command = "/sbin/cryptsetup open /dev/sda2 cryptroot"
|
|
}
|
|
|
|
# Activate LVM
|
|
run lvm {
|
|
description = "Activating LVM volumes"
|
|
runlevel = "S"
|
|
command = "/sbin/lvm vgchange -ay"
|
|
}
|
|
|
|
# Mount root
|
|
run mount-root {
|
|
description = "Mounting root"
|
|
runlevel = "S"
|
|
command = "/bin/mount /dev/vg0/root /mnt/root"
|
|
}
|
|
|
|
# Switch root
|
|
run switch-root {
|
|
description = "Switching to real root"
|
|
runlevel = "S"
|
|
command = "/sbin/initctl switch-root /mnt/root"
|
|
}
|
|
```
|
|
|
|
|
|
Example: Using Runlevel 1 for Switch Root
|
|
-----------------------------------------
|
|
|
|
For more complex initramfs setups where ordering of tasks becomes
|
|
difficult in runlevel S, you can perform the switch-root in runlevel 1:
|
|
|
|
```
|
|
# /etc/finit.conf in initramfs
|
|
|
|
# Start mdevd for device handling
|
|
service mdevd {
|
|
description = "Device event daemon"
|
|
runlevel = "S"
|
|
notify = "s6"
|
|
command = "/sbin/mdevd -D %n"
|
|
}
|
|
run coldplug {
|
|
description = "Coldplug devices"
|
|
runlevel = "S"
|
|
conditions = { "service/mdevd/ready" }
|
|
command = "/sbin/mdevd-coldplug"
|
|
}
|
|
|
|
# Mount the real root filesystem (after devices are ready)
|
|
run mount-root {
|
|
description = "Mounting root"
|
|
runlevel = "S"
|
|
conditions = { "run/coldplug/success" }
|
|
command = "/bin/mount /dev/sda1 /mnt/root"
|
|
}
|
|
|
|
# Transition to runlevel 1 after all S tasks complete
|
|
# The switch-root runs cleanly in runlevel 1
|
|
run switch-root {
|
|
description = "Switching to real root"
|
|
runlevel = "1"
|
|
command = "/sbin/initctl switch-root /mnt/root"
|
|
}
|
|
```
|
|
|
|
This approach separates the initramfs setup (runlevel S) from the
|
|
switch-root operation (runlevel 1), making task ordering simpler.
|
|
|
|
|
|
Hooks
|
|
-----
|
|
|
|
The `HOOK_SWITCH_ROOT` hook runs before the switch begins. Use it for:
|
|
|
|
- Saving state to the new root
|
|
- Unmounting initramfs-only mounts
|
|
- Cleanup tasks
|
|
|
|
Plugins can register for `HOOK_SWITCH_ROOT` just like other hooks:
|
|
|
|
```c
|
|
static void my_switch_root_hook(void *arg)
|
|
{
|
|
/* Cleanup before switch_root */
|
|
}
|
|
|
|
static plugin_t plugin = {
|
|
.name = "my-plugin",
|
|
.hook[HOOK_SWITCH_ROOT] = {
|
|
.cb = my_switch_root_hook
|
|
}
|
|
};
|
|
|
|
PLUGIN_INIT(plugin_init)
|
|
{
|
|
plugin_register(&plugin);
|
|
}
|
|
```
|
|
|
|
|
|
Conditions
|
|
----------
|
|
|
|
After switch_root, the new finit instance starts fresh. No conditions
|
|
or state are preserved across the switch. The new finit will:
|
|
|
|
1. Re-read `/etc/finit.conf` from the new root
|
|
2. Re-initialize all conditions
|
|
3. Start services according to the new configuration
|
|
|
|
|
|
See Also
|
|
--------
|
|
|
|
- [switch_root(8)](https://man7.org/linux/man-pages/man8/switch_root.8.html) - util-linux switch_root utility
|
|
- [Kernel initramfs documentation](https://docs.kernel.org/filesystems/ramfs-rootfs-initramfs.html)
|