#!/bin/sh

# Initial bootstrap of a pristine Debian system.

# Copyright (C) 2007, 2008, 2009, 2010, 2011 Bob Proulx
#
# 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 3 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.  If not, see <http://www.gnu.org/licenses/>.

# Written by Bob Proulx <bob@proulx.com>

VERSION=0
LC_ALL=C
export LC_ALL

test -x /usr/bin/dpkg || exit 1
test -x /usr/bin/apt-get || exit 1
test -r /etc/debian_version || exit 1

progname=$(basename $0)

print_help()
{
    # Create a help message suitable for help2man to format into a man
    # page directly from the online --help and --version message.
cat <<'EOF'
Usage: bootstrap [options]

Initial bootstrap of a pristine Debian system.

Options:
     --help     output help message
     --version  output version information

Examples:

The most common use is to run the script.

  $ bootstrap

But it is also possible to pull this from the web and run it on the
fly.

  $ apt-get install wget

  $ wget -q --no-cache -O - http://www.proulx.com/~bob/debian/bootstrap | sh

Report bugs to Bob Proulx <bob@proulx.com>
EOF
}

print_version()
{
    cat <<EOF
$progname $VERSION
Written by Bob Proulx.

Copyright (C) 2011 Bob Proulx
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
EOF
}

SHORTOPTS="v"
LONGOPTS="help,verbose,version"

if $(getopt -T >/dev/null 2>&1) ; [ $? = 4 ] ; then # New longopts getopt.
    OPTS=$(getopt -o $SHORTOPTS --long $LONGOPTS -n "$progname" -- "$@")
else # Old classic getopt.
    # Special handling for --help and --version on old getopt.
    case $1 in --help) print_help ; exit 0 ;; esac
    case $1 in --version) print_version ; exit 0 ;; esac
    OPTS=$(getopt $SHORTOPTS "$@")
fi

if [ $? -ne 0 ]; then
    echo "'$progname --help' for more information" 1>&2
    exit 1
fi

eval set -- "$OPTS"

verbose=false
while [ $# -gt 0 ]; do
    : debug: $1
    case $1 in
        --help)
            print_help
            exit 0
            ;;
        --verbose|-v)
            verbose=true
            shift
            ;;
        --version)
            print_version
            exit 0
            ;;
        --)
            shift
            break
            ;;
        *)
            echo "Internal Error: option processing error: $1" 1>&2
            exit 1
            ;;
    esac
done

if ! (apt-get --version) >/dev/null 2>&1; then
  : No apt-get available so not an apt machine, exiting.
  exit 1
fi

if ! (dpkg --version) >/dev/null 2>&1; then
  : No dpkg available so not an dpkg machine, exiting.
  exit 1
fi

if [ ! -f /etc/apt/sources.list ]; then
  echo "Error: No /etc/apt/sources.list" 1>&2
  echo "Not running on a Debian GNU/Linux machine?" 1>&2
  exit 1
fi

if [ ! -w / ]; then
  echo "Error: Must be root to run this script" 1>&2
  exit 1
fi

case $(</etc/debian_version) in
    3.0*) debver="woody" ;;
    3.1*) debver="sarge" ;;
    4.0*) debver="etch" ;;
    5.0*) debver="lenny" ;;
    *)
	echo "Unknown /etc/debian_version found."
	exit 1
	;;
esac

case $debver in
  lenny) : okay, working with Lenny, FIXME experimental ;;
  *)
    echo "Error: This script only handles Debian Lenny machines." 1>&2
    exit 1
    ;;
esac

tdir=""
trap 'rm -rf $tdir' EXIT
tdir=$(mktemp -d /tmp/bootstrap.XXXXXXXX) || exit 1

# Quiet down the installer.
export DEBIAN_FRONTEND=noninteractive
export DEBCONF_ADMIN_EMAIL=""
test -t 0 && exec </dev/null

# FIXME: The check for already there needs improvement.
case $(sysctl -n vm.overcommit_memory) in
    2) : okay, vm.overcommit_memory is 2 ;;
    *)
	# Turn off Linux memory overcommit if there is enough swap.
	swap=$(free -m | awk '/^Swap:/{print$2}')
	if echo "$swap" | grep -q -E '^[[:digit:]]+$'; then
	    if (( $swap > 1000 )); then
		sysctl -w vm.overcommit_memory=2
		sleep 5 # Give system time to crash if it is going to.
		# If the system can take it then make it permanent.
		cat >>/etc/sysctl.conf <<EOF

# Disable Linux kernel memory overcommit.
vm.overcommit_memory = 2
EOF
	    fi
	fi
	;;
esac

# Turn on bootlogd which is off by default to enable boot message logging.
if ! grep -q "^BOOTLOGD_ENABLE=Yes" /etc/default/bootlogd; then
    sed --in-place "s/^BOOTLOGD_ENABLE=.*/BOOTLOGD_ENABLE=Yes/" /etc/default/bootlogd
fi

if grep -q '^FSCKFIX=no$' /etc/default/rcS; then
    sed --in-place "s/^FSCKFIX=no$/FSCKFIX=yes/" /etc/default/rcS
fi

cat > $tdir/recommends <<EOF
// This file was placed here at system install time by 'bootstrap'.
// At this time this package is not in any package.
Aptitude::Recommends-Important "false";
EOF
test -f /etc/apt/apt.conf.d/00recommends \
    || touch /etc/apt/apt.conf.d/00recommends
if ! cmp -s $tdir/recommends /etc/apt/apt.conf.d/00recommends; then
    cp $tdir/recommends /etc/apt/apt.conf.d/00recommends
fi

lclmirror=""
if [ _"$(uname -m)" = _i686 ]; then
    lclmirror="/www.proulx.com"
fi
cat > $tdir/sources.list <<EOF
deb http:/$lclmirror/ftp.us.debian.org/debian/ $debver main contrib non-free
deb-src http://ftp.us.debian.org/debian/ $debver main contrib non-free
deb http:/$lclmirror/security.debian.org/ $debver/updates main contrib non-free
deb-src http://security.debian.org/ $debver/updates main contrib non-free
deb http:/$lclmirror/volatile.debian.org/debian-volatile lenny/volatile main contrib non-free
#deb http://volatile.debian.org/debian-volatile lenny/volatile-sloppy main contrib non-free
EOF
test -f /etc/apt/sources.list || touch /etc/apt/sources.list
if ! cmp -s $tdir/sources.list /etc/apt/sources.list; then
    cp -f --backup=numbered $tdir/sources.list /etc/apt/sources.list
fi

if ! apt-get update; then
  echo "Error: Failed apt-get update" 1>&2
  exit 1
fi

if ! apt-get -o DPkg::Options::=--force-confnew upgrade -q -y; then
    echo "Error: apt-get failed" 1>&2
    exit 1
fi

if ! apt-get -o DPkg::Options::=--force-confnew dist-upgrade -q -y; then
    echo "Error: apt-get failed" 1>&2
    exit 1
fi

if [ ! -x /usr/local/bin/ll ]; then
    cat >/usr/local/bin/ll <<\EOF
#!/bin/sh
exec ls -l "$@"
EOF
    chmod a+x /usr/local/bin/ll
fi

if [ ! -d /root/bin ]; then
    mkdir /root/bin
fi

mypurgepkg()
{
    for pkgname in "$@"; do
	if [ -d /usr/share/doc/$pkgname ]; then
	    dpkg --purge $pkgname
	fi
    done
}

myinstallpkg()
{
    for pkgname in "$@"; do
	if [ ! -d /usr/share/doc/$pkgname ]; then
	    apt-get install -y $pkgname
	fi
    done
}

# debsums can verify the integrity of installed package files against
# MD5 checksums installed by the package.
myinstallpkg debsums

# Most packages come with md5sums included.  But some do not.  Walk
# through all of the installed packages that do not have installed
# md5sums and reinstall them.  Now that debsums is installed the post
# installation script will cause an md5sum to be created at
# installation time even if the package doesn't have it.

# initscripts failed to reinstall when in conjuction with other packages.
# Therefore use a for-loop.
for pkg in $(debsums -l); do
    apt-get install --reinstall -y $pkg
done

# Uses GNU locate to greatly speed up finding out which package a file
# belongs to (i.e. a very fast dpkg -S).  Many other uses, including
# options to view all files in a package, calculate disk space used,
# view and check md5sums, list man pages, etc.
myinstallpkg dlocate

# The Real(TM) vi program.  None of that vim similar but different stuff.
myinstallpkg nvi
myinstallpkg less

# nano is the pico-like editor that is the lowest common denominator.
# It can edit files but if we have vi/emacs then we don't need nano.
# Remove it so that it isn't ever chosen as a default fallback editor.
# Without it then vi will be chosen by default.
mypurgepkg nano

# Now that vim is installed it will own the alternatives.  Configure.
update-alternatives --set vi /usr/bin/nvi
update-alternatives --set view /usr/bin/nview
update-alternatives --set ex /usr/bin/nex
update-alternatives --set editor /usr/bin/nvi

# The default installation will include Exim.  That is a good MTA.
# But Postfix is preferred here.
myinstallpkg postfix

# Remove Exim config files now that Postfix is installed.
# FIXME: This is the exim list for etch but need updates for lenny.
eximlist='exim4 exim4-base exim4-config exim4-daemon-light'
for p in $eximlist; do
    if dpkg --status $p 2>/dev/null | grep -q '^Status: deinstall .* config-files'; then
	dpkg --purge $p
    fi
done

# FIXME: Bad result if there were more fields there before like on joseki.
# Correct this problem with an empty field.
# mydestination = foo.example.com, localhost.example.com, , localhost
mydomain=$(postconf -h mydomain)
myhostname=$(postconf -h myhostname)
mydestination="$myhostname, localhost.$mydomain, localhost"
if ! postconf -h mydestination | grep -q "$mydestination"; then
    postconf -e mydestination="$mydestination"
fi

# FIXME: /root/.forward or /etc/aliases root forward

# Install a local caching nameserver.
myinstallpkg bind9

# Resolvconf is a framework for keeping up to date the system's
# information about name servers.  It sets itself up as the
# intermediary between programs that supply this information (such as
# ifup and ifdown, DHCP clients, the PPP daemon and local name
# servers) and programs that use this information (such as DNS caches
# and resolver libraries).
myinstallpkg resolvconf
# This package may require some post installation configuration.
if ! grep -q '^include.*/var/run/bind/named.options' /etc/bind/named.conf; then
    sed --in-place '/^include/s|/etc/bind/named.conf.options|/var/run/bind/named.options|' /etc/bind/named.conf
fi

# Install the SSH server to enable remote access.
myinstallpkg openssh-server

# Sudo is a program designed to allow a sysadmin to give limited root
# privileges to users and log root activity.
myinstallpkg sudo
# Configure it.
if ! grep -q -e 'Defaults.*!lecture' -e 'Defaults.*!fqdn' -e 'Defaults.*!env_reset' /etc/sudoers; then
    sed --in-place '/Defaults/s/.*/Defaults !lecture,!fqdn,env_reset/' /etc/sudoers
fi

# Htop is an ncursed-based process viewer similar to top, but it
# allows to scroll the list vertically and horizontally to see all
# processes and their full command lines.
myinstallpkg htop

# The smartmontools package contains two utility programs (smartctl
# and smartd) to control and monitor storage systems using the
# Self-Monitoring, Analysis and Reporting Technology System
# (S.M.A.R.T.) built into most modern ATA and SCSI hard disks.
myinstallpkg smartmontools
# Configure it.
if ! grep -q '^start_smartd=yes' /etc/default/smartmontools; then
    sed --in-place '/^#start_smartd=/s/.*/start_smartd=yes/' /etc/default/smartmontools
    invoke-rc.d smartmontools start
fi

# aptitude is a terminal-based package manager similar to apt-get but
# rewritten and so is slightly different.  It is much slower.  It is
# documented as the only officially supported tool for some operations.
myinstallpkg aptitude
# On first call to aptitude it will run its own init setup.
aptitude update
# Print out information.  -s simulate, -f fix, -y yes, -D description.
aptitude install -sfy

# Configure so that new users get ./.ssh/ set with these permissions.
test -d /etc/skel/.ssh || mkdir -m 0700 /etc/skel/.ssh

# Configure umask for UPG (user private group) access.
if [ -f /etc/skel/.bash_profile ]; then
    SKELPROFILE=/etc/skel/.bash_profile
elif [ -f /etc/skel/.profile ]; then
    SKELPROFILE=/etc/skel/.profile
fi
if grep -q umask $SKELPROFILE; then
    sed --in-place '/default umask/d;/umask 022/{N;s/.*\n//;/^$/d;};' $SKELPROFILE
fi
if grep -q '^umask 022' /etc/profile; then
    sed --in-place '/umask 022/s/022/02/' /etc/profile
fi

# This is a security feature that is no longer needed.  Clear the
# screen on logout to hide what was done before.  In the environments
# used today this isn't needed anymore since logging out of the
# launching environment will close the terminal window there.  Since
# it gets in the way of administering systems because it clears the
# screen on logout disable it.
rm -f /etc/skel/.bash_logout

cat >$tdir/kernel-img.conf <<EOF
# Kernel image management overrides
# See kernel-img.conf(5) for details
do_symlinks = no
relative_links = yes
do_bootloader = no
do_bootfloppy = no
do_initrd = yes
link_in_boot = no
postinst_hook = update-grub
postrm_hook   = update-grub
EOF
test -f /etc/kernel-img.conf || touch /etc/kernel-img.conf
if ! cmp -s $tdir/kernel-img.conf /etc/kernel-img.conf; then
    cp $tdir/kernel-img.conf /etc/kernel-img.conf
fi
rm -f /vmlinuz /vmlinuz.old /initrd.img /initrd.img.old

# A program to clean out temporary directories such as /tmp.
myinstallpkg tmpreaper
# Disable the warning.  See /usr/share/doc/tmpreaper/README.security.gz .
if grep -q '^SHOWWARNING=true' /etc/tmpreaper.conf; then
    sed --in-place 's/SHOWWARNING=true/SHOWWARNING=false/' /etc/tmpreaper.conf
fi

myinstallpkg rsync
myinstallpkg make

apt-get clean

exit 0
