nixpkgs/boot/boot-stage-1-init.sh
Nicolas Pierron 38e8cf175f * Use normal, dynamically linked binaries for e2fsck, lvm etc. in the
initrd instead of a mix of dietlibc, klibc and static-glibc based
  binaries.  This works by copying what we need from Glibc into the
  initrd and using patchelf to set the ELF interpreter and RPATH
  correctly.  The resulting initrd is about 500 KB smaller, but more
  importantly, it's much easier to maintain - all those
  dietlibc/klibc/static-glibc builds frequently cause build problems.

svn path=/nixos/branches/fix-style/; revision=14178
2009-02-22 16:08:13 +00:00

307 lines
7.3 KiB
Bash

#! @shell@
targetRoot=/mnt/root
export LD_LIBRARY_PATH=@extraUtils@/lib
errorDialog() {
timeout=15
echo
echo "Press within $timeout seconds:"
echo " i) to launch an interactive shell"
echo " f) to start an interactive shell having pid 1"
echo " (needed if you want to start Stage 2 manually)"
echo " *) to continue immediately (ignoring the failing command)"
read -t $timeout reply
case $reply in
f)
exec @shell@;;
i)
echo
echo "Quit interactive shell with exit status of"
echo " 0 : to continue"
echo " non-zero : to get this dialog again"
@shell@ || fail
;;
*)
echo continuing ignoring error;;
esac
}
fail() {
# If starting stage 2 failed, start an interactive shell.
echo "error while running Stage 1"
echo "Stage 1 should mount the root partition containing the nix store on \`$targetRoot'";
echo
errorDialog
}
trap 'fail' ERR;
# Poor man's `basename'.
basename() {
local s="$1"
set -- $(IFS=/; echo $s)
local res
while test $# != 0; do res=$1; shift; done
echo $res
}
# Print a greeting.
echo
echo "<<< NixOS Stage 1 >>>"
echo
# Set the PATH.
export PATH=/empty
for i in @path@; do
PATH=$PATH:$i/bin
if test -e $i/sbin; then
PATH=$PATH:$i/sbin
fi
done
# Mount special file systems.
mkdir -p /etc # to shut up mount
echo -n > /etc/fstab # idem
mkdir -p /proc
mount -t proc none /proc
mkdir -p /sys
mount -t sysfs none /sys
# Process the kernel command line.
stage2Init=/init
for o in $(cat /proc/cmdline); do
case $o in
init=*)
set -- $(IFS==; echo $o)
stage2Init=$2
;;
debugtrace)
# Show each command.
set -x
;;
debug1) # stop right away
fail
;;
debug1devices) # stop after loading modules and creating device nodes
debug1devices=1
;;
debug1mounts) # stop after mounting file systems
debug1mounts=1
;;
esac
done
# Load some kernel modules.
for i in $(cat @modulesClosure@/insmod-list); do
echo "loading module $(basename $i)..."
insmod $i || true
done
# Create /dev/null.
mknod /dev/null c 1 3
# Try to resume - all modules are loaded now.
if test -e /sys/power/tuxonice/resume; then
if test -n "$(cat /sys/power/tuxonice/resume)"; then
echo 0 > /sys/power/tuxonice/user_interface/enabled
echo 1 > /sys/power/tuxonice/do_resume || echo "Failed to resume..."
fi
fi
echo "@resumeDevice@" > /sys/power/resume 2> /dev/null || echo "Failed to resume..."
echo shutdown > /sys/power/disk
# Create device nodes in /dev.
export UDEV_CONFIG_FILE=@udevConf@
udevd --daemon
udevadm trigger
udevadm settle
if type -p dmsetup > /dev/null; then
echo "dmsetup found, starting device mapper and lvm"
dmsetup mknodes
lvm vgscan --ignorelockingfailure
lvm vgchange -ay --ignorelockingfailure
fi
if test -n "$debug1devices"; then fail; fi
# Return true if the machine is on AC power, or if we can't determine
# whether it's on AC power.
onACPower() {
if test -d "/proc/acpi/battery"; then
if ls /proc/acpi/battery/BAT[0-9]* > /dev/null 2>&1; then
if cat /proc/acpi/battery/BAT*/state \
| grep "^charging state" \
| grep -q "discharg" ; then
false
else
true
fi
else
true
fi
else
true
fi
}
# Check the specified file system, if appropriate.
checkFS() {
# Only check block devices.
if ! test -b "$device"; then return 0; fi
# For unclean ext3 file systems, fsck.ext3 should just replay the
# journal and exit, but in practice this takes *much* longer than
# letting the kernel recover the FS. So, don't run fsck on
# journalling file systems.
eval $(fstype "$device")
if test "$FSTYPE" = ext3 -o "$FSTYPE" = ext4 -o "$FSTYPE" = reiserfs -o "$FSTYPE" = xfs -o "$FSTYPE" = jfs; then
return 0;
fi
# Don't run `fsck' if the machine is on battery power. !!! Is
# this a good idea?
if ! onACPower; then
echo "on battery power, so \`fsck' not run on \`$device'"
return 0
fi
FSTAB_FILE="/etc/mtab" fsck -V -v -C -a "$device"
fsckResult=$?
if test $(($fsckResult | 2)) = $fsckResult; then
echo "fsck finished, rebooting..."
sleep 3
reboot
fi
if test $(($fsckResult | 4)) = $fsckResult; then
echo "$device has unrepaired errors, please fix them manually."
fail
fi
if test $fsckResult -ge 8; then
echo "fsck on $device failed."
fail
fi
return 0
}
# Function for mounting a file system.
mountFS() {
local device="$1"
local mountPoint="$2"
local options="$3"
local fsType="$4"
checkFS "$device"
# Mount read-writable.
mount -t "$fsType" -o "$options" "$device" /mnt-root$mountPoint || fail
}
# Try to find and mount the root device.
mkdir /mnt-root
mountPoints=(@mountPoints@)
devices=(@devices@)
fsTypes=(@fsTypes@)
optionss=(@optionss@)
for ((n = 0; n < ${#mountPoints[*]}; n++)); do
mountPoint=${mountPoints[$n]}
device=${devices[$n]}
fsType=${fsTypes[$n]}
options=${optionss[$n]}
# !!! Really quick hack to support bind mounts, i.e., where the
# "device" should be taken relative to /mnt-root, not /. Assume
# that every device that starts with / but doesn't start with /dev
# is a bind mount.
case $device in
/dev/*)
;;
/*)
device=/mnt-root$device
;;
esac
# USB storage devices tend to appear with some delay. It would be
# great if we had a way to synchronously wait for them, but
# alas... So just wait for a few seconds for the device to
# appear. If it doesn't appear, try to mount it anyway (and
# probably fail). This is a fallback for non-device "devices"
# that we don't properly recognise (like NFS mounts).
if ! test -e $device; then
echo -n "waiting for device $device to appear..."
for ((try = 0; try < 10; try++)); do
sleep 1
if test -e $device; then break; fi
echo -n "."
done
echo
fi
echo "mounting $device on $mountPoint..."
mountFS "$device" "$mountPoint" "$options" "$fsType"
done
# If this is a live-CD/DVD, then union-mount a tmpfs on top of the
# original root.
targetRoot=/mnt-root
if test -n "@isLiveCD@"; then
mkdir /mnt-tmpfs
mount -t tmpfs -o "mode=755" none /mnt-tmpfs
mkdir /mnt-union
mount -t aufs -o dirs=/mnt-tmpfs=rw:$targetRoot=ro none /mnt-union
targetRoot=/mnt-union
fi
# Stop udevd.
kill $(minips -C udevd -o pid=)
if test -n "$debug1mounts"; then fail; fi
# `run-init' needs a /dev/console on the target FS.
if ! test -e $targetRoot/dev/console; then
mkdir -p $targetRoot/dev
mknod $targetRoot/dev/console c 5 1
fi
# Start stage 2. `run-init' deletes all files in the ramfs on the
# current /.
if test -z "$stage2Init"; then fail; fi
umount /sys
umount /proc
exec run-init "$targetRoot" "$stage2Init"
echo
echo "$1: failed running $stage2Init"
echo "Dropping into a root shell..."
export $stage2Init; exec @shell@