#!/bin/bash

# This basic script build an initial ramdisk with the
# following tools :
# + busybox (simple version of common unix tools)
# + evms1 tools
# (+ devfsd)

# OK, this script provide me with a kernel and a ramdisk,
# but what next ?
# I added this line to my grub.conf :
# title=Ramdisk EVMS1
# kernel (hd0,0)/boot/linux-evms1 vga=0x31a root=/dev/ram0 rw ramdisk_size=15728640
# initrd (hd0,0)/boot/initrd.gz
# 
# WARNING : don't forget to add ramdisk and initial ramdisk to the kernel
# config if you want your kernel beeing able to use it as rootfs
#
# I wrote this script mainly to be able to resize all my evms partitions.
# For me, it's much more easy than unmounting them and resizing them without
# rebooting.
# Feel free to expand this script to add other tools to the ramdisk to suit your need.
# 
# Comments, flame, help : asm8@wanadoo.fr


###########################
# Configurables variables #
###########################

# Path of the temporary working dir
WORKDIR=/tmp/cl
# Path of kernel sources
KSOURCES=/usr/src/linux
# Kernel config file
#KCONFIG=`pwd`/kernel-cl.conf
KCONFIG=/root/kernel.conf

#########################################################
# Below this line, you don't need to change anything    #
# Just look at the variables below to know where you'll #
# find your kernel and your ramdisk                     #
#########################################################

# Directory where to generate ramdisk conntent
RD_SOURCE=${WORKDIR}/initrd_fs
# Path of the kernel to be generated
KERNEL=${WORKDIR}/linux
# Ramdisk file to be created
INITRD=${WORKDIR}/initrd
# Compressed ramdisk file
INITRDGZ=${WORKDIR}/initrd.gz
# tmp mount point when populating the initrd file
TMP_MP=${WORKDIR}/mp_initrd

# Binaries apps needed on the ramdisk
BINARIES="busybox devfsd strace"
EVMS_APPS="mkreiserfs mkswap mkfs.jfs mkfs.xfs e2fsck resize2fs mke2fs"
# Full path of this binaries (leave it blank, it's automatically
#  generated by check_merged_packages() function )
FP_BINARIES=""

# Definition of used color
RED="\033[0;31m"
GREEN="\033[0;32m"
NORMAL="\033[0m"

RED_STAR="${RED}*${NORMAL}"
GREEN_STAR="${GREEN}*${NORMAL}"

# Display messages functions
function error_msg {
  echo -e "${RED_STAR} $1"
}

function info_msg {
  echo -e "${GREEN_STAR} $1"
}


# Check that needed packages are merged on the current system
function check_merged_packages {
  for app in ${BINARIES}
    do
    fp_app=`which ${app}`
    if [ -z ${fp_app} ]
      then
      error_msg "Can't find ${app} binary."
      error_msg "Make sure ${app} is in your \$PATH."
      exit 1
    fi
    info_msg "Required binary '${app}' found in ${fp_app} :-)"
    FP_BINARIES="${FP_BINARIES} ${fp_app}"
    done
}

# Find usefull apps required by evmsn
function check_evms_apps {
  for app in ${EVMS_APPS}
    do
    fp_app=`which ${app} 2>/dev/null`
    if [ -z ${fp_app} ]
      then
      error_msg "Can't find ${app} binary."
      error_msg "Make sure ${app} is in your \$PATH. Maybe you don't need this tool."
    fi
    info_msg "Binary '${app}' found in ${fp_app} :-)"
    FP_BINARIES="${FP_BINARIES} ${fp_app}"
    done
}

# Generate the ramdisk tree (only dirs)
function create_rd_fs_dir {
  mkdir -p ${RD_SOURCE}
  for dir in etc/init.d etc/devfs.d bin lib/dev-state proc dev usr tmp mnt var/log
    do
    mkdir -p ${RD_SOURCE}/${dir}
    done
  pushd ${RD_SOURCE} 2>&1 1>/dev/null
  ln -sf bin sbin
  popd 2>&1 1>/dev/null
  info_msg "Ramdisk directories generated."
}

# Copy the needed binaries in the ramdisk (only executales bin)
function create_rd_fs_bin {
  # Copy needed binaries
  for bin in ${FP_BINARIES}
    do
    cp ${bin} ${RD_SOURCE}/bin
    done
  info_msg "Ramdisk binaries installed"
  # Create symlinks for commands provided by busybox
  BB_CMDS=`busybox 2>&1 | grep ash -A 10 | sed -e s/,//g`
  pushd ${RD_SOURCE}/bin 2>&1 1>/dev/null
  for bin in ${BB_CMDS}
    do
    [ $bin != busybox ] && ln -s busybox $bin
    done
  popd 2>&1 1>/dev/null
  info_msg "Busybox symlinks created"
}

# Copy the needed binaries'deps in the ramdisk (libraries and symlinks)
function create_rd_fs_lib {
  bin=
  DEPS=`find ${RD_SOURCE}/{bin,lib} -type f -exec ldd {} \; | awk ' /=>/ { print $3}' | sort -u`
  for l in ${DEPS} #/lib/libm.so.6 /lib/libnsl.so.1 /lib/libnss_compat.so.2
    do
    # Copy the lib ...
    cp -dp --parents ${l} ${RD_SOURCE}
    # ... and the realfile if it was a symlink
    [ -h ${l} ] && cp -dp --parents `readlink -f ${l}` ${RD_SOURCE}
    done
  info_msg "Libraries copied"
}

# Create minimum required devices in the /dev dir of the ramdisk
function create_rd_fs_dev {
  mknod ${RD_SOURCE}/dev/ram0 b 1 0
  mknod ${RD_SOURCE}/dev/console c 5 1
  mknod ${RD_SOURCE}/dev/urandom c 1 9
  mknod ${RD_SOURCE}/dev/null c 1 3
  for i in 0 1 2 3 4 5 6
    do
    mknod ${RD_SOURCE}/dev/tty${i} c 4 ${i}
    done
}

# Create scipts and config files in /etc
function create_rd_fs_scripts {

  cat >${RD_SOURCE}/etc/inittab <<EOF
#<tty>:<ignored>:<action>:<process>
console:rl1:sysinit:/etc/init.d/rcS
tty2:rl1:askfirst:/bin/ash
tty3:rl1:askfirst:/bin/ash
tty4:rl1:askfirst:/bin/ash
tty5:rl1:askfirst:/bin/ash
tty6:rl1:askfirst:/bin/ash
EOF

ln -s /proc/mounts ${RD_SOURCE}/etc/mtab

  cat >${RD_SOURCE}/etc/init.d/rcS <<EOF
#!/bin/ash
mount -n -t proc proc /proc
[ -e /dev/.devfsd] || mount -n -t devfs none /dev
devfsd /dev
EOF
chmod +x ${RD_SOURCE}/etc/init.d/rcS

cp -dp --parents /etc/devfsd.conf ${RD_SOURCE}

  cat >${RD_SOURCE}/etc/group <<EOF
root::0:
tty::1:
audio::2:
video::3:
EOF

  cat >${RD_SOURCE}/etc/passwd <<EOF
root:*:0:0:root:/root:/bin/ash
EOF
}

# Add evms1 tools to the ramdisk
function create_rd_fs_evms {
  info_msg "Copying evms tools"
  cp -a /sbin/evms* ${RD_SOURCE}/bin
  info_msg "Copying evms plugins"
  cp -a /lib/evms ${RD_SOURCE}/lib
  # the followinf plugin is not used. We use libext2fsim-1.1.2.so instead.
  rm -f ${RD_SOURCE}/lib/evms/libe2fsim.1.2.1.so
  # required for ncurses apps like evmsn
  cp -dp --parents /usr/share/terminfo/l/linux ${RD_SOURCE}
}

# Prepare a kernel and install modules in the ramdisk
function cook_kernel {
  ( cd ${KSOURCES} ; make mrproper )
  cp ${KCONFIG} ${KSOURCES}/.config
  ( cd ${KSOURCES} ; make oldconfig dep bzImage modules )
  info_msg "Kernel and modules compiled"
  ( cd ${KSOURCES} ; make INSTALL_MOD_PATH=${RD_SOURCE} modules_install )
  info_msg "Modules installed in the ramdisk"
  cp ${KSOURCES}/arch/i386/boot/bzImage ${KERNEL}
}

# Generate the ramdisk filesystem in the $RD_SOURCE diretory
function create_rd_fs {
  check_merged_packages
  check_evms_apps
  create_rd_fs_dir
  create_rd_fs_bin
  create_rd_fs_evms
  create_rd_fs_lib
  create_rd_fs_dev
  create_rd_fs_scripts
  cook_kernel 
}

# Create the initrd file :-)
function create_initrd {
  create_rd_fs
  # Calculate a size for the ramdisk : pad to 1 Mo
  rd_size=`du -s ${RD_SOURCE} | awk '{print $1}'`
  let rd_size=rd_size/1024+1
  
  # Number of inodes 1.5 * number of files for now
  nb_inodes=`find ${RD_SOURCE} | wc -l`
  let nb_inodes=nb_inodes+nb_inodes/2

  info_msg "Generating ramdisk file"
  dd if=/dev/zero of=${INITRD} bs=1k count=${rd_size}k && \
  info_msg "Creating a filesystem on the ramdisk" && \
  mke2fs -b 1024 -F -m 0 -N nb_inodes ${INITRD} && \
  mkdir -p ${TMP_MP} && \
  info_msg "Mounting Ramdisk" && \
  mount -o loop ${INITRD} ${TMP_MP} &&\
  info_msg "Copying files on the ramdisk" && \
  ( cd ${RD_SOURCE} ; cp -a * ${TMP_MP} ) && \
  umount ${TMP_MP}
  info_msg "Ramdisk created and populated !"
}


rm -rf ${WORKDIR}
mkdir -p ${WORKDIR}
create_initrd
gzip -c ${INITRD} > ${INITRDGZ}
