#!/bin/sh ########################################################################## # Copyright (c) 2001-2015 VMware, Inc. All rights reserved. ########################################################################## # # network (Linux) # # Using a combination of a system networking script, ifconfig, ifup, ifdown # and the ip command, attempt to release and renew DHCP leases upon receipt # of suspend and resume events, respectively. # echo `date` ": Executing '$0'" echo . `dirname "$0"`/../../statechange.subr # # find_networking_script -- # # Searches common Linux distro init/rc paths to find a singular network # services script. # # Result: # Returns a valid networking script path on success or "error" on failure. # # Side effects: # None. # find_networking_script() { local script="error" for dir in "/etc/init.d" "/sbin/init.d" "/etc" "/etc/rc.d" ; do if [ -d "$dir/rc0.d" ] && [ -d "$dir/rc1.d" ] && [ -d "$dir/rc2.d" ] && [ -d "$dir/rc3.d" ] && [ -d "$dir/rc4.d" ] && [ -d "$dir/rc5.d" ] && [ -d "$dir/rc6.d" ]; then # Now find the appropriate networking script. if [ -d "$dir/init.d" ]; then if [ -x "$dir/init.d/network" ]; then script="$dir/init.d/network" elif [ -x "$dir/init.d/networking" ]; then script="$dir/init.d/networking" fi else if [ -x "$dir/network" ]; then script="$dir/network" elif [ -x "$dir/networking" ]; then script="$dir/networking" fi fi fi done echo "$script" } # # exec_networking_script -- # # Execute the networking script to bring network interfaces up or down # based on the given input action argument. # exec_networking_script() { local script=$1 local action=$2 # Using SysV "service" if it exists, otherwise fall back to run the # script directly service=`which service 2>/dev/null` if [ $? = 0 -a -n "$service" ]; then serviceName=`basename "$script"` "$service" "$serviceName" "$action" else "$script" "$action" fi return $? } # # exec_systemctl_service -- # # Handle linux distributions that use systemd to replace the legacy # system V startup scripts. The previous network script searching # approach is no longer viable in these systems. Invoke the systemctl # command to control the network service instead. # exec_systemctl_service() { local rc=1 local action=$1 local ctlcmd=$(which systemctl 2>/dev/null) local service [ -z "$ctlcmd" ] && return $rc for svc in systemd-networkd network; do if ! $ctlcmd status $svc | grep -iq 'not-found'; then service=$svc && break fi done [ -z "$service" ] && return $rc $ctlcmd $action $service; rc=$? # When use the systemd-networkd service to shut down interfaces, interface # address and state remain unchanged. Need to use ip command to change its # address and state. if [ $rc = 0 -a $service = 'systemd-networkd' -a $action = 'stop' ]; then config_network_intfs $action; rc=$? fi return $rc } # # del_intf_ip -- # # Use the ip command to remove all the addresses of an interface. # del_intf_ip() { local nic=$1 $ip_cmd addr flush dev $nic return $? } # # ip_intf_ops -- # # Use the ip command to change the state of an interface to up or down. # ip_intf_ops() { local rc=1 local nic=$1 local ops=$2 [ -z "$ip_cmd" ] && return $rc $ip_cmd link set $nic $ops; rc=$? # Remove interface addresses when taking an interface down. if [ $rc = 0 -a $ops = down ]; then del_intf_ip $nic; rc=$? fi return $rc } # # intf_ops -- # # Execute the specified command (ifup or ifdown) if available, otherwise use # the ip command as fallback. If ifup or ifdown fails, run the ip command to # retry the intended operation. # intf_ops() { local rc=0 local cmd=$1 local ops=$2 local nic=$3 local tmp if [ ! -z "$cmd" ]; then tmp=$($cmd $nic 2>&1); rc=$? # Some systems still return a successful status even the command fails # because the interface is not configured in the configuration file. So # have to examine the command output to determine the actual status. if [ $rc = 0 ]; then echo $tmp | egrep -iq 'not configured|ignoring unknown' && rc=1 fi fi # If ifup/ifdown fails, try the ip fallback. if [ -z "$cmd" -o $rc != 0 ]; then ip_intf_ops $nic $ops; rc=$? fi return $rc } # # exec_intf_ops -- # # Perform an operation to bring an individual interface up or down. # exec_intf_ops() { local rc=0 local action=$1 local nic=$2 case $action in start) intf_ops "$ifup_cmd" up $nic; rc=$? ;; stop) intf_ops "$ifdown_cmd" down $nic; rc=$? ;; *) Panic "Illegal interface action: $action" ;; esac return $rc } # # config_network_intfs -- # # For Linux systems not supporting networking scripts to bring interfaces # up or down, provide a way to change the interface state individually. # config_network_intfs() { local rc=0 local action=$1 if [ -f "$activeList" ]; then while read nic; do exec_intf_ops $action $nic rc=$(expr $rc \| $?) done < $activeList fi return $rc } # # run_network_script -- # # Finds out how to run the system's script used to control networking, and # runs it with the given argument (which should be one of the usual SysV # init script arguments). If it does not work, tries the other alternatives. # So far, our alternatives are (a) systemctl (b) network script (c) perform # an individual interface state change. # run_network_script() { local action=$1 local rc=0 local script while true; do exec_systemctl_service $action [ $? != 0 ] || break script=`find_networking_script` if [ $script != "error" ]; then exec_networking_script $script $action [ $? != 0 ] || break fi # Since all the other alternatives fail, need to manually change # individual interface state. config_network_intfs $action; rc=$? break done return $rc } # # save_active_NIC_list -- # # Records a list of every active NIC to /var/run/vmware-active-nics. # # XXX What's the story on aliases? Should they still be included, or will # they be recreated automatically upon resume? # # Results: # $activeList has, one per line, a list of all active NICs. # # Side effects: # None. # save_active_NIC_list() { local intf_out >$activeList # Find out all the non-loopback up interfaces. Use ifconfig if available # otherwise fall back to the ip command. if [ -z "$ifconfig_cmd" ]; then for nic in $($ip_cmd link show up | egrep '\bUP\b' | awk -F: '{print $2}'); do $ip_cmd link show ${nic%@*} | grep -iq 'link/ether' && echo ${nic%@*} >> $activeList done else for nic in $($ifconfig_cmd | sed -n 's/^\([^: \t]*\).*$/\1/p'); do intf_out=$($ifconfig_cmd $nic) echo $intf_out | grep -iq loopback && continue echo $intf_out | egrep -q '\bUP\b' && echo $nic >> $activeList done fi } # # rescue_NIC -- # # For each NIC recorded in $activeList that is not currently "up", run # "ifup $nic" or "ip link set $nic up" to bring the interface up. # # Results: # All downed NICs should be active. # rescue_NIC() { local rc=0 local intf_out if [ -f "$activeList" ]; then while read nic; do if [ -z "$ifconfig_cmd" ]; then intf_out=$($ip_cmd link show $nic up) else intf_out=$($ifconfig_cmd $nic) fi if echo $intf_out | grep -q 'UP'; then echo `date` "[rescue_nic] $nic is already active." else echo `date` "[rescue_nic] activating $nic ..." # Our best effort to activate interfaces, use ifup if available # otherwise use the ip command as fallback. intf_ops "$ifup_cmd" up $nic rc=$(expr $rc \| $?) fi done < $activeList rm -f $activeList fi return $rc } # # TranquilizeNetworkManager -- # # Put the NetworkManager daemon to sleep (maybe). # # See http://projects.gnome.org/NetworkManager/developers/spec.html . # # Results: # Sleep(true) request is sent to the NetworkManager D-Bus interface. # # Side effects: # None. # TranquilizeNetworkManager() { # `which' may be a bit noisy, so we'll shush it. dbusSend=`which dbus-send 2>/dev/null` rc=$? if [ $rc -ne 0 ]; then return $rc fi # Check NetworkManager state before disabling it. nm_state=`$dbusSend --system --print-reply \ --dest=org.freedesktop.NetworkManager \ /org/freedesktop/NetworkManager \ org.freedesktop.DBus.Properties.Get \ string:'org.freedesktop.NetworkManager' \ string:'State' \ | awk '/variant/ {print $3;}'` if [ -z "$nm_state" ]; then return 1 fi # NetworkManager API 0.7/0.8 0.9 # NM_STATE_ASLEEP 1 10 # NM_STATE_DISCONNECTED 4 20 case $nm_state in 1|4|10|20) # Nothing needs to be done. return 0 ;; esac # NetworkManager 0.8.0 and above $dbusSend --system --print-reply \ --dest=org.freedesktop.NetworkManager \ /org/freedesktop/NetworkManager \ org.freedesktop.NetworkManager.Enable boolean:false rc=$? if [ $rc -eq 0 ]; then return $rc fi # NetworkManager 0.7.0 $dbusSend --system --print-reply \ --dest=org.freedesktop.NetworkManager \ /org/freedesktop/NetworkManager \ org.freedesktop.NetworkManager.Sleep boolean:true rc=$? if [ $rc -eq 0 ]; then return $rc fi # NetworkManager 0.6 $dbusSend --system --print-reply \ --dest=org.freedesktop.NetworkManager \ /org/freedesktop/NetworkManager \ org.freedesktop.NetworkManager.sleep rc=$? return $rc } # # WakeNetworkManager -- # # Wake the NetworkManager daemon (maybe). # # See http://projects.gnome.org/NetworkManager/developers/spec.html . # # Results: # Sleep(false)request is sent to the NetworkManager D-Bus interface. # # Side effects: # None. # WakeNetworkManager() { # `which' may be a bit noisy, so we'll shush it. dbusSend=`which dbus-send 2>/dev/null` rc=$? if [ $rc = 0 ]; then # NetworkManager 0.8.0 $dbusSend --system --print-reply \ --dest=org.freedesktop.NetworkManager \ /org/freedesktop/NetworkManager \ org.freedesktop.NetworkManager.Enable boolean:true rc=$? if [ $rc = 0 ]; then return $rc fi # NetworkManager 0.7.0 $dbusSend --system --print-reply \ --dest=org.freedesktop.NetworkManager \ /org/freedesktop/NetworkManager \ org.freedesktop.NetworkManager.Sleep boolean:false rc=$? if [ $rc = 0 ]; then return $rc fi # NetworkManager 0.6 $dbusSend --system --print-reply \ --dest=org.freedesktop.NetworkManager \ /org/freedesktop/NetworkManager \ org.freedesktop.NetworkManager.wake rc=$? fi return $rc } # # sanity_check -- # # Check if the script has all the commands it needs to carry out the # request. So far, it requires either ip or ifconfig command to read # interface configuration. Ifup is not checked here. It is checked at # the place where we need to do individual interface state change. # sanity_check() { ip_cmd=$(which ip 2>/dev/null) ifconfig_cmd=$(which ifconfig 2>/dev/null) ifup_cmd=$(which ifup 2>/dev/null) ifdown_cmd=$(which ifdown 2>/dev/null) [ -z "$ifconfig_cmd" -a -z "$ip_cmd" ] && \ Panic "ip and ifconfig not in search path." } # # main -- # # Main entry point. Perform some sanity checking, then map state change # events to relevant networking operations. # # Results: # See comment at top of file. # main() { exitCode=0 activeList=/var/run/vmware-active-nics case "$1" in poweron-vm) rm -f $activeList ;; suspend-vm) TranquilizeNetworkManager exitCode=$? if [ $exitCode != 0 ]; then sanity_check suspend-vm save_active_NIC_list run_network_script stop exitCode=$? fi ;; resume-vm) WakeNetworkManager exitCode=$? if [ $exitCode != 0 ]; then sanity_check resume-vm # According to hfu, "/etc/init.d/networking restart" on Debian 5.0 # may bring down ethernet interfaces tagged as "allow-hotplug" without # bringing them back up. # # This is especially a problem when reverting to a live, running # VM snapshot where an active NIC list hadn't yet been generated, # resulting in sudden loss of an otherwise operational NIC. # # So, if the active list doesn't exist, assume we're coming back to # a live snapshot and capture the current active list now for # rescue later. if [ ! -s $activeList ]; then save_active_NIC_list fi # We shall use start not restart here. Otherwise we may not be able # to bring back active list on distros like sles11sp2 # -- PR 816791 run_network_script start rescue_NIC exitCode=$? fi ;; *) ;; esac return $exitCode } main "$@"