summaryrefslogtreecommitdiff
path: root/modules/stage-1/stage-1-init.sh
diff options
context:
space:
mode:
authorGregor Kleen <gkleen@yggdrasil.li>2022-05-26 13:58:07 +0200
committerGregor Kleen <gkleen@yggdrasil.li>2022-05-26 13:58:07 +0200
commitfc6cf6169868e60c189e4b243330c3717ff159f3 (patch)
tree3f6dea9c1420e23756257b5abea27ec9ed92d58a /modules/stage-1/stage-1-init.sh
parent84f2affd66a0ff3947b91a30308cb8e6a8ff7594 (diff)
downloadnixos-fc6cf6169868e60c189e4b243330c3717ff159f3.tar
nixos-fc6cf6169868e60c189e4b243330c3717ff159f3.tar.gz
nixos-fc6cf6169868e60c189e4b243330c3717ff159f3.tar.bz2
nixos-fc6cf6169868e60c189e4b243330c3717ff159f3.tar.xz
nixos-fc6cf6169868e60c189e4b243330c3717ff159f3.zip
...
Diffstat (limited to 'modules/stage-1/stage-1-init.sh')
-rw-r--r--modules/stage-1/stage-1-init.sh638
1 files changed, 0 insertions, 638 deletions
diff --git a/modules/stage-1/stage-1-init.sh b/modules/stage-1/stage-1-init.sh
deleted file mode 100644
index ddaf9858..00000000
--- a/modules/stage-1/stage-1-init.sh
+++ /dev/null
@@ -1,638 +0,0 @@
1#! @shell@
2
3targetRoot=/mnt-root
4console=tty1
5verbose="@verbose@"
6
7info() {
8 if [[ -n "$verbose" ]]; then
9 echo "$@"
10 fi
11}
12
13extraUtils="@extraUtils@"
14export LD_LIBRARY_PATH=@extraUtils@/lib
15export PATH=@extraUtils@/bin
16ln -s @extraUtils@/bin /bin
17
18# Copy the secrets to their needed location
19if [ -d "@extraUtils@/secrets" ]; then
20 for secret in $(cd "@extraUtils@/secrets"; find . -type f); do
21 mkdir -p $(dirname "/$secret")
22 ln -s "@extraUtils@/secrets/$secret" "$secret"
23 done
24fi
25
26# Stop LVM complaining about fd3
27export LVM_SUPPRESS_FD_WARNINGS=true
28
29fail() {
30 if [ -n "$panicOnFail" ]; then exit 1; fi
31
32 @preFailCommands@
33
34 # If starting stage 2 failed, allow the user to repair the problem
35 # in an interactive shell.
36 cat <<EOF
37
38An error occurred in stage 1 of the boot process, which must mount the
39root filesystem on \`$targetRoot' and then start stage 2. Press one
40of the following keys:
41
42EOF
43 if [ -n "$allowShell" ]; then cat <<EOF
44 i) to launch an interactive shell
45 f) to start an interactive shell having pid 1 (needed if you want to
46 start stage 2's init manually)
47EOF
48 fi
49 cat <<EOF
50 r) to reboot immediately
51 *) to ignore the error and continue
52EOF
53
54 read -n 1 reply
55
56 if [ -n "$allowShell" -a "$reply" = f ]; then
57 exec setsid @shell@ -c "exec @shell@ < /dev/$console >/dev/$console 2>/dev/$console"
58 elif [ -n "$allowShell" -a "$reply" = i ]; then
59 echo "Starting interactive shell..."
60 setsid @shell@ -c "exec @shell@ < /dev/$console >/dev/$console 2>/dev/$console" || fail
61 elif [ "$reply" = r ]; then
62 echo "Rebooting..."
63 reboot -f
64 else
65 info "Continuing..."
66 fi
67}
68
69trap 'fail' 0
70
71
72# Print a greeting.
73info
74info "<<< NixOS Stage 1 >>>"
75info
76
77# Make several required directories.
78mkdir -p /etc/udev
79touch /etc/fstab # to shut up mount
80ln -s /proc/mounts /etc/mtab # to shut up mke2fs
81touch /etc/udev/hwdb.bin # to shut up udev
82touch /etc/initrd-release
83
84# Function for waiting a device to appear.
85waitDevice() {
86 local device="$1"
87
88 # USB storage devices tend to appear with some delay. It would be
89 # great if we had a way to synchronously wait for them, but
90 # alas... So just wait for a few seconds for the device to
91 # appear.
92 if test ! -e $device; then
93 echo -n "waiting for device $device to appear..."
94 try=20
95 while [ $try -gt 0 ]; do
96 sleep 1
97 # also re-try lvm activation now that new block devices might have appeared
98 lvm vgchange -ay
99 # and tell udev to create nodes for the new LVs
100 udevadm trigger --action=add
101 if test -e $device; then break; fi
102 echo -n "."
103 try=$((try - 1))
104 done
105 echo
106 [ $try -ne 0 ]
107 fi
108}
109
110# Mount special file systems.
111specialMount() {
112 local device="$1"
113 local mountPoint="$2"
114 local options="$3"
115 local fsType="$4"
116
117 mkdir -m 0755 -p "$mountPoint"
118 mount -n -t "$fsType" -o "$options" "$device" "$mountPoint"
119}
120source @earlyMountScript@
121
122# Log the script output to /dev/kmsg or /run/log/stage-1-init.log.
123mkdir -p /tmp
124mkfifo /tmp/stage-1-init.log.fifo
125logOutFd=8 && logErrFd=9
126eval "exec $logOutFd>&1 $logErrFd>&2"
127if test -w /dev/kmsg; then
128 tee -i < /tmp/stage-1-init.log.fifo /proc/self/fd/"$logOutFd" | while read -r line; do
129 if test -n "$line"; then
130 echo "<7>stage-1-init: [$(date)] $line" > /dev/kmsg
131 fi
132 done &
133else
134 mkdir -p /run/log
135 tee -i < /tmp/stage-1-init.log.fifo /run/log/stage-1-init.log &
136fi
137exec > /tmp/stage-1-init.log.fifo 2>&1
138
139
140# Process the kernel command line.
141export stage2Init=/init
142for o in $(cat /proc/cmdline); do
143 case $o in
144 console=*)
145 set -- $(IFS==; echo $o)
146 params=$2
147 set -- $(IFS=,; echo $params)
148 console=$1
149 ;;
150 init=*)
151 set -- $(IFS==; echo $o)
152 stage2Init=$2
153 ;;
154 boot.persistence=*)
155 set -- $(IFS==; echo $o)
156 persistence=$2
157 ;;
158 boot.persistence.opt=*)
159 set -- $(IFS==; echo $o)
160 persistence_opt=$2
161 ;;
162 boot.trace|debugtrace)
163 # Show each command.
164 set -x
165 ;;
166 boot.shell_on_fail)
167 allowShell=1
168 ;;
169 boot.debug1|debug1) # stop right away
170 allowShell=1
171 fail
172 ;;
173 boot.debug1devices) # stop after loading modules and creating device nodes
174 allowShell=1
175 debug1devices=1
176 ;;
177 boot.debug1mounts) # stop after mounting file systems
178 allowShell=1
179 debug1mounts=1
180 ;;
181 boot.panic_on_fail|stage1panic=1)
182 panicOnFail=1
183 ;;
184 root=*)
185 # If a root device is specified on the kernel command
186 # line, make it available through the symlink /dev/root.
187 # Recognise LABEL= and UUID= to support UNetbootin.
188 set -- $(IFS==; echo $o)
189 if [ $2 = "LABEL" ]; then
190 root="/dev/disk/by-label/$3"
191 elif [ $2 = "UUID" ]; then
192 root="/dev/disk/by-uuid/$3"
193 else
194 root=$2
195 fi
196 ln -s "$root" /dev/root
197 ;;
198 copytoram)
199 copytoram=1
200 ;;
201 findiso=*)
202 # if an iso name is supplied, try to find the device where
203 # the iso resides on
204 set -- $(IFS==; echo $o)
205 isoPath=$2
206 ;;
207 esac
208done
209
210# Set hostid before modules are loaded.
211# This is needed by the spl/zfs modules.
212@setHostId@
213
214# Load the required kernel modules.
215mkdir -p /lib
216ln -s @modulesClosure@/lib/modules /lib/modules
217ln -s @modulesClosure@/lib/firmware /lib/firmware
218echo @extraUtils@/bin/modprobe > /proc/sys/kernel/modprobe
219for i in @kernelModules@; do
220 info "loading module $(basename $i)..."
221 modprobe $i
222done
223
224
225# Create device nodes in /dev.
226@preDeviceCommands@
227info "running udev..."
228ln -sfn /proc/self/fd /dev/fd
229ln -sfn /proc/self/fd/0 /dev/stdin
230ln -sfn /proc/self/fd/1 /dev/stdout
231ln -sfn /proc/self/fd/2 /dev/stderr
232mkdir -p /etc/systemd
233ln -sfn @linkUnits@ /etc/systemd/network
234mkdir -p /etc/udev
235ln -sfn @udevRules@ /etc/udev/rules.d
236mkdir -p /dev/.mdadm
237systemd-udevd --daemon
238udevadm trigger --action=add
239udevadm settle
240
241
242# XXX: Use case usb->lvm will still fail, usb->luks->lvm is covered
243@preLVMCommands@
244
245info "starting device mapper and LVM..."
246lvm vgchange -ay
247
248if test -n "$debug1devices"; then fail; fi
249
250
251@postDeviceCommands@
252
253
254# Return true if the machine is on AC power, or if we can't determine
255# whether it's on AC power.
256onACPower() {
257 ! test -d "/proc/acpi/battery" ||
258 ! ls /proc/acpi/battery/BAT[0-9]* > /dev/null 2>&1 ||
259 ! cat /proc/acpi/battery/BAT*/state | grep "^charging state" | grep -q "discharg"
260}
261
262
263# Check the specified file system, if appropriate.
264checkFS() {
265 local device="$1"
266 local fsType="$2"
267
268 # Only check block devices.
269 if [ ! -b "$device" ]; then return 0; fi
270
271 # Don't check ROM filesystems.
272 if [ "$fsType" = iso9660 -o "$fsType" = udf ]; then return 0; fi
273
274 # Don't check resilient COWs as they validate the fs structures at mount time
275 if [ "$fsType" = btrfs -o "$fsType" = zfs -o "$fsType" = bcachefs ]; then return 0; fi
276
277 # Skip fsck for nilfs2 - not needed by design and no fsck tool for this filesystem.
278 if [ "$fsType" = nilfs2 ]; then return 0; fi
279
280 # Skip fsck for inherently readonly filesystems.
281 if [ "$fsType" = squashfs ]; then return 0; fi
282
283 # If we couldn't figure out the FS type, then skip fsck.
284 if [ "$fsType" = auto ]; then
285 echo 'cannot check filesystem with type "auto"!'
286 return 0
287 fi
288
289 # Device might be already mounted manually
290 # e.g. NBD-device or the host filesystem of the file which contains encrypted root fs
291 if mount | grep -q "^$device on "; then
292 echo "skip checking already mounted $device"
293 return 0
294 fi
295
296 # Optionally, skip fsck on journaling filesystems. This option is
297 # a hack - it's mostly because e2fsck on ext3 takes much longer to
298 # recover the journal than the ext3 implementation in the kernel
299 # does (minutes versus seconds).
300 if test -z "@checkJournalingFS@" -a \
301 \( "$fsType" = ext3 -o "$fsType" = ext4 -o "$fsType" = reiserfs \
302 -o "$fsType" = xfs -o "$fsType" = jfs -o "$fsType" = f2fs \)
303 then
304 return 0
305 fi
306
307 # Don't run `fsck' if the machine is on battery power. !!! Is
308 # this a good idea?
309 if ! onACPower; then
310 echo "on battery power, so no \`fsck' will be performed on \`$device'"
311 return 0
312 fi
313
314 echo "checking $device..."
315
316 fsckFlags=
317 if test "$fsType" != "btrfs"; then
318 fsckFlags="-V -a"
319 fi
320 fsck $fsckFlags "$device"
321 fsckResult=$?
322
323 if test $(($fsckResult | 2)) = $fsckResult; then
324 echo "fsck finished, rebooting..."
325 sleep 3
326 reboot -f
327 fi
328
329 if test $(($fsckResult | 4)) = $fsckResult; then
330 echo "$device has unrepaired errors, please fix them manually."
331 fail
332 fi
333
334 if test $fsckResult -ge 8; then
335 echo "fsck on $device failed."
336 fail
337 fi
338
339 return 0
340}
341
342
343# Function for mounting a file system.
344mountFS() {
345 local device="$1"
346 local mountPoint="$2"
347 local options="$3"
348 local fsType="$4"
349
350 if [ "$fsType" = auto ]; then
351 fsType=$(blkid -o value -s TYPE "$device")
352 if [ -z "$fsType" ]; then fsType=auto; fi
353 fi
354
355 # Filter out x- options, which busybox doesn't do yet.
356 local optionsFiltered="$(IFS=,; for i in $options; do if [ "${i:0:2}" != "x-" ]; then echo -n $i,; fi; done)"
357 # Prefix (lower|upper|work)dir with /mnt-root (overlayfs)
358 local optionsPrefixed="$( echo "$optionsFiltered" | sed -E 's#\<(lowerdir|upperdir|workdir)=#\1=/mnt-root#g' )"
359
360 echo "$device /mnt-root$mountPoint $fsType $optionsPrefixed" >> /etc/fstab
361
362 checkFS "$device" "$fsType"
363
364 # Optionally resize the filesystem.
365 case $options in
366 *x-nixos.autoresize*)
367 if [ "$fsType" = ext2 -o "$fsType" = ext3 -o "$fsType" = ext4 ]; then
368 modprobe "$fsType"
369 echo "resizing $device..."
370 e2fsck -fp "$device"
371 resize2fs "$device"
372 elif [ "$fsType" = f2fs ]; then
373 echo "resizing $device..."
374 fsck.f2fs -fp "$device"
375 resize.f2fs "$device"
376 fi
377 ;;
378 esac
379
380 # Create backing directories for overlayfs
381 if [ "$fsType" = overlay ]; then
382 for i in upper work; do
383 dir="$( echo "$optionsPrefixed" | grep -o "${i}dir=[^,]*" )"
384 mkdir -m 0700 -p "${dir##*=}"
385 done
386 fi
387
388 info "mounting $device on $mountPoint..."
389
390 mkdir -p "/mnt-root$mountPoint"
391
392 # For ZFS and CIFS mounts, retry a few times before giving up.
393 # We do this for ZFS as a workaround for issue NixOS/nixpkgs#25383.
394 local n=0
395 while true; do
396 mount "/mnt-root$mountPoint" && break
397 if [ \( "$fsType" != cifs -a "$fsType" != zfs \) -o "$n" -ge 10 ]; then fail; break; fi
398 echo "retrying..."
399 sleep 1
400 n=$((n + 1))
401 done
402
403 [ "$mountPoint" == "/" ] &&
404 [ -f "/mnt-root/etc/NIXOS_LUSTRATE" ] &&
405 lustrateRoot "/mnt-root"
406
407 true
408}
409
410lustrateRoot () {
411 local root="$1"
412
413 echo
414 echo -e "\e[1;33m<<< NixOS is now lustrating the root filesystem (cruft goes to /old-root) >>>\e[0m"
415 echo
416
417 mkdir -m 0755 -p "$root/old-root.tmp"
418
419 echo
420 echo "Moving impurities out of the way:"
421 for d in "$root"/*
422 do
423 [ "$d" == "$root/nix" ] && continue
424 [ "$d" == "$root/boot" ] && continue # Don't render the system unbootable
425 [ "$d" == "$root/old-root.tmp" ] && continue
426
427 mv -v "$d" "$root/old-root.tmp"
428 done
429
430 # Use .tmp to make sure subsequent invokations don't clash
431 mv -v "$root/old-root.tmp" "$root/old-root"
432
433 mkdir -m 0755 -p "$root/etc"
434 touch "$root/etc/NIXOS"
435
436 exec 4< "$root/old-root/etc/NIXOS_LUSTRATE"
437
438 echo
439 echo "Restoring selected impurities:"
440 while read -u 4 keeper; do
441 dirname="$(dirname "$keeper")"
442 mkdir -m 0755 -p "$root/$dirname"
443 cp -av "$root/old-root/$keeper" "$root/$keeper"
444 done
445
446 exec 4>&-
447}
448
449
450
451if test -e /sys/power/resume -a -e /sys/power/disk; then
452 if test -n "@resumeDevice@" && waitDevice "@resumeDevice@"; then
453 resumeDev="@resumeDevice@"
454 resumeInfo="$(udevadm info -q property "$resumeDev" )"
455 else
456 for sd in @resumeDevices@; do
457 # Try to detect resume device. According to Ubuntu bug:
458 # https://bugs.launchpad.net/ubuntu/+source/pm-utils/+bug/923326/comments/1
459 # when there are multiple swap devices, we can't know where the hibernate
460 # image will reside. We can check all of them for swsuspend blkid.
461 if waitDevice "$sd"; then
462 resumeInfo="$(udevadm info -q property "$sd")"
463 if [ "$(echo "$resumeInfo" | sed -n 's/^ID_FS_TYPE=//p')" = "swsuspend" ]; then
464 resumeDev="$sd"
465 break
466 fi
467 fi
468 done
469 fi
470 if test -n "$resumeDev"; then
471 resumeMajor="$(echo "$resumeInfo" | sed -n 's/^MAJOR=//p')"
472 resumeMinor="$(echo "$resumeInfo" | sed -n 's/^MINOR=//p')"
473 echo "$resumeMajor:$resumeMinor" > /sys/power/resume 2> /dev/null || echo "failed to resume..."
474 fi
475fi
476
477# If we have a path to an iso file, find the iso and link it to /dev/root
478if [ -n "$isoPath" ]; then
479 mkdir -p /findiso
480
481 for delay in 5 10; do
482 blkid | while read -r line; do
483 device=$(echo "$line" | sed 's/:.*//')
484 type=$(echo "$line" | sed 's/.*TYPE="\([^"]*\)".*/\1/')
485
486 mount -t "$type" "$device" /findiso
487 if [ -e "/findiso$isoPath" ]; then
488 ln -sf "/findiso$isoPath" /dev/root
489 break 2
490 else
491 umount /findiso
492 fi
493 done
494
495 sleep "$delay"
496 done
497fi
498
499# Try to find and mount the root device.
500mkdir -p $targetRoot
501
502exec 3< @fsInfo@
503
504while read -u 3 mountPoint; do
505 read -u 3 device
506 read -u 3 fsType
507 read -u 3 options
508
509 # !!! Really quick hack to support bind mounts, i.e., where the
510 # "device" should be taken relative to /mnt-root, not /. Assume
511 # that every device that starts with / but doesn't start with /dev
512 # is a bind mount.
513 pseudoDevice=
514 case $device in
515 /dev/*)
516 ;;
517 //*)
518 # Don't touch SMB/CIFS paths.
519 pseudoDevice=1
520 ;;
521 /*)
522 device=/mnt-root$device
523 ;;
524 *)
525 # Not an absolute path; assume that it's a pseudo-device
526 # like an NFS path (e.g. "server:/path").
527 pseudoDevice=1
528 ;;
529 esac
530
531 if test -z "$pseudoDevice" && ! waitDevice "$device"; then
532 # If it doesn't appear, try to mount it anyway (and
533 # probably fail). This is a fallback for non-device "devices"
534 # that we don't properly recognise.
535 echo "Timed out waiting for device $device, trying to mount anyway."
536 fi
537
538 # Wait once more for the udev queue to empty, just in case it's
539 # doing something with $device right now.
540 udevadm settle
541
542 # If copytoram is enabled: skip mounting the ISO and copy its content to a tmpfs.
543 if [ -n "$copytoram" ] && [ "$device" = /dev/root ] && [ "$mountPoint" = /iso ]; then
544 fsType=$(blkid -o value -s TYPE "$device")
545 fsSize=$(blockdev --getsize64 "$device")
546
547 mkdir -p /tmp-iso
548 mount -t "$fsType" /dev/root /tmp-iso
549 mountFS tmpfs /iso size="$fsSize" tmpfs
550
551 cp -r /tmp-iso/* /mnt-root/iso/
552
553 umount /tmp-iso
554 rmdir /tmp-iso
555 continue
556 fi
557
558 if [ "$mountPoint" = / ] && [ "$device" = tmpfs ] && [ ! -z "$persistence" ]; then
559 echo persistence...
560 waitDevice "$persistence"
561 echo enabling persistence...
562 mountFS "$persistence" "$mountPoint" "$persistence_opt" "auto"
563 continue
564 fi
565
566 mountFS "$device" "$mountPoint" "$options" "$fsType"
567done
568
569exec 3>&-
570
571
572@postMountCommands@
573
574
575# Emit a udev rule for /dev/root to prevent systemd from complaining.
576if [ -e /mnt-root/iso ]; then
577 eval $(udevadm info --export --export-prefix=ROOT_ --device-id-of-file=/mnt-root/iso)
578else
579 eval $(udevadm info --export --export-prefix=ROOT_ --device-id-of-file=$targetRoot)
580fi
581if [ "$ROOT_MAJOR" -a "$ROOT_MINOR" -a "$ROOT_MAJOR" != 0 ]; then
582 mkdir -p /run/udev/rules.d
583 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
584fi
585
586
587# Stop udevd.
588udevadm control --exit
589
590# Reset the logging file descriptors.
591# Do this just before pkill, which will kill the tee process.
592exec 1>&$logOutFd 2>&$logErrFd
593eval "exec $logOutFd>&- $logErrFd>&-"
594
595# Kill any remaining processes, just to be sure we're not taking any
596# with us into stage 2. But keep storage daemons like unionfs-fuse.
597#
598# Storage daemons are distinguished by an @ in front of their command line:
599# https://www.freedesktop.org/wiki/Software/systemd/RootStorageDaemons/
600for pid in $(pgrep -v -f '^@'); do
601 # Make sure we don't kill kernel processes, see #15226 and:
602 # http://stackoverflow.com/questions/12213445/identifying-kernel-threads
603 readlink "/proc/$pid/exe" &> /dev/null || continue
604 # Try to avoid killing ourselves.
605 [ $pid -eq $$ ] && continue
606 kill -9 "$pid"
607done
608
609if test -n "$debug1mounts"; then fail; fi
610
611
612# Restore /proc/sys/kernel/modprobe to its original value.
613echo /sbin/modprobe > /proc/sys/kernel/modprobe
614
615
616# Start stage 2. `switch_root' deletes all files in the ramfs on the
617# current root. The path has to be valid in the chroot not outside.
618if [ ! -e "$targetRoot/$stage2Init" ]; then
619 stage2Check=${stage2Init}
620 while [ "$stage2Check" != "${stage2Check%/*}" ] && [ ! -L "$targetRoot/$stage2Check" ]; do
621 stage2Check=${stage2Check%/*}
622 done
623 if [ ! -L "$targetRoot/$stage2Check" ]; then
624 echo "stage 2 init script ($targetRoot/$stage2Init) not found"
625 fail
626 fi
627fi
628
629mkdir -m 0755 -p $targetRoot/proc $targetRoot/sys $targetRoot/dev $targetRoot/run
630
631mount --move /proc $targetRoot/proc
632mount --move /sys $targetRoot/sys
633mount --move /dev $targetRoot/dev
634mount --move /run $targetRoot/run
635
636exec env -i $(type -P switch_root) "$targetRoot" "$stage2Init"
637
638fail # should never be reached