#!/bin/sh

set -e

test -x /sbin/discover || exit 0

# file that determines the discover program's default behavior
CONFFILE=/etc/discover.conf

# list of modules for which to skip loading attempts
SKIPFILE=/etc/discover-autoskip.conf

# file that controls this init script's management of device symlinks
INITFILE=/etc/default/discover

# cache, used more than once
KVERS=$(uname -r)

case "$1" in
    start|restart) ;;
    stop|reload|force-reload) exit 0 ;;
    *) echo "Usage: $0 {start|restart}"; exit 1 ;;
esac

# Load init script parameters.
if [ -r $INITFILE ]; then
    . $INITFILE
fi

discover_uniq() {
  local result=""

  while read module; do
    if ! ( echo "$result" | grep -q "$module " ); then
      result="$result $module "
    fi
  done

  echo "$result"
}

if [ -f /var/lib/discover/crash ]
then
    # The system crashed trying to load a module during the last boot
    # cycle, so add an appropriate "skip" line to the skip file:
    echo "skip $(</var/lib/discover/crash)" >> $SKIPFILE
    rm -f /var/lib/discover/crash
    sync
fi

read_configuration()
{
    filename="$1"

    if [ ! -f "$1" ]; then return; fi
    
    while read action arg argb; do
        case "$action" in
            enable | disable) DISCOVER_ARGS="$DISCOVER_ARGS --$action=$arg" ;;
            skip) SKIPLIST="$SKIPLIST
$arg"
            ;;
            boot) TYPES="$TYPES $(echo $arg | sed 's/,/ /g')" ;;
            map)
                under=$(echo $arg | sed 's/-/_/g')
                case "$KVERS" in 
                    2.6.* | 2.5.*) argb=$(echo $argb | sed 's/-/_/g') ;;
                esac
                eval "map_$under=$argb"
            ;;
        esac
    done < "$filename"
}

read_configuration "$CONFFILE"

case "$KVERS" in
    2.6.* | 2.5.*) read_configuration "$CONFFILE-2.6" ;;
esac

read_configuration "$SKIPFILE"

SKIPFILE_DIR="/etc/discover.d"
if [ -d "$SKIPFILE_DIR" ]; then
    for part in $(run-parts --list "$SKIPFILE_DIR" 2>/dev/null || true); do
        read_configuration "$part"
    done

    case "$KVERS" in
        2.6.* | 2.5.*)
            if [ -d "$SKIPFILE_DIR/2.6" ]; then
                for part in $(run-parts --list "$SKIPFILE_DIR/2.6" 2>/dev/null || true); do
                    read_configuration "$part"
                done
            fi
        ;;
    esac
fi

# Detect hardware:
echo -n "Detecting hardware: " >&2
MODULES=$(discover $DISCOVER_ARGS --module $TYPES | discover_uniq)
# Get rid of ide-scsi for kernels that don't need it. This is a horrible hack,
# but since we're retiring this version anyway I don't care
case "$KVERS" in
    2.[0-4].*) true;;
    *) MODULES=$(echo $MODULES | sed -e 's/ide-scsi //' -e 's/-/_/g');;
esac

echo $MODULES

skip()
{
    echo "$SKIPLIST" | grep -wq -e "$1"
}
            
get_aliases_regexp() {
  searchmod="$(echo $1 | sed -e 's#\(-\|_\)#(-|_)#g')"
  search=""
  
  if [ -e "/etc/modprobe.conf" ]; then search="$search /etc/modprobe.conf"; fi
  if [ -e "/etc/modules.conf" ]; then search="$search /etc/modules.conf"; fi

  if [ -n "$search" ]; then
    grep -h '^alias' $search | ( while read dummy alias module; do
      if [ "$dummy" = alias ]; then
        if [ "$1" = "$alias" ]; then
          echo "|$module"
        elif [ "$1" = "$module" ]; then
          echo "|$alias"
        fi
      fi
    done ) |
    ( case "$KVERS" in 
        2.6.* | 2.5.*) sed 's/-/_/g' ;;
        *) cat ;;
      esac
    )
  fi
}

# Determine if the module is already loaded
is_loaded() {
    module="$1"
    aliases="$(get_aliases_regexp $1)"
    # No cut(1) without /usr
    sed 's/ .*$//' /proc/modules | grep -qE "^(${module}${aliases})\$"
}

# Load the appropriate modules:
for MODULE in $MODULES
do
    # See if we should skip $MODULE:
    if [ "$MODULE" = ignore ] || [ "$MODULE" = unknown ]
    then
        continue
    fi

    over=$(echo $MODULE | sed 's/-/_/g')
    MAPPED=$(eval "echo \$map_$over")
    if [ -n "$MAPPED" ]; then
        MODULE=$MAPPED
    fi
    
    if skip $MODULE
    then
        echo "$MODULE disabled in configuration."
        continue
    fi
    
    case "$MODULE" in
      Server:*)
        continue
      ;;
    esac

    if is_loaded "$MODULE" ; then
        echo "Skipping already loaded module $MODULE." >&2
        continue
    fi

    if ! (modprobe -n ${MODULE}) > /dev/null 2>&1
    then
        echo "Skipping unavailable/built-in $MODULE module." >&2
        continue
    fi

    echo "Loading $MODULE module." >&2

    # Note the module being loaded in /var/lib/discover/crash. If loading
    # the module crashes the machine, this file will exist at the next
    # boot, and we'll add an appropriate "skip" line to the conffile so we
    # don't try to load it again.
    echo $MODULE > /var/lib/discover/crash
    sync

    # '|| true' make sure we start up, even if one module fails to load.
    modprobe $MODULE || true

    # The module loaded without incident, so we can safely remove the crash
    # file.
    rm -f /var/lib/discover/crash
    sync
done

if [ "$MANAGE_CDROM_DEVICES" = true ]; then
    # Remove all old optical drive mount points:
    for CDMOUNT in ${CDROM_BASE_MOUNTPOINT}cdrom?
    do
        mountsent="$(grep " $CDMOUNT " /proc/mounts)" || true
        if [ -z "$mountsent" ]; then
          rm -rf $CDMOUNT
        fi
    done

    # When used with udev in fast machines, the time between the
    # module load and device creation need to be bigger or will fail
    # to make its directory.
    if [ "$(pidof udevd)" ]; then
       sleep 2s
    fi

    # Link /dev/cdromX to all detected CD/DVD drives, and create mount points:
    CDNUM=0
    # XXX sort(1) isn't available if /usr/ isn't mounted
    for CDROM in $(discover $DISCOVER_ARGS --device cdrom | sort)
    do
        ALTCDROM=$(echo $CDROM | sed 's#/dev/scd#/dev/sr#')
        if [ ! -e $CDROM ] && [ ! -e $ALTCDROM ] 
        then
            echo -n "discover reports that $CDROM is a CD/DVD device, but it and $ALTCDROM " >&2
            echo "do not exist.  Not updating /dev/cdrom$CDNUM." >&2
        elif [ ! -b $CDROM ] && [ ! -e $ALTCDROM ]
        then
            
            echo -n "discover reports that $CDROM is a CD/DVD device, but it and $ALTCDROM " >&2
            echo "are not block devices.  Not updating /dev/cdrom$CDNUM." >&2
        elif [ -e /dev/cdrom$CDNUM ] && [ ! -L /dev/cdrom$CDNUM ]
        then
            echo -n "/dev/cdrom$CDNUM exists and is not a symlink.  Not updating " >&2
            echo "/dev/cdrom$CDNUM." >&2
            CDNUM=$(($CDNUM + 1))
        else
            ln -fs $CDROM /dev/cdrom$CDNUM
            if ! grep -q " ${CDROM_BASE_MOUNTPOINT}cdrom${CDNUM} " /proc/mounts
            then
                mkdir -p "${CDROM_BASE_MOUNTPOINT}cdrom${CDNUM}"
            else
                echo "Cannot create directory at ${CDROM_BASE_MOUNTPOINT}cdrom${CDNUM} because it exists."
            fi
            CDNUM=$(($CDNUM + 1))
        fi
    done

    # Link /dev/cdrom to the appropriate device:
    if [ -e /dev/cdrom0 ]
    then
        if [ -L /dev/cdrom ] || [ ! -e /dev/cdrom ]
        then
            ln -fs /dev/cdrom0 /dev/cdrom
            # link the mountpoint only if nothing is mounted there
            if ! grep -q " ${CDROM_BASE_MOUNTPOINT}cdrom " /proc/mounts
            then
                ln -fsn "${CDROM_BASE_MOUNTPOINT}cdrom0" "${CDROM_BASE_MOUNTPOINT}cdrom"
            else
                echo "Cannot create link from "$CDROM_BASE_MOUNTPOINT"cdrom to "$CDROM_BASE_MOUNTPOINT"cdrom0," >&2
                echo "because a filesystem is already mounted at "$CDROM_BASE_MOUNTPOINT"cdrom." >&2
            fi
        else
            echo "/dev/cdrom exists and is not a symlink.  Not updating /dev/cdrom." >&2
        fi
    else
        echo "No CD/DVD drives found." >&2
    fi
fi

# vim:ai:et:sts=4:sw=4:tw=0:
