#!/bin/bash

# Client-side macros for access to AppLogic grid controller
# (C) L.Kalev 2006 (lion//3tera.com)

# temp dir for master-session sockets
export CSPATH=/tmp/apl_lnkroot

# controller name
export AL_CTL
export AL_CTLSOCKOPT
export PWA

ssh_cmd="ssh $AL_SSH_OPT"


# !! _FS must match the value set in '3trsh' for this to work!
_FS='`'

function pack()
   {
   local i="$IFS"
   IFS="$_FS"
   ARGS="$*$_FS"
   IFS="$i"
   return 0
   }

## unpack
#OLD_IFS="$IFS"
#if [[ "$T" = *$_FS* ]] ; then IFS="$_FS" ; fi
#set $T
#IFS="$OLD_IFS"


function controller ()
   {
   [[ -z "$1" ]] && { echo $AL_CTL ; return 0 ; }

   local OPT_FC=
   if [ "$1" = '-f' ] ; then OPT_FC=1 ; shift ; fi

   [[ "$1" != "$AL_CTL" ]] && PWA=

   # close old connection, if needed
   if [[ -S $CSPATH/$1 ]] ; then
      if [[ -z "$OPT_FC" ]] && [[ "$1" != "-" ]] ; then
         AL_CTL=$1
         AL_CTLSOCKOPT="-o ControlPath=$CSPATH/$AL_CTL"
         return 0 # don't re-connect if connection is open
      fi
      echo "Closing old connection to $AL_CTL" >&2
      kill $(<$CSPATH/$AL_CTL.pid)
   fi

   AL_CTL=$1
   if [ "$AL_CTL" = '-' ] ; then AL_CTL= ; return 0 ; fi # disconnect only

   # make sure it has no spaces or other junk
   set -- $AL_CTL
   [[ "$1" != "$AL_CTL" ]] && { echo "invalid hostname" >&2 ; return 1 ; }

   ping -c 1 -n -q "$AL_CTL" >/dev/null || echo "warning: ping $AL_CTL failed"
   AL_CTLSOCKOPT="-o ControlPath=$CSPATH/$AL_CTL"
   }


function sock-chk ()
   {
   [[ -z "$AL_CTL" ]] && { echo "controller name not set, use the 'controller' command to set it" >&2 ; return 1 ; }
   if [[ -S $CSPATH/$AL_CTL ]] ; then return 0 ; fi
   mkdir -p $CSPATH
   # copy stderr descr into '3' and redirect stderr into file
   # NB: can't do that on the 'ssh' command line, 'bash' waits for the file 
   # to get closed (which SSH won't do, being forked) and we get stuck.
   exec 3>&2 2>$CSPATH/$AL_CTL.2
   ssh $AL_SSH_OPT -M -N -f $AL_CTLSOCKOPT $AL_CTL
   local R=$?
   # re-open '2' as the old stderr and close the '3' handle
   exec 2>&3 3>&-
   if [[ $R != 0 ]] ; then
      cat $CSPATH/$AL_CTL.2 >&2
      return $R
   fi
   T=`ps -C ssh -o pid,args | grep -e "-M -N -f $AL_CTLSOCKOPT"`
   set -- $T
   echo $1 >$CSPATH/$AL_CTL.pid
   return 0
   }


function 3t ()
   {
   sock-chk || return 1
   local T=
   local A=
   [[ $# == 0 ]] && T=-t
   [[ -n "$PWA" ]] && A="=$PWA"
   local TC1 TC2
   TC1=`stat --format '%Y' $CSPATH/$AL_CTL.2`
   ARGS="$A"
   if [ $# != 0 ] ; then pack $A "$@" ; fi
   $ssh_cmd $T $AL_CTLSOCKOPT $AL_CTL "$ARGS"
   A=$?
   TC2=`stat --format '%Y' $CSPATH/$AL_CTL.2`
   if [[ "$TC1" != "$TC2" ]] && ! [[ -S $CSPATH/$AL_CTL ]] ; then
      echo "Connection to controller lost, re-run the command to reconnect"
      return 1
   fi
   return $A
   }

function ca ()
   {
   [[ -n "$1" ]] || { PWA= ; return 0 ; }
   # [[ -n "$1" ]] || { echo "use: ca app-name" >&2; return 1; }
   3t app info "$1" >/dev/null || return $?
   export PWA=$1
   return 0
   }

function pwa ()
   {
   echo $PWA
   }

function assh ()
   {
   [[ -z "$1" ]] && { echo "use: assh [app:]comp" >&2 ; return 1 ; }

   local APP

   # this can be removed when we fix the curr. app support in 3trsh
   if [[ $1 == *:* ]] ; then
      APP=$1
   else
     [[ -z "$PWA" ]] && { echo "current app not set, use full name" >&2; return 1; }
     APP=$PWA:$1
   fi


   sock-chk || return 1;

   shift
   local T=
   if [[ -z "$@" && -t 0 && -t 1 ]] ; then
      # echo "using tty"
      T=-t
   fi

   local TC1 TC2
   TC1=`stat --format '%Y' $CSPATH/$AL_CTL.2`
   ARGS="sh $APP"
   if [ $# != 0 ] ; then pack sh $APP "$@" ; fi
   $ssh_cmd $T $AL_CTLSOCKOPT $AL_CTL "$ARGS"
   TC2=`stat --format '%Y' $CSPATH/$AL_CTL.2`
   if [[ "$TC1" != "$TC2" ]] && ! [[ -S $CSPATH/$AL_CTL ]] ; then
      echo "Connection to controller lost, re-run the command to reconnect"
      return 1
   fi
   }


function ascp ()
   {
   # temporary - operation w/o current app can wait
   [[ -n "$PWA" ]] || { echo "current app. not set" >&2; return 1; }

   sock-chk || return 1

   # NB: 'local' and the {#} substr match are specific to 'bash', won't work on std bourne shell
   local A1=$1
   local P1=${1%%:*}
   local P2=
   local S1=
   if [[ "$P1" != $1 ]] ; then # $1 is remote
      S1=${1#*:}
      [[ $S1 == /* ]] || S1=/root/$S1
      [[ -n $P1 ]] || return 1;
      A1="root@$AL_CTL:/app/$PWA/$P1$S1"
   fi

   local A2=$2
   P1=${2%%:*}
   if [[ "$P1" != $2 ]] ; then # $2 is remote
      S1=${2#*:}
      [[ $S1 == /* ]] || S1=/root/$S1
      [[ -n $P1 ]] || return 1;
      A2="root@$AL_CTL:/app/$PWA/$P1$S1"
   fi

# should have the same options as $ssh_cmd
   scp $AL_SSH_OPT $AL_CTLSOCKOPT "$A1" "$A2"
   }


# ---------- various shortcuts

function als ()
   {
   3t app list
   }

function astop ()
   {
   3t app stop "$@"
   }

function astart ()
   {
   3t app start "$@"
   }

# not very useful for now (need to get it to mount on OUR system)
function vmnt ()
   {
   3t vol mount "$@"
   }

function vumnt ()
   {
   3t vol unmount "$@"
   }


# ssh $T -S /tmp/opty3sk -i ~/t-key -o IdentitiesOnly=yes

# master start
# ssh -A -i ~/t-key -M -S /tmp/opty3sk -N -f -o IdentitiesOnly=yes $AL_CTL

