# Copyright (C) 2003 Juergen "George" Sawinski
#                    Rene Rebe
# All rights reserved.
# Partially copied from/rewritten based on hotplug.
# Partially copied from/rewritten based on ROCK Linux build scripts.
# See README file for further explanation and
# LICENCING TERMS (GPL v2 or later).

# DEFAULTS
PATH=/bin:/sbin:/usr/sbin:/usr/bin

SYSCONF=/etc/conf

RPCONFIG=/etc/rockplug/config
RPSCRIPTS=/etc/rockplug/scripts
RPSUBSYS=/var/rockplug
RPLOG=/var/log/rockplug.log

KERNEL=`uname -r`
RPCACHE="$RPSUBSYS/$KERNEL-cache"

MODULES_DIR=/lib/modules/$KERNEL
ARCH=`uname -m | sed -e s/i.86/i386/`
case $ARCH in # TODO: Add other arches that are little-endian (e.g. mipsel)
	i386)	BIGENDIAN=0 ;;
	*)	BIGENDIAN=1 ;;
esac

MODPROBE="modprobe -s"
MODAUTOCLEAN="rmmod -as"
MODCLEAN="rmmod"

SCRIPTS=""
MODULES=""
SKIP_MODULES=""

DEBUG=yes ; export DEBUG
LOGGER=/usr/bin/logger

# VARIABLE MANIPULATION
var_append() {
  eval "[ \"\$$1\" ] && $1=\"\${$1}$2\"" || true
  eval "$1=\"\${$1}\$3\""
}

var_append_list() {
  local var="$1" ; shift
  local sep="$1" ; shift
  local each; for each in $* ""; do
    var_append $var "$sep" $each
  done
}

var_prepend() {
  eval "[ \"\$$1\" ] && $1=\"$2\${$1}\"" || true
  eval "$1=\"\$3\${$1}\""
}

var_prepend_list() {
  local var="$1" ; shift
  local sep="$1" ; shift
  local each; for each in $* ""; do
    var_prepend $var "$sep" $each
  done
}

var_remove() {
  local a=${2//\/\\/}
  local b=${3//\/\\/}
  eval '[ "$'$1'" = "$3" ] && '$1'="" || true'
  eval $1'="${'$1'//$a$b$a/$2}"'
  eval $1'="${'$1'%$a$b}"'
  eval $1'="${'$1'#$b$a}"'
}

var_contains() {
  local tmp
  eval "tmp=\"\$$1\"" ; var_remove tmp "$2" "$3"
  eval "[ \"$tmp\" != \"\$$1\" ]"
}

var_trim() {
  eval "$1=\${$1#$2}"
  eval "$1=\${$1%$2}"
}

#keyp prompt sequence - read key-press
keyp() {
  local seq="`echo -e "$2"`"
  echo "$1"
  stty -echo; read -n ${#seq} -t ${3:-1} kbd; stty echo
  [ "$kbd" = "$seq" ] && return 0;
  return 1
}

#read_config file1 [file2 [...]] - read a file, skipping
#    empty and comment lines
TAB="`echo -ne "\t"`"
read_config() {
  local each ; for each in $*; do
    [ -f "$each" ] && egrep -v "^[ $TAB]*([#].*)?$" $each
  done
}

# init SKIP_MODULES list
var_append_list SKIP_MODULES ' ' \
  `read_config /etc/rockplug/conf/blacklist`

# OUTPUT/LOGGING
# log message - log message to system log
log () {
  $LOGGER -t $(basename $0)"[$$]" "$@"
  [ x$DEBUG = xyes ] && echo "$(basename $0)[$$] $@" 1>&2
}

if [ -t -o -z "$LOGGER" -o x$SYSINIT = xyes ]; then
  mesg () {
    echo "$@"
    log "$@"
  }
  mesg_n () {
    echo -n "$@"
    log "$@..."
  }
else
  mesg () {
    log "$@"
  }
  mesg_n () {
    log "$@..."
  }
fi

# pipe stderr to logfile
exec 2>> $RPLOG

# FILE LOCKING
#lock_clean sub-system - remove stale lockfiles
lock_clean() {
  [ -n "$1" ] && rm -f $RPSUBSYS/$1/*.lck > /dev/null 2>&1
}

#lock file 
lock() {
  if [ -f "$1" ]; then
    return 1
  fi
  touch -f $1.lck
  return 0
}

#unlock file
unlock() {
  [ -f "$1" ] && rm -f $1.lck
}

# MODULE UN-/LOADING
#module_load - load modules given in $MODULES
#module_load module1 [module2 [...]] - load given modules
module_load() {
  local LIST="$*" ; [ -z "$LIST" ] && LIST="$MODULES"
  local module
  for module in $SKIP_MODULES; do
    var_remove LIST ' ' $module
  done
  for module in $LIST; do
    [ -n "`module_count $module`" ] && continue;
    egrep "/${module}.(o|ko):" $MODULES_DIR/modules.dep 1>/dev/null || continue
    status=ok ; mesg_n Loading $module" "
    if chkpoint_in $RPSUBSYS/oops $module.probe; then
      if [ -f $RPSUBSYS/ask ]; then
	if keyp "Load $module (no: N, yes: any key)?" 'N' 0; then
	  $MODPROBE $module 1>&2 || status=failed
	else 
	  status='(no)'
	fi
      else
	$MODPROBE $module 1>&2 || status=failed
      fi
    else
      status="not probed due to potential OOPS on last unload"
    fi
    mesg $status
    chkpoint_out $RPSUBSYS/oops $module.probe
    script=/etc/rockplug/scripts/$module
    [ "$status" = "ok" ] && [ -x $script ] && ACTION=add $script
  done
}

#module_unload - unloads modules given in $MODULES
#module_unload auto - unloads auto-cleanable modules (rmmod -as)
#module_unload all - @FIXME implement
#module_unload module1 [module2 [...]]
module_unload() {
  if [ x$1 = xauto ]; then
    rmmod -as
    return
  fi
  local module
  local count
  local pass=0
  local LIST="$*" ; [ -z "$LIST" ] && LIST="$MODULES"
  local NLIST
  for module in $SKIP_MODULES; do
    var_remove LIST ' ' $module
  done
  #module_expand_deps LIST
  set $LIST ''
  LIST="$*"
  NLIST="$#"
  while [ $NLIST -gt 0 -a "$LIST" != "" ]; do
    for module in $LIST; do
      count="`module_count $module`"
      if [ -z "$count" ]; then
	var_remove LIST ' ' $module
	NLIST=$(( $NLIST - 1 ))
	continue
      fi
      [ $count -gt 0 ] && continue;
      status=ok ; mesg_n Unloading $module" (pass $pass)... "
      if chkpoint_in $RPSUBSYS/oops $module.unload; then
	$MODCLEAN $module || status=failed;
      else
	status="not removed due to potential OOPS on last unload"
      fi
      mesg $status
      chkpoint_out $RPSUBSYS/oops $module.unload
      [ "$status" =  "ok" ] && var_remove LIST ' ' $module
    done
    NLIST=$(( $NLIST - 1 ));
    pass=$(( $pass + 1 ));
  done
  for module in $LIST; do
    mesg Failed unloading $module .
  done
}

#module_count module - return reference count of a module
module_count() {
  grep -E "^$1 " /proc/modules | tr -s " " | cut -f3 -d' '
}

#module_expand_deps var - expand related modules
#  (This is done by scanning /proc/modules!)
module_expand_deps() {
  :
}

# CHECKPOINTING
#chkpoint_in dir file - create checkpoint
chkpoint_in() {
  [ -f $1/$2 ] && return 1
  touch $1/$2
  return 0
}

#chkpoint_out dir file - remove checkpoint
chkpoint_out() {
  rm -f $1/$2
} 

#script_exec - execute scripts given in SCRIPTS
#script_exec script1 [; script2 [; ...]]
script_exec() {
  local LIST="$*" ; [ -z "$LIST" ] && LIST="$SCRIPTS"
  IFS=";" ; set $LIST '' ; unset IFS
  local arg; for arg; do
    [ -z "$arg" ] && continue
    mesg "Executing $arg"
    #[ -x "$arg" ] &&
    eval "$arg"
  done
}

#retrieve_ids prefix first second third - see pci.functions, pci_desc()
#    for explanation
retrieve_ids() {
  local idsfile=$1
  local prefix
  local first first_D
  local second second_D
  local third third_D
  prefix="$2"
  if [ -z "$prefix" ]; then
    first="$3"
  else
    first="$2 $3"
  fi
  second="$4"
  third="$5"
  while read line; do
    set $line ''
    if [ -z "$first_D" ]; then
      shift
      [ -n "$prefix" ] && shift
      first_D="$*";
      continue;
    elif [ "$1" != ":" -a "$1" != "::" -o -z "$second" ]; then
      : > /dev/null
    else
      if [ -z "$second_D" ]; then
	if [ "$second" = "$2" ]; then
	  shift 2
	  second_D="$*"
	fi
      elif [ "$1" != "::" -o -z "$third" ]; then
	: > /dev/null
      else 
	if [ -z "$third_D" ]; then
	  if [ "$third" = "$2" ]; then
	    shift 2
	    third_D="$*"
	  elif [ "$third" = "$2 $3" ]; then
	    shift 3
	    third_D="$*"
	  else
	    : > /dev/null
	  fi
	fi
      fi
    fi	
  done < <(egrep -A 65536 "^$first" $idsfile \
    | sed -e "s,\",\\\\\\\\\",g" \
    -e "s,^$TAB$TAB,:: ," \
    -e "s,^$TAB,: ,")
  [ -z "$first_D" ] && first_D="unknown"
  [ -z "$second_D" ] && second_D="unknown"
  [ -z "$third_D" ] && third_D="unknown"
  var_trim first_D ' '
  var_trim second_D ' '
  var_trim third_D ' '
  export IDS_FIRST="$first_D"
  export IDS_SECOND="$second_D"
  export IDS_THIRD="$third_D"
}

# Local variables:
#   mode: shell-script
#   sh-basic-offset: 2
# End:
