#!/bin/ash

# This file is part of PLD batch-installer
# Copyright (C) 2001 CYBER Service
# 
# 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.
#
# Author: Rafa Kleger-Rudomin
# Contributors: Micha Moskal

################# basic functions ####################

# mount wrapper
mount_source () {
    log info "`nls "Mounting %s on %s type %s" "${source_device}" "${source_mountpoint}" "${source_filesystem}"`"
    if cat /proc/mounts | grep -q " $source_mountpoint"; then umount $source_mountpoint; fi
    test -d ${source_mountpoint} || mkdir ${source_mountpoint}
    log_wrap_verb mount -t ${source_filesystem} ${source_device} ${source_mountpoint} $@
}

# for a given module name, finds what package owns it
find_package () {
    (
	cd /etc/lists.packages
	for i in *; do
	    if grep -q "^${1}\.o" $i; then
		echo "$i"
		break
	    fi
	done
    )
}

# for a given package e.g scsi-low-mod find addons disk
find_addons () {
    (
	cd /etc/lists.addons
	for i in *; do
	    if grep -q "^${1}" $i; then
		echo "$i"
		break
	    fi
	done
    )
}

# main function to load a module
load_module () {
    local package addons ans tmp
    local modules_dir="/lib/modules/`uname -r`"
    if lsmod |grep -q "^$1 "; then log debug "Module $1 already loaded"; return 0; fi
    log info "`nls "Loading module %s" "$1"`"
    test -d "$modules_dir" || die "No $modules_dir directory"

    if test ! -r "$modules_dir/${1}.o"; then
	# try to load from addons diskette
	package=`find_package ${1}`
	test "$package" || die "No package contains module $1"
	addons=`find_addons $package`
	test "$addons" || die "No addons disk contains package $package"
	first=yes
	# the source will be the diskette, temporarily 
	# (that _is_ mountable)
	tmp=$source_not_mountable
	source_not_mountable=""
	while : ; do
	    if grep -q "$source_mountpoint" /proc/mounts; then
		umount $source_mountpoint || :
	    fi
	    if mount /dev/fd0 $source_mountpoint; then
		if load_package $package ${1}.o; then 
		    break 
		else
		    nls "This disk does not contain package %s with module %s.\n" "$package" "$1"
		fi
	    else
		nls "Cannot mount diskette!\n"
	    fi
	    if test "$first" ; then
		nls "Please insert disk \"addons %d\" and press Enter\n" "$addons"
		first=
	    else
	    	nls "Try another disk and press Enter.\n"
	    fi
	    read ans
	done
	# restore correct value
	source_not_mountable=$tmp

	umount $source_mountpoint || :
    fi

    # There is some problem with log_wrap_verb insmod jfs. For unknown
    # reason it hangs, specifically tee command doesn't exit
    # although input pipe has been closed. Therefore we use
    # plain insmod. I guess this might be bug in either 
    # busybox, uclibc, ash or jfs patch.

    if [ "$1" = jfs ] ; then
    	insmod $1
    else
        log_wrap_verb insmod $@
    fi
}

# load package (-pkg.tar.gz) or package with modules (-mod.tar.gz)
# give the name of package, if no -pkg suffix, the -pkg is assumed
# if -mod and $2, $3... given, treat as module names and fetch this
# modules only
load_package () {
    local pkg root file tmp
    local files=""
    if echo $1 | grep -q '\-mod$'; then 
	pkg=$1
	root="/lib/modules/`uname -r`"
	shift
	if test "$*"; then 
	    # get specified files only
	    files="$*"
	fi
    elif echo $1 | grep -q '\-pkg$'; then 
	pkg=$1 
	root=/
    else
	pkg=${1}-pkg 
	root=/
    fi
    log info "`nls "Loading %s" "${pkg}"`"

    tmp=/tmp
    if test -d /dest/installer ; then
        test -d /dest/installer/tmp || mkdir /dest/installer/tmp
	tmp=/dest/installer/tmp
    fi
    
    if test -n "$source_not_mountable"; then
	file="$source_device/$source_dir/$inst_dir/${pkg}.tar.gz"
	log info "`nls "Fetching %s" "$source_device/$source_dir/$inst_dir/${pkg}.tar.gz"`"
	( 
	  cd $tmp 
	  case "$net_proxy" in
	    "" | none )
	      ;;
	    * )
	      export SNARF_PROXY="$net_proxy"
	      ;;
	  esac
	  snarf -n "$file" 
	) || die "Cannot get $file"
	file="$tmp/${pkg}.tar.gz"
    else
	file="${source_mountpoint}${source_dir}/$inst_dir/${pkg}.tar.gz"
	# TODO load packages from bootdisk
	test -f "$file" || { 
	  # try without source_dir (for addons disk)
	  file="${source_mountpoint}/$inst_dir/${pkg}.tar.gz"
	  test -f "$file" || { 
	    echo "No file ${source_mountpoint}${source_dir}/$inst_dir/${pkg}.tar.gz nor ${source_mountpoint}/$inst_dir/${pkg}.tar.gz"
	    return 1
	  }
	}
    fi

    ( 
    if cd $root; then 
	gunzip -c "$file" | tar x $files
	# this is "ugly hack" but what to do?
	if [ "X$files" != "X" ] ; then
	  chown 0.0 $files
	else
	  gunzip -c "$file" | tar t | xargs chown 0.0
	fi
    else 
	log warn "no $root dir, cannot unpack $pkg in this dir"
    fi 
    )

    # remove tempfile if fetched
    if test -n "$source_not_mountable"; then rm -f $file; fi
}

################# support functions ####################

load_cdrom_modules () {
    load_module cdrom
    if [ $KERNEL_VERCODE -ge 4 ] ; then
      load_module zlib_inflate
    fi
    load_module isofs
}

load_ide_modules () {
    load_module ide-mod
    load_module ide-probe-mod
}

load_scsi_modules () {
    load_module scsi_mod
}

load_nfs_modules () {
    load_module sunrpc
    load_module lockd
    load_module nfs
}

load_net_device_modules () {
    local mod mark
    local pre_load i
    
    # if we have it already (e.g. loaded by pcimcia cardmgr), do not load
    if ip link show "$net_device" >/dev/null 2>&1; then return; fi

    if test "x$net_device_module" = "xauto"; then 
	mark=""
	for i in `detect-net-devices -m || :`; do
	    if test "$mark"; then 
		log warn "More than one net device detected, choosen first one. Run detect_net_devices to see all"
	    fi
	    mark=1
	    mod="$i"
	done
	test "$mark" || die "No net devices detected, must specify module by hand"
    else
	mod="$net_device_module"
    fi
    pre_load=`grep -s "^$mod\.o|" /etc/eth.list.in | \
    	sed -e 's/[^|]*|[^|]*|\([^|]*\).*/\1/' | sed -e 's/\.o/ /g'`
    if [ $KERNEL_VERCODE -ge 4 ] ; then
      case "$mod" in
      pcnet32 | 8139too | epic100 | fealnx | via-rhine | winbond-840 )
        pre_load="$pre_load mii"
	;;
      ne | smc-ultra )
        pre_load="$pre_load isa-pnp"
	;;
      esac
    fi
    for i in $pre_load ; do
        load_module $i
    done
    load_module "$mod" "$net_device_module_options"
    set_module_alias $net_device $mod "$net_device_module_options"
}

compute_prefix () {
    case "$net_ipaddr" in
    192.168.*) net_prefix=24 ;;
    *) die "$net_prefix: do not know how to compute prefix for $net_ipaddr, sorry" ;;
    esac
}

guess_net_prefix () {
    case "$net_ipaddr" in
    192.168.*) net_prefix=24 ;;
    *) die "cannot compute prefix for this address, must specify" ;;
    esac
}

# net config
configure_network () {
    if test "${net_v6}" != "yes" -a "${net_v6}" != "no" ; then
      if echo "${net_ipaddr}" | grep : ; then
        net_v6=yes
      else
        net_v6=no
      fi
    fi
    
    # lo
    if test "${net_v6}" = "yes" ; then
      load_module ipv6
    fi
    
    if ip link show lo | grep -q "UP"; then 
      log debug "lo already up and configured"
    else
      log_wrap ip link set lo up
      log_wrap ip addr add 127.0.0.1/8 dev lo
      if test "${net_v6}" = "yes" ; then
        log_wrap ip addr add ::1 dev lo
      fi
    fi
    # source net device
    if ip link show "${net_device}" | grep -q "UP"; then
      log debug "${net_device} already up"
    else
      log_wrap ip link set "$net_device" up
    fi

    # remove any addresses assigned before (if any)
    ip addr show "${net_device}" | grep "^ *inet " \
    | while read inet addr rest; do
      log debug "deleting existing address $addr for ${net_device}"
      log_wrap ip addr del $addr dev ${net_device}
    done

    if test "${net_v6}" = "yes" ; then
      ip addr show "${net_device}" | grep "^ *inet6 " \
      | while read inet addr rest; do
        if echo $addr | grep -q "^fe80" ; then
	  continue
	fi
        log debug "deleting existing v6 address $addr for ${net_device}"
        log_wrap ip addr del $addr dev ${net_device}
      done
    fi

    if test "$net_ipaddr" = "dhcp"; then
      # af_packet is needed for dhcpcd
      load_module af_packet
      # kill old instance of dhcpcd
      kill `ps|grep dhcpcd|sed -e 's/  / /g' |cut -d' ' -f2` 2>/dev/null || :
      log info "waiting for dhcpcd to die (3 seconds)"
      sleep 3
      log info "running dhcpcd"
      dhcpcd "$net_device" || die "Problems with dhcp"
      log info "$net_device configured with dhcp"
    else
      test "x$net_prefix" != "xauto" || guess_net_prefix
      log info "Setting address $net_ipaddr/$net_prefix for ${net_device}"
      if test "$net_v6" = yes ; then
        log_wrap ip -family inet6 addr add "$net_ipaddr/$net_prefix" dev "$net_device"
      else
        log_wrap ip -family inet addr add "$net_ipaddr/$net_prefix" dev "$net_device"
      fi
      # gateway might be not needed in some cases
      if test "$net_gateway" ; then
	log info "Adding default route via $net_gateway dev ${net_device}"
        if test "$net_v6" = yes ; then
          log_wrap ip -family inet6 route add default via "$net_gateway" dev "$net_device" || :
        else
          log_wrap ip -family inet route add default via "$net_gateway" dev "$net_device"
	fi
      fi
      echo "domain localdomain" > /etc/resolv.conf
      for i in $net_dns; do echo "nameserver $i" >> /etc/resolv.conf; done
      if test "$net_v6" = yes ; then
        # belive me or not, but this seems to work... however only
	# at second round, but it can be vmware issue.
        log info "waiting for kernel to establish default route..."
	for i in 1 2 3 4 5 6 7 8 9 a b c d e f g ; do
	  echo -n .
	  sleep 1
	done
	echo "ok."
      fi
      log info "Net device $net_device configured"
    fi
}

autodetect_ide_cdrom () {
for i in a b c d; do 
    test -f /proc/ide/hd${i}/media || continue
    if grep -q cdrom /proc/ide/hd${i}/media; then
	source_device=/dev/hd${i}
	break
    fi
done
test "x$source_device" != "xauto" || die "cannot autodetect cdrom device, must specify one"
}

load_scsi_hostadapters () {
    local modules_dir="/lib/modules/`uname -r`"

    if test "x$scsi_hostadapters" = "xauto"; then
	adapters=`detect-scsi-devices -m || :`
	test "$adapters" || die "No SCSI adapters detected, must specify module(s) by hand"
    else
	adapters="$scsi_hostadapters"
    fi

    modules_o=""
    for i in $adapters; do
	if test -f $modules_dir/${i}.o; then
	    :
	else
	    # ok download modules from source (assume we install _on_ scsi and have source already)
	    # TODO: handle the case we need modules from addons (install _from_ scsi)
	    modules_o="$modules_o ${i}.o"
	fi
    done
    if test "$modules_o"; then 
      # Ignore error here. The most possible reason is that we are installing
      # from scsi cdrom/disk, and modules needs to be fetched from addon
      # disk (load_module handles it).
      load_package scsi-low-mod $modules_o || :
    fi

    for i in $adapters; do
	load_module $i
	## hmm, cannot use this
	#set_module_alias $net_device $net_device_module
	## must use this:
	test ! -f /etc/modules.conf || cp /etc/modules.conf /etc/modules.conf.tmp
	echo "alias scsi_hostadapter $i" >>/etc/modules.conf.tmp
	# alias for devfsd
	echo "alias scsi-hosts $i" >>/etc/modules.conf.tmp
	sort /etc/modules.conf.tmp | uniq >/etc/modules.conf
 	rm -f /etc/modules.conf.tmp
    done
}


# load modules needed for disks mentioned in $1
load_device_modules () {
  if echo "$1" | grep -q '/dev/sd' ; then
      load_package scsi-mod
      load_scsi_modules
      load_scsi_hostadapters
      load_module sd_mod
  fi

  if echo "$1" | grep -q '/dev/\(hd\|ataraid\)' ; then
    # if ide-disk is already loaded we havn't got anything to do.
    # note that this ain't that simple with SCSI -- scsi_hostadapter
    # might have been changed since last load etc.
    if lsmod | grep -q '^ide-disk ' ; then
      : skip
    else
      load_package ide-mod
      load_ide_modules
      load_module ide-disk
    fi
  fi

  if echo "$1" | grep -q '/dev/ida/' ; then
    . installer-raid-functions
    setup_cpqarray
  fi

  if echo "$1" | grep -q '/dev/cciss/' ; then
    . installer-raid-functions
    setup_cciss
  fi

  if echo "$1" | grep -q '/dev/rd/' ; then
    . installer-raid-functions
    setup_DAC960
  fi

  if echo "$1" | grep -q '/dev/ataraid/' ; then
    . installer-raid-functions
    setup_ataraid
  fi
  
  if echo "$1" | grep -q '/dev/i2o/' ; then
    . installer-raid-functions
    setup_i2o
  fi
}

##################################
############# main ###############
##################################

set -e
#set -x

source_mountpoint=/src
source_not_mountable=""
source_only=""
modules_dir="/lib/modules/`uname -r`"

PATH=$PATH:. . installer-functions


find_config_paths $@
load_config

inst_dir="$INSTALLER_DIR"

# needed before $0 testing...
if test "X$source" = "Xnet" ; then
  source_not_mountable=1
fi

# invoked as load_package runs function load_package
if test "x`basename $0 || :`" = "xload_package"; then 
    load_package "$@"; exit $?
elif test "x`basename $0 || :`" = "xload_module"; then 
    load_module "$@"; exit $?
elif test "x`basename $0 || :`" = "xinstaller-prep-source"; then
    # only source (and net) parts are prepared
    source_only=yes
fi
 
if test "x`basename $0`" = "xload_device_modules"; then load_device_modules "$@"; exit $? ; fi

# unneeded, save space
rm -rf /lost+found

######## load pcmcia modules if necessary #########

if test "$pcmcia_controller" -a -x /bin/probe; then
    if test "$pcmcia_controller" = "auto"; then
	pcmcia_controller=`probe -m`
	test "$pcmcia_controller" || log warn "`nls "PCMCIA controller not found"`"
    fi
    load_module pcmcia_core
    load_module "$pcmcia_controller"
    load_module ds
    if test -s /var/run/cardmgr.pid && kill -0 `cat /var/run/cardmgr.pid` 2>/dev/null; then
	: already running
    else
    	# I guess we don't need cardmgr running in background, after
	# it finished configuration of network card.
	cardmgr -o -v
    fi
fi

############## get to the source ##############
case "$source" in
    cdrom)
	load_cdrom_modules
	case "$source_device" in 
	/dev/hd* | auto) 
	    load_ide_modules
	    load_module ide-cd
            make_ide_cd_devfs_links
	    ;;
	/dev/sd* | /dev/scd* )
    	    load_scsi_modules
	    load_scsi_hostadapters
	    load_module sr_mod
	    make_scsi_cd_devfs_links
	    ;;
	esac
	if test "$source_device" = "auto"; then
	    autodetect_ide_cdrom
	fi
	mount_source -o ro
	find_source_dir
	;;
    disk)
	case "$source_device" in 
	/dev/hd*) 
	    load_ide_modules
	    load_module ide-disk
	    ;;
	/dev/sd*)
    	    load_scsi_modules
	    load_scsi_hostadapters
	    load_module sd_mod
	    ;;
	esac
	mount_source -o ro
	find_source_dir
	;;
    nfs)
	load_net_device_modules
	configure_network
	load_nfs_modules
	mount_source -o nolock,rw
	find_source_dir
	;;
    net)
	load_net_device_modules
	configure_network
	;;
    *)
	die "$source: bad source_filesystem"
	;;
esac


############## install packages needed in next stage ##############

load_device_modules "$dest_devices"

# remove modules, they are no longer needed.
rm -f /lib/modules/*/*

# finish if source_only requested
if test "x$source_only" = "xyes"; then exit; fi

# load it *after* removing modules
# basic package
if test -x /bin/parted ; then
  : skip
else
  load_package parted
fi

# load tools needed for particular filesystems
have_ext2=""
have_ext3=""
have_reiserfs=""
have_jfs=""
hava_xfs=""
have_md=""
id=1
while true
do
    eval "var=\$dest_part${id}_filesystem"
    test "$var" || break
    eval "var=\$dest_part${id}_filesystem"
    id=`expr $id + 1`
    case "$var" in
    ext2)
	if test -z "$have_ext2"; then 
	    load_package ext2
	    have_ext2=1
	fi
	;;
    ext3)
        if [ $KERNEL_VERCODE -lt 4 ] ; then
	    die "ext3 unsupported for 2.2.x kernels. sorry"
	fi
	if test -z "$have_ext3"; then 
	    load_package ext2
	    load_package ext3-mod
	    load_module jbd
	    load_module ext3
	    have_ext3=1
	fi
	;;
    xfs)
        if [ $KERNEL_VERCODE -lt 4 ] ; then
	    die "xfs unsupported for 2.2.x kernels. sorry"
	fi
	if test -z "$have_xfs"; then 
	    load_package xfs
	    load_package xfs-mod
	    load_module xfs_support
	    load_module xfs
	    have_xfs=1
	fi
	;;
    jfs)
	if test -z "$have_jfs"; then 
	    load_package jfs
	    load_package jfs-mod
	    load_module jfs
	    have_jfs=1
	fi
	;;
    reiserfs)
	if test -z "$have_reiserfs"; then 
	    load_package reiserfs
	    load_package reiserfs-mod
	    load_module reiserfs
	    have_reiserfs=1
	fi
	;;
    md)
    	if test -z "$have_md"; then
	    load_package md
	    load_package md-mod
	    # loading modules later...
	    have_md=1
	fi
	;;
    swap)
        # nothing to do...
	:
        ;;
    *)
	die "filesystem type '$var' not supported"
	;;
    esac
done


if test "$have_md" ; then
  . installer-raid-functions
  load_soft_raid_modules
fi


# we might have load some additonal modules
rm -fr /lib/modules/*/*

# vim:ft=sh
