If you look back to a post I did in September you will get the basics for setting up your PXE server. You will also need to create a custom kernel, or find one that allows for PXE booting with an NFS root partition. Generally I build a kernel with all of the required drivers installed including NFS root support.
The part that is generally a big problem for me is getting a root image setup to run from. I am a fan of building my own as I have more control in the operation and customization of a system but this can lead to problems. Local binaries included with the distributions I know all require libraries to which they are linked in order to run. So, I wrote a handy script to do all this work for me, from the current running system.
The following works for a RedHat based system. Unfortunately, distributions don't keep binaries in the same place, so if you want to use this you may have to change the file locations. There are four variables to do this job, SBIN_FILES, BIN_FILES, DEV_FILES, USER_BIN_FILES. Each correlates to a directory, for example SBIN_FILES = /sbin. Just find the files you want to include and put it in the corresponding variable. The script will take care of all the dependent libraries for you.
You can also specify some command line options if you like, -delete will clear out the destination directory before it does a build, and -bootdir will change the target build location.
The script uses udev so there isn't much need for /dev entries, otherwise have at it.
#!/bin/bash
BOOT_DIR=/tftpboot/boot-image
MOUNT_POINTS="proc sys mnt/src mnt/dest tmp"
LDD=/usr/bin/ldd
PRE_DELETE=0
SBIN_FILES="init ifconfig reboot poweroff mke2fs mkfs.ext3 mkfs.ext4 mkswap fdisk udevd udevadm mkinitrd ethtool fsck.ext3 fsck.ext4 hdparm shutdown"
BIN_FILES="bash mount cp umount dd rm ls cat more vim tar gzip ps"
DEV_FILES="console null"
USER_BIN_FILES="chroot tclsh vi"
LOG_FILE=/home/mike/nfs_build_log
######
### function setup_programs
### prepends required directories to overall file list to keep things looking clean
######
function setup_binaries {
for file in $SBIN_FILES
do
FINAL_SBIN="$FINAL_SBIN /sbin/$file"
done
for file in $BIN_FILES
do
FINAL_BIN="$FINAL_BIN /bin/$file"
done
for file in $DEV_FILES
do
FINAL_DEV="$FINAL_DEV /dev/$file"
done
for file in $USER_BIN_FILES
do
FINAL_USER_BIN="$FINAL_USER_BIN /usr/bin/$file"
done
# delete everything in the boot directory if asked to
if [ $PRE_DELETE -eq 1 ]
then
rm -rf $BOOT_DIR
fi
}
function log {
echo -e "`date +%H:%M:%S` -- "$1"" >> $LOG_FILE
}
######
### function create_etc
### populates the new /etc directory with a minimal set of startup scripts
### inittab, rc.sysinit, and boot.udev
######
function create_etc {
mkdir -p $BOOT_DIR/etc
# create inittab
echo "id:3:initdefault:" > $BOOT_DIR/etc/inittab
echo "si::sysinit:/etc/rc.sysinit" >> $BOOT_DIR/etc/inittab
echo "id2:3:wait:/bin/bash" >> $BOOT_DIR/etc/inittab
# create rc.sysinit
echo "#!/bin/bash" > $BOOT_DIR/etc/rc.sysinit
echo "echo \"--Running sysinit--\"" >> $BOOT_DIR/etc/rc.sysinit
echo "echo -n \"Mounting proc filesystem: \"; mount -n -t proc /proc /proc; echo \"Done\"" >> $BOOT_DIR/etc/rc.sysinit
echo "echo -n \"Mounting sys filesystem: \"; mount -n -t sysfs /sys /sys; echo \"Done\"" >> $BOOT_DIR/etc/rc.sysinit
echo "echo -n \"Starting udev: \"; /etc/boot.udev; echo \"Done\"" >> $BOOT_DIR/etc/rc.sysinit
echo "echo -n \"Configuring loopback network adapter: \"; /sbin/ifconfig lo 127.0.0.1; echo \"Done\"" >> $BOOT_DIR/etc/rc.sysinit
echo "echo -n \"Clearing /etc/mtab\"; > /etc/mtab" >> $BOOT_DIR/etc/rc.sysinit
chmod ug+x $BOOT_DIR/etc/rc.sysinit
# create boot.udev assuming udevd and udevadm are in /sbin
if [[ "$SBIN_FILES" == *udevd* ]] && [[ "$SBIN_FILES" == *udevadm* ]]
then
echo "#!/bin/bash" > $BOOT_DIR/etc/boot.udev
echo "echo \"--Running udev--\"" >> $BOOT_DIR/etc/boot.udev
echo "echo \"\" > /sys/kernel/uevent_helper" >> $BOOT_DIR/etc/boot.udev
echo "echo -n \"Starting udevd \"" >> $BOOT_DIR/etc/boot.udev
echo "rm -rf /dev/.udev" >> $BOOT_DIR/etc/boot.udev
echo "/sbin/udevd --daemon" >> $BOOT_DIR/etc/boot.udev
echo "/sbin/udevadm trigger --type=subsystems" >> $BOOT_DIR/etc/boot.udev
echo "/sbin/udevadm trigger --type=devices" >> $BOOT_DIR/etc/boot.udev
echo "/sbin/udevadm settle --timeout=180" >> $BOOT_DIR/etc/boot.udev
chmod ug+x $BOOT_DIR/etc/boot.udev
fi
}
######
### function get_libraries
### helper function for copy_files
### executes ldd against a binary to retrieve all dependent libraries
### executes copy_files with the "library_file" flag to prevent it from running get_libraries again
######
function get_libraries {
local FILE_NAME="$1"
log "Checking library for $FILE_NAME"
LDD_OUTPUT=`ldd "${FILE_NAME}" 2> /dev/null`
# e.g. libc.so.6 => /lib64/libc.so.6 (0x00007f7a03b64000)
# look at each index in the output checking if it's a file, then pass it to copy_files e.g. /lib64/libc.so.6
for index in $LDD_OUTPUT
do
if [ -f $index ]
then
LIBRARY_FILES="$LIBRARY_FILES $index"
fi
done
}
######
### function copy_files
### copies a source file to BOOT_DIR with the proper destination directory
### e.g. /bin/ls becomes $BOOT_DIR/bin/ls
### if the file is a symbolic link, follow it through depending if it is a relative or absolute path
######
function copy_files {
local FILE_NAME="$1"
log "Copying $FILE_NAME"
# find where this file should go based BOOT_DIR + it's original path
# e.g. /tftpboot/boot-image + /sbin
DEST_DIR="$BOOT_DIR/`dirname "$FILE_NAME"`"
if [ ! -d "${DEST_DIR}" ]
then
mkdir -p "${DEST_DIR}"
fi
# the -a will preserve any symbolic links
cp -a "${FILE_NAME}" "${DEST_DIR}"
# if this is a link find what the real file is and copy that
if [ -L "$FILE_NAME" ]
then
# ideally we could use canonicalize mode (-f) as that always returns an absolute path but that won't handle links pointing to links
LINK_FILE=`readlink "$FILE_NAME"`
# check to see if this is an absolute path
# e.g. /lib64/libpthread.so.0 -> libpthread-2.10.1.so
LINK_DIR=`dirname "$LINK_FILE"` #e.g. ./
if [ ! ${LINK_DIR:0:1} == "/" ]
then
# find the path of the original file
FILE_DIR=`dirname "$FILE_NAME"` #e.g. /lib64
# change to the original path + relative path and find where we are with pwd
cd "$FILE_DIR"/"$LINK_DIR" #e.g. cd /lib64/.
LINK_PATH=`pwd` #e.g. /lib64
# recreate the file with the proper path of the link
LINK_FILE="$LINK_PATH"/`basename "$LINK_FILE"` # e.g. /lib64/libpthread-2.10.1.so
fi
copy_files "${LINK_FILE}" $2
# need to copy the file, if it returns no path it is in the same dir, otherwise it will be absolute path
fi
if [ ! "$2" == "library_file" ]
then
get_libraries "$FILE_NAME"
fi
}
######
### function create_mount_points
######
function create_mount_points {
for mount in $MOUNT_POINTS
do
mkdir -p $BOOT_DIR/$mount
done
}
######
### function usage
### outputs usage information for this script and then exits
######
function usage {
echo ""
echo -e "Usage:\n`basename $0` -delete -bootdir "
echo "-delete"
echo " remove old data before creating new content, default is to keep"
echo "-bootdir"
echo " directory to copy binaries to, default is /tftpboot/boot-image"
echo ""
exit 0
}
if [ $# -eq 0 ]
then
usage
fi
until [ -z "$1" ]
do
case "$1" in
-delete)
PRE_DELETE=1
;;
-bootdir)
shift
BOOT_DIR="$1"
;;
*)
usage
;;
esac
shift
done
setup_binaries
for file in $FINAL_SBIN $FINAL_BIN $FINAL_DEV $FINAL_USER_BIN
do
copy_files $file
done
if [ -n "$LIBRARY_FILES" ]
then
LIBRARY_FILES=`echo $LIBRARY_FILES | tr " " "\n" | sort -u`
for file in $LIBRARY_FILES
do
copy_files $file "library_file"
done
fi
create_etc