#! /bin/sh
# Sun xVM VirtualBox
# Linux static host networking interface initialization
#

#
# Copyright (C) 2007 Sun Microsystems, Inc.
#
# This file is part of VirtualBox Open Source Edition (OSE), as
# available from http://www.virtualbox.org. This file is free software;
# you can redistribute it and/or modify it under the terms of the GNU
# General Public License (GPL) as published by the Free Software
# Foundation, in version 2 as it comes in the "COPYING" file of the
# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
#

# chkconfig: 35 30 60
# description: VirtualBox permanent host networking setup
#
### BEGIN INIT INFO
# Provides:       vboxnet
# Required-Start: $remote_fs $network
# Required-Stop:  $remote_fs
# Default-Start:  2 3 4 5
# Default-Stop:   0 1 6
# Description:    VirtualBox permanent host networking setup
### END INIT INFO

PATH=/sbin:/bin:/usr/sbin:/usr/bin:$PATH
CONFIG="/etc/vbox/interfaces"
VARDIR="/var/run/VirtualBox"
VARFILE="/var/run/VirtualBox/vboxnet"
TAPDEV="/dev/net/tun"
NOLSB=yes

# Include virtualbox-ose defaults if available
if [ -f /etc/default/virtualbox-ose ] ; then
	. /etc/default/virtualbox-ose
fi

[ -f /lib/lsb/init-functions ] && NOLSB=

if [ -n "$NOLSB" ]; then
    if [ -f /etc/redhat-release ]; then
        system=redhat
    elif [ -f /etc/SuSE-release ]; then
        system=suse
    elif [ -f /etc/gentoo-release ]; then
        system=gentoo
    fi
fi

if [ -z "$NOLSB" ]; then
    . /lib/lsb/init-functions
    fail_msg() {
        [ -f "$VARFILE" ] && rm $VARFILE
    	log_action_end_msg 1
    }
    succ_msg() {
        log_action_end_msg 0
    }
    begin_msg() {
    	log_action_begin_msg $1
    }
else
    if [ "$system" = "redhat" ]; then
        . /etc/init.d/functions
        fail_msg() {
            echo_failure
            echo
            echo "  ($1)"
        }
        succ_msg() {
            echo_success
            echo
        }
    elif [ "$system" = "suse" ]; then
        . /etc/rc.status
        fail_msg() {
            rc_failed 1
            rc_status -v
            echo "  ($1)"
        }
        succ_msg() {
            rc_reset
            rc_status -v
        }
    elif [ "$system" = "gentoo" ]; then
        . /sbin/functions.sh
        fail_msg() {
            eerror "$1"
        }
        succ_msg() {
            eend "$?"
        }
        begin_msg() {
            ebegin "$1"
        }
        if [ "`which $0`" = "/sbin/rc" ]; then
            shift
        fi
    else
        fail_msg() {
            echo " ...failed!"
            echo "  ($1)"
        }
        succ_msg() {
            echo " ...done."
        }
    fi
    if [ "$system" != "gentoo" ]; then
        begin_msg() {
            [ -z "${1:-}" ] && return 1
            if [ -z "${2:-}" ]; then
                echo -n "$1"
            else
                echo -n "$1: $2"
            fi
        }
    fi
fi

failure()
{
    fail_msg "$1"
    # never return with exit code != 0
    exit 0
}

running()
{
    test -f "$VARFILE"
}

# Create all permanent TAP devices registered on the system, add them to a
# bridge if required and keep a record of proceedings in the file
# /var/run/VirtualBox/vboxnet.  If this file already exists, assume that the
# script has already been started and do nothing.
start_network()
{
    begin_msg "Starting VirtualBox host networking"
    # If the service is already running, return successfully.
    if [ -f "$VARFILE" ]; then
      succ_msg
      return 0
    fi
    # Fail if we can't create our runtime record file
    if [ ! -d "$VARDIR" ]; then
      if ! mkdir "$VARDIR" 2> /dev/null; then
        failure "Cannot create $VARDIR"
      fi
    fi
    if ! touch "$VARFILE" 2> /dev/null; then
      failure "Cannot create $VARFILE"
    fi
    # If there is no configuration file, report success
    if [ ! -f "$CONFIG" ]; then
      succ_msg
      return 0
    fi
    # Fail if we can't read our configuration
    if [ ! -r "$CONFIG" ]; then
      failure "Cannot read $CONFIG"
    fi
    # Fail if we don't have tunctl
    if ! VBoxTunctl -h 2>&1 | grep VBoxTunctl > /dev/null; then
      failure "VBoxTunctl not found"
    fi
    # Read the configuration file entries line by line and create the
    # interfaces
    while read line; do
      set ""$line
      # If the line is a comment then ignore it
      if ((! expr match "$1" "#" > /dev/null) && (! test -z "$1")); then
        # Check that the line is correctly formed (an interface name plus one
        # or two non-comment entries, possibly followed by a comment).
        if ((! expr match "$2" "#" > /dev/null) &&
            (test -z "$4" || expr match "$4" "#" > /dev/null)); then
	  if [ "$module_available" != 1 ]
	  then
	 	# Fail if we don't have the kernel tun device
		# Make sure that the tun module is loaded (Ubuntu 7.10 needs this)
		# We only test this once, but we have to do it here, because otherwise an empty
		# configuration file would trigger the modprobe too.
		modprobe tun > /dev/null 2>&1
		if ! cat /proc/misc 2>/dev/null | grep tun > /dev/null
		then
			failure "Linux tun/tap subsystem not available"
		fi
		module_available=1
	  fi
          # Name our parameters, to make this script slightly less unreadable
          interface=$1
          user=$2
          bridge=$3
          # As the very first thing, try delete the interface. Might already
          # exist with different configuration. Ignore errors.
          VBoxTunctl -d $interface > /dev/null 2>&1
          case $user in
            +*)
                group=`echo $user | cut -c2-`
                cmd="VBoxTunctl -t $interface -g $group"
                ;;
            *)
                cmd="VBoxTunctl -t $interface -u $user"
                ;;
          esac
          # Try to create the interface
          if $cmd > /dev/null 2>&1; then
            # On SUSE Linux Enterprise Server, the interface does not
            # appear immediately, so we loop trying to bring it up.
            i=1
            while [ $i -le 10 ]; do
              ifconfig "$interface" up 2> /dev/null
              if ifconfig | grep "$interface" > /dev/null; then
                # Add the interface to a bridge if one was specified
                if [ -n "$bridge" ]; then
                  if brctl addif "$bridge" "$interface" 2> /dev/null; then
                    echo "$interface $user $bridge" >> "$VARFILE"
                  else
                    echo "$interface $user" >> "$VARFILE"
                    echo "Warning - failed to add interface $interface to the bridge $bridge"
                  fi
                else
                  echo "$interface $user" >> "$VARFILE"
                fi
                i=20
              else
                i=`expr $i + 1`
                sleep .1
              fi
            done
            if [ $i -ne 20 ]; then
              echo "Warning - failed to bring up the interface $interface"
            fi
          else
            echo "Warning - failed to create the interface $interface for the user $user"
          fi
        else
          echo "Warning - invalid line in $CONFIG:"
          echo "  $line"
        fi
      fi
    done < "$CONFIG"
    # Set /dev/net/tun to belong to the group vboxusers if it exists and does
    # yet belong to a group.
    if ls -g "$TAPDEV" 2>/dev/null | grep root > /dev/null; then
      chgrp vboxusers "$TAPDEV"
      chmod 0660 "$TAPDEV"
    fi
    succ_msg
    return 0
}

# Shut down VirtualBox host networking and remove all permanent TAP
# interfaces.  This action will fail if some interfaces could not be removed.
stop_network()
{
    begin_msg "Shutting down VirtualBox host networking"
    # If there is no runtime record file, assume that the service is not
    # running.
    if [ ! -f "$VARFILE" ]; then
      succ_msg
      return 0
    fi
    # Fail if we can't read our runtime record file or write to the
    # folder it is located in
    if [ ! -r "$VARFILE" -o ! -w "$VARDIR" ]; then
      failure "Failed to read $VARFILE or to write $VARDIR"
    fi
    # Fail if we don't have tunctl
    if ! VBoxTunctl -h 2>&1 | grep VBoxTunctl > /dev/null; then
      failure "VBoxTunctl not found"
    fi
    # Read the runtime record file entries line by line and delete the
    # interfaces.  The format of the runtime record file is not checked for
    # errors.
    while read line; do
      set ""$line
      # Remove the interface from a bridge if it is part of one
      if [ -n "$3" ]; then
        brctl delif "$3" "$1" 2> /dev/null
      fi
      # Remove the interface.  Roll back everything and fail if this is not
      # possible
      if (! ifconfig "$1" down 2> /dev/null ||
          ! VBoxTunctl -d "$1" > /dev/null 2>&1); then
        while read line; do
          set ""$line
          VBoxTunctl -t "$1" -u "$2" > /dev/null 2>&1
          ifconfig "$1" up 2> /dev/null
          if [ -n "$3" ]; then
            brctl addif "$3" "$1"
          fi
        done < "$VARFILE"
      fi
    done < "$VARFILE"
    rm -f "$VARFILE" 2> /dev/null
    succ_msg
    return 0
}

# Shut down VirtualBox host networking and remove all permanent TAP
# interfaces.  This action will succeed even if not all interfaces could be
# removed.  It is only intended for exceptional circumstances such as
# uninstalling VirtualBox.
force_stop_network()
{
    begin_msg "Shutting down VirtualBox host networking"
    # If there is no runtime record file, assume that the service is not
    # running.
    if [ ! -f "$VARFILE" ]; then
      succ_msg
      return 0
    fi
    # Fail if we can't read our runtime record file or write to the
    # folder it is located in
    if [ ! -r "$VARFILE" -o ! -w "$VARDIR" ]; then
      failure "Failed to read $VARFILE or to write $VARDIR"
    fi
    # Fail if we don't have tunctl
    if ! VBoxTunctl -h 2>&1 | grep VBoxTunctl > /dev/null; then
      failure "VBoxTunctl not found"
    fi
    # Read the runtime record file entries line by line and delete the
    # interfaces.  The format of the runtime record file is not checked for
    # errors.
    while read line; do
      set ""$line
      # Remove the interface from a bridge if it is part of one
      if [ -n "$3" ]; then
        brctl delif "$3" "$1" 2> /dev/null
      fi
      # Remove the interface.
      ifconfig "$1" down 2> /dev/null
      VBoxTunctl -d "$1" > /dev/null 2>&1
    done < "$VARFILE"
    rm -f "$VARFILE" 2> /dev/null
    succ_msg
    return 0
}

case "$1" in
start)
    # try to insert module but do not fail if not possible
    if [ "$LOAD_VBOXDRV_MODULE" = 1 ]; then
        /sbin/modprobe -q vboxdrv || true
    fi
    start_network
    ;;
stop)
    stop_network
    if [ "$LOAD_VBOXDRV_MODULE" = 1 ]; then
    	/sbin/modprobe -qr vboxdrv || true
    fi
    ;;
restart|reload)
    stop_network && start_network
    ;;
force-reload)
    stop_network
    start_network
    ;;
force-stop)
    force_stop_network
    ;;
status)
    if running; then
        echo "VirtualBox host networking is loaded."
    else
        echo "VirtualBox host networking is not loaded."
    fi
    ;;
*)
    echo "Usage: `basename $0` {start|stop|force-stop|restart|force-reload|status}"
    exit 1
esac

exit 0
