#!/bin/bash
#
# --------------------------------------------------------------------------
# Copyright notice
# --------------------------------------------------------------------------
# Copyright: Rene Mayrhofer, Mar. 2000
#
# 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, 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; see the file COPYING.  If not, write to
# the Free Software Foundation, 59 Temple Place - Suite 330,
# Boston, MA 02111-1307, USA.
# On Debian GNU/Linux systems, the complete text of the GNU General
# Public License can be found in `/usr/share/common-licenses/GPL'.
# --------------------------------------------------------------------------
#

# customize here
VERSION_TEMPLATE="_VERSION_"

bytes_per_inode=8096
copy_files_from=/usr/share/mkinitrd-cd
reserve_kbytes=100
cdboot_required_modules="cdrom scsi_mod sr_mod ide-mod ide-probe ide-probe-mod ide-core ide-detect ide-cd"
#hdboot_additional_modules="sd_mod ide-disk jbd ext3 reiserfs xfs xfs_support pagebuf"
hdboot_additional_modules="sd_mod ide-disk jbd ext3 reiserfs ext2 xfs"
usbboot_additional_modules="usbcore usb-uhci usb-ohci ehci-hcd usb-storage sd_mod fat vfat loop"

# do not edit below this line
# =================================================

if [ $# -lt 3 ] || ( [ "$3" != "min" ] && [ "$3" != "full" ] ) ; then
  echo "Usage: $0 <path to modules> <created initrd image> <size> [<version>]"
  echo
  echo "Where <size> can be either 'min' or 'full'. 'min' copies only the "
  echo "modules needed for booting from CD-ROM drives, 'full' additionally "
  echo "copies all modules needed to boot from harddisk partitions."
  echo
  echo "If <version> is specified, it will replace any occurance of the string"
  echo "'$VERSION_TEMPLATE' in the used id.txt file for checking if the correct"
  echo "CD-ROM is inserted (template taken from"
  echo "/etc/mkinitrd-cd/id.txt)."
  exit 1
fi

if [ `id -u` -ne 0 ]; then
  echo "This script must be run as root."
  exit 2
fi

if [ ! -d $1 ]; then
  echo "$1 is not a directory."
  exit 3
fi

umask 077
set -e

olddir=`pwd`

image=$2
# create temporary name for the image
tmpimage=`mktemp`
modulepath=$1
imagesize=$3
# temporary image names can not created with tempfile
mntpoint=`mktemp -d`
tmpstructure=`mktemp -d`
# this is a temporary build directory for building busybox binaries
tmpbuilddir=`mktemp -d`

# detect which version of the directory layout we have (2.2.x or 2.4.x)
if [ -d $modulepath/kernel/drivers ]; then
  # 2.4.x layout
  moduledirs="kernel/drivers/scsi kernel/drivers/ide kernel/drivers/cdrom kernel/drivers/block kernel/fs kernel/drivers/usb"
else
  # 2.2.x layout
  moduledirs="scsi cdrom block fs"
fi

# first create the structure that the initrd image should contain
echo -n "Creating file structure ... "
mkdir $tmpstructure/bin
mkdir $tmpstructure/dev
mkdir $tmpstructure/etc
mkdir $tmpstructure/lib
mkdir $tmpstructure/lib/discover
mkdir $tmpstructure/mnt
mkdir $tmpstructure/mnt/media
mkdir $tmpstructure/mnt/root
mkdir $tmpstructure/modules
mkdir $tmpstructure/sbin
mkdir $tmpstructure/tmp
mkdir $tmpstructure/proc
mkdir $tmpstructure/usr
mkdir $tmpstructure/usr/i386-uclibc-linux
mkdir $tmpstructure/usr/i386-uclibc-linux/lib
echo "done"

# devices
echo -n "Creating device files ... "
cd $tmpstructure/dev
#for n in console null ram ram[01] tty[012345] ttyS[0123] fd stdout stderr \
#  hd[abcd] scd[0123456789] log; do
##  cp -a /dev/$n $tmpstructure/dev
#  echo $n
#  /sbin/MAKEDEV $n
#done
tar -C $tmpstructure/dev --preserve --same-owner --atime-preserve \
    -xzf $copy_files_from/initrd-dev.tar.gz
echo "done"

cd $olddir
echo "Copying modules ... "
# copy the modules listed in scsiprobe.dat as well as common modules
# (in $additional_modules)
for n in $moduledirs; do
  if [ -d $modulepath/$n ] ; then
    for f in `find $modulepath/$n/ -name '*.o'`; do
      # copy only those that are also listed in scsiprobe.dat
      mod=`expr $(basename $f) : '\(.*\).o'`
      echo -ne "    $mod\t"
      # cosmetics (another tab for short file names
      if [ `expr "$mod" : '.*'` -lt 4 ]; then
        echo -ne "\t"
      fi
      echo -n "... "
      # I don't know why this doesn't work - it should....
      #if ( ( echo "$cdboot_required_modules" | grep $mod >/dev/null ) ||
      #     ( [ "$imagesize" = "min" ] &&
      #       ( cat $copy_files_from/scsiprobe-min.dat | grep $mod >/dev/null ) ) ||
      #     ( [ "$imagesize" = "full" ] &&
      #       ( echo "$hdboot_additional_modules" | grep $mod >/dev/null ) ||
      #       ( cat $copy_files_from/scsiprobe.dat | grep $mod >/dev/null ) ) )
      #then
      # so try this instead - it is more complicated, slower and just plain 
      # ugly but it works
      copyit=0
      if echo "$cdboot_required_modules" | grep $mod >/dev/null; then
        copyit=1
      elif [ "$imagesize" = "min" ]; then
        if cat $copy_files_from/scsiprobe-min.dat | grep $mod >/dev/null; then
          copyit=1
        fi
      else
        # $imagesize = "full" here
        if echo "$hdboot_additional_modules" | grep $mod >/dev/null ||
           echo "$usbboot_additional_modules" | grep $mod >/dev/null ||
           cat $copy_files_from/scsiprobe.dat | grep $mod >/dev/null; then
          copyit=1
        fi
      fi
      if [ "$copyit" -eq 1 ]; then
        cp -a $f $tmpstructure/modules/
        echo "copied"
      else
        echo "skipped"
      fi
    done
  fi
done
# determine the biggest of the modules. this will have to fit on the initrd
# disk for loading - therefore this much free space has to be left on the image
maxsize=0
for f in $tmpstructure/modules/*; do
  modsize=`ls -l $f | awk '{print $5}'`
  if [ $modsize -gt $maxsize ]; then
    maxsize=$modsize
  fi
done
gzip -9 $tmpstructure/modules/*
echo "done"
echo "The largest module will need $maxsize bytes to be unzipped."
reserve_kbytes=`expr $maxsize / 1024 + $reserve_kbytes`

echo "Creating / copying binaries and libraries (this may take some time) ... "
echo -n "    Extracting busybox source ... "
tar -C $tmpbuilddir -xjf $copy_files_from/busybox-*.tar.bz2
busybox_builddir="$tmpbuilddir/`basename $tmpbuilddir/busybox-*`"
echo "done"
# this is the list of needed binaries from busybox
echo -n "    Adapting busybox binary to the initrd system needs ... "
busybox_bin="ash cat chmod chgrp cut dirname dmesg echo expr find grep gunzip kill ln ls md5sum mknod mktemp mount mv ps pwd rm rmdir sleep test tr umount uname uniq"
busybox_sbin="chroot syslogd klogd pivot_root insmod"
edit_file="$busybox_builddir/.config"
# build the list of defines for the functions to use
for f in $busybox_bin $busybox_sbin; do
  echo "CONFIG_`echo $f | tr \[a-z\] \[A-Z\]`=y" >> $edit_file
done
# additional features for busybox
#echo "#define BB_CHMOD_CHOWN_CHGRP" >> $edit_file
echo "HAVE_DOT_CONFIG=y" >> $edit_file
echo "CONFIG_FEATURE_BUFFERS_GO_ON_STACK=y" >> $edit_file
echo "CONFIG_FEATURE_DEVFS=y" >> $edit_file
echo "CONFIG_FEATURE_DEVPTS=y" >> $edit_file
echo "EXTRA_CFLAGS_OPTIONS=\"\"" >> $edit_file
echo "CONFIG_FEATURE_FANCY_ECHO=y" >> $edit_file
# this is needed for the shell
echo "CONFIG_FEATURE_SH_IS_ASH=y" >> $edit_file
echo "CONFIG_FEATURE_SH_IS_HUSH=n" >> $edit_file
echo "CONFIG_FEATURE_SH_IS_LASH=n" >> $edit_file
echo "CONFIG_FEATURE_SH_IS_MSH=n" >> $edit_file
echo "CONFIG_FEATURE_SH_IS_NONE=n" >> $edit_file
echo "CONFIG_ASH_OPTIMIZE_FOR_SIZE=y" >> $edit_file
# these are good for debugging the initrd (giving the shell a bit more
# usefulness for interactive commands)
echo "CONFIG_FEATURE_COMMAND_EDITING=y" >> $edit_file
echo "CONFIG_FEATURE_COMMAND_SAVEHISTORY=y" >> $edit_file
echo "CONFIG_FEATURE_COMMAND_TAB_COMPLETION=y" >> $edit_file
echo "CONFIG_FEATURE_COMMAND_HISTORY=15" >> $edit_file

echo "CONFIG_FEATURE_NEW_MODULE_INTERFACE=y" >> $edit_file
echo "CONFIG_FEATURE_INSMOD_VERSION_CHECKING=y" >> $edit_file
echo "CONFIG_FEATURE_CHECK_TAINTED_MODULE=y" >> $edit_file
echo "CONFIG_FEATURE_LS_FOLLOWLINKS=y" >> $edit_file
echo "CONFIG_FEATURE_AUTOWIDTH=y" >> $edit_file
echo "CONFIG_FEATURE_HUMAN_READABLE=y" >> $edit_file

echo "CONFIG_FEATURE_IPC_SYSLOG=y" >> $edit_file
echo "CONFIG_LOGREAD=y" >> $edit_file

echo "CONFIG_FEATURE_MOUNT_LOOP=y" >> $edit_file

echo "CONFIG_FEATURE_BUFFERS_USE_MALLOC=n" >> $edit_file
echo "CONFIG_FEATURE_BUFFERS_GO_ON_STACK=y" >> $edit_file
echo "CONFIG_FEATURE_BUFFERS_GO_IN_BSS=n" >> $edit_file

echo "CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE=16" >> $edit_file

echo "done"
echo -n "    Building busybox binary ... "
cd $busybox_builddir
if ! /usr/bin/yes n | make oldconfig >/dev/null 2>/dev/null || \
   ! make CC=i386-uclibc-linux-gcc >/dev/null 2>/dev/null ; then
  echo "Error: unable to build busybox !"
  exit 1
fi
echo "done"
echo -n "    Installing binaries in temporary file structure ... "
install $busybox_builddir/busybox $tmpstructure/bin
cd $tmpstructure/bin
ln -s ash sh
ln -s test \[
install $copy_files_from/chown-wrapper $tmpstructure/bin/chown
# now make the links for busybox
cd $tmpstructure/bin
for f in $busybox_bin; do
  ln -s busybox $f
done

# now /sbin
install /sbin/MAKEDEV $tmpstructure/sbin
cd $tmpstructure/sbin
for f in $busybox_sbin; do
  ln -s ../bin/busybox $f
done
install $copy_files_from/discover $tmpstructure/sbin
install $copy_files_from/paste $tmpstructure/sbin
#########install $copy_files_from/insmod $tmpstructure/sbin
echo "done"

echo -n "    Creating config files in temporary file structure ... "
# now /etc
touch $tmpstructure/etc/fstab
echo -e "root:x:0:0:root:/:/bin/sh" > $tmpstructure/etc/passwd
echo -e "root:x:0:\ndisk:x:6:" > $tmpstructure/etc/group
# the identifier for checking if the CD is correct
if [ -z "$4" ]; then
  install /etc/mkinitrd-cd/id.txt $tmpstructure/etc
else
  sed "s/${VERSION_TEMPLATE}/$4/" < /etc/mkinitrd-cd/id.txt > $tmpstructure/etc/id.txt
fi
# the modules autoprobing database and utilities
cp $copy_files_from/scsiprobe.dat $tmpstructure/etc
cp $copy_files_from/discover.conf $tmpstructure/etc
# pci device names and modules
#cp /lib/discover/list.xml $tmpstructure/lib/discover
#cp /lib/discover/pci*.xml $tmpstructure/lib/discover
cp /lib/discover/* $tmpstructure/lib/discover
echo "done"

# now /lib - with uClibc it is now wonderfully simple because we don't need to
# strip it in any way. Just copy it as it is, it's small enough (half the size
# of a stripped, minimized glibc with only the needed functions).
echo -n "    Copying uClibc ... "
cp -a /usr/i386-uclibc-linux/lib/ld-uClibc*.so* \
	/usr/i386-uclibc-linux/lib/libc.so* \
	/usr/i386-uclibc-linux/lib/libdl.so* \
	/usr/i386-uclibc-linux/lib/libdl-*.so \
        /usr/i386-uclibc-linux/lib/libuClibc*.so \
        $tmpstructure/usr/i386-uclibc-linux/lib
ln -s /usr/i386-uclibc-linux/lib/ld-uClibc*.so* \
	/usr/i386-uclibc-linux/lib/libc.so* \
	/usr/i386-uclibc-linux/lib/libdl.so* \
	/usr/i386-uclibc-linux/lib/libdl-*.so \
        /usr/i386-uclibc-linux/lib/libuClibc*.so \
	$tmpstructure/lib
echo "done"

echo -n "Copying bootup files ... "
# finally the linuxrc file
install $copy_files_from/linuxrc $tmpstructure/
cp $copy_files_from/initrd-common-definitions.sh $tmpstructure/
cp $copy_files_from/probe-devs.sh $tmpstructure/
echo "done"

cd $olddir
# determine the size of the filesystem
size=`du -s $tmpstructure | awk '{print $1}'`
size=`expr $size + $reserve_kbytes`
echo "The image will take ${size} kb (including ${reserve_kbytes} kb of free"\
	"space for temp files)."

# now create the image
echo -n "Creating image file ... "
/bin/dd if=/dev/zero of=$tmpimage bs=1024 count=$size 2> /dev/null || {
  echo "failed during creation with dd !"
  exit 2
}
echo y | /sbin/mke2fs -i $bytes_per_inode -m 0 -q $tmpimage \
	> /dev/null 2> /dev/null || {
  echo "failed during creation with mke2fs !"
  exit 2
}
echo "done"
echo -n "Copying structure to image file ... "
mount -o loop -t ext2 $tmpimage $mntpoint

# copy the structure onto it
cp -a $tmpstructure/* $mntpoint
rm -rf $mntpoint/lost+found

# save and compress the image
umount $mntpoint
cd $olddir
echo "done"
echo -n "Compressing image file ... "
gzip -9 < $tmpimage > $image
echo "done"

# clean up
echo -n "Cleaning up ... "
rm -rf $tmpimage $mntpoint $tmpstructure $tmpbuilddir
echo "done"

exit 0
