#!/bin/bash
#
# columbus 0.1.1
# (C) thomas@apestaart.org
# GPL

# automatically guess what network we're on
# and perform network-specific stuff
# using an arp broadcast ping

# use /etc/columbus/networks as the file to use in pinging stuff
# format of that files is
# MAC\tIP\tnetwork
# where MAC is a six-byte address separated by :'s
# IP is the ip address on that network of that host's MAC address
# network is the user-friendly name of the network

# log to syslog

# in /etc/columbus, scripts named pre.[net] are executed before symlinks
# then, each file in the tree /etc/columbus/[net]/ is ln -sf'd to /
# then, the script post.[net] is executed

VERSION=0.1.1
IFCONFIG=/sbin/ifconfig
COLUMBUS=/etc/columbus
NETWORKS=$COLUMBUS/networks
ARPING="/sbin/arping -w 1 -c 1 -D"
RESTART_NET="/etc/rc.d/init.d/network restart"

debug()
{
  if test ! -z $DEBUG; then 
    echo "$@"
  fi
}

arp_scan()
# scans the network based on information in $NETWORKS
# puts the probed net name in $NET_FOUND 
{
  for a in `cat $NETWORKS | tr "\t" ","`; do 
    MAC=`echo $a | cut -d, -f1`
    IP=`echo $a | cut -d, -f2`
    NET=`echo $a | cut -d, -f3`
    # Get the second line of the arping result
    debug "arpinging $IP for MAC address $MAC ..."
    RESULT=`$ARPING $IP | head -n 2 | tail -n 1`
    MAC_FOUND=`echo $RESULT | cut -d\[ -f2 | cut -d\] -f1`
    if test "x$MAC" = "x$MAC_FOUND"; then
      debug "Network $NET found."
      NET_FOUND=$NET
      return 1
    fi
  done
  # return failure
  debug "Could not determine net !"
  return 0
}

check_sync()
{
  debug "Checking if network is in sync ..."
  arp_scan
  
  if test ! $?; then
    # could not get what net we're in
    echo "could not get net"
    return 0
  fi
  NET_CURRENT=`cat $COLUMBUS/current`
  if test "$NET_CURRENT" != "$NET_FOUND"; then
    debug "Network not in sync."
    return 0
  else
    debug "Network in sync."
    return 1
  fi
}

set_net()
{
  NET=$@

  debug "Setting net to $NET ..."
  # apply configuration
  cd $COLUMBUS
  # apply pre configuration
  if test -x "pre.$NET"; then
    $LOGGER "Executing pre.$NET"
    ./pre.$NET
  fi
  # now apply system configuration
  if test -d "$NET"; then
    $LOGGER "Symlinking files in $NET"
    cd $NET
    find * -type f -exec ln -sf $COLUMBUS/$NET/{} /{} \;
  fi
  cd ..
  
  # apply post configuration
  if test -x "post.$NET"; then
    $LOGGER "Executing post.$NET"
    ./post.$NET
  fi
  # save new last set network
  echo $NET > $COLUMBUS/current
}  

parse_options()
{
HELP="\
columbus $VERSION, (c) thomas@apestaart.org\n\n\
-a	always sets new network\n\
-c	clear your IP address before starting scan\n\
-d dev	use this device\n\
-h	help\n\
-l	log to syslog\n\
-n net	forcefully set this net\n\
-s	check if the current settings match the current network\n\
	and forcefully sync if they don't\n\
-v	verbose"

  # Note that we use `"$@"' to let each command-line parameter expand to a \
  # separate word. The quotes around `$@' are essential!
  # We need TEMP as the `eval set --' would nuke the return value of getopt.
  TEMP=`getopt -o acd:hln:sv -- "$@"`

  if [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi

  # defaults
  IFDEV=eth0
  LOGGER="echo"

  eval set -- "$TEMP"
  while true ; do
    case "$1" in
      -a) APPLY="yes" ; shift ;;
      -c) CLEAR="yes" ; shift ;;
      -d) IFDEV="$2" ; shift 2 ;;
      -h) echo -e "$HELP" ; exit ;;
      -l) LOGGER="logger -t columbus -s" ; shift ;;
      -n) NET="$2" ; shift 2;;
      -s) SYNC="yes"; shift ;;
      -v) DEBUG="yes"; shift ;;
      --) shift ; break ;;
       *) echo "List options here!" ; exit 1 ;;
    esac
  done
}

# start of main

parse_options $@

# if we've been asked to forcefully set a network, then do that
if test ! -z "$NET"; then
  $LOGGER "Forcing network $NET"
  set_net $NET
  exit
fi

# if we're asked to clear the ip address then do that
if test ! -z "$CLEAR"; then
  $IFCONFIG $IFDEV 0.0.0.0
fi

# if we're asked to bring it in sync, then do that
if test ! -z "$SYNC"; then
  check_sync
  if test $? -eq 0; then
    # set the net to what has been found
    echo "Resyncing net to $NET_FOUND"
    set_net $NET_FOUND
    $RESTART_NET
  fi
  exit
fi

# scan the network
arp_scan
# did we find it ?
if test -z "$NET_FOUND"; then
  $LOGGER "could not find network !"
  exit
fi

$LOGGER "network $NET_FOUND found"

# change it if allowed
if test ! -z "$APPLY"; then
  set_net $NET_FOUND
fi

NET_CURRENT=`cat $COLUMBUS/current`
if test "$NET_CURRENT" != "$NET_FOUND"; then
  $LOGGER "Last set network $NET_CURRENT not in sync with current $NET_FOUND !"
fi

