#!/bin/bash 
#
# updatediskless is a script to configure the diskless boot image.
#   It takes three parameters:
#     1. The name of the directory where the "root" directory of the diskless 
#        environment resides.
#     2. The kernel version of the kernel to be used on the diskless clients.
#     3. The destination directory for the image file and kernel.
#
#   This script can be run multiple times and should be run everytime you 
#   update the kernel on this share.
#
# Copyright (C) 2003 Daniel Walsh <dwalsh@redhat.com>
# Copyright (C) 2005 Jason Vas Dias <jvdias@redhat.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
#
TOP_DIR=`pwd`
if [ $# != 3 ]; then
	echo Usage: `basename $0` disklessDir kernelVersion destinationDir 
	exit 1 
fi
ROOT=$1
VERSION=$2
DESTDIR=$3
IMAGEFILE=$DESTDIR/initrd.img
MNTPOINT=/tmp/$$
/bin/rm -rf $MNTPOINT
/bin/mkdir -p $MNTPOINT
KERNEL=$ROOT/boot/vmlinuz-$VERSION 
MODULES=$ROOT/lib/modules/$VERSION
INSTALLDIR=/usr/share/system-config-netboot/diskless
unalias cp >/dev/null 2>&1
if [ -e "$KERNEL" ]; then
    if [ ! -e "$MODULES"  ]; then
	echo could not find modules in $MODULES
	exit -1 ;
    fi
else
    echo Could not find kernel $KERNEL
    exit -1 ;
fi
#  
#
#  Create the initrd first as a plain directory to determine size
#
/bin/mkdir -p $MNTPOINT || exit -1;
SIDEFFECTS=$MNTPOINT
CLEANUP=''
#
#
#  Exit function to clean up side effects:
die(){ r=$?; [ $# -gt 0 ] && r=$1; [ "$r" -eq 0 ] && r=1; [ -z "$r" ] && r=1; 
       cd /tmp; /bin/rm -rf $SIDEFFECTS; 
       [ -n "$CLEANUP" ] && $CLEANUP;
       exit $r; 
     }
#
#  Copy the diskless init script onto the image 
#
/bin/cp $INSTALLDIR/disklessrc $MNTPOINT/disklessrc || die;
#
#  create required directories on the image 
#
/bin/mkdir -p $MNTPOINT/proc/ || die;
/bin/mkdir -p $MNTPOINT/sbin/ || die;
/bin/mkdir -p $MNTPOINT/mnt || die;
/bin/mkdir -p $MNTPOINT/lib || die;
/bin/mkdir -p $MNTPOINT/tmp || die;
/bin/mkdir -p $MNTPOINT/dev || die;
/bin/mkdir -p $MNTPOINT/var/lib/nfs   || die;
/bin/mkdir -p $MNTPOINT/var/lib/dhcp  || die;
/bin/mkdir -p $MNTPOINT/usr/share/hwdata/  || die;
/bin/mkdir -p $MNTPOINT/etc/rc.d/init.d/   || die;
/bin/mkdir -p $MNTPOINT/etc/sysconfig/network-scripts  || die;
/bin/ln -s sbin $MNTPOINT/bin  || die;
# mknod'ing the devices instead of copying them works both with and
# without devfs...
/bin/mknod $MNTPOINT/dev/console c 5 1 || die;
/bin/mknod $MNTPOINT/dev/null c 1 3    || die;
/bin/mknod $MNTPOINT/dev/ram b 1 1     || die;
/bin/mknod $MNTPOINT/dev/systty c 4 0  || die;
for i in 1 2 3 4; do
    /bin/mknod $MNTPOINT/dev/tty$i c 4 $i || die;
done
#
#  copy required files on the image 
#
/bin/cp "$ROOT"/etc/rc.d/init.d/functions $MNTPOINT/etc/rc.d/init.d/ || die;
/bin/cp "$ROOT"/etc/sysconfig/network-scripts/network-functions $MNTPOINT/etc/sysconfig/network-scripts/ || die;
for ((i=0;i<10;i=i+1)); do
    /bin/cat << __EOF > $MNTPOINT/etc/sysconfig/network-scripts/ifcfg-eth$i
DEVICE=eth$i
BOOTPROTO=dhcp
ONBOOT=yes
TYPE=Ethernet
__EOF
    [ $? -ne 0 ] && die;
done;
/bin/cp "$ROOT"/usr/share/hwdata/pcitable $MNTPOINT/usr/share/hwdata/ || die;
BINS="/sbin/busybox.anaconda /sbin/insmod /sbin/modprobe /sbin/rmmod /sbin/dhclient /bin/bash /bin/mount /sbin/route /sbin/ip /usr/bin/expr /sbin/lspci /sbin/ifconfig /sbin/consoletype /sbin/pivot_root /bin/hostname /bin/domainname /usr/bin/host"
SCRIPTS="/sbin/dhclient-script"
for bin in $BINS $SCRIPTS; do
    if [ ! -e $ROOT/$bin ]; then 
	echo "Required binary $bin is not found in client root $ROOT."
	echo "Install required packages in the client root and retry."
	die;
    fi
    /bin/cp -fp $ROOT/$bin $MNTPOINT/sbin/;
    if [ $? -ne 0 ]; then
	echo "Copying required binary $bin to initrd failed.";
	die;
    fi;
done
/bin/mv $MNTPOINT/sbin/busybox.anaconda $MNTPOINT/sbin/busybox || die;
cd $MNTPOINT/sbin/
BBS="awk basename cat cut df grep ln ls more rm cp mv echo tr which head tail tee test touch true uniq sed sleep sort umount uname mktemp readlink"
for bb in $BBS; do
    /bin/ln -sf busybox $bb || die;
done
cd ../etc
/bin/touch fstab || die;
/bin/ln -sf /proc/mounts mtab || die;
( /usr/sbin/chroot $ROOT /usr/bin/ldd $BINS  ) | 
   /bin/grep '^[ 	]' | 
   /bin/sed 's/^[^>]*>[ ]//;s/^[ 	]*//;s/[( ].*$//;/^[ 	]*$/d' | /bin/sort | /usr/bin/uniq |
   while read f; do 
       d=${f%/*}
       if [ ! -d $MNTPOINT/$d ]; then
          /bin/mkdir -p $MNTPOINT/$d || die;
       fi
       L=`/usr/bin/readlink $ROOT/$f`; 
       if [ -n "$L" ]; then 
          /bin/cp -fp $ROOT/$d/$L $MNTPOINT/$d/$L || die;
          /bin/ln -s $L $MNTPOINT/$f || die;
       else 
          /bin/cp -fp $ROOT/$f $MNTPOINT/$f || die;
       fi; 
   done || { echo "Copying required libraries to initrd failed."; die; };
cd $TOP_DIR
# Copy required modules files to initrd:
if [ ! -e "$MODULES"/modules.dep ]; then
    echo "Required file $MODULES/modules.dep not found . Run 'depmod -a' in the client root and retry."
    die;
fi
NEWMODDIR=$MNTPOINT/lib/modules/$VERSION
/bin/mkdir -p $NEWMODDIR || die;
/bin/ls ${MODULES}/. | grep 'modules\.' | while read f; do /bin/cp -fp ${MODULES}/$f $NEWMODDIR || die; done
for d in kernel/drivers/net kernel/fs/nfs kernel/fs/lockd kernel/net/sunrpc kernel/net/ipv6 kernel/crypto;
do
    if [ ! -d "$MODULES"/$d ]; then
	echo "Required modules directory $MODULES/$d not found."
	echo "Install required modules directory in client root and retry."
	die;
    fi
    (cd $MODULES; /bin/tar -cpf - $d) | (cd $NEWMODDIR; /bin/tar -xpf -);
    r=$?
    if [ "$r" -ne 0 ]; then
	echo "Copy of required modules directory $d to initrd failed."
	die $r;
    fi;
done    
#
#   Find missing module dependencies in initrd:
#
moddeps()
{
    if [ ! -d "$1" ] || [ ! -d "$1/kernel" ] || [ ! -r "$1/modules.dep" ]; then 
	echo "moddeps: Expected name of initrd modules directory first argument - got $1"
	return 1;
    fi;
    m=$1
    r=${m%/*/*/*}    
    h=''
    t=`/bin/mktemp /tmp/XXXXXX`
    while read l; do
	if [[ "$l" = *\\ ]]; then
	    h="$h"${l/\\};
	elif [ -n "$h" ]; then
	    h="$h$l"
	    echo $h
	    h='';
	else
	    echo $l;
	fi;
    done < $m/modules.dep > $t;
    kv=${m##*/}
    kvr=(`echo $kv | /bin/sed 's/\([0-9]*\)\.\([0-9]*\)[^0-9].*$/\1 \2/'`);
    mo='.ko';
    if [ ${#kvr[@]} -eq 2 ] && [ ${kvr[0]} -le 2 ] && [ ${kvr[1]} -le 4 ]; then
	mo='.o';
    fi;
    find $m -name "*$mo" | while read o; do
	mos=(${o#${r}})
	while [ ${#mos[@]} -gt 0 ]; do
	    o=${mos[0]}
	    if [ ! -e "$r/$o" ]; then
		echo "$o";
	    fi;
	    mos[0]=''
	    mos=(${mos[@]} `/bin/egrep "^$o:" $t | /bin/sed 's/^.*://'`)
        done;
    done;
    /bin/rm -f $t;
    return 0;
}
moddeps "$NEWMODDIR" | sed 's#^/##' | 
        ( cd "$ROOT"; /bin/tar -cpf - -T -) |
	( cd "$MNTPOINT"; /bin/tar -xpf -);
if [ $? -ne 0 ]; then
    echo "Copying modules to initrd failed."
    die;
fi;
#
#  Create a new image file
#
duks=(`/usr/bin/du -ks $MNTPOINT 2>/dev/null || echo ''`)
dus=${duks[0]}
imagesize=16000;
if [ -n ${dus} ] && [ ${dus} -gt 1024 ]; then
    imagesize=`echo -e 'scale=2\nr=('$dus'*1.05)\nr*=1024\nscale=0\nif((r%1024)>0){(r/1024)+1}else{r/1024}\n' | /usr/bin/bc`
fi;
if [ -z ${imagesize} ] || ! { echo "$imagesize" | /bin/egrep -q '^[0-9]+$'; }; then
    imagesize=16000;
fi;
uncompressedimage=/var/tmp/image$$
TMPDIR=$MNTPOINT
MNTPOINT=/mnt/$$
/bin/mkdir -p $MNTPOINT || die;
/bin/dd if=/dev/zero of=$uncompressedimage bs=1k count=$imagesize 2> /dev/null || die;
img=$uncompressedimage
SIDEFFECTS="$MNTPOINT $uncompressedimage";
# We have to "echo y |" so that it doesn't complain about $IMAGE not
# being a block device
if ! echo y | /sbin/mke2fs $img >/dev/null 2>/dev/null ; then
   echo 'mke2fs failed.'
   die;
fi;
/sbin/tune2fs -i0 $img >/dev/null 2>&1 || die;
SELINUX=
sedir=`while read d dir type r; do if [ "$type" = "selinuxfs" ]; then echo $dir; break; fi; done < /proc/mounts`
if [ -e $sedir/enforce ]; then
   SELINUX=1
#  SELinux is enabled; could be enforcing / permissive, doesn't matter
fi
if [ -n "$SELINUX" ]; then
#  do NOT enable creation of SELinux xattr labels in the initrd 
    /bin/mount -t ext2 -o loop,context=system_u:object_r:removable_t $img $MNTPOINT || die;
else
#  SELinux is disabled / not present; no labels will be created
    /bin/mount -t ext2 -o loop $img $MNTPOINT || die;
fi
CLEANUP="/bin/umount -f $MNTPOINT"
#
# We don't need this directory, so let's save space
/bin/rm -rf $MNTPOINT/'lost+found'
#
#  Copy the temp directory to the initrd:
#
if ! (cd $TMPDIR; /bin/tar -cpf - . || die ) | (cd $MNTPOINT; /bin/tar -xpf -) ; then
   echo "Creation of initrd failed."
   die
fi;
/bin/rm -rf $TMPDIR;
SIDEFFECTS=$uncompressedimage
# 
#  Unmount and compress image file to be ready to boot
#
/bin/sync
/bin/umount $MNTPOINT
CLEANUP=''
/bin/rmdir  $MNTPOINT
/bin/mkdir -p "$DESTDIR" ;
if [ ! -d "$DESTDIR" ]; then
   echo "Cannot create $DESTDIR directory.";
   die;
fi
if [ -e "$IMAGEFILE" ]; then
    /bin/mv -f "$IMAGEFILE" "$IMAGEFILE".backup;
fi
if ! /usr/bin/gzip -c -9 $uncompressedimage > "$IMAGEFILE"; then
   echo " gzip -c -9 $uncompressedimage > $IMAGEFILE failed.";
   die;
fi
/bin/rm -f $uncompressedimage
SIDEFFECTS=$IMAGEFILE
if ! /bin/cp -a "$KERNEL" "$DESTDIR"/vmlinuz; then
   echo "/bin/cp -a $KERNEL $DESTDIR/vmlinuz failed.";
   die;
fi
exit 0;
