#!/bin/sh
# /usr/local/sbin/dphys-config-filesys - add filesystem stuff
# author Neil Franklin, last modification 2006.10.12
#   derived from dphys-setup-extra-fs 1.0, Elmar S. Heeb <heeb@phys.ethz.ch>
#   2009.07.02 because of  [ -o ]  changed to ( [] || [] ), not tested after!!!
# copyright ETH Zuerich Physics Departement,
#   use under either modified/non-advertising BSD or GPL license

# this script is called by dphys-config
#   once on each config list file or script install/update
#   no parameters or environment variables are evaluated
#     reads all input from dirnames list file /etc/dphys-config-filesys.list
# it requires in /etc/dphys-config.list 2 lines, for config file and script:
#   dphys-config-filesys.list:/etc/:if [ -f /usr/local/sbin/dphys-config-filesys ] ; then /usr/local/sbin/dphys-config-filesys ; fi
#   local/sbin/dphys-config-filesys:/usr/:chmod 755 {}; {}


### ------ configuration for this site

# --- CONF_* various site or subnet dependant user config variables

# log what we have done
CONF_LOG_DONE=yes


# --- DEBUG_*, various debugging settings

# force lots of debugging output
#   this can also be enabled by  touch /dphys-config-filesys.debug
#DEBUG_LOG_STEP=yes


# --- SYS_*, various system internal values

# name of directory names list file on server
SYS_CONFLIST=/etc/dphys-config-filesys.list



### ------ actual implementation from here on
# no user settings any more below this point

set -e


# --- get ready to work

# sanitise this place, else some commands may fail
#   must be before any commands are executed, incl config/input processing
# /usr/local added for FreeBSD, because their ports end up in there
PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin
export PATH


# --- tidy up some commands, make systematic

# stuff that goes wrong, not expected by user, not in data output, use >&2
#   so also with $0 in case this script was called by an other script
# something within the system that user does not expect, give up
CMD_FATAL="echo $0: FATAL:"
# something from users input, user will correct this and continue
CMD_ERROR="echo $0: ERROR:"
# something we can continue with, but may be wrong, and user may not suspect it
CMD_WARNING="echo $0: WARNING:"
# something most likely not wrong, but tell user for the odd case it is wrong
CMD_NOTE="echo $0: NOTE:"

# normal stuff users expect, so to stdout as normal output, no $0, no marking
CMD_INFO="echo"
# stuff users asked for, so add to stdout as normal output, no $0, but mark it
CMD_DEBUG="echo DEBUG:"


# and also put out some permanent messages, in case running as cron/init.d job
BASENAME="`basename $0`"
# something within the system that user does not expect, give up
CMD_LOG_FATAL="logger -t ${BASENAME} -p user.error FATAL:"
# something from users input, user can possibly correct this and continue
CMD_LOG_ERROR="logger -t ${BASENAME} -p user.error ERROR:"
# something we can continue with, but may be wrong, and user may not suspect it
CMD_LOG_WARNING="logger -t ${BASENAME} -p user.warning WARNING:"
# something most likely not wrong, but tell user for the odd case it is wrong
CMD_LOG_NOTE="logger -t ${BASENAME} -p user.notice NOTE:"

# normal stuff users expect, so no marking
CMD_LOG_INFO="logger -t ${BASENAME} -p user.info"
# stuff users asked for, but mark it as special
CMD_LOG_DEBUG="logger -t ${BASENAME} -p user.debug DEBUG:"


# other stuff we may want to use
CMD_SLEEP="sleep 2"
CMD_WAIT="read -p ---DEBUG-wait-after-step--- dummy"


# --- config file stuff

# what we are
NAME=dphys-config-filesys
PNAME=dphys-config-filesys

# check user config file(s), let user override settings
if [ -e /etc/${PNAME} ] ; then
  . /etc/${PNAME}
fi

# allow us to set logging despite no command line options and parser
if [ -f /${NAME}.debug ] ; then
  DEBUG_LOG_STEP=yes
fi


# --- control variable output

# set config option controllable stuff
if [ x${CONF_LOG_DONE} = xyes ] ; then
  CMD_LOG_IF_DONE="${CMD_LOG_INFO}"
else
  CMD_LOG_IF_DONE=true
fi

# set debug option controllable stuff
if [ x${DEBUG_LOG_STEP} = xyes ] ; then
  CMD_LOG_IF_DEBUG="${CMD_LOG_DEBUG}"
else
  CMD_LOG_IF_DEBUG=true
fi


# show what config settings this debug run is using
${CMD_LOG_IF_DEBUG} "CONF_LOG_DONE=${CONF_LOG_DONE}"


# --- install each directory name

if ( [ ! -f "${SYS_CONFLIST}" ] || [ ! -s "${SYS_CONFLIST}" ] ) ; then

  # directory name list not found, so we can not work
  ${CMD_FATAL} "no or empty directory name list ${SYS_CONFLIST}" >&2
  ${CMD_LOG_FATAL} "no or empty directory name list ${SYS_CONFLIST}"
  exit 1

fi

# get rid of comments and empty lines (may be emptied comment lines)
cut -f 1 -d '#' "${SYS_CONFLIST}" | grep -v '^ *$' | while read LINE ; do

  ${CMD_LOG_IF_DEBUG} "LINE=\"${LINE}\""

  # extract from LINE format:  type:parameter[:parameter...]
  if ! echo "${LINE}" | grep -q ':' ; then
    ${CMD_ERROR} "config line \"${LINE}\" has no \":\" to split on" >&2
    ${CMD_LOG_ERROR} "config line \"${LINE}\" has no \":\" to split on"
    exit 1
  fi

  TYPE="`echo "${LINE}" | cut -f 1 -d ":"`"

  ${CMD_LOG_IF_DEBUG} "TYPE=${TYPE}"

  case "${TYPE}" in

    directory)
      # line format:  directory:<dir>:<owner>:<group>:<mode>
      #   example:    directory:/scratch:root:root:1777
      DIR="`echo "${LINE}"   | cut -f 2 -d ':'`"
      OWNER="`echo "${LINE}" | cut -f 3 -d ':'`"
      GROUP="`echo "${LINE}" | cut -f 4 -d ':'`"
      MODE="`echo "${LINE}"  | cut -f 5 -d ':'`"

      if [ ! -e "${DIR}" ] ; then
        mkdir -p "${DIR}"
        ${CMD_LOG_IF_DEBUG} "made DIR=${DIR}"
      else
        if [ ! -d "${DIR}" ] ; then
          ${CMD_ERROR} "File ${DIR} already exists and is not a directory" >&2
          ${CMD_LOG_ERROR} "File ${DIR} already exists and is not a directory"
          exit 1
        fi
      fi
      chown "${OWNER}:${GROUP}" "${DIR}"
      chmod "${MODE}" "${DIR}"
      ${CMD_LOG_IF_DEBUG} "DIR=${DIR}" \
          "OWNER=${OWNER} GROUP=${GROUP} MODE=${MODE}"
      ;;

    symlink)
      # line format:  symlink:<link>:<target>
      #   example:    symlink:/pub:/net/debian/export/pub
      LINK="`echo "${LINE}"   | cut -f 2 -d ':'`"
      TARGET="`echo "${LINE}" | cut -f 3 -d ':'`"
      if [ "${LINK}" = "" ]; then
        LINK="`basename "${TARGET}"`"
      fi

      if [ ! -e "${LINK}" ] ; then
        ln -sf "${TARGET}" "${LINK}"
        ${CMD_LOG_IF_DEBUG} "made LINK=${LINK}"
      else
        if [ ! -L "${LINK}" ] ; then
          ${CMD_ERROR} "File ${LINK} already exists and is not a symlink" >&2
          ${CMD_LOG_ERROR} "File ${LINK} already exists and is not a symlink"
          exit 1
        fi
      fi
      ${CMD_LOG_IF_DEBUG} "LINK=${LINK} TARGET=${TARGET}"
      ;;

    partition)
      # line format:  partition:<mountpoint>:<device>:<fs-type>
      #   example:    partition:/export/data1:/dev/sdb1:ext3
      MPOINT="`echo "${LINE}" | cut -f 2 -d ':'`"
      DEV="`echo "${LINE}"    | cut -f 3 -d ':'`"
      FS="`echo "${LINE}"     | cut -f 4 -d ':'`"

      if [ ! -e "${DEV}" ]; then
        ${CMD_ERROR} "line for ${MPOINT} has non-existant" \
            "device ${DEV}" >&2
        ${CMD_LOG_ERROR} "line for ${MPOINT} has non-existant" \
            "device ${DEV}"
        exit 1
      fi

      if [ ! -e "${MPOINT}" ] ; then
        mkdir -p "${MPOINT}"
        ${CMD_LOG_IF_DEBUG} "made MPOINT=${MPOINT}"
      else
        if [ ! -d "${MPOINT}" ] ; then
          ${CMD_ERROR} "File ${MPOINT} already exists and is not directory" >&2
          ${CMD_LOG_ERROR} "File ${MPOINT} already exists and is not directory"
          exit 1
        fi
      fi

      # possible existing line (or empty)
      awk '$2=="'"${MPOINT}"'"{print}' /etc/fstab > /etc/fstab.old.$$
      # potentially new line we want
      printf "%s\t%s\t%s\t%s\t%s\t%s\n" "${DEV}" "${MPOINT}" "${FS}" \
          "defaults" "0" "2" > /etc/fstab.new.$$
      if ! cmp -s /etc/fstab.new.$$ /etc/fstab.old.$$ ; then
        # differs, remove old entry (if present) ...
        awk '$2!="'"${MPOINT}"'"{print}' /etc/fstab > /etc/fstab.tmp.$$
        # ... and add new entry
        cat /etc/fstab.tmp.$$ /etc/fstab.new.$$ > /etc/fstab
        rm /etc/fstab.tmp.$$
      fi
      rm /etc/fstab.new.$$ /etc/fstab.old.$$

      mount "${MPOINT}" 2> /dev/null || true
      ${CMD_LOG_IF_DEBUG} "MPOINT=${MPOINT} DEV=${DEV} FS=${fFS}"
      ;;

    nfsmount)
      # line format:  nfsmount:<mountpoint>:<server>:<export>:<nfs-options>
      #   example:    nfsmount:/var/mail:mail.phys:/var/mail:rw,soft,noac,rsize=8192,wsize=8192,retry=1
      MPOINT="`echo "${LINE}" | cut -f 2 -d ':'`"
      SERVER="`echo "${LINE}" | cut -f 3 -d ':'`"
      EXPORT="`echo "${LINE}" | cut -f 4 -d ':'`"
      NFSOPT="`echo "${LINE}" | cut -f 5 -d ':'`"
      if [ "${NFSOPT}" = "" ]; then
        NFSOPT=defaults
      fi

      if [ ! -e "${MPOINT}" ] ; then
        mkdir -p "${MPOINT}"
        ${CMD_LOG_IF_DEBUG} "made MPOINT=${MPOINT}"
      else
        if [ ! -d "${MPOINT}" ] ; then
          ${CMD_ERROR} "File ${MPOINT} already exists and is not directory" >&2
          ${CMD_LOG_ERROR} "File ${MPOINT} already exists and is not directory"
          exit 1
        fi
      fi

      # possible existing line (or empty)
      awk '$2=="'"${MPOINT}"'"{print}' /etc/fstab > /etc/fstab.old.$$
      # potentially new line we want
      printf "%s\t%s\t%s\t%s\t%s\t%s\n" "${SERVER}:${EXPORT}" "${MPOINT}" \
          "nfs" "${NFSOPT}" "0" "0" > /etc/fstab.new.$$
      if ! cmp -s /etc/fstab.new.$$ /etc/fstab.old.$$ ; then
        # differs, remove old entry (if present) ...
        awk '$2!="'"${MPOINT}"'"{print}' /etc/fstab > /etc/fstab.tmp.$$
        # ... and add new entry
        cat /etc/fstab.tmp.$$ /etc/fstab.new.$$ > /etc/fstab
        rm /etc/fstab.tmp.$$
      fi
      rm /etc/fstab.new.$$ /etc/fstab.old.$$

      mount "${MPOINT}" 2> /dev/null || true
      ${CMD_LOG_IF_DEBUG} "MPOINT=${MPOINT}" \
          "SERVER=${SERVER} EXPORT=${EXPORT} NFSOPT=${NFSOPT}"
      ;;

    *)

      ${CMD_ERROR} "unknown line type: ${TYPE}" >&2
      ${CMD_LOG_ERROR} "unknown line type: ${TYPE}"
      exit 1

  esac

done


# --- finish off

${CMD_LOG_IF_DONE} "has updated filesystem and /etc/fstab"
exit 0
