#! /bin/sh

while [ $# -gt 0 ]; do
  case "$1" in
    -u)              BUILD="$2"; shift ;;
    --builddir=*)    BUILD="${1#*=}" ;;
    -s)              VPATH="$2"; shift ;;
    --bindir=*)      VPATH="${1#*=}" ;;
    -c)              DEVICE="$2"; shift ;;
    --device=*)      DEVICE="${1#*=}" ;;
    -b)              BLKSIZ="$2"; shift ;;
    --blksiz=*)      BLKSIZ="${1#*=}" ;;
    -z)              SRVSIZ="$2"; shift ;;
    --size=*)        SRVSIZ="${1#*=}" ;;
    /*)              EXPORT="$EXPORT $1" ;;
    -x)              EXPORT="$2"; shift ;;
    --export=*)      EXPORT="${1#*=}" ;;
    *:*)             PORT="${1##:*}"; SERVER="${1%%*:}" ;;
    -p)              PORT="$2"; shift ;;
    --port=*)        PORT="${1#*=}" ;;
    -n)              OPTSOCK="-n $2"; shift ;;
    --nchan=*)       OPTSOCK="-n ${1#*=}" ;;
    -i)              OPTSIG="-i $2"; shift ;;
    --signature=*)   OPTSIG="-i ${1#*=}" ;;
    --raid=*)        OPTRAID="-${1#*=}" ;;
    -l|-0|-1)        OPTRAID="$1" ;;
    -t)              OPTTIME="-t $2"; shift ;;
    --timeout=*)     OPTTIME="-t ${1#*=}" ;;
    --ro|-r)         OPTRO="-r" ;;
    -w)              OPTORDER="-w $2"; shift ;;
    --order=*)       OPTORDER="-w ${1#*=}" ;;
    --md5sum|-m)     OPTMD5SUM="-m" ;;
    --async|-a)      OPTASYNC="-a" ;;
    --lock|-k)       OPTLOCK="-k" ;;
    -h)              OPTRCACHE="-h $2"; shift ;;
    --cache=*)       OPTRCACHE="-h ${1#*=}" ;;
    --removable)     OPTREMOVABLE="-m" ;;
    --notest|--without-test)
                     NOTEST=1 ;;
    --with-ioctl)    NOIOCTL="" ;;
    --noioctl|--without-ioctl)
                     NOIOCTL=1 ;;
    --reload|--with-reload)
                     NORELOAD="" ;;
    --noreload|--without-reload)
                     NORELOAD=1 ;;
    --norestart|--without-restart)
                     NORESTART=1 ;;
    --start|start)   COMMAND="${1#--}" ;;
    --stop|stop)     COMMAND="${1#--}" ;;
    --restart|restart)
                     COMMAND="${1#--}" ;;
    --load|load)     COMMAND="${1#--}" ;;
    --unload|unload) COMMAND="${1#--}" ;;
    --reload|reload) COMMAND="${1#--}" ;;
    --test|test)     COMMAND="${1#--}" ;;
    --command)       COMMAND="${2}" ; shift ;;
    -*|*) echo $0: unknown option: $1; exit 1 ;;
  esac
  shift
done

BUILD="${BUILD-/tmp}"
VPATH="${VPATH-`pwd`/nbd}"
DEVICE="${DEVICE-/dev/nda}"
SERVER="${SERVER-localhost}"
CLIENT="${CLIENT-localhost}"
EXPORT="${EXPORT-/tmp/core0 /tmp/core1}"
BLKSIZ="${BLKSIZ-1024}"
SRVSIZ="${SRVSIZ-4096K}"


case $SRVSIZ in 
  *B|*b) SRVSIZ=${SRVSIZ%[Bb]} ;;
  *K|*k) SRVSIZ=${SRVSIZ%[Kk]}; let SRVSIZ*=1024 ;;
  *M|*m) SRVSIZ=${SRVSIZ%[Mm]}; let SRVSIZ*=$(( 1024 * 1024 )) ;;
  *)     let SRVSIZE*=$(( $BLKSIZ )) ;;
esac
let SRVBLKS=$(( $SRVSIZ / $BLKSIZ ))
[ $SRVBLKS -gt 0 ] || exit 4

PORT=${PORT-3033}

OPTSOCK="${OPTSOCK--n 4}"
OPTSIG="${OPTSIG--i NBDabcdefNBD}"
OPTBLK="${OPTBLK--b $BLKSIZ}"
OPTRAID="${OPTRAID-}"
OPTTIME="${OPTTIME--t 120}"
OPTRO="${OPTRO-}" # "-r"
OPTORDER="${OPTORDER--w 500}"
OPTMDSUM="${MD5SUM-}" # "-m"
OPTASYNC="${OPTASYNC-}" # "-a"
OPTRCACHE="${OPTRCACHE-}"
OPTREMOVABLE="${OPTREMOVABLE-}"

RAHEAD=${RAHEAD-20}
MERGE_REQS=${MERGE_REQS-0}
CHK_PART=${CHK_PART-0}
OPT_REQ_TIMEO="${OPT_REQ_TIMEO--p 5}"
SYNC_INTVL=${SYNC_INTVL-0}

MODPARMS="rahead=$RAHEAD merge_requests=$MERGE_REQS sync=$SYNC_INTVL check_partitions=$CHK_PART"

OPTCLI="$SERVER:$PORT $OPTSOCK $OPTBLK $OPTSIG $OPTTIME $OPTMD5SUM $OPTCLISSL $OPT_REQ_TIMEO $OPTJRNL $OPTASYNC -d 1 $DEVICE"

OPTSRV="$PORT $EXPORT $OPTSIG $OPTTIME $OPTBLK $OPTRAID $OPTSRVSSL $OPTRO $OPTORDER $OPTLOCK $OPTSASYNC $OPTRCACHE $OPTREMOVABLE"


[ -d "$BUILD" ] || exit 1
[ -d "$VPATH" ] || exit 2

PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin

findpath() {
  local name="$1"

  [ -x $BUILD/$name ] && { echo $BUILD/$name; return 0; }
  [ -x $VPATH/$name ] && { echo $VPATH/$name; return 0; }

  local ifs="$IFS"
  IFS=":"
  set -- $PATH
  IFS="$ifs"

  for i; do
    [ -x $i/$name ] && { echo $i/$name; return 0; }
  done
  echo "$name"
  return 1
}

stop() {

       if [ -n "$OPTSIG" ]; then
         set -- $OPTSIG
         local sig="$2"
         if [ -n "$sig" ]; then
           if [ -s /var/run/enbd-client-$sig.pid ]; then
             kill `head -1 /var/run/enbd-client-$sig.pid` >/dev/null 2>&1
           fi
           sleep 2
           if [ -s /var/run/enbd-client-$sig.pid ]; then
             kill -9 `head -1 /var/run/enbd-client-$sig.pid` >/dev/null 2>&1
             sleep 1
           fi
           if [ -s /var/run/enbd-server-$sig.pid ]; then
             kill `head -1 /var/run/enbd-server-$sig.pid` >/dev/null 2>&1
           fi
           sleep 2
           if [ -s /var/run/enbd-server-$sig.pid ]; then
             kill -9 `head -1 /var/run/enbd-server-$sig.pid` >/dev/null 2>&1
           fi
           sleep 1
         fi
       fi
       if [ -n "$DEVICE" ]; then
         local devletter="${DEVICE#*nd}"
         set -- `grep '^[[]'"$devletter"'. Cpids:' /proc/nbdinfo | \
           sed -e 's/[^(]*(//' | sed -e 's/[()]//g'`
         for i; do
           [ "$i" -le 0 ] && continue
           kill $i  >/dev/null 2>&1
           sleep 2
           kill -9 $i >/dev/null 2>&1
           sleep 1
         done
       fi
       for e in $EXPORT; do
         fuser -kuv "$e"
       done
}

unload() {
  while lsmod | grep -qsw enbd_bufferwr ; do
    rmmod enbd_bufferwr
    echo -n .
    sleep 1
  done
  while lsmod | grep -qsw enbd_ioctl ; do
    rmmod enbd_ioctl
    echo -n .
    sleep 1
  done
  while lsmod | grep -qsw enbd ; do
    rmmod enbd
    echo -n .
    sleep 1
  done
}

load() {
    while ! lsmod | grep -qsw enbd ; do
      insmod $BUILD/enbd.o $MODPARMS
      echo -n .
      sleep 1
    done
    if [ -z "$NOIOCTL" ]; then
      while ! lsmod | grep -qsw enbd_ioctl ; do
        insmod $BUILD/enbd_ioctl.o
        echo -n .
        sleep 1
      done
    fi
    if [ -z "$NOBUFFERWR" ]; then
      while ! lsmod | grep -qsw enbd_bufferwr ; do
        insmod $BUILD/enbd_bufferwr.o
        echo -n .
        sleep 1
      done
    fi
}

major=43
MAXCON=16

makedev() {
  local dev="$1"
  case $dev in
  *a) minor="$[ 0 * $MAXCON ]";;
  *b) minor="$[ 1 * $MAXCON ]";;
  *c) minor="$[ 2 * $MAXCON ]";;
  *d) minor="$[ 3 * $MAXCON ]";;
  *e) minor="$[ 4 * $MAXCON ]";;
  *f) minor="$[ 5 * $MAXCON ]";;
  *g) minor="$[ 6 * $MAXCON ]";;
  *h) minor="$[ 7 * $MAXCON ]";;
  *) minor=err-$dev ;;
  esac

  [ -b $dev ] || [ -b $dev/0 ] || mknod $dev b $major $minor

  local j=0
  local k
  while [ $j -lt $(( $MAXCON - 1 )) ]; do
    let j=j+1
    [ -b $dev$j ] && continue
    [ -b $dev/$j ] && continue
    k=$(( $j + $minor ))
    mknod $dev$j b $major $k
  done
}

 
start() {
  # make absolute name
  case $DEVICE in
    /*) ;;
    */*) $DEVICE=`pwd`/$DEVICE ;;
    *) DEVICE="/dev/$DEVICE" ;;
  esac

  if ! [ -b $DEVICE ] && ! [ -b $DEVICE/0 ]; then
    ( cd `dirname $DEVICE` ; makedev $DEVICE; )
    [ -b $DEVICE ] || [ -b $DEVICE/0 ] || exit 3
  fi

  for i in $EXPORT; do
    [ -s "$i" ] && continue
    if ! dd if=/dev/zero bs=$BLKSIZ count=$SRVBLKS of="$i" ; then
        echo "cannot write $(( $BLKSIZ * $SRVBLKS )) bytes to `dirname $i`, aborting"
        exit 5
    fi
  done

  echo enbd-server
  (
   nice -19 $ENBD_SERVER $OPTSRV
   pstree -p | grep enbd-server
   sleep 300
  ) &

  echo -n delay 5s; for i in 1 2 3 4 5; do sleep 1; echo -n .; done; echo
  
  echo enbd-client
  (
   nice -19 $ENBD_CLIENT $OPTCLI
   pstree -p | grep enbd-client
   sleep 10
  ) &
  wait $!
}

dotest() {
  if [ -z "$NOIOCTL" ]; then
      $ENBD_TEST $DEVICE -s 1M -i 1:2:3:4:5
  else
      $ENBD_TEST $DEVICE -s 1M 
  fi
}



main() {

  case "$1" in
    start)     start ;;
    stop)      stop ;;
    restart)   stop ; start ;;
    load)      load ;;
    unload)    unload ;;
    reload)    unload ; load ;;
    test)      dotest ;;
    "") 
               stop
               sleep 2
               if [ -z "$NORELOAD" ]; then
                 unload
                 if [ -z "$NOLOAD" ]; then
                   load
                 fi
               fi
               if [ -z "$NORESTART" ]; then
                 start
               fi
               if [ -z "$NOTEST" ]; then
                 dotest
               fi
    ;;
  esac
}

ENBD_SERVER=`findpath enbd-server`
ENBD_CLIENT=`findpath enbd-client`
ENBD_TEST=`findpath enbd-test`

main $COMMAND

