#!/bin/bash

#
# This script is a simple daemon that checks (and restart/reinit) your vpnc
# and your RH kerberos ticket. The script is remember your password so you 
# don't have to repeatedly type your password.
#
# Usage:   rh-vpnc-watcher [ vpnc-config [ watch-addr ]]
#
#  	   vpnc-config   -- vpnc config file (default: /etc/vpnc/default.conf)
#	   watch-addr    -- an internal VPN address (default: porkchop.devel.redhat.com)
#
# Copyright (C) 2008 Karel Zak <kzak@redhat.com>
# 
# This file is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This file is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
#
# Last change: Thu Feb 21 13:00:06 CET 2008
#

VPNC_CONFIG=${1:-"/etc/vpnc/default.conf"}
VPNC_WATCH_ADDR=${2:-"10.10.36.73"}
VPNC_BIN="/usr/sbin/vpnc"

# Set when your local username is different to VPN/kerberos username
#LOCAL_ACCOUNT="matej"

PWD_TIMEOUT=30
PING_TIMEOUT=5
PROGNAME=$(basename $0)
PIDFILE="/var/run/${PROGNAME}.pid"
LOGFILE="/var/log/rh-vpnc-watcher"

export LC_ALL=C
INITIALIZED=""
loops=0

function log()
{
	echo "$(date '+%b %d %H:%M:%S') $PROGNAME[$MY_PID]: $1" >> $LOGFILE
}

function fatal() 
{
	if [ -n "$LOGFILE_ENABLED" ]; then
		log "FATAL: $1"
	else
		echo "$PROGNAME: FATAL: $1"
	fi
	rm -f "$PIDFILE"
	exit 1
}

function warning() 
{
	if [ -n "$LOGFILE_ENABLED" ]; then
		log "WARNING: $1"
	else
		echo "$PROGNAME: WARNING: $1"
	fi
}

function infomsg() 
{
	if [ -n "$LOGFILE_ENABLED" ]; then
		log "INFO: $1"
	else
		echo "$PROGNAME: INFO: $1"
	fi
}

function killpid()
{
	local pid

	pid=$1
	kill -TERM $pid &> /dev/null
	usleep 100000
	if [ -d "/proc/$pid" ]; then
		kill -KILL $pid &> /dev/null
	fi
}

function vpnc_stop()
{
	local pid

	pid=$(/sbin/pidof vpnc)
	if [ -n "$pid" ]; then
		infomsg "vpnc ($pid) is running, stopping"
		killpid $pid
	fi
}

function vpnc_start()
{
	local i

	infomsg "starting vpnc"
expect -f - &> /dev/null << EOF
spawn /usr/sbin/vpnc $VPNC_CONFIG
expect "Enter password for $VPNC_USER@$VPNC_GW: "
send $VPNC_PWD\n
expect eof
EOF
	for i in 1 2 4 6 8; do
		if [ -n "$(/sbin/pidof vpnc)" ]; then
			return 0
		fi
		sleep $i
	done
	warning "start vpnc failed"
	return 1
}

function kerb_init()
{
	infomsg "call kinit"

	su $LOCAL_ACCOUNT -c "( expect -f - << EOF
spawn /usr/kerberos/bin/kinit
expect \"Password for $VPNC_USER@REDHAT.COM: \"
send $VPNC_PWD\n
expect eof
EOF
)" &> /dev/null

}

function kerb_check()
{
	local ex extime

	extime=0
	mytime=$(date '+%s')

	ex="$(su - $LOCAL_ACCOUNT -c /usr/kerberos/bin/klist 2> /dev/null | gawk '/^[0-9]+\/[0-9]+\/[0-9]+.*REDHAT.COM@REDHAT.COM$/ { print $3, $4 }')"
	
	if [ -n "$ex" ]; then
		extime=$(date --date "$ex" '+%s')
	fi
	
	if [ $mytime -ge $(( $extime - 1200 )) ]; then
		warning "kerberos ticket for $LOCAL_ACCOUNT expires now or in next 10 mins, reinitialize"
		kerb_init
	fi
}

# check dependencies
if [ ! -x "$VPNC_BIN" ]; then
	fatal "Sir, install vpnc!"
fi
if [ ! -x "/usr/bin/expect" ]; then
	fatal "Sir, install expect!"
fi


# running?
if [ -f "$PIDFILE" ]; then
	old_pid=$(cat $PIDFILE)
	if [ -d "/proc/$old_pid" ]; then
		echo "$PROGNAME: FATAL: a $PROGNAME ($old_pid) is already running"
		exit 1
	fi
fi
echo "$$" > $PIDFILE

# config file
if [ ! -f "$VPNC_CONFIG" ]; then
	fatal "cannot found $VPNC_CONFIG"
fi

# gateway
VPNC_GW=$(gawk '/^IPSec gateway / { print $3 }' $VPNC_CONFIG)
if [ -z "$VPNC_GW" ]; then
	fatal "cannot found 'IPSec gateway' in $VPNC_CONFIG"
fi

# user
VPNC_USER=$(gawk '/^Xauth username / { print $3 }' $VPNC_CONFIG)
if [ -z "$VPNC_USER" ]; then
	fatal "cannot found 'Xauth username' in $VPNC_CONFIG"
fi

# default interface
IF_DEFAULT=$(route -n | gawk '/^0\.0\.0\.0/ { print $8 }')
if [ -z "$IF_DEFAULT" ]; then
	fatal "default system route undefined"
fi

ping -c 2 -w $PING_TIMEOUT -v -I $IF_DEFAULT $VPNC_GW &> /dev/null
if [ "$?" != "0" ]; then
	warning "IPSec gateway $IF_DEFAULT is unreachable"
	LINK_UP=0
else
	LINK_UP=1
fi

echo -n "Enter your password for $VPNC_USER@$VPNC_GW: "
read -t $PWD_TIMEOUT -s VPNC_PWD <&1
echo

LOCAL_ACCOUNT=${LOCAL_ACCOUNT:-"$VPNC_USER"}

# start new background subshell
while [ 1 ]; do

	if [ -z "$INITIALIZED" ]; then
		trap "{ vpnc_stop; rm -f $PIDFILE; infomsg 'terminated'; exit 1; }" TERM INT EXIT
		MY_PID=$(pidof -x $PROGNAME)
		echo "$MY_PID" > $PIDFILE

		touch $LOGFILE
		LOGFILE_ENABLED="yes"

		infomsg "----- successfully started -----"
		INITIALIZED="yes"
	fi

	if [ "$LINK_UP" == "0" ]; then
		infomsg "$IF_DEFAULT seems DOWN, waiting"
		while [ 1 ]; do
			ping -c 2 -w 20 -I $IF_DEFAULT $VPNC_GW &> /dev/null
			if [ "$?" == "0" ]; then
				LINK_UP=1
				break
			fi
			sleep 5
		done
		infomsg "$IF_DEFAULT seems UP"
	fi
	ping -c 2 -w $PING_TIMEOUT $VPNC_WATCH_ADDR  &> /dev/null
	if [ "$?" != "0" ]; then
		infomsg "VPN seems DOWN"
		# check link
		ping -c 2 -w 10 -I $IF_DEFAULT $VPNC_GW &> /dev/null
		if [ "$?" != "0" ]; then
			LINK_UP=0
		else
			# link is OK, restart VPN
			LINK_UP=1
			vpnc_stop
			vpnc_start
			kerb_check
			# DNS get screwed all the time
			rndc flush
		fi
	else
		# VPN and links are UP, so .. relax!
		sleep 60
	
		# check kerberos ticket (every 10mins)
		let loops++
		if [ $loops -ge 10 ]; then
			loops=0
			kerb_check
		fi
			
	fi
	
done &

exit 0