#!/bin/bash # Shell script to prepare a root FS image based on the Novell/SUSE rescue # image source. You must have a xen kernel installed for this to work. # (c) Kurt Garloff , 2005-01-24, GNU GPL # Maintained by Charles Coffing # # TODO: # - trap SIGINT and clean up # - can't know that /sbin/depmod will work with this rescue image # - support domUloader # - check for disk full in exchange_kernel_modules MB_DEFAULT=88 usage() { echo "Usage:" echo " mk-xen-rescue-img.sh source destination [MB [kernelver]]" echo "Purpose:" echo " Creates a root filesystem and an associated config file for a Xen-compatible" echo " Linux virtual machine." echo "Arguments:" echo " * 'source' is the source from which to copy files. Normally, this is" echo " the SUSE rescue image, such as /media/cdrom/boot/x86_64/rescue," echo " but it could also be an already-mounted root filesystem." echo " * 'destination' is the pathname of a disk image to be created, based on" echo " 'source'." echo " * 'MB' is the size of disk image (default $MB_DEFAULT)." echo " * 'kernelver' is the version of an installed Xen-compatible Linux kernel" echo " (from the kernel-xen package) to install in the disk image. If not" echo " specified, the newest of those currently installed will be selected." exit 2 } usage_error() { echo "Error: $@" echo "" usage } error() { echo "Error: $@" if [ ! -z "$DST_MNT" ]; then umount "$DST_MNT" rmdir "$DST_MNT" fi exit 1 } cp_error() { error "Failed to copy files to '$DST'." } # Determine Xen kernel version get_xen_kernel() { if [ -z "$KVER" ]; then KVER="`cd /lib/modules && ls -td *-xen/kernel | head -n1 | sed 's@/kernel@@'`" else if [ ! -d /lib/modules/$KVER/kernel ]; then error "/lib/modules/$KVER/kernel does not exist." fi fi if [ -z "$KVER" ]; then error "You need to have a 'kernel-xen' package installed." fi } cp_rescue() { local LSRC=$1 local LDST=$2 # The rescue image has empty directories all hard-linked to the same # thing, probably to conserve inodes. This gives 'cp' heartburn. # I want hard linked directories to be created separately (i.e., # dereferenced), but I don't want to dereference soft links. # 'tar' handles this nicely. (cd "$LSRC" && tar --atime-preserve --one-file-system -cf - *) | \ (cd "$LDST" && tar -xmf -) } # Create FS and copy rescue FS create_basis_and_mount() { echo "Creating disk image within '$DST'..." dd if=/dev/zero of="$DST" bs=4096 count=1 2>/dev/null >/dev/null 2>&1 dd if=/dev/zero of="$DST" bs=1048576 count=0 seek=$MB >/dev/null 2>&1 /sbin/mke2fs -b 1024 -i 2048 -L xen_rescue -F "$DST" >/dev/null 2>&1 DST_MNT="$DST".mnt mkdir -p "$DST_MNT" mount -o loop "$DST" "$DST_MNT" if [ $? -ne 0 ]; then error "Failed to mount destination disk image via loopback." fi echo "Copying files from '$SRC'..." if test -d "$SRC"; then if [ ! -d "$SRC"/dev ] || [ ! -d "$SRC"/sbin ]; then error "'$SRC' does not look like a valid root filesystem." fi cp_rescue "$SRC" "$DST_MNT" if [ $? -ne 0 ]; then cp_error fi else mkdir -p "$DST".src if test ! -z "`file $SRC | grep gzip`"; then cp -p "$SRC" "$DST".stage.gz gunzip -f "$DST".stage.gz || rm -f "$DST".stage.gz mount -o loop "$DST".stage "$DST".src if [ $? -ne 0 ]; then rm "$DST".stage rmdir "$DST".src error "Failed to mount the source via loopback." fi cp_rescue "$DST".src "$DST_MNT" if [ $? -ne 0 ]; then umount "$DST".src rm "$DST".stage rmdir "$DST".src cp_error fi umount "$DST".src rm "$DST".stage else mount -o loop "$SRC" "$DST".src if [ $? -ne 0 ]; then rmdir "$DST".src error "Failed to mount the source via loopback." fi cp_rescue "$DST".src "$DST_MNT" if [ $? -ne 0 ]; then umount "$DST".src rmdir "$DST".src cp_error fi umount "$DST".src fi rmdir "$DST".src fi } # Save some inodes remove_some_files() { # these consume an excessive amount of inodes rm -f "$DST_MNT"/dev/sd[c-z][a-z][0-9]* # hwclock does not work rm -f "$DST_MNT"/etc/init.d/boot.d/*boot.clock } xenify_image() { # Xen doesn't yet support virtualizing USB sed -i "s/^usbfs/#usbfs/" "$DST_MNT"/etc/fstab } # Copy extra files to rescue image copy_extra_files() { test ! -e "$DST_MNT"/sbin/depmod && cp -p /sbin/depmod "$DST_MNT"/sbin/ cp -p /usr/share/doc/packages/xen/boot.local.xenU "$DST_MNT"/etc/init.d/boot.local } # Remove kernel modules and replace by ours exchange_kernel_modules() { echo "Updating kernel within '$DST'..." KMODDIR=/lib/modules/$KVER/kernel MNTKVER="`cd "$DST_MNT"/lib/modules && ls -td */kernel 2>/dev/null | sed 's@/kernel@@' 2>/dev/null`" for dir in $MNTKVER; do if test "$MNTKVER" != "$KVER"; then rm -rf "$DST_MNT"/lib/modules/$MNTKVER fi done mkdir -p "$DST_MNT"$KMODDIR # complete trees for dir in arch crypto lib net security; do cp -ax $KMODDIR/$dir "$DST_MNT"$KMODDIR/ done mkdir -p "$DST_MNT"$KMODDIR/fs cp -p $KMODDIR/fs/*.ko "$DST_MNT"$KMODDIR/fs/ # a selection of FS for fs in afs autofs autofs4 cifs exportfs ext3 fat jbd jfs msdos ncpfs nfsd nls ntfs reiser4 reiserfs smbfs subfs sysv udf ufs vfat xfs; do if test -e $KMODDIR/fs/$fs; then cp -ax $KMODDIR/fs/$fs "$DST_MNT"$KMODDIR/fs/ fi done # some drivers mkdir -p "$DST_MNT"$KMODDIR/drivers/char for name in lp.ko n_hdlc.ko raw.ko; do cp -p $KMODDIR/drivers/char/$name "$DST_MNT"$KMODDIR/drivers/char/ done mkdir -p "$DST_MNT"$KMODDIR/drivers/block for name in umem.ko nbd.ko loop.ko loop_fish2.ko cryptoloop.ko; do cp -p $KMODDIR/drivers/block/$name "$DST_MNT"$KMODDIR/drivers/block/ done mkdir -p "$DST_MNT"$KMODDIR/drivers/base cp -p $KMODDIR/drivers/base/firmware_class.ko "$DST_MNT"$KMODDIR/drivers/base/ mkdir -p "$DST_MNT"$KMODDIR/drivers/md cp -p $KMODDIR/drivers/md/*.ko "$DST_MNT"$KMODDIR/drivers/md/ mkdir -p "$DST_MNT"$KMODDIR/drivers/xen for dir in $KMODDIR/drivers/xen/*front; do cp -ax $dir "$DST_MNT"$KMODDIR/drivers/xen/ done if test -e $KMODDIR/kernel; then mkdir -p "$DST_MNT"$KMODDIR/kernel cp -ax $KMODDIR/kernel/* "$DST_MNT"$KMODDIR/kernel/ fi # Kernel image + System.map mkdir -p "$DST_MNT"/boot rm -f "$DST_MNT"/boot/vmlinuz-* "$DST_MNT"/boot/System.map-* "$DST_MNT"/boot/initrd-* cp -p /boot/vmlinuz-$KVER "$DST_MNT"/boot/ if test -e /boot/initrd-$KVER-domU; then cp -p /boot/initrd-$KVER-domU "$DST_MNT"/boot/initrd-$KVER else cp -p /boot/initrd-$KVER "$DST_MNT"/boot/ fi cp -p /boot/System.map-$KVER "$DST_MNT"/boot/ depmod -a $KVER -b "$DST_MNT" -F "$DST_MNT"/boot/System.map-$KVER } # umount and display message umount_and_msg() { umount "$DST_MNT" rmdir "$DST_MNT" DST_MNT= echo "'$DST' has been prepared successfully." CFGFILE=/etc/xen/vm/${DST##*/} if test -e $CFGFILE; then mv -b $CFGFILE $CFGFILE.save fi if test "${DST:0:1}" != "/"; then DST="`pwd`/$DST" fi cp -p /etc/xen/examples/xmexample.rescue $CFGFILE sed -i "/^disk/s@^.*\$@disk = [ \'file:$DST,hda1,w\' ]@" $CFGFILE # These next two lines are only applicable if not using domUloader, # but try anyway. sed -i "/^kernel/s@^.*\$@kernel = \"/boot/vmlinuz-$KVER\"@" $CFGFILE sed -i "/^ramdisk/s@^.*\$@ramdisk = \"/boot/initrd-$KVER\"@" $CFGFILE echo "" echo "Config file '$CFGFILE' has been created. Please review!" echo "You may also want to add an IP address to the config file." echo "Start the domain with 'xm create -c $CFGFILE'." } if test -z "$1" -o -z "$2"; then usage fi SRC=$1 if [ ! -e "$SRC" ]; then usage_error "'$SRC' does not exist." fi DST=$2 if [ -e "$DST" ]; then error "'$DST' already exists." fi MB=$MB_DEFAULT [ ! -z "$3" ] && MB=$3 KVER=$4 if [ `id -u` -ne 0 ]; then usage_error "Please run this script as root." fi get_xen_kernel create_basis_and_mount remove_some_files copy_extra_files xenify_image exchange_kernel_modules umount_and_msg