#!/bin/bash
#
# Wicked ifup network interface configuration compatibility script.
#
# Copyright (c) 2013 SUSE LINUX Products GmbH, Nuernberg, Germany.
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation; either version 2 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# this program.  If not, see <http://www.gnu.org/licenses/>.
#
# Authors: Marius Tomaschewski <mt@suse.de>
#          Pawel Wieczorkiewicz <pwieczorkiewicz@suse.de>
#
###

unset POSIXLY_CORRECT ; set +o posix # we are using non-posix bash features
export PATH=/sbin:/usr/sbin:/bin:/usr/bin
if test -f /etc/sysconfig/network/config ; then
	.  /etc/sysconfig/network/config
fi

#
# Exit codes (from sysconfig's functions.common):
#
R_SUCCESS=0		# interface has been set up
R_ERROR=1		# any unspecified error
R_USAGE=2		# wrong usage
R_NOTALLOWED=4		# insufficient privilege
R_INACTIVE=5		# the interface is not up and it should not
R_NOTCONFIGURED=5	# the bootmode does not match the current mode
R_NODEV=7		# the given interface does not exist
R_NOTRUNNING=7		# the interface is not up but it should be up
R_NOCONFIG=9		# we could not find a matching configuration file
R_DHCP_BG=12		# [dhcp client is running], but no address up to now
R_NOT_UP2DATE=13	# returned from ifprobe if configuration has changed
R_PROPERTY_NOT_SET=14	# some property of the iface could not be set.

#
# Helper functions
#
usage ()
{
	echo "Usage: if{up,down,status,probe} [<config>] <interface> [-o <options>]"
	echo ""
	echo "Common options are:"
	echo "    debug      : be verbose"
	echo ""
	echo "Options for if{status,probe} are:"
	echo "    quiet      : Do not print out errors, but just signal the result through exit status"
	echo ""
	echo "Options for ifdown are:"
	echo "    force      : Forces persistent (nfsroot type) interface down"
	echo "    release    : Override active config to force a lease release"
	echo "    no-release : Override active config to skip a lease release"
	echo ""
	echo "Unknown options are simply ignored, so be careful."
	echo ""
	exit $R_USAGE
}

mesg()
{
	local p s t

	case $1 in
	-p) shift; p="$1" ; shift ;;
	esac
	test $PPID -eq 1 || s="-s"
	test "X$SCRIPTNAME" = "X" || t="$SCRIPTNAME[$$]"

	printf -- "%s\n" "$*" | logger ${p:+-p $p} ${s:+-s} ${t:+-t "$t"}
}

debug()
{
	test "X${DEBUG:-no}" != "Xno" && mesg -p debug "$*"
}

systemd_get_service_id()
{
	local service="$1"
	local _id=`systemctl --no-pager -p Id show "$service" 2>/dev/null`
	echo "${_id#Id=}"
}

systemd_get_network_service_id()
{
	systemd_get_service_id network.service
}


######################################################################
# Check responsibility
#
network_service_id=`systemd_get_network_service_id`
case $network_service_id in
wicked.service|"")
	;;
*)
	mesg "Network is managed by '$network_service_id' -> skipping"
	exit $R_USAGE
	;;
esac


######################################################################
# Commandline parsing
#
SCRIPTNAME="${0##*/}"
case $SCRIPTNAME in
	ifup|ifdown|ifstatus|ifprobe)				;;
	*)		usage					;;
esac

INTERFACE=""
case $1 in
	""|-*)		usage					;;
	*)		INTERFACE=$1 ; shift			;;
esac
CONFIG=""
case $1 in
	""|-o)		shift					;;
	-*)		usage					;;
	*)		CONFIG="$INTERFACE" ; # unused
			INTERFACE="$1" ; shift			;;
esac
case $1 in
	-o)		shift					;;
esac
OPTIONS=$@
opt_debug=""
opt_quiet=""
opt_force=""
opt_release=""
while [ $# -gt 0 ]; do
	case $1 in
	debug)		DEBUG="yes"				;;
	debug=*)	DEBUG="${1#debug=}"			;;
	quiet)		opt_quiet="--quiet"			;;
	force)          opt_force="--force device-exists"	;;
	release)	opt_release="--release"			;;
	no-release)	opt_release="--no-release"		;;
	*)		debug "unknown option \"$1\""		;;
	esac
	shift
done

# apply debug from config or argument
case $DEBUG in
no|"")	DEBUG="no"			;;
yes)	opt_debug="--debug most"	;;
*)	opt_debug="--debug ${DEBUG}"	;;
esac

# Map wicked return codes to compatibility codes
rc_map_return()
{
	case $1 in
	6)	# NI_WICKED_RC_NOT_CONFIGURED
		return $R_NOTCONFIGURED
	;;
	*)
		return ${1:-1}
	;;
	esac
}

# Map wicked status codes to compatibility codes
#
# Note: wicked ifstatus returns mapped LSB 0..4 for "ifstatus all"
# and > 150 for "ifstatus <ifname>", but without to reduce most of
# the codes to 5,7,9 and 12 as sysconfig ifstatus does (see above),
# thus a simple $(($1 - 150)) is not enough. We map it separately.
#
rc_map_status()
{
	case $1 in
	0)	# NI_WICKED_ST_OK
		return $R_SUCCESS
	;;
	1)	# NI_WICKED_ST_ERROR
		return $R_ERROR
	;;
	2)	# NI_WICKED_ST_FAILED
		return $R_NOTRUNNING
	;;
	3)	# NI_WICKED_ST_UNUSED
		return $_NOTCONFIGURED
	;;
	4)	# NI_WICKED_ST_USAGE
		return $R_USAGE
	;;
	155)	# NI_WICKED_ST_DISABLED
		return $R_INACTIVE
	;;
	156)	# NI_WICKED_ST_NOT_STARTED
		return $R_NOTCONFIGURED
	;;
	157)	# NI_WICKED_ST_NO_DEVICE
		return $R_NODEV
	;;
	158)	# NI_WICKED_ST_NOT_RUNNING
		return $R_NOTRUNNING
	;;
	159)	# NI_WICKED_ST_NO_CONFIG
		return $R_NOCONFIG
	;;
	162)	# NI_WICKED_ST_IN_PROGRESS
		return $R_DHCP_BG
	;;
	164)	# NI_WICKED_ST_CHANGED_CONFIG
		return $R_NOT_UP2DATE
	;;
	165)	# NI_WICKED_ST_NOT_IN_STATE
		return $R_NOTRUNNING
	;;
	166)	# NI_WICKED_ST_PERSISTENT_ON
		return $R_NOTALLOWED
	;;
	168)	# NI_WICKED_ST_NO_CARRIER
		return $R_DHCP_BG
	;;
	*)	# any other
		return ${1:-1}
	;;
	esac
}

rc=$R_SUCCESS
wicked_client="/usr/sbin/wicked"
case $SCRIPTNAME in
	ifup)
		$wicked_client \
			${opt_debug} \
			ifup "$INTERFACE"
		rc=$(rc_map_return "$?")
	;;
	ifdown)
		$wicked_client \
			${opt_debug} \
			ifdown \
			  ${opt_force} \
			  ${opt_release} \
			  "$INTERFACE"
		rc=$(rc_map_return "$?")
	;;
	ifstatus)
		$wicked_client \
			${opt_debug} \
			ifstatus \
			  ${opt_quiet} \
			  "$INTERFACE"
		rc=$(rc_map_status "$?")
	;;
	ifprobe)
		$wicked_client \
			${opt_debug} \
			ifcheck \
			  --changed \
			  ${opt_quiet} \
			  "$INTERFACE" \
		rc=$(rc_map_status "$?")
	;;
esac

exit $rc
