#!/bin/bash

shopt -s -o nounset

declare DEBUG=OFF


declare LIST=/var/local/lib/mirror/bark.lst
declare CFG=/etc/sysconfig/bark.cfg
#declare SOCKET=/tmp/socket
#declare LOGGER=/usr/local/bin/cmd_logger

#declare UNIXCLIENT=/usr/local/bin/unixclient
declare SYSLOGCONF=/etc/syslog.conf
declare SERVICES=/etc/services
declare SYSLOGD=syslogd
declare FACILITY=local1.notice  # can be overridden in cfg
declare INTERVAL=20             # can be overridden in cfg
declare ENABLED=N
#declare TAG=$HOSTNAME

declare DEBUG_FAC=local2.debug   # debug outout
declare DEBUG_TAG=bark           # debug tag for logs
declare SED="/bin/sed -i" 
declare SLEEP=/bin/sleep
declare GREP=/bin/grep
declare PKILL=/usr/bin/pkill
declare ECHO=/bin/echo
declare UPNP=/usr/local/sbin/upnpc-static
declare NATPMP=/usr/local/sbin/natpmp-23
declare SERVICE_PORT=23

declare TIMEOUT=10                      # socket comms timeout
declare LOGGER=/usr/bin/logger
declare DELIMITER="^"                          # command delimter for packing cmds.

if [ -e $CFG ]; then
   source $CFG
fi

declare OLD_IFS


debug()
  {
  if [ $DEBUG == "ON" ]; then
     /usr/bin/logger -t $DEBUG_TAG -p $DEBUG_FAC "$*"
  fi
  }


send_msg()
{
  declare QUEST
  debug "cmd $*"
  #IFS=$OLD_IFS
 
  # send the message into the handle 7 (cmd listener)
  echo "$*" >&7

  # read from handle 6, cmdld output
  while read -t $TIMEOUT -d $'\r' -u 6 LINE; do
    #declare RES=$(echo $LINE | /bin/sed 's/.$//')
    debug "LINE: ${LINE}"
    #open_files
    if [[ $LINE = $'?' ]]; then
      QUEST=1
      continue
    fi
    if [[ $LINE = $'' ]] && [[ $QUEST -eq 0 ]]; then
      break
    fi
    if [[ $LINE = $'' ]] && [[ $QUEST -eq 1 ]]; then
      continue
    fi
    if [ "$LINE" == $'OK' ] || [ "$LINE" = $'0' ]; then
      QUEST=0
      continue
    fi

    #LINE=$(echo -n "$LINE" | sed 's/.$//')
    echo "$LINE" #| /bin/sed 's/.$//'
    #echo "$LINE"
    #TMP_RESULT="${TEMP_RESULT}${LINE}"
    #debug "Line read: ${LINE}"

  done
  #debug "Res: $?"
  #IFS=$OLD_IFS
  #TMP_RESULT=$(echo $TMP_RESULT | sed 's/.$//')  # strip the newline
#  TMP_RESULT=$(echo $TMP_RESULT | sed 's/\r$//')  # strip the newline
  #echo "$TMP_RESULT" #| sed 's/.$//g'  # strip the newline
#  sleep 60
  #debug "TMP $TMP_RESULT"
  #IFS="$DELIMITER"
}


msg_handler ()
{
  declare RESULT
  declare TMP_RESULT
  declare MSG_LIST
  #OLD_IFS="$IFS"
  IFS=$':'

  declare -i CNT=0

  # clear the input pipe: file handle #6
  for i in $(seq 6); do
    read -t 1 -u 6
    # some alternatives to try
    # read up until carriage return
    #read -t 0 -d $'\r' -u 6
    # read 255 chars
    #read -t 0 -n 255 -u 6
  done	

  for CMD in $*; do
    #debug "IFS $IFS $CMD"
    if [ $CNT -ne 0 ]; then  # only add demimiter if more than one response
       RESULT="${RESULT}${DELIMITER}"
       MSG_LIST="${MSG_LIST}^"
    fi
    IFS=$' \t\n' #OLD_IFS
    CMD=$(echo "$CMD" | sed 's/"//g')
    TMP_RESULT=$(send_msg "$CMD")
    RESULT="${RESULT}${TMP_RESULT}"

    #MSG_LIST="${MSG_LIST}${DELIMITER}${CMD}"
	CMD="${CMD/*./}"
    MSG_LIST="${MSG_LIST}${CMD/ /_}"
    let "CNT=$CNT+1"

  done
  IFS=$' \t\n' #$OLD_IFS

  $LOGGER -t "${TAG}^${MSG_LIST}" -p $FACILITY "${RESULT}"
  #debug "tag: ${TAG}$*"
  
  debug "$LOGGER -t \"${MSG_LIST}\" -p $FACILITY \"${RESULT}\""
  debug "result ${RESULT}"
}


upnp_handler ()
{
  local UPNPH=$(upnp_mapping)
  debug "UPNP $UPNPH"
  if [ -n "$UPNPH" ]; then
    $LOGGER -t "${TAG}^upnp" -p $FACILITY "${UPNPH}"
    debug "$LOGGER -t ${TAG}^upnp -p $FACILITY ${UPNPH}"
    return 0  # global flag to set if found valid firewall poker
  else
    return 1
  fi
}

natpmp_handler ()
{
   #debug "$NATPMP"
   local NATPMPH=$($NATPMP)
   if [ -n "$NATPMPH" ]; then
     $LOGGER -t "${TAG}^natpmp" -p $FACILITY "${NATPMPH}"
     debug "$LOGGER -t ${TAG}^natpmp -p $FACILITY ${NATPMPH}"
     return 0
   else
     return 1
   fi
}


daemon()
  {
  while true; do
  
    # source in settings (interval)
    if [ -e $CFG ]; then
	  . $CFG
	fi
	
    $SLEEP $INTERVAL   # a little different.  Sleep first.

    if [ "$ENABLED" = "Y" ]; then

       exec 3<$LIST
  
       while read CMD <&3; do
 	     if [ ! -z "${CMD}" ]; then
   	       debug "Sent cmd: ${CMD}"
               msg_handler "${CMD}"
	     fi	 
       done

      3<&-

# MPG, disabled 05/06/08.  upnp is rather slow response
# will wait for wide area publishing in zeroconf.
      # handle nat traversal
#      natpmp_handler
#      if [ $? -ne 0 ]; then
#        upnp_handler
#        if [ $? -ne 0 ]; then # upnp failed
#          : pass  #insert other handlers here
#        fi
#      fi 

   
    fi  
  
  done  
  }


# call external app that wrap script for socket IO.
#send_msg()
#  {
#  #debug "$UNIXCLIENT $SOCKET $LOGGER -z -m $*"
#  #$UNIXCLIENT $SOCKET $LOGGER -m "$*"
#  do_cmd "$*"
#  }


# sends single message.
single_shot()
  {
  STATUS=$(status)
  # if OFF then turn it on, send, then set back to OFF.
  if [ $STATUS -eq 0 ]; then
    debug "Sending cmd: $*"
    turn_on   # turn on the syslog messaging
    msg_handler "$*"
    turn_off   # turn messaging back off
  else
    debug "Sending cmd: $*"
    # just send message
    msg_handler "$*"
  fi
  }
  

# check to see if facility not commented
# 0 disabled, 1 enabled
status()
  {
  $GREP -c "^${FACILITY}" $SYSLOGCONF
  }

get_host()
  {
  $GREP "local1.notice[ ]*" $SYSLOGCONF | cut -d@ -f2
  }

set_host()
  {
  $SED "s/\(local1.notice[ ]*@\)\(.*\)/\1${1}/" $SYSLOGCONF
  reload_syslog
  }

turn_on()
  {
  $SED "s/#${FACILITY}/${FACILITY}/" $SYSLOGCONF
  $SED 's/\(ENABLED=\)./\1Y/' $CFG
  reload_syslog
  }

turn_off()
  {
  $SED "s/${FACILITY}/#${FACILITY}/" $SYSLOGCONF  
  $SED 's/\(ENABLED=\)./\1N/' $CFG
  reload_syslog
  }

reload_syslog()
  {
  $PKILL -HUP $SYSLOGD
  }

list_add()
  {
  echo "$*" >> $LIST
  }

list_clear()
  {
  > $LIST
  }

list_show()
  {
  if [ -e ${LIST} ]; then
    /bin/cat ${LIST}
  fi	
  }

list_default()
  {
  list_clear
  list_add "*.dcmd tr:*.sysd ut:*.sysd vn:*.sysd cw:*.sysd un:*.sysd mf"
  }

set_interval()
  {
  $SED "s/\(^INTERVAL=\).*/\1${1}/" $CFG
  }

show_interval()
  {
  $GREP "INTERVAL" $CFG | cut -d= -f2
  }

get_port()
  {
  local SRV=$($GREP syslog $SERVICES)
  expr "$SRV" : "syslog[[:space:]]\{0,\}\(.*\)\/udp"
  }

set_port()
  {
   if [ ${1} -gt 1025 -a ${1} -lt 63536 ]; then
     $SED "s/\(^syslog[[:space:]]\{1,\}\)[0-9]*\(\/udp$\)/\1${1}\2/" $SERVICES
     reload_syslog
   else
     $ECHO "Invalid port: ${1}.  Use port between 1025 and 65535."
      exit 2
   fi
  }



upnp_mapping ()
  {
  #  Warning:  hardcode eth0
  local IPA=$(/sbin/ifconfig eth0 | grep -o "inet addr:[0-9.]*" | cut -d: -f2)
  MY_MAP=$($UPNP -l | grep -c "TCP ${IPA} ${SERVICE_PORT} .* Adtec")
  debug "MY_MAP: $MY_MAP"
  EXT_IP=$($UPNP -e)
  debug "EXT_IP: $EXT_IP"
  if [ $MY_MAP -gt 0 ]; then  # found a upnp mapping
     set -- $($UPNP -l | grep -m 1 "TCP ${IPA} ${SERVICE_PORT} .* Adtec")
     debug "MAP FOUND: $1 $2 $3 $4"
     local EXT_PORT=$4
     if [ -n "$EXT_IP" ]; then  # have an external IP
       echo "${EXT_IP}:${EXT_PORT}" 
     fi
  else  # no mapping found
    while true; do
      let "RND=$RANDOM+32767"  # get a random port
      local EXT_TEST=$($UPNP -l | grep -c "TCP .* .* ${RND} Adtec")
      if [ ${EXT_TEST} -eq 0 ]; then   # no one has claimed that external port
        debug "Found free port: $RND, setting."
        $UPNP -a ${IPA} ${SERVICE_PORT} ${RND} TCP
        if [ $? -eq 0 ]; then   # success 
	  debug "Set external port $RND to internal ${SERVICE_PORT}"
          echo "${EXT_IP}:${RND}" 
	  break
        else
          # failed to set port
	  break  # avoid infinite loop
        fi
      fi
    done          
  fi
  }


usage()
{
  local PROG=`basename $0`

  $ECHO ""
  $ECHO "\"$PROG\" is an application that communicates to cmdld and syslog "
  $ECHO "to get system information into a network log system.  Provisions are "
  $ECHO "provided to manage the syslog network"
  $ECHO ""
  $ECHO "Usage: $PROG [options] where [options] are:"
  $ECHO "       -d             = Enter daemon mode"
  $ECHO "       -h [host]      = Sets host"
  $ECHO "       -s             = Shows status.  Displays host and uses exit code"
  $ECHO "                        to inidcate on/off.  1 - ON, 0 - OFF"
  $ECHO "       -y             = Turn on network logging"
  $ECHO "       -n             = Turn off network logging"
  $ECHO "       -m [message]   = Send a single message and exit"
  $ECHO "       -a [command]   = Add a command to the list [daemon only]"
  $ECHO "                        Commands with white space need to be quoted, "
  $ECHO "                        $PROG -a \"*.dcmd tra\"" 
  $ECHO "       -c             = Clear the list [daemon mode]"
  $ECHO "       -l             = Display the list [daemon mode]"
  $ECHO "       -i             = Shows the interval [daemon mode]"
  $ECHO "       -j [interval]  = Sets the interval [daemon mode]"
  $ECHO "       -q             = Shows the syslog send port"
  $ECHO "       -p [interval]  = Sets the syslog send port"
  $ECHO "       -e             = Sets the default list [mediamanage systems]"
  $ECHO ""
  $ECHO "Commands can be packed by separating with a colon:"
  $ECHO "    $PROG -m \"*.dcmd tra:*.sysd cw\""
  $ECHO ""
}

while getopts "udh:synm:a:clij:p:qez" OPTION ; do
  case $OPTION in
    z)
      DEBUG=ON   #set, since rest may use.
      continue;;
    u) 
      usage
      break;;
    d)
      daemon
      break;;
    n)
      turn_off
      break;;
    y)
      turn_on
      break;;
    h)
      set_host $OPTARG
      break;;
    s) 
      get_host
      exit $(status)
      break;;
    m)
      single_shot "$OPTARG"
      break;;
    a) 
      list_add "$OPTARG"
      break;;
    c)
      list_clear 
      break;;
    l)
      list_show    
      break;;
    i)
      show_interval
      break;;
    j)
      set_interval "$OPTARG" 
      break;;
    p) 
      set_port "$OPTARG"
      break;;
    q) 
      get_port
      break;;  
    e)
      list_default
      break;;  
    ?)
      $ECHO "ERROR- Illegal command line option. Use -u for usage."
      exit 1;;
  esac
done

exit 0
