#! /bin/sh

# DIET-PC master initialisation script

# Copyright (C) 2002-2013 Paul A Whittaker <whitpa@users.sourceforge.net>

# 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; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

[ -f /etc/profile ] && . /etc/profile
export PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin
umask 022

notify ()
{
    # To aid degugging, you might wish to uncomment the sleep command, to give
    # you a chance to press the <Pause> key and see errors before they scroll
    # off.

    #busybox sleep 3
    echo -e "${HL_ON}$*${HL_OFF}"
    case "$HAVE_SPLASH" in
    TRUE)
	NOTIFY_COUNT=$(($NOTIFY_COUNT + 1))
	echo "show $((65535 * $NOTIFY_COUNT / $NOTIFY_TOTAL))" >>/proc/splash
	;;
    esac
}



busybox mount /proc
# If the console is a VT, we can use ANSI escapes to do highlighting.  "tty"
# doesn't work until /dev is mounted, so use console=xxx in /proc/cmdline to
# determine this.
case "`busybox sed -e 's/^.*\<console=\([^[:blank:]]\+\).*$/\1/' /proc/cmdline`" in
tty[0-9]*)
    HL_ON='\033[1;39m'
    HL_OFF='\033[m\017'
    ;;
*)
    HL_ON=
    HL_OFF=
    ;;
esac
LOCKDOWN="`busybox grep -q '\<lockdown\>' /proc/cmdline && echo 'TRUE'`"
if [ -f /proc/splash ]; then
    HAVE_SPLASH=TRUE
    NOTIFY_COUNT=0
    NOTIFY_TOTAL=$((`busybox grep -s '^\s*\<notify\>' /etc/rc /etc/rc.d/* /usr/etc/rc.d/* /usr/local/etc/rc.d/* | busybox wc -l`-1))
else
    HAVE_SPLASH=
fi
notify 'Mounting core filesystems'
ROOTDEV=`busybox sed -e 's/^.*\<root=\([^[:blank:]]\+\).*$/\1/' /proc/cmdline`
case "$ROOTDEV" in
''|/*)
    ;;
*)
    ROOTDEV="/dev/$ROOTDEV"
    ;;
esac
# If root is not an initramfs ...
if busybox grep -q /dev/root /proc/mounts; then
    # ... or ramdisk, and a root device node exists, then always fsck it,
    # regardless of what /etc/fstab specifies.
    if [ "$ROOTDEV" != '/dev/ram0' -a -e "$ROOTDEV" ]; then
	fsck -aCT "$ROOTDEV"
	case $? in
	0|1)
	    ;;
	2|3)
	    exec reboot -ifd
	    ;;
	*)
	    echo 'Fsck failed ... need help!'
	    [ "$LOCKDOWN" ] && busybox sleep 10 || busybox sulogin
	    exec reboot -ifd
	    ;;
	esac
    fi
    busybox mount /tmp
    if busybox grep /dev/root /proc/mounts | busybox grep -q '\<ro\>'; then
	# If root is read-only and it is not prohibited by a "no_root_union"
	# kernel parameter, try to mount a new root as a union of the original
	# (read-only) root filesystem and a tmpfs (read-write) filesystem.
	busybox mkdir -m 0755 /tmp/.overlay
	busybox modprobe -q unionfs
	busybox grep -qv '\<no_root_union\>' /proc/cmdline && busybox mount \
		-t unionfs -o dirs=/tmp/.overlay=rw:/=ro none /mnt
	if [ $? -eq 0 ]; then
	    cd /mnt
	    busybox pivot_root . ./oldroot
	    busybox umount /oldroot/proc 2>/dev/null
	    busybox mount /proc
	else
	    # If not mounted already (via a kernel feature), mount a writable
	    # tmpfs /dev.
	    busybox awk 'BEGIN {n=0} $2 == "/dev" {n=1} END {exit n}' \
		    /proc/mounts && busybox mount -t tmpfs -o \
		    nr_blocks=256,nr_inodes=1024,mode=0755 none /dev
	    # If the root filesystem is still read-only, try to remount it
	    # read-write.  Remounts don't seem to work reliably when using the
	    # Busybox mount applet, so only use it as a last resort.
            mount -o remount,rw / 2>/dev/null || busybox mount -o remount,rw /
	    if [ $? -ne 0 ]; then
		# If still no-go, there is no point in continuing - let the
		# operator try to fix it and then reboot.
		echo 'Root filesystem is not writable, please fix!'
		[ "$LOCKDOWN" ] && busybox sleep 10 || busybox sulogin
		exec reboot -ifd
	    fi
        fi
    fi
fi
busybox mount /sys 2>/dev/null
busybox mkdir -p /dev/pts
busybox mount /dev/pts 2>/dev/null
busybox mkdir -p /dev/shm
busybox mount /dev/shm 2>/dev/null

notify 'Initializing busybox'
busybox --install -s
# Remove any residual junk from /var/run.
rm -f /var/run/* 2>/dev/null

notify 'Initializing /dev'
ln -sf "$ROOTDEV" /dev/root 2>/dev/null
ln -snf /proc/self/fd /dev/fd
ln -sf /proc/self/fd/0 /dev/stdin
ln -sf /proc/self/fd/1 /dev/stdout
ln -sf /proc/self/fd/2 /dev/stderr
ln -sf /proc/kcore /dev/core
echo '/sbin/mdev' >/proc/sys/kernel/hotplug
mdev -s

if [ "$LOCKDOWN" ]; then
    # Remove root's password hash, thereby preventing root login and su to root.
    sed -i -e 's/^root:[^:]\+:/root:*:/' /etc/shadow
    # Turn off console login.
    sed -i -e '/^logn:/d' /etc/inittab
    kill -HUP 1
    # Set TCP wrappers to deny by default.
    echo 'ALL: ALL' >/etc/hosts.deny
    # Remove the setuid bit from busybox, to prevent use of setuid-root applets.
    chmod u-s /bin/busybox
    # Prevent unprivileged users from running an X11 terminal emulator.
    XTERM=`which xterm`
    [ "$XTERM" ] && chmod 700 "$XTERM"
fi

notify 'Initializing modules'
mkdir -p /lib/modules/`uname -r`
depmod -a
# If an eth0 hint has been provided via the boot loader or via
# /etc/modprobe.conf, use it now, in case there are multiple PCI NICs.
[ "$eth0" ] && modprobe -q `echo $eth0 | tr , ' '`
modprobe -q eth0
# Forceload some difficult modules that will otherwise neither coldplug nor
# hotplug.
modprobe -q parport
modprobe -q ppp
modprobe -q tun
modprobe -q floppy
modprobe -q loop
# Coldplug all platform, PCI, PCMCIA and USB devices initially found (in that
# order).
for BUS in platform pci pcmcia usb; do
    if [ -d /sys/bus/$BUS/devices ]; then
	notify "Initializing $BUS devices"
	cat /sys/bus/$BUS/devices/*/modalias 2>/dev/null | while read MODULE; \
		do
	    modprobe -q $MODULE
	done
    fi
done
# Some last resorts, needed only in the event that bus coldplugging has not
# initialised the necessary device.
modprobe -q ide-generic
modprobe -q scsi_hostadapter
modprobe -q sound-slot-0
# Attempt to mount the legacy usb device filesystem if a USB host controller is
# now online.
mount /proc/bus/usb 2>/dev/null

# Set a provisional hostname.
if [ "`hostname`" = '(none)' ]; then
    # First try the contents of /etc/hostname, then the name of the special
    # non-localhost loopback address.
    HN=`cat /etc/hostname 2>/dev/null` || HN=`checkhostname -n 127.0.1.1`
    # Failing that, check all local MAC addresses against /etc/ethers.  If
    # there is a match, set the hostname from the hostname field of the first
    # matching entry.
    if [ $? -ne 0 -a -f /etc/ethers ]; then
	HN=`/sbin/ifconfig -a | awk 'BEGIN {printf "{IGNORECASE=1}\n"} \
		/HWaddr/ {printf "$1 == \"%s\" {print $2; exit}\n", $NF}' | \
		awk -f - /etc/ethers`
    fi
    # As a last resort, derive a hostname from the MAC address of a local
    # network interface.
    [ "$HN" ] || HN=`ifconfig -a | awk '/HWaddr/ {printf "X%s\n", $NF; exit}' \
	    | tr -d :`
    # Use what we have.
    [ "$HN" ] && hostname "$HN"
fi

notify 'Initializing network'
mkdir -p /etc/network
(cd /etc/network; mkdir -p if-up.d if-down.d if-pre-up.d if-post-down.d)
if [ ! -f /etc/network/interfaces ]; then
    notify 'Network not configured - using DHCP'
    tail -n +3 /proc/net/dev | cut -f1 -d: | while read IFACE; do
	case "$IFACE" in
	lo)
	    IFMETHOD='loopback'
	    ;;
	sit*)
	    IFMETHOD='manual'
	    ;;
	*)
	    IFMETHOD='dhcp'
	    ;;
	esac
	echo "auto $IFACE" >>/etc/network/interfaces
	echo "iface $IFACE inet $IFMETHOD" >>/etc/network/interfaces
    done
fi
ifup -a
if [ -x /bin/ifplugd -a -x /etc/ifplugd/ifplugd.action ]; then
    for IFACE in `netstat -r | awk '/^[0-9]/ {print $NF}'`; do
	ifplugd -i "$IFACE" -p -q
    done
fi

if [ ! -s /etc/hosts.allow ]; then
    # In the absence of an explicit configuration, set TCP wrappers to grant
    # access to clients on any local subnet or with the same DNS domain name
    # suffix.
    ifconfig | grep '\<inet addr:' | sed \
	    's/^.*inet addr:\([0-9.]\+\).*Mask:\([0-9.]\+\).*$/\1 \2/' | \
	    while read ADDR MASK; do
	FQHN=`checkhostname -n $ADDR`
	DN=`echo "$FQHN" | cut -f2- -d.`
	[ "$DN" -a "$DN" != "$FQHN" ] && echo "ALL: .$DN" >>/etc/hosts.allow
	NETWORK=
	eval `ipcalc -n $ADDR 2>/dev/null`
	[ "$NETWORK" ] && echo "ALL: $NETWORK/$MASK" >>/etc/hosts.allow
    done
fi

# Check reverse lookups on all active non-loopback interfaces (now that we
# (possibly) have DNS).
HN=
for IPADDR in `ifconfig | grep 'inet addr:[0-9.]\+' | sed -e \
	's/^.*inet addr:\([0-9.]\+\).*$/\1/' | grep -v '^127\.'`; do
    HN=`checkhostname -n "$IPADDR" | cut -f1 -d.`
    [ "$HN" ] && break
done
if [ "$HN" ]; then
    # Any hostname found via reverse lookup will be considered more
    # authoritative than the provisional hostname (N.B. a hostname set by
    # /sbin/udhcpc-helper from DHCP results is provisional).  If you don't
    # want this behaviour, comment out everything except the "else" clause of
    # this "if" statement (including if, else, and fi lines).
    hostname "$HN"
else
    # If there is no better hostname, endorse the provisional one by adding
    # an /etc/hosts entry for it using a local IP address.
    sed -i -e '/\<'"`hostname`"\$/d /etc/hosts
    echo -e "${IPADDR:-127.0.1.1}\t`hostname`" >>/etc/hosts
fi

# If possible, set the system clock at this point.  Fsck needs this for
# scheduled checks, and loggers need it for meaningful timestamps.  Change -u
# to -l if you want your system clock to keep local time rather than UTC time.
# NTP synchronisation might be slow, but we can't background this, as it might
# be our last hope of getting a plausible clock setting (before fsck etc).
if [ -c /dev/rtc ]; then
    notify 'Setting time from hardware clock'
    if [ "`hwclock -u -r | egrep -w '(1970|2000)'`" ]; then
	notify 'Sanity check failed - ignoring data from hardware clock'
    else
	hwclock -u -s
    fi
    if checkhostname -q ${timeserver:-timeserver}; then
	notify 'Synchronising clock with network time server'
	ntpd -n -q -p ${timeserver:-timeserver}
	hwclock -u -w
	ntpd -p ${timeserver:-timeserver}
    fi
fi

notify 'Mounting all filesystems'
# We might have portmap (IPV4 only) or rpcbind (IPV4+IPV6) or neither.
portmap 2>/dev/null || rpcbind 2>/dev/null || true
# If you reference a network block device (/dev/nbd0 etc al) in /etc/fstab,
# you must prepare it for use here, e.g.
#nbd-client myserver 2000 /dev/nbd0 -persist >/dev/null 2>&1
# Scan for RAID arrays and try to assemble all that are found.  Only start
# fully intact arrays.
if [ "`which mdadm`" ]; then
    if [ ! -e /etc/mdadm.conf ]; then
	echo 'MAILADDR root' >/etc/mdadm.conf
	mdadm --examine --brief --scan --config=partitions >>/etc/mdadm.conf
    fi
    mdadm --assemble --scan --no-degraded --auto=yes
    mdadm --monitor --scan --daemonise --pid-file=/var/run/mdmonitor.pid
fi
# Scan for LVM volume groups and activate all that are found.
if [ "`which lvm`" ]; then
    rm -f /etc/lvm/cache/.cache
    lvm pvscan
    lvm vgscan
    lvm vgchange -ay
fi
fsck -aCRAT
case $? in
0|1)
    ;;
2|3)
    exec reboot -ifd
    ;;
*)
    echo 'Fsck failed ... need help!'
    [ "$LOCKDOWN" ] && sleep 10 || sulogin
    exec reboot -ifd
    ;;
esac
mount -a 2>/dev/null
if [ $? -ne 0 ]; then
    # The kernel might have been too slow to load the necessary modules during
    # the first attempt, so wait a moment and then retry.
    sleep 2
    mount -a 2>/dev/null
fi
swapon -a 2>/dev/null

# Logging chews up a little memory but is a valuable debugging aid.
notify 'Starting loggers'
if checkhostname -q ${logserver:-logserver}; then
    syslogd -m 0 -R ${logserver:-logserver}
else
    syslogd -m 0 -C
fi
klogd

# Start crond (if available).
if [ -x /sbin/crond ]; then
    notify 'Starting cron'
    mkdir -p /var/spool/cron/crontabs
    chmod 1733 /var/spool/cron/crontabs
    crond
fi

notify 'Starting power management'
# If CPU frequency scaling is available, install a demand-driven governor
# to reduce power consumption and heat output when high CPU performance is not
# needed.
for CPU in /sys/devices/system/cpu/cpu*; do
    if [ -f $CPU/cpufreq/scaling_governor ]; then
	echo 'conservative' >$CPU/cpufreq/scaling_governor 2>/dev/null
	if [ "`cat $CPU/cpufreq/scaling_governor`" != 'conservative' ]; then
	    echo 'ondemand' >$CPU/cpufreq/scaling_governor 2>/dev/null
	fi
    fi
done
# Set a 30 minute idle spin-down on all hard disks.
for DISK in /dev/hd? /dev/sd?; do
    [ "$DISK" = 'hd?' -o "$DISK" = 'sd?' ] && continue
    hdparm -S 241 $DISK >/dev/null 2>&1
done

if [ -f /etc/sysctl.conf ]; then
    notify 'Adjusting kernel parameters'
    sysctl -e -p
fi


# If you want to force canonicalization of files in /usr/etc or /usr/local/etc,
# uncomment the following "if".  This will delete everything in /etc that
# previously got copied from /usr/etc and /usr/local/etc, and thereby force
# them to be copied again.  This is the only way to ensure that package removal
# doesn't leave config file remnants, but may prove very confusing to users,
# who will most likely expect the /etc copies to be authoritative and
# persistent.
#if [ -s /etc/shadowfiles ]; then
#    (
#    cd /etc
#    sed -e "s/'/'\"'\"'/g" -e "s/^/rm -f '/" -e "s/\$/'/" shadowfiles | sh
#    )
#fi
# Since packages installed in /usr and /usr/local can't directly own files in
# /etc, we will non-destructively merge /usr/etc and /usr/local/etc into /etc.
# If the root filesystem is persistent or has a persistent layer, pre-existing
# copies in /etc will supercede the installation defaults in /usr[/etc].
# Otherwise, changes will be lost when the system is rebooted, and permanent
# changes can only occur by modifiying the /usr/[local/]etc master copies.
for PKG_ROOT in /usr /usr/local; do
    if [ -d $PKG_ROOT/etc ]; then
	(
	cd $PKG_ROOT/etc
	find . -type f -print
	find . -print | cpio -d -p /etc 2>/dev/null
	)
    fi
done > /etc/shadowfiles

# Initialise optional packages.
#
# Rather than executing rc.d files (which would have significant fork and exec
# overheads), we source them and run the functions and/or aliases they define.
# This is fast, but also fragile, because a syntax error in any rc.d file will
# kill this script.  On a persistent root filesystem, we have no way of knowing
# whether or not configure actions have already occurred, so the rc.d scripts
# must be "replay-safe" and should avoid unnecessary reconfiguration.
ls /etc/rc.d | while read SVC; do
    . /etc/rc.d/$SVC
    eval ${SVC}_configure
    logger -t 'rc' -p daemon.info "$SVC configured"
    eval ${SVC}_start
    logger -t 'rc' -p daemon.info "$SVC started"
done

# Display an active interface summary just before the login prompt appears, so
# that the IP address(es) in use remain visible on the console as long as
# possible.
ifconfig | awk 'BEGIN {i=""} $0 !~ /^[[:blank:]]/ && $0 != "" {i=$1}
	$2 ~ /^addr:/ {print i; print $0}'

[ "$HAVE_SPLASH" ] && echo 'show 65535' >>/proc/splash
