# -*- mode: shell-script;-*-
# vim:syntax=sh
# $Id: hotplug.functions 351 2005-03-17 19:44:59Z zoz $
#
# Setup and BASH utility functions for use in hotplug agents
#
# Most essential parameters are passed from the kernel using
# environment variables.  For more information, see the docs
# on-line at http://linux-hotplug.sourceforge.net or the
# sources for each hotplug-aware kernel subsystem.
#

CL_ARGS=$*

if [ -r /etc/sysconfig/hotplug ] ; then
	. /etc/sysconfig/hotplug
fi

export PATH=/sbin:/usr/sbin:/bin:/usr/bin
KERNEL=`uname -r`
MODULE_DIR=/lib/modules/$KERNEL
HOTPLUG_DIR=/etc/hotplug
HOTPLUG_BIN=/sbin/udevsend
test -z "$HOTPLUG_LOGFILE" && HOTPLUG_LOGFILE=/var/log/hotplug.log

# mount point for sysfs
if [ -f /proc/mounts ]; then
    while read MNTPT MNTDIR MNTSYS MNTTYPE; do
        if test "$MNTSYS" = "sysfs"; then
            SYSFS="$MNTDIR"
            break;
        fi 
    done </proc/mounts
fi
if [ "$SYSFS" = needed ] ; then
    debug_mesg "Sysfs not mounted, cannot execute"
    exit 1
fi

#
# for diagnostics
#
STAMP=""
mesg () {
    echo "`eval $STAMP`$@" >&2
}

debug_mesg () {
    case "$HOTPLUG_DEBUG" in
        yes|verbose|max) mesg "$@" ;;
        *) ;;
    esac
}

syslog_auto() {
  case "$SYSLOG_AUTO" in
    syslog|file)
      :
      ;;
    *)
      if rcsyslog status >/dev/null 2>&1 ; then
        SYSLOG_AUTO=syslog
      else
        SYSLOG_AUTO=file
      fi
      export SYSLOG_AUTO
      ;;
  esac
  echo $SYSLOG_AUTO
}

to_syslog() {
  if [ "$WERE_ALREADY_HERE" != yes ] ; then
    export WERE_ALREADY_HERE=yes
    which logger >/dev/null 2>&1 || to_mylog $HOTPLUG_LOGFILE
    case $1 in
      all) $0 $CL_ARGS 2>&1 | logger -t $0[${LOGPID:-$$}];;
      err) $0 $CL_ARGS 2>&1 >/dev/null | logger -t $0[${LOGPID:-$$}];;
    esac
    exit
  fi
}

to_mylog() {
  local FILE=$2
  if [ ! -w "$FILE" ] ; then
    FILE=/dev/console
    test -w "$FILE" || return
  fi
  STAMP='date +"%b %d %T %N $0[${LOGPID:-$$}] "'
  case $1 in
    all) exec 1>> $2
         exec 2>> $2;;
    err) exec 2>> $2
         exec 1> /dev/null;;
  esac
}

to_log() {
  test "$HOTPLUG_SYSLOG" = auto && HOTPLUG_SYSLOG=`syslog_auto`
  case "$HOTPLUG_SYSLOG" in
    file)
      to_mylog $1 $HOTPLUG_LOGFILE
      ;;
    console)
      to_mylog $1 /dev/console
      ;;
    syslog|*)
      to_syslog $1
      ;;
  esac
}

# copy stdout/err, because rccoldplug wants to get output there
if [ "$WERE_ALREADY_HERE" != yes ] ; then
  test -t 1 && exec 3>&1 || exec 3>/dev/null
  test -t 2 && exec 4>&2 || exec 4>/dev/null
fi
# now redirect stdout/err to syslog in some cases
case "$HOTPLUG_DEBUG" in
  off) ;;
  default|""|no)
    to_log err
    ;;
  verbose|yes)
    to_log all
    ;;
  max) 
    to_log all
    set -x
    : \$0=$0
    ;;
esac

#
# Not "modprobe --autoclean" ... one driver module can handle many
# devices.  Unloading should be done when no devices are present.
# Autocleaning happens if none of the devices are open, once any of
# them gets opened; wrong timing.
#
MODPROBE="/sbin/modprobe -s"
#MODPROBE="/sbin/modprobe -vs"


####################################################################
#
# usage: load_driver type filename description
#
# modprobes driver module(s) if appropriate, and optionally
# invokes a driver-specific setup script (or user-mode driver).
#
# the "modules.*map" format file is guaranteed to exist
#
load_drivers ()
{
    local LOADED LOADED_MODULES AGENT FILENAME DESCRIPTION FAST_MAP_MODULES
    DRIVERS=""

    # make this routine more readable
    AGENT=$1
    FILENAME=$2
    DESCRIPTION=$3

    # try parsing by binary or shell scripts
    case $FILENAME in
        */modules.${AGENT}map) FAST_MAP_MODULES=yes ;;
    esac
    if [ "$FAST_MAP_MODULES" = yes -a -x /sbin/module_$AGENT ] ; then
        DRIVERS=`/sbin/module_$AGENT $AGENT`
    elif [ "$FILENAME" = hwinfo ] ; then
        ${AGENT}_map_modules < /dev/null
    else
        ${AGENT}_map_modules < $FILENAME
    fi

    # FIXME remove dups and blacklisted modules from $DRIVERS here

    if [ "$DRIVERS" = "" ]; then
        debug_mesg "No drivers for '$DESCRIPTION' (from $FILENAME)"
	return
    fi

    # Note that DRIVERS aren't all going to be modules.
    # For USB, some user-mode drivers or setup scripts may be listed.
    debug_mesg "Setup '$DRIVERS' for '$DESCRIPTION' (from $FILENAME)"

    # either kernel or user mode drivers may need to be set up
    LOADED=false
    LOADED_MODULES=
    for MODULE in $DRIVERS
    do
	# kernel 2.6 has a new innovative module interface
	# all - will be replaced with _ by the kernel
	MODULE_MATCH="`echo $MODULE | sed -e 's@-@[_-]@g'`"
	# maybe driver modules need loading
	if ! lsmod | grep -q "^$MODULE_MATCH " > /dev/null 2>&1; then
	    if grep -q "^$MODULE\$" $HOTPLUG_DIR/blacklist \
		    >/dev/null 2>&1; then
		debug_mesg "... blacklisted module:  $MODULE"
		continue
	    fi

	    if [ "$LOADED" != false \
	         -a "$HOTPLUG_LOAD_MULTIPLE_MODULES" != yes ] ; then
		debug_mesg "Loading of $MODULE skipped, because we have" \
		           "module '$LOADED' already loaded"
		continue
	    fi

	    if [ -n "$HOTPLUG_TRACE" ] ; then
		sync
		echo >/dev/console
		echo "$AGENT: loading '$MODULE' for $DESCRIPTION" > /dev/console
		SLEEP="`(test $x -gt 0) 2>/dev/null && echo $x`"
		sleep ${SLEEP:-2}
	    fi

	    # statically linked modules aren't shown by 'lsmod',
	    # and user mode drivers will ONLY have a setup script;
	    # it's not an error if a module doesn't exist or won't load.
	    if $MODPROBE -n $MODULE >/dev/null 2>&1 &&
		    ! $MODPROBE $MODULE >/dev/null 2>&1 ; then
		mesg "... can't load module $MODULE"
	    else
		# /etc/modules.conf may have set non-default module
		# parameters ... handle per-device parameters in apps
		# (ioctls etc) not in setup scripts or modules.conf
		LOADED=$MODULE
                LOADED_MODULES="$LOADED_MODULES $MODULE"
	    fi
	else
	    # This module is already loaded
	    LOADED=$MODULE
            LOADED_MODULES="$LOADED_MODULES $MODULE"
	fi

	# always run setup scripts after any matching kernel code has had
	# a chance to do its thing, no matter whether it was dynamically
	# or statically linked, or if there is only a user mode driver.
	# the script might re-enumerate usb devices after firmware download,
	# giving kernel code another chance.
	if [ -x $HOTPLUG_DIR/$AGENT/$MODULE ]; then
	    debug_mesg "Run '$AGENT/$MODULE' for '$DESCRIPTION' (from $FILENAME)"
	    $HOTPLUG_DIR/$AGENT/$MODULE
	    LOADED=$MODULE
	fi

	if [ $LOADED = false ]; then
	    mesg "missing kernel or user mode driver $MODULE "
	fi
	if echo "$MODULE" | grep -q "usb-storage" > /dev/null 2>&1 ; then
	    [ -x /usr/sbin/updfstab ] &&  /usr/sbin/updfstab
	fi
    done
    DRIVERS="$LOADED_MODULES"
}

call_udev () {
    if [ -z "$1" ] ; then
        debug_mesg "call_udev: give an event type when calling udev"
        return 1
    fi
    if [ -x /sbin/udevsend ] ; then
	# We need to close all fd's.
	# Better to do this within udevsend ...
        /sbin/udevsend $1 <&- 1>&- 2>&- 3>&- 4>&-
    else
        debug_mesg "call_udev: there is no executable /sbin/udevsend"
    fi
}

####################################################################
#
# usage: create_hwcfg_file name module
#
# creates a hwcfg file with name 'hwcfg-<name>'.
# If given the module <module> will be loaded.
#
create_hwcfg_file () {
    local module suffix mod
    declare -i suffix=0

    if [ -z "$1" ]; then
	debug_mesg "create_hwcfg_dummy: need a configuration name"
	return 1
    fi

    if [ -n "$2" ]; then
        module="$2"
    fi

    if [ ! -d /etc/sysconfig/hardware ]; then
	debug_mesg "No hwcfg directory present"
	return 0
    fi

    hwcfg_file=/etc/sysconfig/hardware/hwcfg-$1

    cat <<- EOF > $hwcfg_file
	# This file is automatically created from hotplug agent and may be
	# changed when certain packages (hotplug, kernel, etc.) are updated.
	# If you change things manually here and want this configuration
	# to persist updates, then remove the following line.
	# HOTPLUG-FLAG: autocreated
	#
	EOF
    echo "STARTMODE='auto'" >> $hwcfg_file
    for mod in $module; do
        echo "MODULE_$suffix='$mod'"
        echo "MODULE_OPTIONS_$suffix=''"
        : $((suffix++))
    done >> $hwcfg_file
    if [ "$suffix" -eq 0 ] ; then
        echo -e "MODULE=''\nMODULE_OPTIONS=''" >> $hwcfg_file
    fi
}
