Files
s390-tools/netboot/mk-pxelinux-ramfs
Christian Ehrhardt 3fc01e7520 mk-pxelinux-ramfs: make infinite loop a timeout
So far it infinitely retried the bootscript, that doesn#t seem
reasonable. Per IRC discussion this is now changed to a 600 sec timeout
waiting for a config to appear to then run once on that config.

In case the pxe boot fails the it is now no more a retry loop, but
instead considered a crash as init exits.

Closes: #16

Signed-off-by: Christian Ehrhardt <christian.ehrhardt@canonical.com>
Acked-by: Viktor Mihajlovski <mihajlov@linux.vnet.ibm.com>
Signed-off-by: Michael Holzheu <holzheu@linux.vnet.ibm.com>
2017-11-14 14:46:09 +01:00

411 lines
8.8 KiB
Bash
Executable File

#!/bin/bash
#
# netboot - PXE-style boot for KVM on s390
#
# Sample script to build an initramfs image suitable for performing a
# PXELINUX-style boot for KVM guests from a DHCP/BOOTP Server.
# Has to be executed on the KVM host where it will be deployed and needs
# to be re-run after kernel updates on the host, unless the virtio
# drivers are statically built into the host kernel.
#
# The script requires a busybox install tree, e.g. resulting from a build
# from source, after make defconfig && make install
#
# To keep things simple, we don't include udev but use devtmpfs
# which means the host kernel must have been built with CONFIG_DEVTMPFS=y
#
# Sample invocation:
#
# ./mk-pxelinux-ramfs -b /downloads/busyboxdir pxelinux.initramfs
#
# Copyright IBM Corp. 2017
#
# s390-tools is free software; you can redistribute it and/or modify
# it under the terms of the MIT license. See LICENSE for details.
# Variables
cmd=$(basename $0)
busyboxdir=
builddir=
initramfs=
success=no
# Cleanup on exit
cleanup()
{
if [ -n $builddir ]
then
rm -rf $builddir
fi
}
trap cleanup EXIT
# Usage
usage()
{
cat <<-EOF
Usage: $cmd -b BUSYBOX_DIR [-k KERNEL_VERSION] INITRAMFS_FILE
Build a PXELINUX style boot initramfs INITRAMFS_FILE using a busybox installed
in BUSYBOX_DIR and kernel modules from the currently running kernel or from
the kernel version specified with the '-k KERNEL_VERSION' option.
OPTIONS
-b Search installed busybox in directory BUSYBOX_DIR
-k Use KERNEL_VERSION instead of currently running kernel
-h Print this help, then exit
EOF
}
# Get shared objects for binary
sharedobjs()
{
ldd $1 | sed -e 's?[^/]*??' -e 's/(.*)//'
}
# Check args
args=$(getopt b:k:h $*)
if [ $? = 0 ]
then
set -- $args
while [ -n $1 ]
do
case $1 in
-b) busyboxdir=$2; shift 2;;
-k) kernelversion=$2; shift 2;;
-h) usage; exit 0;;
--) shift; break;;
*) echo "$cmd: Unexpected argument $1, exiting..." >&2; exit 1;;
esac
done
fi
if [ $# != 1 -o "$busyboxdir" = "" ]
then
usage >&2
exit 1
fi
# Full output file path
initramfs=$(readlink -m $(dirname $1))/$(basename $1)
# Exit on error
set -e
# Module locations
if [ -n $kernelversion ]; then
moddir=/lib/modules/$kernelversion
else
moddir=/lib/modules/$(uname -r)
fi
netdir=$moddir/kernel/drivers/net
blkdir=$moddir/kernel/drivers/block
# Setup build directory
builddir=$(mktemp -d)
echo "$cmd: Building in $builddir"
ramfsdirs="/bin /dev /etc /lib64 /lib /mnt /proc /run /sbin /sys /tmp /usr /var"
for d in $ramfsdirs
do
mkdir -p $builddir/$d
done
# Kexec
echo "$cmd: Copying kexec"
# Install both binary and required shared libraries
OLDPATH=$PATH
PATH=$OLDPATH:/sbin:/usr/sbin
kexec_bin=$(which kexec)
kexec_sos=$(sharedobjs $kexec_bin)
PATH=$OLDPATH
cp $kexec_bin $builddir/sbin
for so in $kexec_sos
do
mkdir -p $builddir/$(dirname $so)
cp $so $builddir/$(dirname $so)
done
# virtio module(s), if present
echo "$cmd: Copying virtio modules"
mkdir -p $builddir/$netdir
mkdir -p $builddir/$blkdir
set +e
cp $netdir/virtio_net.ko $builddir/$netdir 2> /dev/null
cp $blkdir/virtio_blk.ko $builddir/$blkdir 2> /dev/null
set -e
# Busybox (+ dependencies)
echo "$cmd: Copying busybox files"
cp -a $busyboxdir/* $builddir
busybox_sos=$(sharedobjs $busyboxdir/bin/busybox)
for so in $busybox_sos
do
mkdir -p $builddir/$(dirname $so)
cp $so $builddir/$(dirname $so)
done
# ad_packet module(s), if present
echo "$cmd: Copying af_packet modules"
packetdir=$moddir/kernel/net/packet
mkdir -p $builddir/$packetdir
set +e
cp $packetdir/* $builddir/$packetdir 2> /dev/null
set -e
# Init script
echo "$cmd: Making init script"
# --- begin init script
cat <<'EOF' > $builddir/init
#!/bin/sh
/bin/mount -t devtmpfs none /dev
/bin/mount -t proc none /proc
/bin/mount -t sysfs none /sys
/bin/mount -t tmpfs none /run
/sbin/modprobe virtio_net
/sbin/udhcpc -O pxeconffile -O pxepathprefix &
/sbin/pxeboot.script
EOF
# --- end init script
chmod +x $builddir/init
# udhcpc script
echo "$cmd: Making DHCP script"
mkdir -p $builddir/usr/share/udhcpc
# -- begin dhcp script
cat <<'EOF' > $builddir/usr/share/udhcpc/default.script
#!/bin/sh
# Setup name resolution and PXE boot configuration
# called by udhcpc
RESOLVCONF="/etc/resolv.conf"
PXECONF="/etc/pxe.conf"
ccidr()
{
# clumsy netmask to cidr transformation
# with minimal sanity checking
OLDIFS=$IFS
IFS=.
c=0
n=4
for i in $1
do
n=$(/usr/bin/expr $n - 1)
case $i in
255) c=$(/usr/bin/expr $c + 8);;
254) c=$(/usr/bin/expr $c + 7); break;;
252) c=$(/usr/bin/expr $c + 6); break;;
248) c=$(/usr/bin/expr $c + 5); break;;
240) c=$(/usr/bin/expr $c + 4); break;;
224) c=$(/usr/bin/expr $c + 3); break;;
192) c=$(/usr/bin/expr $c + 2); break;;
128) c=$(/usr/bin/expr $c + 1); break;;
0) break;;
*) c=0; break;;
esac
if [ $n = 0 ]
then
break
fi
done
IFS=$OLDIFS
echo $c
}
echo "DHCP response $1: "
case "$1" in
deconfig)
echo " interface: $interface"
/sbin/ip route flush table all
/sbin/ip addr flush $interface
/sbin/ip link set $interface up
/bin/rm -f $PXECONF $RESOLVCONF
;;
renew|bound)
echo " interface: $interface $ip $subnet"
echo " router: $router"
echo " domain: $domain $dns"
echo " tftp: $siaddr"
# flush routes
/sbin/ip route flush table all
# setup if link
/sbin/ip addr flush $interface
/sbin/ip link set $interface up
# setup if addr
if [ -n "$subnet" ]
then
maskedip="$ip"/$(ccidr $subnet)
else
maskedip="ip"
fi
/sbin/ip addr add $maskedip broadcast $broadcast dev $interface
# setup default routes
if [ -n "$router" ]
then
/sbin/ip route add default via $router
fi
# setup resolv.conf
if [ -n "$domain" ]
then
echo "search $domain" > $RESOLVCONF
for i in $dns
do
echo " nameserver $i" >> $RESOLVCONF
done
fi
# pxe control
if [ -n "$siaddr" ]
then
echo "siaddr=$siaddr" > $PXECONF
echo "interface=$interface" >> $PXECONF
echo "ip=$ip" >> $PXECONF
fi
;;
nak)
;;
*)
exit 1
;;
esac
exit 0
EOF
# -- end dhcp script
chmod +x $builddir/usr/share/udhcpc/default.script
# pxeboot script
echo "$cmd: Making PXE boot script"
# -- begin pxeboot script
cat <<'EOF' > $builddir/sbin/pxeboot.script
#!/bin/sh
# Perform a PXE style boot using kexec
# Supports only super-simple config files
set -e
# Check if a valid pxe conf is available
# so far a non configurable 600 sec timeout
PXE_CONF=/etc/pxe.conf
TIMEOUT=600
WAITED=0
echo "waiting for pxe config from DHCP (max $TIMEOUT sec)"
while [ ! -f $PXE_CONF ]
do
sleep 1
WAITED=$(($WAITED + 1))
if [ $WAITED -gt $TIMEOUT ]
then
echo Error waiting for PXE configuration from DHCP
exit
fi
done
# Source the DHCP-generated TFTP info
# Currently we are just looking for siaddr
. $PXE_CONF
# Retrieve the config (default only for now)
CONFIGS=""
if [ -n "$siaddr" ];
then
# Enable UUID based config on s390
if [ $(/bin/uname -m) = "s390x" ]
then
set +e
uuid=$(/bin/grep UUID /proc/sysinfo | tail -1 | tr -d ' ' | cut -d ':' -f 2) 2> /dev/null
set -e
else
# not caring for other arches right now
uuid=""
fi
CONFIGS="$CONFIGS $uuid"
# Enable MAC based config
ifaddr=$(/bin/cat /sys/class/net/$interface/address | tr ':' '-')
CONFIGS="$CONFIGS 01-$ifaddr"
# Enable IP based config
iphex=$(printf %02X $(echo $ip | tr '.' ' '))
for i in 8 7 6 5 4 3 2 1
do
CONFIGS="$CONFIGS $(echo $iphex | cut -c 1-$i)"
done
# Finally enable default config
CONFIGS="$CONFIGS default"
set +e
for c in $CONFIGS
do
echo "fetching config pxelinux.cfg/$c from $siaddr"
if /usr/bin/tftp -g -l /tmp/config -r pxelinux.cfg/$c $siaddr
then
break
fi
done
fi
if [ ! -f /tmp/config ]
then
echo no config found
exit
fi
# Simple config file parsing, only one entry allowed
kernel=$(/bin/grep -i "^[[:space:]]*kernel" /tmp/config | sed "s/^[[:space:]]*kernel[[:space:]]*//I")
initrd=$(/bin/grep -i "^[[:space:]]*initrd" /tmp/config | sed "s/^[[:space:]]*initrd[[:space:]]*//I")
append=$(/bin/grep -i "^[[:space:]]*append" /tmp/config | sed "s/^[[:space:]]*append[[:space:]]*//I")
if [ -z "$kernel" ]
then
echo no kernel statement found in config
exit
else
echo fetch kernel $kernel from $siaddr
/usr/bin/tftp -g -l /tmp/kernel -r $kernel $siaddr
fi
if [ -n "$initrd" ]
then
echo fetch initrd $initrd from $siaddr
/usr/bin/tftp -g -l /tmp/initrd -r $initrd $siaddr
INITRD="--initrd=/tmp/initrd"
else
INITRD=""
fi
if [ -z "$append" ]; then
echo "Kexec load: kexec -l /tmp/kernel $INITRD"
kexec -l /tmp/kernel $INITRD
else
echo "Kexec load: kexec -l /tmp/kernel $INITRD --append=\"$append\""
kexec -l /tmp/kernel $INITRD --append="$append"
fi
kexec -e
EOF
# -- end pxeboot script
chmod +x $builddir/sbin/pxeboot.script
# The final initramfs
echo Building initramfs
cd $builddir
find . | cpio -o -Hnewc | gzip - > $initramfs
cd $OLDPWD