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

View File

@ -129,7 +129,7 @@ let
# work. # work.
extraUtils = pkgs.runCommand "extra-utils" extraUtils = pkgs.runCommand "extra-utils"
{ buildInputs = [pkgs.nukeReferences]; { 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"); doublePatchelf = (pkgs.stdenv.system == "armv5tel-linux");
} }
'' ''
@ -139,28 +139,21 @@ let
# Copy what we need from Glibc. # Copy what we need from Glibc.
cp -pv ${pkgs.glibc}/lib/ld*.so.? $out/lib cp -pv ${pkgs.glibc}/lib/ld*.so.? $out/lib
cp -pv ${pkgs.glibc}/lib/libc.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/libpthread.so.* $out/lib
cp -pv ${pkgs.glibc}/lib/librt.so.* $out/lib cp -pv ${pkgs.glibc}/lib/librt.so.* $out/lib
cp -pv ${pkgs.glibc}/lib/libdl.so.* $out/lib cp -pv ${pkgs.glibc}/lib/libdl.so.* $out/lib
cp -pv ${pkgs.gcc.gcc}/lib*/libgcc_s.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. # Copy some utillinux stuff.
cp -v ${pkgs.utillinux}/bin/mount ${pkgs.utillinux}/bin/umount \ cp -v ${pkgs.utillinux}/sbin/blkid $out/bin
${pkgs.utillinux}/sbin/fsck ${pkgs.utillinux}/sbin/switch_root \
${pkgs.utillinux}/sbin/blkid ${pkgs.utillinux}/bin/setsid $out/bin
cp -pdv ${pkgs.utillinux}/lib/libblkid*.so.* $out/lib cp -pdv ${pkgs.utillinux}/lib/libblkid*.so.* $out/lib
cp -pdv ${pkgs.utillinux}/lib/libuuid*.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. # Copy dmsetup and lvm.
cp -v ${pkgs.lvm2}/sbin/dmsetup $out/bin/dmsetup cp -v ${pkgs.lvm2}/sbin/dmsetup $out/bin/dmsetup
cp -v ${pkgs.lvm2}/sbin/lvm $out/bin/lvm cp -v ${pkgs.lvm2}/sbin/lvm $out/bin/lvm
@ -174,12 +167,8 @@ let
cp -v ${pkgs.udev}/lib/udev/*_id $out/bin cp -v ${pkgs.udev}/lib/udev/*_id $out/bin
cp -pdv ${pkgs.udev}/lib/libudev.so.* $out/lib 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. # 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. # Maybe copy splashutils.
${optionalString enableSplashScreen '' ${optionalString enableSplashScreen ''
@ -188,6 +177,10 @@ let
${config.boot.initrd.extraUtilsCommands} ${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. # 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 for i in $out/bin/* $out/lib/*; do if ! test -L $i; then nuke-refs $i; fi; done
@ -201,27 +194,15 @@ let
fi fi
done 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. # Make sure that the patchelf'ed binaries still work.
echo "testing patched programs..." 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 export LD_LIBRARY_PATH=$out/lib
$out/bin/mount --version | grep "mount from" $out/bin/mount --help 2>&1 | grep "BusyBox"
$out/bin/umount --version | grep "umount "
$out/bin/udevadm --version $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:" $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" LVM_SYSTEM_DIR=$out $out/bin/lvm version 2>&1 | tee -a $out/log | grep "LVM"
$out/bin/mdadm --version $out/bin/mdadm --version
$out/bin/basename --version
$out/bin/modprobe --version
${config.boot.initrd.extraUtilsCommandsTest} ${config.boot.initrd.extraUtilsCommandsTest}
''; # */ ''; # */
@ -291,25 +272,20 @@ let
bootStage1 = pkgs.substituteAll { bootStage1 = pkgs.substituteAll {
src = ./stage-1-init.sh; src = ./stage-1-init.sh;
shell = "${extraUtils}/bin/bash"; shell = "${extraUtils}/bin/ash";
isExecutable = true; isExecutable = true;
inherit udevConf extraUtils; inherit udevConf extraUtils modulesClosure;
inherit (config.boot) resumeDevice devSize runSize; inherit (config.boot) resumeDevice devSize runSize;
inherit (config.boot.initrd) checkJournalingFS inherit (config.boot.initrd) checkJournalingFS
preLVMCommands postDeviceCommands postMountCommands kernelModules; preLVMCommands postDeviceCommands postMountCommands kernelModules;
# !!! copy&pasted from upstart-jobs/filesystems.nix. fsInfo =
mountPoints = let f = fs: [ fs.mountPoint (if fs.device != null then fs.device else "/dev/disk/by-label/${fs.label}") fs.fsType fs.options ];
if fileSystems == [] in pkgs.writeText "initrd-fsinfo" (concatStringsSep "\n" (concatMap f 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;
}; };

View File

@ -252,10 +252,6 @@ in
'' ''
# We need mke2fs in the initrd. # We need mke2fs in the initrd.
cp ${pkgs.e2fsprogs}/sbin/mke2fs $out/bin 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 = boot.initrd.postDeviceCommands =