|
| 1 | +Switch Root |
| 2 | +=========== |
| 3 | + |
| 4 | +Finit supports switching from an initramfs to a real root filesystem |
| 5 | +using the `initctl switch-root` command. This is useful for systems |
| 6 | +that use an initramfs for early boot (LUKS, LVM, network boot, etc.) |
| 7 | +and need to transition to the real root before starting services. |
| 8 | + |
| 9 | + |
| 10 | +Usage |
| 11 | +----- |
| 12 | + |
| 13 | +```sh |
| 14 | +initctl switch-root NEWROOT [INIT] |
| 15 | +``` |
| 16 | + |
| 17 | +- `NEWROOT`: Path to the mounted new root filesystem (e.g., `/mnt/root`) |
| 18 | +- `INIT`: Optional path to init on the new root (default: `/sbin/init`) |
| 19 | + |
| 20 | + |
| 21 | +Requirements |
| 22 | +------------ |
| 23 | + |
| 24 | +1. Must be run during runlevel S (bootstrap) or runlevel 1 |
| 25 | +2. `NEWROOT` must be a mount point (different device than /) |
| 26 | +3. `INIT` must exist and be executable on the new root |
| 27 | +4. Finit must be running as PID 1 (in initramfs) |
| 28 | + |
| 29 | + |
| 30 | +How It Works |
| 31 | +------------ |
| 32 | + |
| 33 | +1. Runs `HOOK_SWITCH_ROOT` for any cleanup scripts/plugins |
| 34 | +2. Runs `HOOK_SHUTDOWN` to notify plugins |
| 35 | +3. Stops all services and kills remaining processes |
| 36 | +4. Exits all plugins gracefully |
| 37 | +5. Moves `/dev`, `/proc`, `/sys`, `/run` to new root |
| 38 | +6. Deletes initramfs contents (if on tmpfs/ramfs) to free memory |
| 39 | +7. Moves new root mount to `/` |
| 40 | +8. Chroots to new root |
| 41 | +9. Reopens `/dev/console` for stdin/stdout/stderr |
| 42 | +10. Execs new init as PID 1 |
| 43 | + |
| 44 | + |
| 45 | +Example: Initramfs finit.conf |
| 46 | +----------------------------- |
| 47 | + |
| 48 | +Configuration file `/etc/finit.conf` in the initramfs: |
| 49 | + |
| 50 | +``` |
| 51 | +# /etc/finit.conf in initramfs |
| 52 | +
|
| 53 | +# Mount the real root filesystem |
| 54 | +run [S] name:mount-root /bin/mount /dev/sda1 /mnt/root -- Mounting root filesystem |
| 55 | +
|
| 56 | +# Switch to real root after mount completes |
| 57 | +run [S] name:switch-root /sbin/initctl switch-root /mnt/root -- Switching to real root |
| 58 | +``` |
| 59 | + |
| 60 | +For more complex setups (LUKS, LVM, etc.): |
| 61 | + |
| 62 | +``` |
| 63 | +# Unlock LUKS volume |
| 64 | +run [S] name:cryptsetup /sbin/cryptsetup open /dev/sda2 cryptroot -- Unlocking encrypted root |
| 65 | +
|
| 66 | +# Activate LVM |
| 67 | +run [S] name:lvm /sbin/lvm vgchange -ay -- Activating LVM volumes |
| 68 | +
|
| 69 | +# Mount root |
| 70 | +run [S] name:mount-root /bin/mount /dev/vg0/root /mnt/root -- Mounting root |
| 71 | +
|
| 72 | +# Switch root |
| 73 | +run [S] name:switch-root /sbin/initctl switch-root /mnt/root -- Switching to real root |
| 74 | +``` |
| 75 | + |
| 76 | + |
| 77 | +Example: Using Runlevel 1 for Switch Root |
| 78 | +----------------------------------------- |
| 79 | + |
| 80 | +For more complex initramfs setups where ordering of tasks becomes |
| 81 | +difficult in runlevel S, you can perform the switch-root in runlevel 1: |
| 82 | + |
| 83 | +``` |
| 84 | +# /etc/finit.conf in initramfs |
| 85 | +
|
| 86 | +# Start mdevd for device handling |
| 87 | +service [S] name:mdevd notify:s6 /sbin/mdevd -D %n -- Device event daemon |
| 88 | +run [S] name:coldplug <service/mdevd/ready> /sbin/mdevd-coldplug -- Coldplug devices |
| 89 | +
|
| 90 | +# Mount the real root filesystem (after devices are ready) |
| 91 | +run [S] name:mount-root <run/coldplug/success> /bin/mount /dev/sda1 /mnt/root -- Mounting root |
| 92 | +
|
| 93 | +# Transition to runlevel 1 after all S tasks complete |
| 94 | +# The switch-root runs cleanly in runlevel 1 |
| 95 | +run [1] name:switch-root /sbin/initctl switch-root /mnt/root -- Switching to real root |
| 96 | +``` |
| 97 | + |
| 98 | +This approach separates the initramfs setup (runlevel S) from the |
| 99 | +switch-root operation (runlevel 1), making task ordering simpler. |
| 100 | + |
| 101 | + |
| 102 | +Hooks |
| 103 | +----- |
| 104 | + |
| 105 | +The `HOOK_SWITCH_ROOT` hook runs before the switch begins. Use it for: |
| 106 | + |
| 107 | +- Saving state to the new root |
| 108 | +- Unmounting initramfs-only mounts |
| 109 | +- Cleanup tasks |
| 110 | + |
| 111 | +Plugins can register for `HOOK_SWITCH_ROOT` just like other hooks: |
| 112 | + |
| 113 | +```c |
| 114 | +static void my_switch_root_hook(void *arg) |
| 115 | +{ |
| 116 | + /* Cleanup before switch_root */ |
| 117 | +} |
| 118 | + |
| 119 | +static plugin_t plugin = { |
| 120 | + .name = "my-plugin", |
| 121 | + .hook[HOOK_SWITCH_ROOT] = { |
| 122 | + .cb = my_switch_root_hook |
| 123 | + } |
| 124 | +}; |
| 125 | + |
| 126 | +PLUGIN_INIT(plugin_init) |
| 127 | +{ |
| 128 | + plugin_register(&plugin); |
| 129 | +} |
| 130 | +``` |
| 131 | +
|
| 132 | +
|
| 133 | +Conditions |
| 134 | +---------- |
| 135 | +
|
| 136 | +After switch_root, the new finit instance starts fresh. No conditions |
| 137 | +or state are preserved across the switch. The new finit will: |
| 138 | +
|
| 139 | +1. Re-read `/etc/finit.conf` from the new root |
| 140 | +2. Re-initialize all conditions |
| 141 | +3. Start services according to the new configuration |
| 142 | +
|
| 143 | +
|
| 144 | +See Also |
| 145 | +-------- |
| 146 | +
|
| 147 | +- `switch_root(8)` - BusyBox switch_root utility |
| 148 | +- `initramfs(7)` - Linux initramfs documentation |
| 149 | +- [Kernel initramfs documentation](https://docs.kernel.org/filesystems/ramfs-rootfs-initramfs.html) |
0 commit comments