#!/bin/sh
#
# chkconfig: 345 30 70
# description: Loads and unloads the drbd module
#
# complete rewrite from scratch by Philipp Reisner in March and April 2001
#
MODPROBE=/sbin/modprobe
DRBDSETUP=/usr/sbin/drbdsetup
RMMOD=/sbin/rmmod

#=================
# The parser for the configuration file
#

function openSection()
{
  local LINE TOKEN 
  read LINE TOKEN
  if [ "$TOKEN" != "{" ]; then 
    echo "$0: expected { found $TOKEN in line $LINE" 
    exit 20;
  fi
}

function skipSection()
{
  local TOKEN
  while read LINE TOKEN; do
    if [ "$TOKEN" = "{" ]; then skipSection; fi
    if [ "$TOKEN" = "}" ]; then return; fi
  done
}

function readHostSection()
{
  local LINE TOKEN M_DEVICE M_DISK M_ADDRESS M_PORT
  openSection
  while read LINE TOKEN; do
    case "$TOKEN" in 
	device=*)  M_DEVICE=${TOKEN/device=/};;
	disk=*)    M_DISK=${TOKEN/disk=/};;
	address=*) M_ADDRESS=${TOKEN/address=/};;
	port=*)    M_PORT=${TOKEN/port=/};;
        "}")  
	     if [ -z "$M_DEVICE" ]; then 
               echo "$0: device missing in line $LINE"; exit 20; fi
             if [ -z "$M_DISK" ]; then 
               echo "$0: disk missing in line $LINE"; exit 20; fi
             if [ -z "$M_ADDRESS" ]; then 
               echo "$0: address missing in line $LINE"; exit 20; fi
             if [ -z "$M_PORT" ]; then 
               echo "$0: address missing in line $LINE"; exit 20; fi

             case "$SECTION" in
               self)  DEVICE=$M_DEVICE
                      DISK=$M_DISK
                      ADDR="$M_ADDRESS:$M_PORT"
                      ;;
               other) O_DEVICE=$M_DEVICE
                      O_DISK=$M_DISK
                      O_ADDR="$M_ADDRESS:$M_PORT"
                      ;;
             esac
             return;;
	*) echo "$0: unexpected token $TOKEN found in line $LINE" 
	   exit 20;
	   ;;
    esac
  done
}

function readOptionsSection()
{
  local LINE TOKEN
  openSection
  case "$SECTION" in
    disk) DISK_OPTS="";;
    net) NET_OPTS="";;
  esac
  while read LINE TOKEN; do
    case "$TOKEN" in 
      "}")  return;;
      *) case "$SECTION" in
           disk) DISK_OPTS="$DISK_OPTS --$TOKEN" ;;
	   net) NET_OPTS="$NET_OPTS --$TOKEN" ;;
	 esac;;
    esac;
  done
}

function readResourceSection()
{
  local LINE TOKEN HNAME
  openSection
  unset PROTO FSCK_CMD
  unset DEVICE DISK ADDR
  unset O_DEVICE O_DISK O_ADDR

  while read LINE TOKEN; do
    case "$TOKEN" in 
      disk|net)
            SECTION="$TOKEN"
	    readOptionsSection
            ;;
      on)   read LINE HNAME
	    if [ "$HNAME" = $(hostname -s) ]; then SECTION="self";
	    else SECTION="other"; fi
            if [ "$SECTION" = "self" -a -n "$DEVICE" ]; then
              echo "$0: a second host section for this machine in line $LINE"
              exit 20
            fi
            if [ "$SECTION" = "other" -a -n "$O_DEVICE" ]; then
              echo "$0: no host section for this machine in line $LINE"
              exit 20
            fi
            readHostSection
	    ;;
      protocol*) PROTO=${TOKEN/protocol=/};;
      fsckcmd*) FSCK_CMD=${TOKEN/fsckcmd=/};;
      "}")  if [ -z "$FSCK_CMD" ]; then 
               echo "$0: fsckcmd missing in line $LINE"; exit 20; fi
            if [ -z "$PROTO" ]; then 
               echo "$0: protocol missing in line $LINE"; exit 20; fi
            return;;      
	*)  echo "$0: unexpected token $TOKEN found in line $LINE" 
	    exit 20;
	    ;;
    esac;
  done
}


function readConfigFile()
# $1 ... resource to configure. (or empty string for all)
# $2 ... action function to call for each/the resource
{
  local LINE TOKEN RES
  if [ ! -e /etc/drbd.conf ]; then 
    echo "$0: can not find /etc/drbd.conf";
    return 20;
  fi
  awk '/^[^#]/{if(match($1,"=")) { printf NR" "$1; for (i=2;i<=NF;i++){printf "\ "$i} printf "\n"; } else for(i=1;i<=NF;i++){print NR" "$i;}}' /etc/drbd.conf | {
    while read LINE TOKEN; do
      case "$TOKEN" in 
        resource)
	  read LINE RES
	  if [ -z "$1" -o "$RES" = "$1" ]; then 
            readResourceSection
	    case "$2" in
  	      doConfig) doConfig;;
              becomePri) becomePri;;
              becomeSec) becomeSec;;
              reportStatus) reportStatus;;
            esac
          else 
            openSection
            skipSection
          fi;;
	*) echo "$0: unexpected token $TOKEN found in line $LINE" 
	   exit 20;;
      esac;
    done
  if [ "$2" = "doConfig" ]; then
    echo -n "Waiting for synchronisation to finish... [ "
    wait
    echo "]"
  fi

  if [ "$2" = "reportStatus" ]; then
    if [ "$STATUS_RETVAL" = "1" ]; then
      echo "running"
    else
      echo "stopped"
    fi
  fi
  }
}

#
# End of parser
#=================
# These functions are called by the parser to perform the actual actions.
# They will find these variables in the environment:
#  $RES $DEVICE $DISK $DISK_OPTS $ADDR $O_ADDR $PROTO $NET_OPTS $FSCK_CMD
#

function doConfig()
{
  local ERRTXT
  echo -n "Setting up $RES..."
  if ERRTXT=$($DRBDSETUP $DEVICE disk $DISK $DISK_OPTS && 
     $DRBDSETUP $DEVICE net $ADDR $O_ADDR $PROTO $NET_OPTS 2>&1); then
    echo "[ OK ]"
    ( sleep 1 #this is necessary to achive nice formated output :)
      if $DRBDSETUP $DEVICE wait; then
        echo -n "$RES:OK " 
      else
        echo -n "$RES:FAIL "
      fi
    ) &
  else
    echo "[ FAIL ]"
    echo "$ERRTXT"
  fi
}

function becomePri()
{
  $DRBDSETUP $DEVICE pri
  if mount | grep "$DEVICE" > /dev/null; then
    echo "$0: $DEVICE is already mounted"
  else
    $FSCK_CMD $DEVICE
    mount $DEVICE
  fi
}

function becomeSec()
{
  local ERRTXT
  $DRBDSETUP $DEVICE sec 2> /dev/null
  if [ "$?" != "0" ]; then
    umount $DEVICE 2> /dev/null
    if [ "$?" != "0" ]; then
      fuser -k -m $DEVICE > /dev/null
      sleep 1   #Hopefully the signals get delivered within one second...
      ERRTXT=$(umount $DEVICE 2>&1)
      if [ "$?" != "0" ]; then
        echo "$0: umount FAILED:"
	echo "$ERRTXT"
      fi
    fi
    ERRTXT=$($DRBDSETUP $DEVICE sec 2>&1)
    if [ "$?" != "0" ]; then
      echo "$0: drbdsetup FAILED:"
      echo "$ERRTXT"
    fi
  fi
}

function reportStatus()
{
  local MINOR
  MINOR=${DEVICE//\/dev\/nb/}
  if grep "^${MINOR}:" /proc/drbd | grep "st:Secondary" > /dev/null; then
    STATUS_RETVAL=0;
  fi
}

#
# End of action functions.
#=================
# Here we have the implementation of the commands.
#

function drbd()
{
  local MINOR_COUNT
  case "$COMMAND" in
    start) 
       if [ ! -e /proc/drbd ]; then
         MINOR_COUNT=`grep resource /etc/drbd.conf | wc -l | sed -e s/\ //g`
         $MODPROBE -s drbd minor_count=$MINOR_COUNT 
         if [ ! $? ]; then
           echo "$0: Can not load the drbd module.";
  	 exit 10;
         fi
       fi
       readConfigFile "$RESOURCE" doConfig
       ;;
    stop) 
       if [ -e /proc/drbd ]; then
         $RMMOD -s drbd 
         if [ ! $? ]; then 
           echo "$0: Could not remove the drbd module.";
  	 exit 10;
         fi
       fi
       ;;
    *)
       echo "Usage: /etc/init.d/drbd {start|stop}"
       exit 1;;
  esac
  exit 0
}

function datadisk()
{
  case "$COMMAND" in
    start) 
       readConfigFile "$RESOURCE" becomePri
       ;;
    stop)
       readConfigFile "$RESOURCE" becomeSec
       ;;
    status)
       STATUS_RETVAL=1   #1=Ok
       readConfigFile "$RESOURCE" reportStatus
       ;;
    *)
       echo "Usage: /etc/ha.d/resources.d/datadisk {start|status|stop}"
       exit 1;;
  esac
  exit 0
}

#
# End of the command functions.
#=================
# The main program.
#

if [ "$#" = "2" ]; then
  RESOURCE="$1"
  COMMAND="$2"
elif [ "$#" = "1" ]; then
  RESOURCE=""  #affect all resources from drbd.conf
  COMMAND="$1"
else 
  COMMAND="usage"
fi

case `basename "$0"` in
  drbd) drbd ;;
  datadisk) datadisk ;;
  *) echo "$0: Do not know what to do...";;
esac
  
