#!/bin/sh

## this script makes sure that all NFS mounts (as declared in /etc/fstab)
## are properly mounted.
## if somebody is logged into the machine, this should not do anything
## (to not interfere with the admin, when fixing things)

## case#1
## simply call "mount" (again)

## case#2
## reboot the machine so it can automatically mount


## options:
### -f: force (do not abort if somebody is logged in)
### -r: reboot if NFS-shares are missing
### -s <time>: sleep <time> seconds before doing anything
###            (usefull when started from cron @reboot)
###

## TODO:
### make '-s' take a list of times (e.g. 60,60,120), and it will retry multiple times
### (if -r was given, reboot should only occur at the *last* attempt)

DELAY=0
FORCE=no
FSTAB=/etc/fstab
DRY_RUN=no
VERBOSITY=0
FORCE_TERMINAL=no
alias debug=false
alias domount=mount_via_mount

usage() {
    cat <<EOF 1>&2
Usage: $0 [OPTIONS] [<fstab>]

OPTIONS
   -f       : force (do not abort if somebody is logged in)
   -r       : reboot if NFS-shares are missing
   -s <time>: sleep <time> seconds before doing anything
            (useful when started from cron @reboot)
   -n       : dry-run
   -v       : increase verbosity
   -q       : decrease verbosity
   -d       : debug mode (aka: -v -n)
   -t       : terminal mode (output to stdout/stderr)

   <fstab>  : mount definition (default: /etc/fstab)
EOF
}
OPTIND=1 # Reset is necessary if getopts was used previously in the script.  It is a good idea to make this local in a function.
while getopts ":hfrs:dtqvn" opt; do
  case "$opt" in
    h)
      usage
      exit 0
      ;;
    f)  FORCE=yes
      ;;
    r)  alias domount=mount_via_reboot
      ;;
    s)  DELAY=${OPTARG}
      ;;
    d)  DRY_RUN=yes
        VERBOSITY=$((VERBOSITY+1))
      ;;
    t)  FORCE_TERMINAL=yes
      ;;
    v)  VERBOSITY=$((VERBOSITY+1))
      ;;
    q)  VERBOSITY=$((VERBOSITY-1))
      ;;
    n)  DRY_RUN=yes
      ;;
    '?')
      usage
      exit 1
      ;;
    esac
done
shift "$((OPTIND-1))" # Shift off the options and optional --.

if [ "x${FORCE_TERMINAL}" != "xyes" ]; then
    if [ -t 0 ]; then
        FORCE_TERMINAL=yes
    fi
fi
if [ "x${FORCE_TERMINAL}" = "xyes" ]; then
## interactive shell
error() {
 echo "$@" 1>&2
}
else
## non-interactive (e.g. run as daemon)
error() {
 logger -p daemon.warning "$@"
}
fi

## mount-functions:
### $1: source (e.g. "remotehost:/Net/share/homes")
### $2: target (e.g. "/home")

## "mount" by rebooting the system
mount_via_reboot() {
  local source="$1"
  local target="$2"

  if [ "x${DRY_RUN}" != "xyes" ]; then
      error "rebooting to mount '${source}' onto '${target}'"
      reboot
  else
      error "(not) rebooting to mount '${source}' onto '${target}'"
  fi
}
## "mount" by calling mount
mount_via_mount() {
  local source="$1"
  local target="$2"

  if [ "x${DRY_RUN}" != "xyes" ]; then
      error "mounting ('${source}' onto) '${target}'"
      mount "${target}"
  else
      error "(not) mounting '${source}' onto '${target}'"
  fi
}

## exits with 'true' is somebody is logged in
have_loggedin() {
  debug "checking who is online"
  if [ ${VERBOSITY} -gt 1 ]; then
      who | grep . | sed -e 's|^|\t|'
  fi
  who | grep . 2>&1 >/dev/null
}

## prints all NFS-shares (found in /etc/fstab or the file privided as argument)
## which are not marked as "noauto", one by line
list_needed_nfs() {
  local spec
  local file
  local type
  local opts
  cat "$1" | sed -e 's|#.*||' | egrep "^[^[:blank:]]*" | while read spec file type opts
  do
      case "${type}" in
        nfs*)
          ;;
        *)
          continue
          ;;
       esac
       if echo "${opts}" | grep -w "noauto" >/dev/null 2>&1; then
         continue
       fi
       echo "${spec} ${file}"
  done
}

is_mounted() {
  local MOUNTS=/proc/mounts
  cut -d' ' -f2 "${MOUNTS}" | egrep "^$1$" >/dev/null 2>&1
}

filter_existing_nfs() {
  while read source target
  do
      is_mounted ${target} || echo "${source}" "${target}"
  done
}

if [ "x$1" != "x" ]; then
    FSTAB="$1"
fi

if [ ${VERBOSITY} -gt 0 ]; then
  alias debug=error
fi

## shall we sleep?
debug "sleeping for ${DELAY} seconds"
test $((DELAY)) -gt 0 && sleep $((DELAY))

## check if the fstab-file exists. exit otherwise
debug "reading fstab: '${FSTAB}'"
if [ -f "${FSTAB}" -a -r "${FSTAB}" ]; then
 :
else
 usage
 error "unable to read fstab-file: '${FSTAB}'"
 exit 1
fi

## check if there are users (and we are not forced)
if have_loggedin && test "x${FORCE}" != "xyes"; then
 exit 0
fi


list_needed_nfs "${FSTAB}" | filter_existing_nfs | while read source target
do
 domount "${source}" "${target}"
done
