#!/bin/bash
# $Header$

export LC_ALL=C

# set global vars, make statefile existent, and do basic sanity checks

VERBOSE=${IF_VERBOSE:-}

if [ -n "${IF_DEBUG:-}" ]; then
  set -x
fi
set -u

me="$0"
[ ${IF_DEBUG:-} ] && echo "$me $IFACE $MODE debug"

[ "$METHOD" = "manual" ] || exit 0
[ "$ADDRFAM" = "inet" ] || exit 0

# common functions for interface scripts

#
# misc helper functions
#

error()
{
  echo "$me: ERR: $1" >&2
}

warn()
{
  echo "$me: WARN: $1" >&2
}

abort()
{
  error "$1"
  add_down "abort" "1"
  exit 1
}

msg()
{
  echo "$me: $1" >&2
}

verbose()
{
  [ $VERBOSE ] && echo "$me: $*"
}

cmd()
{
  local RET
  [ $VERBOSE ] && echo "$me: $*"
  SHOW_ERRORS=1
  if [ "$1" = "--no-errors" ]; then
    shift
    SHOW_ERRORS=0
  fi
  eval $1
  RET=$?
  if [ "$RET" -ne 0 -a "${NO_ERRORS:-}" = "1" ]; then
    echo >&2 "$me: $1 returned return value $RET"
  fi
  return $RET
}

# set_bool_value key{0/1} value, where key is a path in
# /proc/sys/net/ipv4/conf/$IF_DEVICE
set_bool_value()
{
  KEY="$1"
  VAL="$2"

  [ -z "${VAL:-}" ] && exit 0

  IFACEDIR="/proc/sys/net/ipv4/conf/$IF_DEVICE"

  if ! echo $VAL | grep -s -q '\(0\|1\)'; then
    abort "no valid value $VAL for key $KEY"
  fi
  
  if [ -e "$IFACEDIR/$KEY" ]; then
    echo $VAL > $IFACEDIR/$KEY
  else
    abort "No such key $KEY in $IFACEDIR for $IF_DEVICE"
  fi
}

#
# functions for handling the state
#

# add_down KEY VAL
add_down()
{
  KEY="$1"
  VAL="$2"

  # add ip commands to down script
  verbose "add_down $1 $2"
  echo "$1: $2" >> $STATEFILE
}

get_down()
{
  KEY="$1"
  
  # execute and remove commands from state file
  if ! ls -n "$STATEFILE" | grep -q -- '^-....-..-. [[:digit:]]\+ 0 '; then
    abort "$STATEFILE isn't a plain file, or is writeable by non-root user"
  fi

  # read state file and return commands

  < $STATEFILE sed -n "/^$KEY:/s/^[^\:]*\:[[:space:]]\(.*\)/\1/p"
}

usrlesstac()
{
  sed '1!G;h;$!d;'
}

exec_down()
{
  KEY="$1"
  CMD="$2"
  verbose "exec_down $KEY $CMD"
  
  # execute and remove commands from state file
  if ! ls -n "$STATEFILE" | grep -q -- '^-....-..-. [[:digit:]]\+ 0 '; then
    abort "$STATEFILE isn't a plain file, or is writeable by non-root user"
  fi
  
  # read state file and execute commands _in_ _reverse_ _order_

  COMMAND="$CMD"
  if [ "$CMD" = "exec" ]; then
    COMMAND=""
  fi
  if [ -n "$CMD" ]; then
    < $STATEFILE sed -n "/^$KEY:/s/^[^\:]*\:[[:space:]]\(.*\)/\1/p" \
    | usrlesstac | while read line; do
      verbose "exec_down $COMMAND $line"
      eval "cmd \"$COMMAND $line\""
    done
  fi
  
  # remove commands from STATEFILE
  
  sed -i "/^${KEY}:/d" $STATEFILE
}

remove_state()
{
  rm -f "$STATEFILE"
}

# dev_state_entry DEVICE KEY
dev_state_entry()
{
  local dev key
  dev="$1"; key="$2"

  # check parameter
  local state
  state="$STATEDIR/$dev.state"
  [ ! -f "$state" ] && return 1
  
  < $state sed -n "/^$key: /{s/^[^:]\+:[[:space:]]*\(.*\)/\1/;p;q;}"
}

# state_entry KEY
state_entry()
{
  dev_state_entry "$IFACE" "$1"
  return $?
}

# find_if_by_state KEY PATTERN
list_if_by_state()
{
  local key pat
  key="$1"; pat="$2"; shift 2

  [ -z "${key:-}" -o -z "${pat:-}" ] && return 1

  local sfile dev
  if [ $# -eq 0 ]; then
    for sfile in $STATEDIR/*.state; do
      dev="${sfile%.state}"
      dev="${dev##*/}"

      if dev_state_entry "$dev" "$key" | grep -q "$pat"; then
        echo "$dev"
      fi
    done
  else
    for dev; do
      if dev_state_entry "$dev" "$key" | grep -q "$pat"; then
        echo "$dev"
      fi
    done
  fi
}

#
# helper functions for network interfaces
#

# list_ifaces
list_ifaces()
{
  < /proc/net/dev sed -n '/:/{s/^\([^:]\+\):.*/\1/;p;}'
}

# if_exists IFACE
if_exists()
{
  for iface in $(list_ifaces); do
    [ "$iface" = "$1" ] && return 0
  done
  return 1
}

# is_if_up IFACE
is_if_up()
{
  local state

  if if_exists "$1"; then
    if [ "$(ip link show "$1" | sed -n '1{s/.*[,<]\(UP\)[,>].*/\1/;p;q}')" = \
         "UP" ]; then
      return 0
    else
      return 1
    fi
  else
    return 0
  fi
}

# is_bonding_master IFACE
is_bonding_master()
{
  local state

  if if_exists "$1"; then
    if [ "$(ip link show "$1" | sed -n '1{s/.*[,<]\(MASTER\)[,>].*/\1/;p;q}')" = \
         "MASTER" ]; then
      return 0
    else
      return 1
    fi
  else
    return 0
  fi
}

# has_ip_address IFACE
has_ip_address()
{
  ip addr show dev "$1" | grep -s -q inet[^6]
}

# get_if_mac IFACE
get_if_mac()
{
  ip --oneline link show "$1" 2>/dev/null | \
     sed 's/.*[[:space:]]\+link\/ether[[:space:]]\+\([0-9a-fA-F:]\+\).*/\1/;'
}

# returns v4 or v6 for appropriate address
addrtype()
{
  ADDR="$1"
  if echo $ADDR | grep -q '^[0-9\.]\+$'; then
    echo "v4"
    return 0
  elif echo $ADDR | grep -q '^[0-9a-fA-F\:]\+$'; then
    echo "v6"
    return 0
  else
    echo "unknown address format"
    exit 1
  fi
}

#
# helper functions for vlan management
#

modprobe 8021q 2> /dev/null || :

VLAN_CFG=/proc/net/vlan/config

if [ ! -r "$VLAN_CFG" ]; then
  VLAN_SUPPORT=no
  # provide dummy functions
  find_vlan() { return 0; }
  get_vlan_id() { echo 0; }
  is_active_vlan_master() { return 0; }
else
VLAN_SUPPORT=yes

# the real functions
read_vlan_config()
{
  < "$VLAN_CFG" sed -n '1d;2d;{s/|/ /g;s/[[:space:]]\+/ /g;p;}'
}

#find_vlan MASTER VLANID
find_vlan()
{
  local iface vlanid master
  read_vlan_config \
  | while read iface vlanid master; do
    if [ "$master" = "$1" -a "$vlanid" = "$2" ]; then
      echo "$iface"
      break
    fi
  done
}

# get_vlan_id IFACE
get_vlan_id()
{
  local iface vlanid master
  read_vlan_config \
  | while read iface vlanid master; do
    if [ "$iface" = "$1" ]; then
      echo "$vlanid"
      exit 42
    fi
  done
  [ $? -eq 42 ] || echo 0
  return 0
}

# is_active_vlan_master IFACE
is_active_vlan_master()
{
  local iface vlanid master
  read_vlan_config | (
    while read iface vlanid master; do
      if [ "$master" = "$1" ]; then
        exit 0
      fi
    done
    exit 1
  )
  return $?
}

# is_vlan_slave IFACE
is_vlan_slave()
{
  read_vlan_config | \
     sed 's/^\([^[:space:]]\+\)[[:space:]]\+\([^[:space:]]\+\)[[:space:]]\+\([^[:space:]]\+\)$/\1/' | \
     grep -q "$1"
  return $?
}

fi

# init code that should be executed for every script

# physical device defaults to logical
if [ -z "${IF_DEVICE:-}" ]; then
  IF_DEVICE="$IFACE"
  export IF_DEVICE
fi

# it is possible that /usr is not yet mounted, we try to get by without /usr
PATH="/sbin:/bin"
NWSTATE="/etc/network/run"
STATEDIR="$NWSTATE/ifupdown-scripts-zg2"
STATEFILE="$STATEDIR/$IFACE.state"

# check that noone but root can write to $NWSTATE

if ! ls -nd $(readlink --canonicalize "$NWSTATE") | grep -q -- '^d....-..-. [[:digit:]]\+ 0 0 '; then
  abort "$NWSTATE is not (or does not point to) a directory, or is writeable by non-root user"
fi

if ! [ -e "$STATEFILE" ]; then
  umask 022
  if ! mkdir -p $STATEDIR; then
    abort "Unable to create $STATEDIR"
  fi
  touch $STATEFILE
fi
chown root $STATEFILE
chmod 644 $STATEFILE

# check for abort condition
if [ -f "$STATEFILE" -a "$MODE" = 'start' ]; then
  if grep -q '^abort:' $STATEFILE; then
    verbose "$IFACE $MODE abort"
    exit 0
  fi
fi


# Sanitize environment variables
IF_VLAN_ID=$[${IF_VLAN_ID:-0}+0]
IF_TYPE=${IF_TYPE:-}
IF_MASTER=${IF_MASTER:-}
IF_SCOPE=${IF_SCOPE:-}
IF_PROXY_ARP=${IF_PROXY_ARP:-}
IF_ACCEPT_REDIRECTS=${IF_ACCEPT_REDIRECTS:-}
IF_FLAGS=${IF_FLAGS:-}

verbose "$IFACE $MODE"

# on bonding interfaces, ifupdown doesn't set IF_TYPE
if [ -z "$IF_TYPE" ] && is_bonding_master $IF_DEVICE; then
  IF_TYPE="bonding-master"
fi


# vi:sw=2

# end of file
