typioca: 2.7.0 -> 2.8.0
[NixPkgs.git] / nixos / modules / system / boot / stage-1-init.sh
blob086e5d65da2f2bbe0b9482953da4b07b28abb059
1 #! @shell@
3 targetRoot=/mnt-root
4 console=tty1
5 verbose="@verbose@"
7 info() {
8 if [[ -n "$verbose" ]]; then
9 echo "$@"
13 extraUtils="@extraUtils@"
14 export LD_LIBRARY_PATH=@extraUtils@/lib
15 export PATH=@extraUtils@/bin
16 ln -s @extraUtils@/bin /bin
17 # hardcoded in util-linux's mount helper search path `/run/wrappers/bin:/run/current-system/sw/bin:/sbin`
18 ln -s @extraUtils@/bin /sbin
20 # Copy the secrets to their needed location
21 if [ -d "@extraUtils@/secrets" ]; then
22 for secret in $(cd "@extraUtils@/secrets"; find . -type f); do
23 mkdir -p $(dirname "/$secret")
24 ln -s "@extraUtils@/secrets/$secret" "$secret"
25 done
28 # Stop LVM complaining about fd3
29 export LVM_SUPPRESS_FD_WARNINGS=true
31 fail() {
32 if [ -n "$panicOnFail" ]; then exit 1; fi
34 @preFailCommands@
36 # If starting stage 2 failed, allow the user to repair the problem
37 # in an interactive shell.
38 cat <<EOF
40 An error occurred in stage 1 of the boot process, which must mount the
41 root filesystem on \`$targetRoot' and then start stage 2. Press one
42 of the following keys:
44 EOF
45 if [ -n "$allowShell" ]; then cat <<EOF
46 i) to launch an interactive shell
47 f) to start an interactive shell having pid 1 (needed if you want to
48 start stage 2's init manually)
49 EOF
51 cat <<EOF
52 r) to reboot immediately
53 *) to ignore the error and continue
54 EOF
56 read -n 1 reply
58 if [ -n "$allowShell" -a "$reply" = f ]; then
59 exec setsid @shell@ -c "exec @shell@ < /dev/$console >/dev/$console 2>/dev/$console"
60 elif [ -n "$allowShell" -a "$reply" = i ]; then
61 echo "Starting interactive shell..."
62 setsid @shell@ -c "exec @shell@ < /dev/$console >/dev/$console 2>/dev/$console" || fail
63 elif [ "$reply" = r ]; then
64 echo "Rebooting..."
65 reboot -f
66 else
67 info "Continuing..."
71 trap 'fail' 0
74 # Print a greeting.
75 info
76 info "\e[1;32m<<< @distroName@ Stage 1 >>>\e[0m"
77 info
79 # Make several required directories.
80 mkdir -p /etc/udev
81 touch /etc/fstab # to shut up mount
82 ln -s /proc/mounts /etc/mtab # to shut up mke2fs
83 touch /etc/udev/hwdb.bin # to shut up udev
84 touch /etc/initrd-release
86 # Function for waiting for device(s) to appear.
87 waitDevice() {
88 local device="$1"
89 # Split device string using ':' as a delimiter as bcachefs
90 # uses this for multi-device filesystems, i.e. /dev/sda1:/dev/sda2:/dev/sda3
91 local IFS=':'
93 # USB storage devices tend to appear with some delay. It would be
94 # great if we had a way to synchronously wait for them, but
95 # alas... So just wait for a few seconds for the device to
96 # appear.
97 for dev in $device; do
98 if test ! -e $dev; then
99 echo -n "waiting for device $dev to appear..."
100 try=20
101 while [ $try -gt 0 ]; do
102 sleep 1
103 # also re-try lvm activation now that new block devices might have appeared
104 lvm vgchange -ay
105 # and tell udev to create nodes for the new LVs
106 udevadm trigger --action=add
107 if test -e $dev; then break; fi
108 echo -n "."
109 try=$((try - 1))
110 done
111 echo
112 [ $try -ne 0 ]
114 done
117 # Create the mount point if required.
118 makeMountPoint() {
119 local device="$1"
120 local mountPoint="$2"
121 local options="$3"
123 local IFS=,
125 # If we're bind mounting a file, the mount point should also be a file.
126 if ! [ -d "$device" ]; then
127 for opt in $options; do
128 if [ "$opt" = bind ] || [ "$opt" = rbind ]; then
129 mkdir -p "$(dirname "/mnt-root$mountPoint")"
130 touch "/mnt-root$mountPoint"
131 return
133 done
136 mkdir -m 0755 -p "/mnt-root$mountPoint"
139 # Mount special file systems.
140 specialMount() {
141 local device="$1"
142 local mountPoint="$2"
143 local options="$3"
144 local fsType="$4"
146 mkdir -m 0755 -p "$mountPoint"
147 mount -n -t "$fsType" -o "$options" "$device" "$mountPoint"
149 source @earlyMountScript@
151 # Copy initrd secrets from /.initrd-secrets to their actual destinations
152 if [ -d "/.initrd-secrets" ]; then
154 # Secrets are named by their full destination pathname and stored
155 # under /.initrd-secrets/
157 for secret in $(cd "/.initrd-secrets"; find . -type f); do
158 mkdir -p $(dirname "/$secret")
159 cp "/.initrd-secrets/$secret" "$secret"
160 done
163 # Log the script output to /dev/kmsg or /run/log/stage-1-init.log.
164 mkdir -p /tmp
165 mkfifo /tmp/stage-1-init.log.fifo
166 logOutFd=8 && logErrFd=9
167 eval "exec $logOutFd>&1 $logErrFd>&2"
168 if test -w /dev/kmsg; then
169 tee -i < /tmp/stage-1-init.log.fifo /proc/self/fd/"$logOutFd" | while read -r line; do
170 if test -n "$line"; then
171 echo "<7>stage-1-init: [$(date)] $line" > /dev/kmsg
173 done &
174 else
175 mkdir -p /run/log
176 tee -i < /tmp/stage-1-init.log.fifo /run/log/stage-1-init.log &
178 exec > /tmp/stage-1-init.log.fifo 2>&1
181 # Process the kernel command line.
182 export stage2Init=/init
183 for o in $(cat /proc/cmdline); do
184 case $o in
185 console=*)
186 set -- $(IFS==; echo $o)
187 params=$2
188 set -- $(IFS=,; echo $params)
189 console=$1
191 init=*)
192 set -- $(IFS==; echo $o)
193 stage2Init=$2
195 boot.persistence=*)
196 set -- $(IFS==; echo $o)
197 persistence=$2
199 boot.persistence.opt=*)
200 set -- $(IFS==; echo $o)
201 persistence_opt=$2
203 boot.trace|debugtrace)
204 # Show each command.
205 set -x
207 boot.shell_on_fail)
208 allowShell=1
210 boot.debug1|debug1) # stop right away
211 allowShell=1
212 fail
214 boot.debug1devices) # stop after loading modules and creating device nodes
215 allowShell=1
216 debug1devices=1
218 boot.debug1mounts) # stop after mounting file systems
219 allowShell=1
220 debug1mounts=1
222 boot.panic_on_fail|stage1panic=1)
223 panicOnFail=1
225 root=*)
226 # If a root device is specified on the kernel command
227 # line, make it available through the symlink /dev/root.
228 # Recognise LABEL= and UUID= to support UNetbootin.
229 set -- $(IFS==; echo $o)
230 if [ $2 = "LABEL" ]; then
231 root="/dev/disk/by-label/$3"
232 elif [ $2 = "UUID" ]; then
233 root="/dev/disk/by-uuid/$3"
234 else
235 root=$2
237 ln -s "$root" /dev/root
239 copytoram)
240 copytoram=1
242 findiso=*)
243 # if an iso name is supplied, try to find the device where
244 # the iso resides on
245 set -- $(IFS==; echo $o)
246 isoPath=$2
248 esac
249 done
251 # Set hostid before modules are loaded.
252 # This is needed by the spl/zfs modules.
253 @setHostId@
255 # Load the required kernel modules.
256 echo @extraUtils@/bin/modprobe > /proc/sys/kernel/modprobe
257 for i in @kernelModules@; do
258 info "loading module $(basename $i)..."
259 modprobe $i
260 done
263 # Create device nodes in /dev.
264 @preDeviceCommands@
265 info "running udev..."
266 ln -sfn /proc/self/fd /dev/fd
267 ln -sfn /proc/self/fd/0 /dev/stdin
268 ln -sfn /proc/self/fd/1 /dev/stdout
269 ln -sfn /proc/self/fd/2 /dev/stderr
270 mkdir -p /etc/systemd
271 ln -sfn @linkUnits@ /etc/systemd/network
272 mkdir -p /etc/udev
273 ln -sfn @udevRules@ /etc/udev/rules.d
274 mkdir -p /dev/.mdadm
275 systemd-udevd --daemon
276 udevadm trigger --action=add
277 udevadm settle
280 # XXX: Use case usb->lvm will still fail, usb->luks->lvm is covered
281 @preLVMCommands@
283 info "starting device mapper and LVM..."
284 lvm vgchange -ay
286 if test -n "$debug1devices"; then fail; fi
289 @postDeviceCommands@
292 # Check the specified file system, if appropriate.
293 checkFS() {
294 local device="$1"
295 local fsType="$2"
297 # Only check block devices.
298 if [ ! -b "$device" ]; then return 0; fi
300 # Don't check ROM filesystems.
301 if [ "$fsType" = iso9660 -o "$fsType" = udf ]; then return 0; fi
303 # Don't check resilient COWs as they validate the fs structures at mount time
304 if [ "$fsType" = btrfs -o "$fsType" = zfs -o "$fsType" = bcachefs ]; then return 0; fi
306 # Skip fsck for apfs as the fsck utility does not support repairing the filesystem (no -a option)
307 if [ "$fsType" = apfs ]; then return 0; fi
309 # Skip fsck for nilfs2 - not needed by design and no fsck tool for this filesystem.
310 if [ "$fsType" = nilfs2 ]; then return 0; fi
312 # Skip fsck for inherently readonly filesystems.
313 if [ "$fsType" = squashfs ]; then return 0; fi
315 # Skip fsck.erofs because it is still experimental.
316 if [ "$fsType" = erofs ]; then return 0; fi
318 # If we couldn't figure out the FS type, then skip fsck.
319 if [ "$fsType" = auto ]; then
320 echo 'cannot check filesystem with type "auto"!'
321 return 0
324 # Device might be already mounted manually
325 # e.g. NBD-device or the host filesystem of the file which contains encrypted root fs
326 if mount | grep -q "^$device on "; then
327 echo "skip checking already mounted $device"
328 return 0
331 # Optionally, skip fsck on journaling filesystems. This option is
332 # a hack - it's mostly because e2fsck on ext3 takes much longer to
333 # recover the journal than the ext3 implementation in the kernel
334 # does (minutes versus seconds).
335 if test -z "@checkJournalingFS@" -a \
336 \( "$fsType" = ext3 -o "$fsType" = ext4 -o "$fsType" = reiserfs \
337 -o "$fsType" = xfs -o "$fsType" = jfs -o "$fsType" = f2fs \)
338 then
339 return 0
342 echo "checking $device..."
344 fsck -V -a "$device"
345 fsckResult=$?
347 if test $(($fsckResult | 2)) = $fsckResult; then
348 echo "fsck finished, rebooting..."
349 sleep 3
350 reboot -f
353 if test $(($fsckResult | 4)) = $fsckResult; then
354 echo "$device has unrepaired errors, please fix them manually."
355 fail
358 if test $fsckResult -ge 8; then
359 echo "fsck on $device failed."
360 fail
363 return 0
366 escapeFstab() {
367 local original="$1"
369 # Replace space
370 local escaped="${original// /\\040}"
371 # Replace tab
372 echo "${escaped//$'\t'/\\011}"
375 # Function for mounting a file system.
376 mountFS() {
377 local device="$1"
378 local mountPoint="$2"
379 local options="$3"
380 local fsType="$4"
382 if [ "$fsType" = auto ]; then
383 fsType=$(blkid -o value -s TYPE "$device")
384 if [ -z "$fsType" ]; then fsType=auto; fi
387 # Filter out x- options, which busybox doesn't do yet.
388 local optionsFiltered="$(IFS=,; for i in $options; do if [ "${i:0:2}" != "x-" ]; then echo -n $i,; fi; done)"
389 # Prefix (lower|upper|work)dir with /mnt-root (overlayfs)
390 local optionsPrefixed="$( echo "$optionsFiltered" | sed -E 's#\<(lowerdir|upperdir|workdir)=#\1=/mnt-root#g' )"
392 echo "$device /mnt-root$mountPoint $fsType $optionsPrefixed" >> /etc/fstab
394 checkFS "$device" "$fsType"
396 # Create backing directories for overlayfs
397 if [ "$fsType" = overlay ]; then
398 for i in upper work; do
399 dir="$( echo "$optionsPrefixed" | grep -o "${i}dir=[^,]*" )"
400 mkdir -m 0700 -p "${dir##*=}"
401 done
404 info "mounting $device on $mountPoint..."
406 makeMountPoint "$device" "$mountPoint" "$optionsPrefixed"
408 # For ZFS and CIFS mounts, retry a few times before giving up.
409 # We do this for ZFS as a workaround for issue NixOS/nixpkgs#25383.
410 local n=0
411 while true; do
412 mount "/mnt-root$mountPoint" && break
413 if [ \( "$fsType" != cifs -a "$fsType" != zfs \) -o "$n" -ge 10 ]; then fail; break; fi
414 echo "retrying..."
415 sleep 1
416 n=$((n + 1))
417 done
419 # For bind mounts, busybox has a tendency to ignore options, which can be a
420 # security issue (e.g. "nosuid"). Remounting the partition seems to fix the
421 # issue.
422 mount "/mnt-root$mountPoint" -o "remount,$optionsPrefixed"
424 [ "$mountPoint" == "/" ] &&
425 [ -f "/mnt-root/etc/NIXOS_LUSTRATE" ] &&
426 lustrateRoot "/mnt-root"
428 true
431 lustrateRoot () {
432 local root="$1"
434 echo
435 echo -e "\e[1;33m<<< @distroName@ is now lustrating the root filesystem (cruft goes to /old-root) >>>\e[0m"
436 echo
438 mkdir -m 0755 -p "$root/old-root.tmp"
440 echo
441 echo "Moving impurities out of the way:"
442 for d in "$root"/*
444 [ "$d" == "$root/nix" ] && continue
445 [ "$d" == "$root/boot" ] && continue # Don't render the system unbootable
446 [ "$d" == "$root/old-root.tmp" ] && continue
448 mv -v "$d" "$root/old-root.tmp"
449 done
451 # Use .tmp to make sure subsequent invocations don't clash
452 mv -v "$root/old-root.tmp" "$root/old-root"
454 mkdir -m 0755 -p "$root/etc"
455 touch "$root/etc/NIXOS"
457 exec 4< "$root/old-root/etc/NIXOS_LUSTRATE"
459 echo
460 echo "Restoring selected impurities:"
461 while read -u 4 keeper; do
462 dirname="$(dirname "$keeper")"
463 mkdir -m 0755 -p "$root/$dirname"
464 cp -av "$root/old-root/$keeper" "$root/$keeper"
465 done
467 exec 4>&-
472 if test -e /sys/power/resume -a -e /sys/power/disk; then
473 if test -n "@resumeDevice@" && waitDevice "@resumeDevice@"; then
474 resumeDev="@resumeDevice@"
475 resumeInfo="$(udevadm info -q property "$resumeDev" )"
476 else
477 for sd in @resumeDevices@; do
478 # Try to detect resume device. According to Ubuntu bug:
479 # https://bugs.launchpad.net/ubuntu/+source/pm-utils/+bug/923326/comments/1
480 # when there are multiple swap devices, we can't know where the hibernate
481 # image will reside. We can check all of them for swsuspend blkid.
482 if waitDevice "$sd"; then
483 resumeInfo="$(udevadm info -q property "$sd")"
484 if [ "$(echo "$resumeInfo" | sed -n 's/^ID_FS_TYPE=//p')" = "swsuspend" ]; then
485 resumeDev="$sd"
486 break
489 done
491 if test -n "$resumeDev"; then
492 resumeMajor="$(echo "$resumeInfo" | sed -n 's/^MAJOR=//p')"
493 resumeMinor="$(echo "$resumeInfo" | sed -n 's/^MINOR=//p')"
494 echo "$resumeMajor:$resumeMinor" > /sys/power/resume 2> /dev/null || echo "failed to resume..."
498 @postResumeCommands@
500 # If we have a path to an iso file, find the iso and link it to /dev/root
501 if [ -n "$isoPath" ]; then
502 mkdir -p /findiso
504 for delay in 5 10; do
505 blkid | while read -r line; do
506 device=$(echo "$line" | sed 's/:.*//')
507 type=$(echo "$line" | sed 's/.*TYPE="\([^"]*\)".*/\1/')
509 mount -t "$type" "$device" /findiso
510 if [ -e "/findiso$isoPath" ]; then
511 ln -sf "/findiso$isoPath" /dev/root
512 break 2
513 else
514 umount /findiso
516 done
518 sleep "$delay"
519 done
522 # Try to find and mount the root device.
523 mkdir -p $targetRoot
525 exec 3< @fsInfo@
527 while read -u 3 mountPoint; do
528 read -u 3 device
529 read -u 3 fsType
530 read -u 3 options
532 # !!! Really quick hack to support bind mounts, i.e., where the
533 # "device" should be taken relative to /mnt-root, not /. Assume
534 # that every device that starts with / but doesn't start with /dev
535 # is a bind mount.
536 pseudoDevice=
537 case $device in
538 /dev/*)
540 //*)
541 # Don't touch SMB/CIFS paths.
542 pseudoDevice=1
545 device=/mnt-root$device
548 # Not an absolute path; assume that it's a pseudo-device
549 # like an NFS path (e.g. "server:/path").
550 pseudoDevice=1
552 esac
554 if test -z "$pseudoDevice" && ! waitDevice "$device"; then
555 # If it doesn't appear, try to mount it anyway (and
556 # probably fail). This is a fallback for non-device "devices"
557 # that we don't properly recognise.
558 echo "Timed out waiting for device $device, trying to mount anyway."
561 # Wait once more for the udev queue to empty, just in case it's
562 # doing something with $device right now.
563 udevadm settle
565 # If copytoram is enabled: skip mounting the ISO and copy its content to a tmpfs.
566 if [ -n "$copytoram" ] && [ "$device" = /dev/root ] && [ "$mountPoint" = /iso ]; then
567 fsType=$(blkid -o value -s TYPE "$device")
568 fsSize=$(blockdev --getsize64 "$device" || stat -Lc '%s' "$device")
570 mkdir -p /tmp-iso
571 mount -t "$fsType" /dev/root /tmp-iso
572 mountFS tmpfs /iso size="$fsSize" tmpfs
574 cp -r /tmp-iso/* /mnt-root/iso/
576 umount /tmp-iso
577 rmdir /tmp-iso
578 if [ -n "$isoPath" ] && [ $fsType = "iso9660" ] && mountpoint -q /findiso; then
579 umount /findiso
581 continue
584 if [ "$mountPoint" = / ] && [ "$device" = tmpfs ] && [ ! -z "$persistence" ]; then
585 echo persistence...
586 waitDevice "$persistence"
587 echo enabling persistence...
588 mountFS "$persistence" "$mountPoint" "$persistence_opt" "auto"
589 continue
592 mountFS "$device" "$(escapeFstab "$mountPoint")" "$(escapeFstab "$options")" "$fsType"
593 done
595 exec 3>&-
598 @postMountCommands@
601 # Emit a udev rule for /dev/root to prevent systemd from complaining.
602 if [ -e /mnt-root/iso ]; then
603 eval $(udevadm info --export --export-prefix=ROOT_ --device-id-of-file=/mnt-root/iso)
604 else
605 eval $(udevadm info --export --export-prefix=ROOT_ --device-id-of-file=$targetRoot)
607 if [ "$ROOT_MAJOR" -a "$ROOT_MINOR" -a "$ROOT_MAJOR" != 0 ]; then
608 mkdir -p /run/udev/rules.d
609 echo 'ACTION=="add|change", SUBSYSTEM=="block", ENV{MAJOR}=="'$ROOT_MAJOR'", ENV{MINOR}=="'$ROOT_MINOR'", SYMLINK+="root"' > /run/udev/rules.d/61-dev-root-link.rules
613 # Stop udevd.
614 udevadm control --exit
616 # Reset the logging file descriptors.
617 # Do this just before pkill, which will kill the tee process.
618 exec 1>&$logOutFd 2>&$logErrFd
619 eval "exec $logOutFd>&- $logErrFd>&-"
621 # Kill any remaining processes, just to be sure we're not taking any
622 # with us into stage 2. But keep storage daemons like unionfs-fuse.
624 # Storage daemons are distinguished by an @ in front of their command line:
625 # https://www.freedesktop.org/wiki/Software/systemd/RootStorageDaemons/
626 for pid in $(pgrep -v -f '^@'); do
627 # Make sure we don't kill kernel processes, see #15226 and:
628 # http://stackoverflow.com/questions/12213445/identifying-kernel-threads
629 readlink "/proc/$pid/exe" &> /dev/null || continue
630 # Try to avoid killing ourselves.
631 [ $pid -eq $$ ] && continue
632 kill -9 "$pid"
633 done
635 if test -n "$debug1mounts"; then fail; fi
638 # Restore /proc/sys/kernel/modprobe to its original value.
639 echo /sbin/modprobe > /proc/sys/kernel/modprobe
642 # Start stage 2. `switch_root' deletes all files in the ramfs on the
643 # current root. The path has to be valid in the chroot not outside.
644 if [ ! -e "$targetRoot/$stage2Init" ]; then
645 stage2Check=${stage2Init}
646 while [ "$stage2Check" != "${stage2Check%/*}" ] && [ ! -L "$targetRoot/$stage2Check" ]; do
647 stage2Check=${stage2Check%/*}
648 done
649 if [ ! -L "$targetRoot/$stage2Check" ]; then
650 echo "stage 2 init script ($targetRoot/$stage2Init) not found"
651 fail
655 mkdir -m 0755 -p $targetRoot/proc $targetRoot/sys $targetRoot/dev $targetRoot/run
657 mount --move /proc $targetRoot/proc
658 mount --move /sys $targetRoot/sys
659 mount --move /dev $targetRoot/dev
660 mount --move /run $targetRoot/run
662 exec env -i $(type -P switch_root) "$targetRoot" "$stage2Init"
664 fail # should never be reached