#!/bin/bash
#
# /sbin/hwup
#
# Configuring hardware (Preliminary version)
# $Id: hwup 1193 2005-04-22 15:11:01Z zoz $
#

usage () {
	echo $@
	echo "Usage: hw{up,down,status} [<config>] <hwdesc> [-o <options>]"
#	echo "       (see man hwup for details)"
	echo
	echo "Options are:"
	echo "    auto     : we were called from an automated process (e.g. hotplug)"
	exit $R_USAGE
}

# Debugging
# . ./functions

R_INTERNAL=1      # internal error, e.g. no config or missing scripts
cd /etc/sysconfig/hardware || exit $R_INTERNAL
test -f scripts/functions && . scripts/functions || exit $R_INTERNAL
test -f ./config && . ./config

######################################################################
# Commandline parsing
#
# hw{up,down,status} [<config>] <hwdesc> [-o <options>]
SCRIPTNAME=${0##*/}
debug $*
HWDESC=$1
case "$HWDESC" in ""|-h|*help*) usage; esac
shift
if [ -n "$1" -a "$1" != "-o" ] ; then
	CONFIG=$HWDESC
	HWDESC=$1
fi
shift
test "$1" = "-o" && shift
OPTIONS=$@
MODE=manual
while [ $# -gt 0 ]; do
	case $1 in
		auto)    MODE=auto ;;
		hotplug) MODE=auto
		         HOTPLUG=yes ;;
		fast)    FAST=yes;;
		*)       debug "unknown option $1 ignored" ;;
	esac
	shift
done


######################################################################
# Get a configuration name and additional information
#
if [ "$FAST" = yes ] ; then
	set -- $(IFS="-"; echo $HWDESC)
	if [ "$2" = "devpath" ]; then
		HWD_BUS_N=1;
		HWD_BUSNAME_0=$1
		HWD_BUSID_0=${3##*/}
		HWD_DEVPATH=$3
		HWD_DEVICEPATH=$3
		for cfg in hwcfg-*bus-${HWD_BUSNAME_0}-${HWD_BUSID_0}; do
		    if test -f $cfg; then
			HWD_CONFIG_0=${cfg##hwcfg-}
			HWD_CONFIG_N=1
		    fi
		done 
	fi
else	
	# Maybe we already got an configuration name at the command line, but call
	# getcfg in any case, because it provides more information.
	eval `/sbin/getcfg -d . -f hwcfg- -- $HWDESC 2>/dev/null`
fi
if [ -z "$CONFIG" -a -n "$HWD_CONFIG_0" ] ; then
	CONFIG=$HWD_CONFIG_0
fi
# Normally we are interested only in the last bus. (It is not of interest that
# below a scsi bus is a pci bus. Otherwise use HWD_BUSNAME_<n>, HWD_BUSID_<n>.)
eval export HWD_BUSNAME=\$HWD_BUSNAME_$((HWD_BUS_N-1))
eval export HWD_BUSID=\$HWD_BUSID_$((HWD_BUS_N-1))

if [ -z "$CONFIG" ] ; then
	case ${HWDESC%%-*} in
		static|boot) CONFIG=$HWDESC;;
	esac
fi

debug "HWDESC = $HWDESC      CONFIG = $CONFIG"

######################################################################
# Now source the configuration file
#
# First remove all variables starting with MODULE because there are variables
# starting with MODULE and unknown suffix in the config file. Otherwise
# environment variables would irritate the code that looks for all MODULE*
# variables.
unset ${!MODULE*}
if [ -n "$CONFIG" -a -r ./hwcfg-$CONFIG ] ; then
	. ./hwcfg-$CONFIG
fi

######################################################################
# What shell we do if there is no configuration data?
# - fail
# - get it automatically
# - ask the user
if [ "$SCRIPTNAME" = hwup -a \
     \( -z "$CONFIG" -o ! -r hwcfg-$CONFIG -o -n "$NODATA" \) ] ; then
	case $FAILURE_ACTION in
		off)
			test "$HOTPLUG" != yes && \
				logerror "No configuration found for $HWDESC"
			exit $R_NOCONFIG
			;;
		auto-off|auto-manual|manual)
			# to be implemented, see doc/Specification
			logerror "No configuration found for $HWDESC and autoconfig still not
implemented"
			exit $R_NOCONFIG
			;;
		*)
			logerror "No configuration found for $HWDESC"
			exit $R_NOCONFIG
			;;
	esac
fi


######################################################################
# Check if we are supposed to initialize the device
#
# empty $STARTMODE means 'auto'
case "$STARTMODE" in
	off)
		message "$SCRIPTNAME: used configuration has STARTMODE=$STARTMODE."
		exit 0;
		;;
	manual)
		if [ "$MODE" != manual ] ; then
			message "$SCRIPTNAME: used configuration has STARTMODE=$STARTMODE," \
			     "but we are called '$MODE'."
			exit 0;
		fi
		;;
esac

######################################################################
# execute individual prestart or predown scripts if available
#
if [ "$SCRIPTNAME" = hwup ] ; then
# NOTE: 'eval echo' in the next line is necessary to expand settings
# like PRE_UP_SCRIPT="~root/bin/foo"
	for PUS in `eval echo $PRE_UP_SCRIPT scripts/$PRE_UP_SCRIPT`; do
		if [ -x "$PUS" -a ! -d "$PUS" ] ; then
			debug "executing additional start script $PUS"
			$PUS $CONFIG $HWDESC ${OPTIONS:+-o $OPTIONS}
		fi
	done
fi
if [ "$SCRIPTNAME" = hwdown ] ; then
# NOTE: 'eval echo' in the next line is necessary to expand settings
# like PRE_DOWN_SCRIPT="~root/bin/foo"
	for PDS in `eval echo $PRE_DOWN_SCRIPT scripts/$PRE_DOWN_SCRIPT`; do
		if [ -x "$PDS" -a ! -d "$PDS" ] ; then
			debug "executing additional stop script $PDS"
			$PDS $CONFIG $HWDESC ${OPTIONS:+-o $OPTIONS}
		fi
	done
fi

######################################################################
# Call a specialized down script, depending on the event
#
# have a look at 'Call a specialized down script' above
if [ "$SCRIPTNAME" = hwdown ]; then
    # Call an event-specific script if one exists
    if [ "$HWD_DEVTYPE" ]; then
	SCRIPT_DOWN=`eval echo \\$SCRIPTDOWN_$HWD_DEVTYPE`
    fi
    # Call a generic script if no event-specific exists
    if [ -z "$SCRIPT_DOWN" ]; then
	SCRIPT_DOWN=`eval echo \\$SCRIPTDOWN`
    fi
    if test -n "$SCRIPT_DOWN" && test -x "./scripts/$SCRIPT_DOWN" ; then
	debug "Calling scripts/$SCRIPT_DOWN $CONFIG"
	./scripts/$SCRIPT_DOWN $CONFIG $HWDESC ${OPTIONS:+-o $OPTIONS}
    fi
fi

######################################################################
# Load all modules
#
declare -i M=0 N=0
for MODVAR in ${!MODULE*}; do
	: MODVAR=$MODVAR
	# There is a silly design bug: $MODULE_OPTIONS and $MODULE_UNLOAD
	# must not be interpreted as additional MODULE*
	case "$MODVAR" in
		MODULE_OPTIONS*|MODULE_UNLOAD*) continue ;;
	esac
	INDEX=${MODVAR#MODULE}

	eval ML[$M]=\$MODULE$INDEX
	eval test -z "\${ML[$M]}" && continue
	eval MO[$M]=\$MODULE_OPTIONS$INDEX
	eval MU[$M]=\${MODULE_UNLOAD$INDEX:-$MODULE_UNLOAD}
	: $((M++))
done

if [ "$SCRIPTNAME" = hwup ] ; then
	while [ $N -lt $M ] ; do
		message "hwup: Loading module '${ML[$N]}'" \
		        "${MO[$N]:+with options '${MO[$N]}' }" \
  		        "for device '$HWDESC'"
		/sbin/modprobe ${ML[$N]} ${MO[$N]}
		: $((N++))
	done
fi

if [ "$SCRIPTNAME" = hwdown -a "$HOTPLUG" != yes ] ; then

	# For many devices we can find the currently used driver name in sysfs.
	# 'getcfg' writes this name to $HWD_DRIVER. But sometimes the driver name
	# differs from the module name and we need the latter. Therefore we test
	# the name in $HWD_DRIVER. If it is not valid we use the module name from
	# the configuration file as fallback.
	# Since we need the sysfs driver path to look for a module link anyway, we
	# don't use $HWD_DRIVER currently.
	# FIXME: getcfg should provide $HWD_MODULE
	SYSFS_DRIVER_PATH=`ls -d /sys/bus/$HWD_BUSNAME/drivers/*/$HWD_BUSID 2>/dev/null`
	SYSFS_DRIVER_PATH=${SYSFS_DRIVER_PATH%/$HWD_BUSID}
	SYSFS_MODULE_PATH=$(cd $SYSFS_DRIVER_PATH 2>/dev/null &&
	                    cd module 2>/dev/null; pwd -P)
	SYSFS_MODULE=${SYSFS_MODULE_PATH##*/}
	if grep -qs "^\<$SYSFS_MODULE\>" /proc/modules; then
		echo SYSFS_MODULE=$SYSFS_MODULE
		while [ $N -lt $M ] ; do
			test "${ML[$N]}" = "$SYSFS_MODULE" && break
			: $((N++))
		done
		if [ $N -eq $M ] ; then
			ML[0]=$SYSFS_MODULE
			MU[0]=yes
			M=1
		fi
		N=0
	fi
	while [ $N -lt $M ] ; do
		if [ "${MU[$N]}" = no ] ; then
			message "hwdown: Module '${ML[$N]}' should not be unloaded" \
			        "for device '$HWDESC'"
			: $((N++))
			continue
		fi
		if /sbin/modprobe -r ${ML[$N]} ; then
			message "hwdown: Unloading module '${ML[$N]}'" \
			        "for device '$HWDESC'"
		else
			logerror "Could not unload module '${ML[$N]}'" \
			         "for device '$HWDESC'"
		fi
		: $((N++))
	done
fi

if [ "$SCRIPTNAME" = hwstatus ] ; then
	SYSFS_DRIVER_PATH=`ls -d /sys/bus/$HWD_BUSNAME/drivers/*/$HWD_BUSID 2>/dev/null`
	SYSFS_DRIVER_PATH=${SYSFS_DRIVER_PATH%/$HWD_BUSID}
	SYSFS_MODULE_PATH=$(cd $SYSFS_DRIVER_PATH 2>/dev/null &&
	                    cd module 2>/dev/null; pwd -P)
	SYSFS_MODULE=${SYSFS_MODULE_PATH##*/}
	echo DRIVER=${SYSFS_DRIVER_PATH##*/}
	if lsmod | grep -qs "\<$SYSFS_MODULE\>"; then
		echo MODULE=${SYSFS_MODULE}
	fi
	: # to be implemented
fi


######################################################################
# Call a specialized up script, depending on the event
#
# have a look at 'Call a specialized down script' above
if [ "$SCRIPTNAME" = hwup ]; then
    # Call an event-specific script if one exists
    if [ "$HWD_DEVTYPE" ]; then
	SCRIPT_UP=`eval echo \\$SCRIPTUP_$HWD_DEVTYPE`
    fi
    # Call a generic script if no event-specific exists
    if [ -z "$SCRIPT_UP" ]; then
	SCRIPT_UP=`eval echo \\$SCRIPTUP`
    fi
    if test -n "$SCRIPT_UP" && test -x "./scripts/$SCRIPT_UP" ; then
	debug "Calling scripts/$SCRIPT_UP $CONFIG"
	./scripts/$SCRIPT_UP $CONFIG $HWDESC ${OPTIONS:+-o $OPTIONS}
    fi
fi

######################################################################
# execute individual poststart or postdown scripts if available
#
if [ "$SCRIPTNAME" = hwup ] ; then
# NOTE: 'eval echo' in the next line is necessary to expand settings
# like POST_UP_SCRIPT="~root/bin/foo"
	for PUS in `eval echo $POST_UP_SCRIPT scripts/$POST_UP_SCRIPT`; do
		if [ -x "$PUS" -a ! -d "$PUS" ] ; then
			debug "executing additional start script $PUS"
			$PUS $CONFIG $HWDESC ${OPTIONS:+-o $OPTIONS}
		fi
	done
fi
if [ "$SCRIPTNAME" = hwdown ] ; then
# NOTE: 'eval echo' in the next line is necessary to expand settings
# like POST_DOWN_SCRIPT="~root/bin/foo"
	for PDS in `eval echo $POST_DOWN_SCRIPT scripts/$POST_DOWN_SCRIPT`; do
		if [ -x "$PDS" -a ! -d "$PDS" ] ; then
			debug "executing additional stop script $PDS"
			$PDS $CONFIG $HWDESC ${OPTIONS:+-o $OPTIONS}
		fi
	done
fi

