#!/bin/sh
#
# Copyright (C) 1998-99, VMware, Inc.  All Rights Reserved.
#
# This script manages the services needed to run VMware

# Basic support for IRIX style chkconfig
# chkconfig: 3 90 08
# description: Manages the services needed to run VMware

vmware_etc_dir=/etc/vmware

#
# Make the old installer aware of the new installer.sh mechanism.
# Kludge time :)
#

if [ "$1" = "stop" ]; then
    pid=$$
    ppid=`grep "^PPid:" /proc/"$pid"/status | cut -f 2`
    parent_name=`grep "^Name:" /proc/"$ppid"/status | cut -f 2`
    if [ "$parent_name" = "install.pl" ]; then
	# The old installer called this script
	vmware_installer="$vmware_etc_dir"/installer.sh
        if [ -x "$vmware_installer" ]; then
	    echo 'A previous installation of VMware has been detected.'
	    echo

	    kind=`"$vmware_installer" kind`
	    echo 'The previous installation was made by the '"$kind"' installer,'
	    echo 'which is more recent than this installer.'
	    echo 'This means that your are trying to downgrade or remove VMware.'
	    echo -n 'Do you really want to do this? (yes/no) [n] '
	    read answer
            echo
	    case "$answer" in
		y|Y|yes|Yes|YES)
		    vmware_db_bkp="$vmware_etc_dir"/db.tar.gz
		    "$vmware_installer" convertdb old "$vmware_db_bkp"
		    "$vmware_installer" uninstall
		    tar -C / -xzopf "$vmware_db_bkp"
		    rm -f "$vmware_db_bkp"
		    
		    exit 0
		    ;;
		    
		*)
		    # Prevent the display of the old installer's error message
		    kill "$ppid"
		    exit 1
		    ;;
	    esac
        else
	    echo 'Error: Unable to find the previous installer object '"$vmware_installer"'.'
	    echo

	    # Exit with 0 otherwise the user will never be able to re-install
            exit 0
	fi
    fi
fi

#
# From now on, this is a normal startup script. I swear.
#

vmware_checkvm="$vmware_etc_dir"/checkvm
if [ -x "$vmware_checkvm" ]; then
    # The toolbox is installed
    if "$vmware_checkvm" >/dev/null 2>&1; then
	# We are in a VM. Do nothing.
        exit 1
    fi
fi

# Since this script is installed, our database should be installed too and
# should contain the basic information
vmware_db="$vmware_etc_dir"/locations
if [ ! -r "$vmware_db" ]; then
    echo 'Warning: Unable to find VMware'"'"'s database '"$vmware_db"'.'
    echo	    
    
    exit 1
fi

# Load an answer from the database
db_load_answer() {
    id="$1"

    grep '^remove_answer '"$id"'$\|^answer '"$id"' ' "$vmware_db" | tail -1 | sed -n 's/^answer '"$id"' //p'
}

# Add a file to the database
db_add_file() {
    file="$1"

    echo 'file '"$file" >> "$vmware_db"
}

# Clear the configuration flag
clear_config_flag() {
    confflag="$1"

    touch "$confflag"
    chmod 644 "$confflag"
    db_add_file "$confflag"
}

vmware_init_dir=`db_load_answer 'INITDIR'`
# This defines echo_success() and echo_failure() on RedHat
# This defines obsolete daemon() and killproc() on some distributions
if [ -r "$vmware_init_dir"/init.d/functions ]; then
    . "$vmware_init_dir"/init.d/functions
fi

# This defines $rc_done and $rc_failed on S.u.S.E.
if [ -f /etc/rc.config ]; then
     # Don't quite feel like including the entire file.
     # There could be conflicts.
     rc_done="\033[71G\033[32mdone\033[m"
     rc_failed="\033[71G\033[31m\033[1mfailed\033[m"
else
     rc_done="\033[71G done"
     rc_failed="\033[71Gfailed"
fi

subsys=vmware
driver=vmmon
ppuser=vmppuser
vnet=vmnet
bridge=vmnet-bridge
dhcpd=vmnet-dhcpd
vmnet0=${vnet}0
vmnet1=${vnet}1
ping=vmware-ping

#
# Utilities
#

# Initialize some variables with values from the database
variables_init() {
    vmware_bin_dir=`db_load_answer 'BINDIR'`
    vmware_networking=`db_load_answer 'NETWORKING'`
    vmware_bridgeif=`db_load_answer 'VNET_INTERFACE'`
    vmnetHostOnlyAddress=`db_load_answer 'VNET_HOSTONLY_HOSTADDR'`
    vmnetHostOnlyNetmask=`db_load_answer 'VNET_HOSTONLY_NETMASK'`
}

#
# Count the number of running virtual machines
# by looking at the number of references to the
# $driver module.
#
countVMs() {
    # The ^ matters because our modules can depend on other kernel modules
    echo `/sbin/lsmod | \
       awk 'BEGIN { n = 0; } /^'"$driver"'/ { n = $3; } END { print n; }'`
}

#
# Check if a route to a network via a specific interface
# is installed.  Return true if it is _NOT_ present.
#
noRoutePresent() {
    test X`/sbin/route -n | awk '/'"$1"'.*'"$2"'/ { print "yes"; }'` != Xyes
}

#
# Check if the Class C network is reachable by pinging
# the host at <addr> (the address we'll be assign to
# ourself.  This is used to check/verify a host-only
# config before enabling the interface and starting DHCPD
#
# NB: If you don't want to do this test, just substitute
#     false for it.
#
lookForHostOnlyNetwork() {
    "$vmware_bin_dir"/"$ping" "$1"
}

#
# XXX This assumes a Class C address; should use the netmask
# XXX to calculate the network number but that's too painful
# XXX in the shell.
#
ipv4Network() {
    echo "$1" | sed 's/[.][0-9]*$//'
}

vmware_failed() {
   if `type -type echo_failure >/dev/null`; then
      echo_failure
   else
      echo -ne "$rc_failed"
   fi
}

vmware_success() {
   if `type -type echo_success >/dev/null`; then
      echo_success
   else
      echo -ne "$rc_done"
   fi
}

# Execute a macro
# Parameters:
#  $1 = service name
#  $2 = executed name
# Works with bash, zsh
vmware_exec() {
   # Display the service name
   echo -n '   '"$1"

   # execute the macro and trash its output
   # On Caldera 2.2, SIGHUP is sent to all our children when this script exits
   # I wanted to use shopt -u huponexit instead but their bash is too old
   if (trap '' SIGHUP; "$2") >/dev/null 2>&1
   then
      vmware_success
      echo
      return 0
   else
      vmware_failed
      echo
      return 1
   fi
}

# Execute a macro in the background
# Parameters:
#  $1 = service name
#  $2 = executed name
# Works with bash, zsh
vmware_bg_exec() {
   # Display the service name
   echo -n '   '"$1"' (background)'

   # execute the macro in the background and log its output
   # On Caldera 2.2, SIGHUP is sent to all our children when this script exits
   # I wanted to use shopt -u huponexit instead but their bash is too old
   (trap '' SIGHUP; "$2") 2>&1 | logger -t 'VMware[init]' -p daemon.err &

   # Display the execution result
   vmware_success
   echo
   return 0
}

#
# Macro definitions
#

# Start the virtual machine monitor kernel service
vmware_start_vmmon() {
   /sbin/insmod -s -f "$driver" || exit 1

   exit 0
}

# Stop the virtual machine monitor kernel service
vmware_stop_vmmon() {
   if (/sbin/lsmod | grep -- ^"$driver") >/dev/null 2>&1;
   then
      /sbin/rmmod "$driver" || exit 1
   fi

   exit 0
}

# Start the virtual machine parallel port kernel service
vmware_start_vmppuser() {
   if ! grep -q ' parport_release[^'$'\t'']*$' /proc/ksyms; then
     # parport support is not built in the kernel
     /sbin/modprobe parport || exit 1
   fi
   if ! grep -q ' parport_pc_[^'$'\t'']*$' /proc/ksyms; then
     # parport_pc support is not built in the kernel
     /sbin/modprobe parport_pc || exit 1
   fi
   /sbin/insmod -s -f "$ppuser" || exit 1

   exit 0
}

# Stop the virtual machine parallel port kernel service
vmware_stop_vmppuser() {
   if (/sbin/lsmod | grep -- ^"$ppuser") >/dev/null 2>&1;
   then
      /sbin/rmmod "$ppuser" || exit 1
   fi
   # Try to unload the modules. Failure is allowed because some other process
   # could be using them.
   /sbin/modprobe -r parport_pc >/dev/null 2>&1
   /sbin/modprobe -r parport >/dev/null 2>&1

   # Return the right exitcode even if the previous command failed
   exit 0
}

# Start the virtual ethernet kernel service
vmware_start_vmnet() {
   /sbin/insmod -s -f "$vnet" || exit 1

   exit 0
}

# Stop the virtual ethernet kernel service
vmware_stop_vmnet() {
   if (/sbin/lsmod | grep -- ^"$vnet") >/dev/null 2>&1;
   then
      /sbin/rmmod "$vnet" || exit 1
   fi

   exit 0
}

# Start the network bridge user service
vmware_start_bridge() {
   # Run the daemon in its own directory (to avoid busy devices)
   cd "$vmware_bin_dir" && "$vmware_bin_dir"/"$bridge" -d /dev/"$vmnet0" "$vmware_bridgeif" || exit 1

   exit 0
}

# Stop the network bridge user service
vmware_stop_bridge() {
   if `which start-stop-daemon >/dev/null`; then
      start-stop-daemon --stop --user 0 --oknodo --exec "$vmware_bin_dir"/"$bridge" || exit 1
   elif `type -type killproc >/dev/null`; then
      # Suppress unwanted output
      killproc "$vmware_bin_dir"/"$bridge" >/dev/null 2>&1 || exit 1
   else
      echo Unable to stop "$vmware_bin_dir"/"$bridge"
      exit 1
   fi

   exit 0
}

# Start the host-only network user service
vmware_start_hostonly() {
   #
   # Do a cursory check to see if the host-only network
   # configuration is still ok.  We do this so that mobile
   # hosts don't get setup at install time and then moved to
   # a new locale where the host-only network config is no
   # longer valid.
   #
   # NB: This really needs to be done at power-on time when
   #     VM is configured to use host-only networking so that
   #     we aren't fooled by dynamic changes in the network.
   #
   # XXX ping takes 10 seconds to timeout if noone answers;
   #	  that slows boot too much so we do this bit in the
   #	  background.
   #
   if lookForHostOnlyNetwork "$vmnetHostOnlyAddress"; then
      echo "Host-only networking disabled because $vmnetHostOnlyAddress"
      echo "appears to be a real, physical, existing address."
      echo "Check your host-only network configuration."
      exit 1
   else
      # Configure the virtual interface and define the private IP network
      # 2.0.x does not install a route when the interface is
      # marked up, but 2.2.x does.  Since we want to see any
      # errors from route we don't just discard messages from
      # route, but instead check if the route got installed
      # before manually adding one.
      netnum=`ipv4Network "$vmnetHostOnlyAddress" "$vmnetHostOnlyNetmask"`
      if ifconfig "$vmnet1" inet "$vmnetHostOnlyAddress" \
                            netmask "$vmnetHostOnlyNetmask" up \
         && noRoutePresent "$netnum" "$vmnet1"; then
         route add -net "$netnum".0 netmask "$vmnetHostOnlyNetmask" "$vmnet1"
      fi

      # Set up a dhcp server on the private IP network
      #
      # NB: The dhcp logs its output, we can safely trash it
      #
      # Run the daemon in its own directory (to avoid busy devices)
      cd "$vmware_bin_dir" && "$vmware_bin_dir"/"$dhcpd" \
		    -cf "$vmware_etc_dir"/"$vmnet1".conf \
		    -lf "$vmware_etc_dir"/"$vmnet1".leases \
		    -pf /var/run/"$vmnet1".pid \
		    "$vmnet1" >/dev/null 2>&1
   fi

   exit 0
}

# Stop the host-only network user service
vmware_stop_hostonly() {
   # Stop the dhcp server
   if `which start-stop-daemon >/dev/null`; then
      start-stop-daemon --stop --user 0 --oknodo --exec "$vmware_bin_dir"/"$dhcpd" || exit 1
   elif `type -type killproc >/dev/null`; then
      # Suppress unwanted output
      killproc "$vmware_bin_dir"/"$dhcpd" >/dev/null 2>&1 || exit 1
   else
      echo Unable to stop "$vmware_bin_dir"/"$dhcpd"
      exit 1
   fi

   # Terminate the private network
   netnum=`ipv4Network "$vmnetHostOnlyAddress" "$vmnetHostOnlyNetmask"` || exit 1
   noRoutePresent "$netnum" "$vmnet1" || \
      route del -net "$netnum".0 netmask "$vmnetHostOnlyNetmask" || exit 1
   # To test if the interface exists, we can not just look at the exitcode
   # because old versions of ifconfig don't exit with 1 when invoked with a
   # non-existing interface
   present=`ifconfig "$vmnet1" 2>/dev/null`
   if [ "$present" != "" ]; then
      ifconfig "$vmnet1" down || exit 1
   fi

   exit 0
}

# See how we were called.
case "$1" in
  start)
        if [ -e "$vmware_etc_dir"/not_configured ]; then
	   echo 'VMware is installed, but it has not been (correctly) configured'
           echo 'for the running kernel. To (re-)configure it, invoke the'
           echo 'following command: '"$vmware_bin_dir"'/vmware-config.pl.'
           echo

           exit 1
        fi

	# VMware is configured. Fetch useful information from the database
        variables_init

	echo "Starting VMware services:"
        exitcode='0'

	vmware_exec 'Virtual machine monitor' vmware_start_vmmon
        exitcode=`expr "$exitcode" + "$?"`

        # The module is not installed on kernels 2.0.x
        if [ -r /lib/modules/preferred/misc/"$ppuser".o -o -r /lib/modules/`uname -r`/misc/"$ppuser".o ]; then
	   vmware_exec 'Virtual bidirectional parallel port' vmware_start_vmppuser
           exitcode=`expr "$exitcode" + "$?"`
        fi

	if [ "$vmware_networking" = "yes" ]; then
	    vmware_exec 'Virtual ethernet' vmware_start_vmnet
	    exitcode=`expr "$exitcode" + "$?"`

	    vmware_exec 'Bridged networking' vmware_start_bridge
	    exitcode=`expr "$exitcode" + "$?"`

	    if [ -n "$vmnetHostOnlyAddress" -a -n "$vmnetHostOnlyNetmask" ]; then
                vmware_bg_exec 'Host-only networking' vmware_start_hostonly
                exitcode=`expr "$exitcode" + "$?"`
	    fi
	fi

        if [ "$exitcode" -gt 0 ]; then
	   clear_config_flag "$vmware_etc_dir"/not_configured
           exit 1
        fi

	[ -d /var/lock/subsys ] || mkdir -p /var/lock/subsys
	touch /var/lock/subsys/"$subsys"
	;;

  stop)
        variables_init

	# look for running vm's
	nvms="`countVMs`"
	if [ "$nvms" -gt 0 ]; then
	    if [ "$nvms" -gt 1 ]; then
	        echo "There are $nvms VMs still running.  Please shut them down first."
	    else
	        echo "There is one VM still running.  Please shut it down first."
	    fi
	    exit 1;
	fi

	echo "Stopping VMware services:"
        exitcode='0'

	vmware_exec 'Virtual machine monitor' vmware_stop_vmmon
        exitcode=`expr "$exitcode" + "$?"`

        # The module is not installed on kernels 2.0.x
        if [ -r /lib/modules/preferred/misc/"$ppuser".o -o -r /lib/modules/`uname -r`/misc/"$ppuser".o ]; then
           vmware_exec 'Virtual bidirectional parallel port' vmware_stop_vmppuser
           exitcode=`expr "$exitcode" + "$?"`
        fi

	if [ "$vmware_networking" = "yes" ]; then
	    # NB: must kill off processes using vmnet before
	    #     unloading module

            vmware_exec 'Bridged networking' vmware_stop_bridge
            exitcode=`expr "$exitcode" + "$?"`

            if [ -n "$vmnetHostOnlyAddress" -a -n "$vmnetHostOnlyNetmask" ]; then
            vmware_exec 'Host-only networking' vmware_stop_hostonly
            exitcode=`expr "$exitcode" + "$?"`
            fi

	    vmware_exec 'Virtual ethernet' vmware_stop_vmnet
            exitcode=`expr "$exitcode" + "$?"`
        fi

        if [ "$exitcode" -gt 0 ]; then
           exit 1
        fi

	rm -f /var/lock/subsys/"$subsys"
	;;

  status)
	# look for running vm's
	nvms="`countVMs`"
	if [ "$nvms" -gt 0 ]; then
	    echo "There are $nvms virtual machines running"
	else
	    echo "There are no virtual machines running"
	fi

	if [ "$vmware_networking" = "yes" ]; then
            status "$bridge"
            status "$dhcpd"
        fi

	echo -n "Module $driver "
	/sbin/modprobe "$driver" >/dev/null 2>&1 && echo installed || echo "not installed"
	echo -n "Module $ppuser "
	/sbin/modprobe "$ppuser" >/dev/null 2>&1 && echo installed || echo "not installed"
	if [ "$vmware_networking" = "yes" ]; then
            echo -n "Module $vnet "
            /sbin/modprobe "$vnet" >/dev/null 2>&1 && echo installed || echo "not installed"
        fi
	;;

  restart)
	"$0" stop && "$0" start
	;;

  *)
	echo "Usage: `basename "$0"` {start|stop|status|restart}"
	exit 1
esac

exit 0
