DHCP configured NFS root using initrd-tools
Just over a year ago (4 Sep 2001), Russell Coker dropped a note in here
re. getting an initrd NFS root boot working using initrd-tools's mkinitrd.
I've developed a slightly different version which includes DHCP support,
rather than a hardcoded network config. I've also used modprobe to
figure out the module dependencies. The new mkinitrd script is below,
if anyone's interested.
The DHCP config should specify the 'next-server' and 'filename' parameters
to the client for the NFS server and export, e.g. for dhcp3:
next-server preston;
filename "/netboot/root/gromit";
(Note udhcpc doesn't ask for the root-path option, which is why I've
"overloaded" the filename parameter).
Other details of the netboot are as per Russell's post (though I use PXE
& etherboot to fetch + load the nbi image, works well).
--
Ben Low
"A man can't be too careful in the choice of his enemies."
- Oscar Wilde (1854-1900)
/etc/mkinitrd/scripts/nfsroot:
#!/bin/sh
# mkinitrd initrd-module-and-script-generator
# - copies required modules and creates a set of initrd scripts so as
# to mount an nfs root when booting a diskless box
# i.e. this script is run when *generating* the initrd image,
# and is meant to figure out what modules need to be included
# in the initrd image; it further generates script(s) to be run by the
# initrd's init to mount the nfs root at boot time
#
# env vars set by mkinitrd:
# INITRDDIR - initrd assembly area (e.g. /tmp/mkinitrd.4553/initrd)
# MODULEDIR - module source directory (e.g. /lib/modules/2.4.18)
#
# works with initrd-tools (0.1.33), udhcpc (0.9.7-4)
#
# BDL Jul02
# Changelog:
# - removed pivot_root hack, now done by mkinitrd's init (initrd-tools 0.1.26)
# initrd required programs:
# mkinitrd conveniently handles including the required binaries
# (and supporting libraries) listed in /etc/mkinitrd/exe
# - i.e. make sure mount, pivot_root etc are listed there
# - inbuilt exe list is: /bin/sleep /sbin/modprobe /sbin/insmod /bin/ash
# (ref. /usr/sbin/mkinitrd)
#
# required modules:
# mkinitrd can probe to determine which modules need to be
# included in the initrd image for local devices (eg ide-disk),
# but can't grok nfs
# - need to manually copy them into the initrd image (e.g below)
# - or, just say "MODULES=all" in mkinitrd.conf (at 100Mb/s, what's an
# extra MB or two? :-)
#
#################################
# required modules
# - dependancies are automatically determined (below)
## mkinitrd.conf MODULES=all
#MODULES=
# only include those which are going to be loaded
MODULES=`grep -v '^#' /etc/mkinitrd/modules`
#################################
if ! [ "$INITRDDIR" -a "$MODULEDIR" ]; then
echo "usage: INITRDDIR=<initrd dir> MODULEDIR=<module dir> $0"
exit 1
fi
############################################################################
# 1. create various mount points (in initrd filesystem)
# create var ramfs mount point (mainly for dhcp params)
# - initrd fs will generally be cramfs, which is ro (should be called cromfs!)
[ -d "$INITRDDIR/var" ] || mkdir $INITRDDIR/var
# create nfs mount point in initrd fs
[ -d "$INITRDDIR/mnt" ] || mkdir $INITRDDIR/mnt
# hey, can't hurt... (right? :-)
[ -d "$INITRDDIR/proc" ] || mkdir $INITRDDIR/proc
############################################################################
# 2. copy required modules
# a simple modprobe -v -n <module> does pretty much what we want
# (lists modules which would be loaded, i.e. the dependancies, of
# <module>), though we need to tell it to look in the new moduledir,
# not the running kernel's
modulesconf=`mktemp -p $INITRDDIR/..` || exit 1
# fabricate a bare modules.conf for the target module tree
# (basically a stripped down /etc/modules.conf; ref. modules.conf(5))
# - must quote special chars (e.g. ` => \')
cat > "$modulesconf" <<-EOF
depfile=$MODULEDIR/modules.dep
generic_stringfile=$MODULEDIR/modules.generic_string
pcimapfile=$MODULEDIR/modules.pcimap
isapnpmapfile=$MODULEDIR/modules.isapnpmap
usbmapfile=$MODULEDIR/modules.usbmap
parportmapfile=$MODULEDIR/modules.parportmap
ieee1394mapfile=$MODULEDIR/modules.ieee1394map
pnpbiosmapfile=$MODULEDIR/modules.pnpbiosmap
path[toplevel]=$MODULEDIR
EOF
# determine full paths of desired modules and cp into INITRDDIR
# - cpio invocation is as per mkinitrd
(
for m in $MODULES; do
# look for "insmod <module>", one for each dependancy + the target
modprobe -C "$modulesconf" -v -n $m | awk '$1 ~ /insmod/ { print $2 }'
# alas, in '-n' mode, modprobe doesn't return correct exit status
if [ $? -ne 0 ]; then
echo "modprobe failed, aborting" >&2
exit 1
fi
done
) | cpio -pLd --quiet $INITRDDIR/
rm "$modulesconf"
############################################################################
# 3. create initrd config scripts:
# i) configure networking and fetch nfs params via dhcp client
# ii) mount nfs fs
#
# The mkinitrd init runs the files in /scripts/* (sourcing .sh files in a
# subshell).
# These scripts may in turn call others, e.g. the DHCP client script.
# - note that due to the initrd fs being read-only, it's difficult to pass
# parameters back to the caller (i.e. by writing a state file to be
# sourced by others)
# - thus, a writeable tmp filesystem is required
#
# i) the DHCP client (scripts/40networking) calls /etc/udhcpc-script when
# a lease is acquired (or not acquired - ref udhcpc(8))
# - this client script gets passed the DHCP parameters as env variables
# - the nfs-network is configured
# ii) 45mountnfs
# - uses the DHCP-supplied 'filename' as the nfs export to use as /
#
########################################
# dhcp guff
UDHCPCSCRIPT=/etc/udhcpc-script
UDHCPENV=/var/dhcp.env
cat > $INITRDDIR/$UDHCPCSCRIPT <<EOF
#!/bin/sh
# udhcpc client script
# - udhcp will have set various env variables according to the new state of
# the interface
# - NOTE: udhcpc doesn't actually configure the interface
# - NOTE: some options are very similar (cf. bootfile, boot_file !)
# - udhcpc only requests a select few options: option-{1,3,6,12,51,53,54}
# = {subnet mask, router, dns, hostname, dhcp msg type, serverid, lease t.}
# see http://www.freesoft.org/CIE/RFC/2131/8.htm for bootp/dhcp format
# - ISC DHCP options => udhcp script env variables:
# - filename => boot_file
# - dhcp-server-identifier => serverid (i.e. DHCP server address)
# - next-server => siaddr (NOTE: v0.9.6 has a bug siaddr==yiaddr)
#
case "\$1" in
deconfig) echo "\$0: \$interface: deconfig"
# i/f up, but deconfigured
ifconfig "\$interface" 0.0.0.0
;;
bound) echo "\$0: \$interface: bound to \$ip"
ifconfig "\$interface" "\$ip" netmask "\$subnet" \
\${broadcast:+broadcast \$broadcast}
route add default gw "\$router"
# set hostname, may be echoed back to the server in the
# client's dhclient script (so as to get added to lease file)
[ "\$hostname" ] && hostname "\$hostname"
# udhcp 0.9.6 bug: siaddr incorrectly == yiaddr
siaddr="\$serverid"
# ok, write env to file for others to source...
set > $UDHCPENV
;;
renew) echo "\$0: \$interface: renew \$ip";
;;
nak) echo "\$0: \$interface: nak: \$message"
;;
*) echo "\$0: huh? unknown udhcpc action: [\$1]"
;;
esac
EOF
chmod 755 $INITRDDIR/$UDHCPCSCRIPT
########################################
# scripts/
cat > $INITRDDIR/scripts/05mounts <<'EOF'
#!/bin/sh
trap '/bin/echo "FAILED [$0]"; /bin/sh' 0
set -e
[ -d /proc/1 ] || mount -n -t proc proc /proc
mount -n -t ramfs none /var
trap - 0
EOF
chmod 755 $INITRDDIR/scripts/05mounts
# note: heredoc not quoted-literal, watch interpolation
cat > $INITRDDIR/scripts/40networking <<EOF
#!/bin/sh
trap '/bin/echo "FAILED [$0]"; /bin/sh' 0
set -e
ifconfig lo 127.0.0.1 netmask 255.0.0.0
# udhcpc: lightweight dhcp client
# -f = foreground (don't fork)
# -n = "now" (exit if lease cannot be obtained)
# -q = quit after (not) obtaining lease
# -s = script to run on dhcp events (e.g. getting a lease)
# UDHCPCSCRIPT manages the interface config and writes the DHCP parameters
# (options) to UDHCPENV, as shell environment variables
udhcpc --foreground --now --quit --script=$UDHCPCSCRIPT
trap - 0
EOF
chmod 755 $INITRDDIR/scripts/40networking
# note: heredoc not quoted-literal, watch interpolation
cat > $INITRDDIR/scripts/45mountnfs <<EOF
#!/bin/sh
trap '/bin/echo "FAILED [$0]"; /bin/sh' 0
set -e
. $UDHCPENV
# could/should mount w/ 'lock'?
mount -n -o nolock,rw,rsize=8192,wsize=8192 "\$siaddr:\$boot_file" /mnt
trap - 0
EOF
chmod 755 $INITRDDIR/scripts/45mountnfs
# leave things as tidy as when we arrived :-)
cat > $INITRDDIR/scripts/S99unmounts <<'EOF'
#!/bin/sh
trap '/bin/echo "FAILED [$0]"; /bin/sh' 0
set -e
umount -n /var
umount -n /proc
trap - 0
EOF
chmod 755 $INITRDDIR/scripts/S99unmounts
Reply to: