Use BusyBox in the initrd

Using BusyBox instead of Bash plus a bunch of other tools gives us a
much more feature-full, yet smaller initrd.  In particular, BusyBox
contains networking commands such as ip and a DHCP client, useful for
NFS boots.  It's also much more convenient for rescue situations
because the shell has builtin readline support and there are many more
tools (including vi).
This commit is contained in:
Eelco Dolstra 2012-05-21 15:26:07 -04:00
parent 70089950d2
commit 9692495df0
5 changed files with 34 additions and 118 deletions

View file

@ -196,7 +196,6 @@
./system/boot/luksroot.nix
./system/boot/modprobe.nix
./system/boot/stage-1.nix
./system/boot/stage-1-extratools.nix
./system/boot/stage-2.nix
./system/etc/etc.nix
./system/upstart-events/control-alt-delete.nix

View file

@ -1,54 +0,0 @@
{ config, pkgs, ...}:
with pkgs.lib;
let
staticBusybox = pkgs.busybox.override {
enableStatic = true;
};
in
{
###### interface
options = {
boot.initrd.withExtraTools = mkOption {
default = false;
type = with types; bool;
description = ''
Have busybox utils in initrd, and an interactive bash.
'';
};
};
config = {
boot.initrd.extraUtilsCommands = mkIf config.boot.initrd.withExtraTools ''
cp -pv ${pkgs.ncurses}/lib/libncurses*.so.* $out/lib
cp -pv ${pkgs.readline}/lib/libreadline.so.* $out/lib
cp -pv ${pkgs.readline}/lib/libhistory.so.* $out/lib
rm $out/bin/bash
cp -pv ${pkgs.bashInteractive}/bin/bash $out/bin
cp -pv ${staticBusybox}/bin/busybox $out/bin
shopt -s nullglob
for d in bin sbin; do
pushd ${staticBusybox}/$d
# busybox has these, but we'll put them later
GLOBIGNORE=.:..:mke2fs:ip:modprobe
for a in *; do
if [ ! -e $out/bin/$a ]; then
ln -sf busybox $out/bin/$a
fi
done
popd
done
shopt -u nullglob
unset GLOBIGNORE
'';
boot.initrd.extraUtilsCommandsTest = mkIf config.boot.initrd.withExtraTools ''
$out/bin/busybox
'';
};
}

View file

@ -3,7 +3,7 @@
targetRoot=/mnt-root
export LD_LIBRARY_PATH=@extraUtils@/lib
export PATH=@extraUtils@/bin
export PATH=@extraUtils@/bin:@extraUtils@/sbin
fail() {
@ -50,7 +50,7 @@ EOF
esac
}
trap 'fail' ERR
trap 'fail' 0
# Print a greeting.
@ -60,8 +60,9 @@ echo
# Mount special file systems.
mkdir -p /etc # to shut up mount
echo -n > /etc/fstab # idem
mkdir -p /etc
touch /etc/fstab # to shut up mount
touch /etc/mtab # to shut up mke2fs
mkdir -p /proc
mount -t proc none /proc
mkdir -p /sys
@ -120,6 +121,8 @@ done
# Load the required kernel modules.
mkdir -p /lib
ln -s @modulesClosure@/lib/modules /lib/modules
echo @extraUtils@/bin/modprobe > /proc/sys/kernel/modprobe
for i in @kernelModules@; do
echo "loading module $(basename $i)..."
@ -259,16 +262,12 @@ mountFS() {
# Try to find and mount the root device.
mkdir /mnt-root
mountPoints=(@mountPoints@)
devices=(@devices@)
fsTypes=(@fsTypes@)
optionss=(@optionss@)
exec 3< @fsInfo@
for ((n = 0; n < ${#mountPoints[*]}; n++)); do
mountPoint=${mountPoints[$n]}
device=${devices[$n]}
fsType=${fsTypes[$n]}
options=${optionss[$n]}
while read -u 3 mountPoint; do
read -u 3 device
read -u 3 fsType
read -u 3 options
# !!! Really quick hack to support bind mounts, i.e., where the
# "device" should be taken relative to /mnt-root, not /. Assume
@ -300,7 +299,7 @@ for ((n = 0; n < ${#mountPoints[*]}; n++)); do
# that we don't properly recognise.
if test -z "$pseudoDevice" -a ! -e $device; then
echo -n "waiting for device $device to appear..."
for ((try = 0; try < 20; try++)); do
for try in $(seq 1 20); do
sleep 1
if test -e $device; then break; fi
echo -n "."
@ -326,7 +325,7 @@ udevadm control --exit || true
# Kill any remaining processes, just to be sure we're not taking any
# with us into stage 2.
kill -9 -- -1
pkill -9 -v 1
if test -n "$debug1mounts"; then fail; fi

View file

@ -129,7 +129,7 @@ let
# work.
extraUtils = pkgs.runCommand "extra-utils"
{ buildInputs = [pkgs.nukeReferences];
allowedReferences = [ "out" modulesClosure ]; # prevent accidents like glibc being included in the initrd
allowedReferences = [ "out" ]; # prevent accidents like glibc being included in the initrd
doublePatchelf = (pkgs.stdenv.system == "armv5tel-linux");
}
''
@ -139,28 +139,21 @@ let
# Copy what we need from Glibc.
cp -pv ${pkgs.glibc}/lib/ld*.so.? $out/lib
cp -pv ${pkgs.glibc}/lib/libc.so.* $out/lib
cp -pv ${pkgs.glibc}/lib/libm.so.* $out/lib
cp -pv ${pkgs.glibc}/lib/libpthread.so.* $out/lib
cp -pv ${pkgs.glibc}/lib/librt.so.* $out/lib
cp -pv ${pkgs.glibc}/lib/libdl.so.* $out/lib
cp -pv ${pkgs.gcc.gcc}/lib*/libgcc_s.so.* $out/lib
# Copy BusyBox.
cp -rvd ${pkgs.busybox}/{bin,sbin} $out/
chmod -R u+w $out
# Copy some utillinux stuff.
cp -v ${pkgs.utillinux}/bin/mount ${pkgs.utillinux}/bin/umount \
${pkgs.utillinux}/sbin/fsck ${pkgs.utillinux}/sbin/switch_root \
${pkgs.utillinux}/sbin/blkid ${pkgs.utillinux}/bin/setsid $out/bin
cp -v ${pkgs.utillinux}/sbin/blkid $out/bin
cp -pdv ${pkgs.utillinux}/lib/libblkid*.so.* $out/lib
cp -pdv ${pkgs.utillinux}/lib/libuuid*.so.* $out/lib
# Copy some coreutils.
cp -v ${pkgs.coreutils}/bin/basename $out/bin
cp -v ${pkgs.coreutils}/bin/mkdir $out/bin
cp -v ${pkgs.coreutils}/bin/mknod $out/bin
cp -v ${pkgs.coreutils}/bin/chmod $out/bin
cp -v ${pkgs.coreutils}/bin/cat $out/bin
cp -v ${pkgs.coreutils}/bin/chroot $out/bin
cp -v ${pkgs.coreutils}/bin/sleep $out/bin
cp -v ${pkgs.coreutils}/bin/ln $out/bin
# Copy dmsetup and lvm.
cp -v ${pkgs.lvm2}/sbin/dmsetup $out/bin/dmsetup
cp -v ${pkgs.lvm2}/sbin/lvm $out/bin/lvm
@ -174,12 +167,8 @@ let
cp -v ${pkgs.udev}/lib/udev/*_id $out/bin
cp -pdv ${pkgs.udev}/lib/libudev.so.* $out/lib
# Copy bash.
cp -v ${pkgs.bash}/bin/bash $out/bin
ln -sv bash $out/bin/sh
# Copy modprobe.
cp -v ${pkgs.module_init_tools}/sbin/modprobe $out/bin/modprobe.real
cp -v ${pkgs.module_init_tools}/sbin/modprobe $out/bin/modprobe
# Maybe copy splashutils.
${optionalString enableSplashScreen ''
@ -188,6 +177,10 @@ let
${config.boot.initrd.extraUtilsCommands}
# Strip binaries further than normal.
chmod -R u+w $out
stripDirs "lib bin" "-s"
# Run patchelf to make the programs refer to the copied libraries.
for i in $out/bin/* $out/lib/*; do if ! test -L $i; then nuke-refs $i; fi; done
@ -201,27 +194,15 @@ let
fi
done
# Make the modprobe wrapper that sets $MODULE_DIR.
cat > $out/bin/modprobe <<EOF
#! $out/bin/bash
export MODULE_DIR=${modulesClosure}/lib/modules
exec $out/bin/modprobe.real "\$@"
EOF
chmod u+x $out/bin/modprobe
# Make sure that the patchelf'ed binaries still work.
echo "testing patched programs..."
$out/bin/bash --version | grep "bash, version"
$out/bin/ash -c 'echo hello world' | grep "hello world"
export LD_LIBRARY_PATH=$out/lib
$out/bin/mount --version | grep "mount from"
$out/bin/umount --version | grep "umount "
$out/bin/mount --help 2>&1 | grep "BusyBox"
$out/bin/udevadm --version
$out/bin/blkid -v 2>&1 | tee -a $out/log | grep "blkid from util-linux"
$out/bin/dmsetup --version 2>&1 | tee -a $out/log | grep "version:"
LVM_SYSTEM_DIR=$out $out/bin/lvm version 2>&1 | tee -a $out/log | grep "LVM"
$out/bin/mdadm --version
$out/bin/basename --version
$out/bin/modprobe --version
${config.boot.initrd.extraUtilsCommandsTest}
''; # */
@ -291,25 +272,20 @@ let
bootStage1 = pkgs.substituteAll {
src = ./stage-1-init.sh;
shell = "${extraUtils}/bin/bash";
shell = "${extraUtils}/bin/ash";
isExecutable = true;
inherit udevConf extraUtils;
inherit udevConf extraUtils modulesClosure;
inherit (config.boot) resumeDevice devSize runSize;
inherit (config.boot.initrd) checkJournalingFS
preLVMCommands postDeviceCommands postMountCommands kernelModules;
# !!! copy&pasted from upstart-jobs/filesystems.nix.
mountPoints =
if fileSystems == []
then abort "You must specify the fileSystems option!"
else map (fs: fs.mountPoint) fileSystems;
devices = map (fs: if fs.device != null then fs.device else "/dev/disk/by-label/${fs.label}") fileSystems;
fsTypes = map (fs: fs.fsType) fileSystems;
optionss = map (fs: fs.options) fileSystems;
fsInfo =
let f = fs: [ fs.mountPoint (if fs.device != null then fs.device else "/dev/disk/by-label/${fs.label}") fs.fsType fs.options ];
in pkgs.writeText "initrd-fsinfo" (concatStringsSep "\n" (concatMap f fileSystems));
};

View file

@ -252,10 +252,6 @@ in
''
# We need mke2fs in the initrd.
cp ${pkgs.e2fsprogs}/sbin/mke2fs $out/bin
# And `ip' (which needs libresolv.so).
cp ${pkgs.iproute}/sbin/ip $out/bin
cp ${pkgs.glibc}/lib/libresolv.so.* $out/lib
'';
boot.initrd.postDeviceCommands =