#!/bin/bash
#
# Copyright (c) 1999 by Marcus Brinkmann <Marcus.Brinkmann@ruhr-uni-bochum.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 Library General Public License for more details.
#
#   You should have received a copy of the GNU General Public
#   License along with this library; if not, write to the Free Software
#   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

# INSTRUCTIONS:
#
# * You need dpkg, sharutils (uudecode) and patch to use this script.
#   If you do not use Debian, you can find dpkg on any Debian mirror
#   in debian/project/dpkg/.
# * Mount your hurd partition, created with "mke2fs -o hurd /dev/???"
# * Make a directory somewhere, and put the following files in it:
#   cross-install    - this file. Installs the base packages on the hurd partition.
#   native-install   - To be run after booting to the Hurd.
#   dpkg-hurd        - a script to cross install packages even if they contain pre* scripts.
#   Also the packages mentioned in ${plist}, below.

# cross-install <path>
#
# for example: cross-install /gnu
#
#    1 Jan 1999 - v0.0 <Marcus.Brinkmann@ruhr-uni-bochum.de>
#                      Original created by Marcus Brinkmann.
#                 v0.1 Added etc/login and terminal support.
#                 v0.2 Fixed typo in passwd file.
#    4 Jan 1999 - v1.0 Added logging and verbose messages.
#                      Adding checks for certain malfunctions.
#                      Cleaned up the hacks.
#                      Only using ONE directory of base packages.
#   16 Jan 1999 - v1.1 now works with or without the symlink /usr -> /
#                      Uncommented timezone option.
#                      Install perl tar file only if it exists.
#   16 Feb 1999 - v1.2 Fix stupid error.
#                      Added error check for dpkg run.
#   2  Mar 1999 - v1.3 Replace [ == ] with [ = ], because former doesn't
#                      work on all systems (Emmanuel Michon).
#                      Only install known packages.
#                      Change working directory before applying patches (Brian May)
#                      Check if servers.boot is a conffile and needs to be copied.
#   6  Mar 1999 - v1.4 slang0.99.38 -> slang1 (Brian May)
#                      Don't bail out for NFS kernel problem (Brian May).
#                 v1.5 Do install terminfo in /usr/share, not in /share.
#                      Copy boot/servers.boot, not /servers.boot (Gavin Lewis)
#   28 Mar 1999	- v1.6 Add link from mtab to fstab.
#                      Patch shlibs of libc6 package, or dpkg-shlibdeps won't work.
#                      Make it work for flat and hierarchical repository.
#                      Move if/fi to include more patches against dpkg (thanks Santiago!)
#   27 Apr 1999 - v2.0 Update package list, move perl.dist to perl (because
#                      glibc 2.1.1 needs perl to configure, sucker!)
#                      Copy also dpkg-hurd to ${dest}.
#    2 May 1999 - v2.1 Remove base/timezones, add base/e2fsprogs.
#    7 May 1999 - v2.2 Add base/grub.
#                      Do not ask about /usr symlink. All known problems are
#                      resolved.
#   13 May 1999 - v2.3 Add editors/ed to oplist (requested by Roland McGrath)
#                      base/libstdc++2.9 for dselect
#                      Replace /etc/motd with something more sensible.
#   17 May 1999 - v2.4 Added syslogd and inetutils. A lot of packages are
#                      now required which were optional before. I am not
#                      going to take a risk. Base is base.
#                      Add ncurses-base.
#                      Don't do the mtab symlink. It is harmful for e2fsck.
#   27 May 1999 - v2.5 Commented out some old fixes. I am going to remove
#                      them silently in one of the next versions.
#                      Add /etc/hosts.
#   15 Jun 1999 - v2.6 Add adduser.
#   30 Jun 1999 - v2.7 Add overwrite stuff to grep, so it doesn't bail out.
#                      Also mention that you need dpkg, sharutils and patch.
#                      Reported by Ognyan Kulev.
#    2 Jul 1999 - v2.8 Removed some patches. We don't want to carry around
#                      this crap for ever.
#   22 Jul 1999 - v3.0 Work with perl5, add libgdbmg1.
#                      Only setup /etc/ttys if it is not a conffile.
#                      Really fix the overwrite stuff now (blech).
#    6 Aug 1999 - v3.1 Add base/perl-base dummy package.
#    7 Sep 1999 - v3.2 Add base/libwrap0, base/tcpd, base/dpkg-mountable,
#                      base/dpkg-multicd, base/libnet-perl.
#                      Apply patch to support download and option parsing by
#                      Daniel Burrows.
#   19 Sep 1999 - v3.3 Create subdirectories when downloading files
#                      (reported by Peter Hung <peter@wahoo.com.tw>).
#   27 Sep 1999 - v4.0 Remove terminfo for mach console, use with latest
#                      ncurses package! Remove /etc/ttys, it is in hurd
#                      package since a long time. Add new option
#                      domainname. Don't create /etc/motd.
#   15 Oct 1999 - v4.1 Add bash symlink *sigh*
#   08 Dec 1999 - v4.2 Remove libc6 fake package.
#                      Remove locales.
#    1 Mar 2000	- v4.3 Various updates.
#    1 Apr 2000 - v4.4 libreadline2g in oldlibs now.
#    8 Aug 2000 - v5.0 Changes by Santiago Vila:
#    8 Dec 2000 - v5.1 Update to current package situation.
#    6 Mar 2001 - v5.2 Likewise.
#
# * "download" is now a URL containing *everything* but dists/unstable/...
# Both ftp:// and http:// are allowed (since it's wget who does it).
#
# * Downloaded packages are kept in .../partial, I can provide a script
# to update the repository using the retrieved files if this an absolute
# requirement, but I don't think cross-install should become a do-it-all
# script. Also, the Packages.gz file which is used to guess the package
# filename is kept in .../partial, this is to not override whatever
# existing Packages.gz in the repository.
#
# * Changelog: s/Mai/May/
#
# * Removed section names from plist.
#
# * New variable dpkghurdoptions="--force-depends --force-overwrite"
#   This is just to improve readability in dpkg command line (it was
#   too large).
#
# * LANG=C (maybe the right thing to do here is LC_ALL=C, do not remember
# which one overrides the other, sorry).
#
# * Added libdb2 to plist, it's needed by perl-5.005-base.
# 
# * Introduced a shell function, cross_install(), it is used twice, one
# for the packages in plist, and another one for packages in oplist.
# This is to avoid code duplication. (BTW: For this function I've used
# 2-space tabs, feel free to put real tabs again :-)
#
#			Also add libstdc++2.10 (we'll need it)
#   24 Oct 2000 Fix options (reported by Colin Watson <cjw44@flatline.org.uk>)
#               Replace libreadline2g by libreadline4 for bash (reported by ???)

plist="adduser apt apt-utils base-files base-passwd bash
       bsdutils debconf debianutils diff
       dpkg e2fsprogs ed
       fileutils findutils gettext-base gnumach
       grep groff-base grub gzip hostname hurd
       inetutils libc0.2 libdb2 libdb3 libgdbmg1 libncurses5
       libpam-modules libpam-runtime libpam0g
       libperl5.6 libpopt0 libreadline4
       libstdc++2.10
       libwrap0 mawk nano ncurses-base ncurses-bin
       passwd perl-base sed shellutils slang1
       syslogd tar tcpd
       textutils zlib1g"

oplist="less"
#oplist=""

# If you only want a minimal base system, leave out packages listed in
# ${oplist}.
#
# It is possible to work from a Debian mirror, use .../binary-hurd-i386/ as
# the repository directory.
#
# Then login as user ROOT, enter the directory ("cd path/to/repository")
# and run
#  ./cross-install /gnu
# where /gnu is the mount point of your hurd partition.

#
# CONFIGURATION
#
hostname="hurd"
domainname="hurdnet.org"
dpkghurd="`pwd`/dpkg-hurd"
dpkghurdoptions="--force-depends --force-overwrite"
nativeinstall="`pwd`/native-install"
log="`pwd`/install.log"
download="ftp://ftp.de.debian.org/pub/debian"
dist="sid"
arch="hurd-i386"
dest=""
repository="`pwd`"

LANG=C
LC_ALL=C

while [ "$#" -ne 0 ]
do
  case "$1" in
    "--host"|"--hostname"|"--domain"|"--dpkghurd"|"--nativeinstall"|"--log"|"--download"|"--repository")
      if [ "$#" -lt 2 ]
      then
        echo "$1 needs an argument"
	exit 1
      fi
      case "$1" in
        "--host"|"--hostname") hostname="$2";;
	"--domain") domainname="$2";;
	"--dpkghurd") dpkghurd="$2";;
	"--nativeinstall") nativeinstall="$2";;
	"--log") log="$2";;
	"--download") download="$2";;
	"--repository") repository="$2";;
      esac
      shift 2
      ;;
   "--help" | "--hel" | "--hel" | "-h")
     echo "Usage: cross-install DEST-DIR [OPTION]..."
     echo "Cross-installation script for Debian GNU/Hurd"
     echo
     echo "   Options:"
     echo " --host, --hostname name   Set the hostname of the new system"
     echo " --domain        name      Set the domainname of the new system"
     echo " --dpkghurd      file      Give the filename of the dpkg-hurd script"
     echo " --nativeinstall file      Give the filename of the native-install script"
     echo " --log           file      Give the filename of the generated output log"
     echo " --download      server    Download missing files from the given Debian mirror"
     exit 0
     ;;
    -*)
     echo "Bad option: $1"
     exit 1
     ;;
    *)
     dest="$1"
     shift
     ;;
  esac
done

echo "Determining destination directory."
expr "${dest}" > /dev/null : / \
 && { echo "You are using an absolute path, fine." ; \
      dest="${dest}"; } \
 || { echo "You are using a relative path, prepending working directory." ; \
      dest="`pwd`/${dest}"; }

set -u -e

echo "Creating a Debian GNU/Hurd system in ${dest},"
echo "logging dpkg output in ${log}."
cat > ${log} < /dev/null

PS4='+$LINENO: '

err ()
{
    echo "$0: $1" 1>&2
    exit 1
}

###########################
# function cross_install()
###########################

cross_install() {
  pname=$1
  whatifnot=$2

  package=`find ${repository} -name "${pname}_*.deb" 2> /dev/null | tail -n 1`
  matches=`find ${repository} -name "${pname}_*.deb" 2> /dev/null | wc -l`
  if [ ${matches} = 0 ] ; then
    if ! [ -z "$download" ]; then
      echo "- ${pname} not found, downloading from ${download}."
      mkdir -p ${repository}/partial
      if [ ! -f ${repository}/partial/Packages.gz ]; then
        wget -cP ${repository}/partial ${download}/dists/${dist}/main/binary-${arch}/Packages.gz
      fi
      filename=`zcat ${repository}/partial/Packages.gz | awk -vp=$pname '
        $0 ~ "^Package:" { package = $2 }
        $0 ~ "^Filename:" && p == package { print $2 }'`
      wget --retr-symlinks -cP ${repository}/partial ${download}/$filename
      package=`find ${repository} -name "${pname}_*.deb" 2> /dev/null | tail -n 1`
      matches=`find ${repository} -name "${pname}_*.deb" 2> /dev/null | wc -l`
    fi
  fi

  if [ ${matches} = 0 ] ; then
    if [ "${whatifnot}" = "error" ]; then
      echo - ${pname} could not be found, but it is needed
      exit 1
    else
      echo - ${pname} could not be found, let us hope it is not vital
    fi
  else
    if [ ${matches} != 1 ] ; then
      echo "More than one version of package ${pname} available, choosing the alphabetically last."
    fi
    echo - $package
    ${dpkghurd} --unpack $package $dpkghurdoptions >> ${log} 2>&1 || break
  fi
}


#############################
# Comply with Hurd philosophy.
#############################

if [ ! -e ${dest}/usr ] ; then
	echo
	echo Setting up link from usr to root directory to comply with Hurd philosophy.
	rm -f ${dest}/usr
	ln -s . ${dest}/usr
fi

###########################
# Prepare dpkg's database.
###########################
# INFO: This is needed so dpkg-hurd does actually work.
echo
echo Preparing dpkg\'s database.

install -d -m 755 -o root -g root ${dest}/var/lib/dpkg/{info,updates}
touch ${dest}/var/lib/dpkg/{status,available}

##########################
# cross-install packages.
##########################
echo
echo "I will now install packages. If you have multiple versions of the"
echo "same package available, it may be that I don't recognize the latest."
echo "Please remove the offending old version in this case and start from"
echo -n "scratch."

if [ "x${download}" != x ] ; then
	echo "Missing packages will be downloaded from ${download}"
else
	echo
fi
echo
echo "Installing necessary packages, this may take some time."

cd ${dest}
for p in ${plist}
do
    cross_install $p "error"
done

echo
echo "Installing optional packages, this may take some time."
for p in ${oplist}
do
    cross_install $p "warning"
done

set +e
errors=`grep -v 'trying to overwrite\|but is unpacked but not configured\|Preparing to replace\|Unpacking\|pre-dependency problem\|Selecting previously deselected package\|pre-depends on\|overriding problem because --force enabled\|(Reading database ...\| package architecture (hurd-i386) does not match system\|is not installed.\|but has never been configured.\|dpkg-deb: building package' ${log}`
set -e

if [ ! "x${errors}" = x ] ; then
	echo
	echo 'ERROR: dpkg did return unusual messages, please investigate:'
	echo 'INSTALLATION ABORTED!'
	echo
	grep -v '^rm: /tmp/.*: Device or resource busy$\|dpkg - warning, overriding problems\|trying to override\|Preparing to replace\|Unpacking\|pre-dependency problem\|Selecting previously deselected package\|pre-depends on\|overriding problem because --force enabled\|(Reading database ...\| package architecture (hurd-i386) does not match system\|is not installed.\|but has never been configured.\|dpkg-deb: building package' ${log}
	echo
	echo 'INSTALLATION ABORTED because of unknown errors. Partial installation'
	echo 'is NOT usable. Correct the error and start from scratch.'
	exit 1
fi

##############################
# Prepare initial translator.
##############################
# FIX: Should be done in the Hurd preinst.
echo
echo "Setting up initial translator hook."

install -d -m 755 -o root -g root ${dest}/servers/socket
touch ${dest}/servers/exec

#####################################
# Make sure boot/servers.boot exists
#####################################
# Explanation: It is a conf file in some versions of Hurd.

echo
echo "Latest Hurd packages declare boot/servers.boot as a conffile, let me check."

if [ -e ${dest}/boot/servers.boot.dpkg-new ] ; then
	echo "Yes, found it. I will copy boot/servers.boot.dpkg-new to boot/servers.boot"
	cp ${dest}/boot/servers.boot.dpkg-new ${dest}/boot/servers.boot
else
	echo "Nope, not your version. Please make sure that you check boot/servers.boot after"
        echo "your next update of the hurd package, because your changes will be overridden."
fi

#####################################
# Make sure libexec/runsystem exists
#####################################
# Explanation: It is managed by update-alternatives.
#
if [ ! -e ${dest}/libexec/runsystem -a -e ${dest}/libexec/runsystem.gnu ] ; then
	echo "Copying libexec/runsystem, so the system can boot."
	cp ${dest}/libexec/runsystem.gnu ${dest}/libexec/runsystem
fi

#########################
# Make sure sh exists
#########################
# Explanation: Thorsten did a strange bash NMU.
if [ ! -e ${dest}/bin/sh ] ; then
	echo "Creating symlink /bin/sh."
	ln -s bash ${dest}/bin/sh
fi

############################
# Move perl in proper place
############################
# Explanation: Latest glibc package needs perl (and awk) to configure!

if [ ! -e ${dest}/usr/bin/perl ] ; then
	if [ -e ${dest}/usr/bin/perl.dist ] ; then
		echo
		echo "Moving perl into place."
		mv ${dest}/usr/bin/perl.dist ${dest}/usr/bin/perl
	else
		if [ -e ${dest}/usr/bin/perl-5.005.dist ] ; then
		echo
		echo "Found versioned perl."
		# We only copy it here so the postinst does the right thing.
		# A mv or ln won't work!
		cp ${dest}/usr/bin/perl-5.005.dist ${dest}/usr/bin/perl
		else
			echo
			echo "Warning, I could not find perl!"
		fi
	fi
fi

#############################
# Copy native install script
#############################
echo
echo "Copying the scripts native-install and dpkg-hurd."

cp ${nativeinstall} ${dest}
cp ${dpkghurd} ${dest}

########################################################
# make some essential directories for configure of dpkg
########################################################
# FIX: In sysvutils, together with split-init
if [ ! -e ${dest}/etc/rc0.d ] ; then
        echo
        echo "Making etc/rc?.d directories for dpkg."
        for nr in 0 1 2 3 4 5 6 S
        do
                install -d -m 755 -o root -g root ${dest}/etc/rc${nr}.d
        done
        install -d -m 755 -o root -g root ${dest}/etc/rc.boot
fi

##################
# Setup hostname.
##################
echo
echo "Hey, ${hostname} is really a nice hostname for your machine!"

echo ${hostname} > ${dest}/etc/hostname

###################
# Setup /etc/hosts
###################
echo
echo "Preparing /etc/hosts."

echo "127.0.0.1		localhost ${hostname}" > ${dest}/etc/hosts

#########################
# Setup /etc/resolv.conf
#########################
echo
echo "Preparing /etc/resolv.conf."

if [ -e /etc/resolv.conf ] ; then
	echo Copying this machine\'s resolv.conf to the Hurd partition.
	echo If this is not correct, please edit.
	cp /etc/resolv.conf ${dest}/etc/resolv.conf
else
	echo Setting up bogus file, PLEASE EDIT.
	echo "domain ${domainname}" > ${dest}/etc/resolv.conf
	echo "search localnet" >> ${dest}/etc/resolv.conf
	echo "nameserver WWW.XXX.YYY.ZZZ" >> ${dest}/etc/resolv.conf
fi

echo
echo "Now boot the Hurd and run"
echo "./native-install"
echo
