#! /bin/bash

# $Id: fai-mirror 3027 2005-11-10 22:37:16Z lange $
#*********************************************************************
#
# fai-mirror -- create and manage a partitial mirror for FAI
#
# This script is part of FAI (Fully Automatic Installation)
# (c) 2004-2005, Thomas Lange, lange@informatik.uni-koeln.de
#
#*********************************************************************
# 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; see the file COPYING. If not, write to the
# Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
# MA 02111-1307, USA.
#*********************************************************************

version="Version 1.4, 28-apr-2005"

# variables: NFSROOT, FAI_CONFIGDIR
# NFSROOT_PACKAGES, packages from make-fai-nfsroot.conf

debdist=sarge
set -a 
. /etc/fai/fai.conf
. /etc/fai/make-fai-nfsroot.conf
FAI_ROOT=/ # do not execute in chroot
set +a

trap "umount_dirs" EXIT ERR
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
usage() {

    echo "fai-mirror -- create and manage a partitial mirror for FAI."
    echo "$version"
    echo "Please the the manual page fai-mirror(1)."
    exit
}
# - - - - - - - - - - - - - - - - - - - - - - - - - -
die() {

    local e=$1   # first parameter is the exit code
    shift

    echo "$@"    # print error message
    exit $e
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
excludeclass() {

    # removes/excludes all classes in $* from $classes
    local insert eclasses newclass c e

    eclasses="$*"
    eclasses=${eclasses//,/ }

    for c in $classes; do
        insert=1
        for e in $eclasses; do
          [ $c = $e ] && insert=0
        done
        [ $insert = 1 ] && newclass="$newclass $c"
    done
    classes="$newclass"
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
get_addpackages() {

    # add packages (mostly kernels) which are defined in the variable $addpackages
    # TODO: maybe better to create a tmp file with all these package
    # names, then to call install_packages for the tmp file

    local f p pkg pkglist

    for f in $(echo $FAI_CONFIGDIR/class/*.var); do
	pkg=$(egrep ^addpackages= $f | sed 's/addpackages=//'|sed 's/"//g'| perl -pe 's/\$[\w_-]+//g')

	[ -n "$pkg" ] && pkglist="$pkglist $pkg"
    done
    [ -z "$pkglist" ] && return 0

    echo -n "Adding packages from variable \$addpackages:"
    # loop over the list, because maybe some packages doesn't exist in the
    # partitial mirror, but only in the local repository. These can't be
    # downloaded via the normal method
    for p in $pkglist; do
	echo -n " $p"
	# test if .deb file is available
	if [ ! -f $FAI_CONFIG/files/packages/${p}_*.deb ]; then
	    apt-get $qflag -d $aptoptions -y --force-yes --fix-missing install $p
	fi
    done
    echo ""
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
umount_dirs() {

    [ "$FAI_DEBMIRROR" ] && umount $FAI_ROOT/$MNTPOINT 2>/dev/null 1>&2 || true
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
cleandirs() {

    return # currently nothing to do
#    rm $statefile
#    rm -rf $mirrordir/.apt-move  $statefile
#    [ $debug -eq 1 ] || rm -rf $aptcache $archivedir
#    rm -rf $aptcache $archivedir
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
initialize() {

    # TODO: root is only needed when FAI_DEBMIRROR is defined. Then we
    # must mount a directory

    # store all packages temporary in the mirror partition so we need only a move,
    # not a copy later during apt-move
    aptcache=$mirrordir/aptcache   # holds the package cache data
    archivedir=$aptcache/var/cache/apt/archives
    aptmovefile=$aptcache/etc/apt-move.conf # stores apt-move.conf
    statefile=$aptcache/statefile

    # also used in install_pacakges.conf
    export aptoptions=" \
       -o Aptitude::Log=/dev/null \
      -o Dir::State::status=$statefile \
      -o Dir::Cache=$aptcache/var/cache/apt \
      -o Dir::State=$aptcache/var/cache/apt \
      -o Dir::Cache::Archives=$archivedir \
      -o Dir::Etc::sourcelist=$aptcache/etc/apt/sources.list \
      -o Dir::State::Lists=$aptcache/var/lib/apt/lists/"

    # not needed
    # -o APT::Get::ReInstall
    # ."-o APT::Get::Download-Only=true -o Aptitude::Cmd-Line::Download-Only=true "

    # we only need some empty dirs
    mkdir -p $archivedir/partial $aptcache/etc/apt
    mkdir -p $aptcache/var/lib/apt/lists/partial 
    > $statefile
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
delete_base_packages() {

    # now delete all packages that are already included in base.tgz
    local p

    if [ ! -f $NFSROOT/var/tmp/base-pkgs.lis ]; then
	echo "$NFSROOT/var/tmp/base-pkgs.lis not available."
	echo "Can't remove wasteful packages that are already in base.tgz."
	return
    fi
    echo "Removing packages that are already included in base.tgz"
    for p in $(cat $NFSROOT/var/tmp/base-pkgs.lis); do
	if [ -f $archivedir/${p}_*.deb ]; then
	    [ $verbose -eq 1 ] && echo "deleting package $p"
	    rm $archivedir/${p}_*.deb
        # else commands only for debugging
        #    else
        #	echo "package $p not found"
        #	ls $archivedir/${p}_*.deb
	fi
    done
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
add_base_packages() {

    local plist
    # add packages from base.tgz and additional packages in nfsroot

    echo "Adding packages from base.tgz and packages defined in make-fai-nfsroot.conf."
    if [ -f $NFSROOT/var/tmp/base-pkgs.lis ]; then
	plist=$(< $NFSROOT/var/tmp/base-pkgs.lis)
	apt-get $qflag -d $aptoptions -y --force-yes --fix-missing install $plist
    fi

    # arch dependent packages defined in make-fai-nfsroot
    if [ -f $NFSROOT/var/tmp/packages.arch ]; then
	plist=$(< $NFSROOT/var/tmp/packages.arch)
	apt-get $qflag -d $aptoptions -y --force-yes --fix-missing install $plist
    fi

    # also add packages defined in make-fai-nfsroot.conf
    apt-get $qflag -d $aptoptions -y --force-yes --fix-missing install $packages $NFSROOT_PACKAGES
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
set-classes() {

    # if -c is given, ignore -x
    if [ -n "$cclasses" ]; then
	export classes=${cclasses//,/ }
	return
    fi

    set +e
    # all available file names are classes
    classes=$(cd $FAI_CONFIGDIR/package_config; ls -1 | egrep "^[A-Z0-9_-]+$")
    addclasses=$(grep -h PACKAGES $FAI_CONFIGDIR/package_config/* | awk '{printf $3"\n"$4"\n"$5"\n"$6"\n"}')
    export classes=$(echo -e "$classes\n$addclasses\n" | sort | uniq)
    [ -n "$exclasses" ] && excludeclass $exclasses
    set -e
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

[ -x "$(which apt-move)" ] || die 5 "apt-move not found. Please install package."

preserve=0
verbose=0
add=0
qflag=-qq
while getopts "avhx:pc:" opt ; do
    case "$opt" in
	a) add=1 ;;
	h) usage ;;
	x) exclasses="$OPTARG";;
	c) cclasses="$OPTARG";;
	p) preserve=1;;
	v) verbose=1; vflag=-v; qflag='';;
	?) die 1 "Unknown option";;
    esac
done
shift $(($OPTIND - 1))

[ -n "$exclasses" -a -n "$cclasses" ] && die 3 "Options -x and -c not allowed at the same time."
debug=0
[ $debug -eq 0 ] && quiet=-q

mirrordir=$1
if [ -z "$mirrordir" ]; then
    die 2 "Please give the directory for the mirror."
fi
if [ -d $mirrordir/pool -o -d $mirrordir/dists ]; then
    die 3 "Please first remove $mirrordir/pool and $mirrordir/dists"
fi

initialize

# if we are using nfs mounts for Debian mirror, this may fail here, since inside a chroot environment different dir are used

# if sources.list includes file AND FAI_DEBMIRROR is defined we have to mount
# otherwise mounting is not needed. call task_mirror

if [ "$FAI_DEBMIRROR" ]; then
    mount -r $FAI_DEBMIRROR $FAI_ROOT/$MNTPOINT || exit 9
fi

# TODO: use -p to preserve sources.list
perl -p -e 's/file:/copy:/' /etc/fai/sources.list > $aptcache/etc/apt/sources.list

echo "Getting package information"
apt-get $quiet $aptoptions update >/dev/null

set-classes
echo "Downloading packages for classes:" $classes
FAI=$FAI_CONFIGDIR install_packages -d $vflag
[ $add -eq 1 ] && add_base_packages
get_addpackages
umount_dirs
trap "" EXIT ERR
[ $add -eq 0 ] && delete_base_packages

# create mirror directory structure
echo "Calling apt-move"
cat > $aptmovefile <<EOF   # generate apt-move.conf
APTSITES=*
LOCALDIR=$mirrordir
DIST=$debdist
FILECACHE=$archivedir
LISTSTATE=$aptcache/var/lib/apt/lists
DELETE=no
CONTENTS=no
PKGCOMP='none gzip'
EOF
apt-move $quiet -c $aptmovefile update
echo "$0 finished."
echo -n "Mirror size and location: ";du -sh $mirrordir
cleandirs
