#!/bin/sh
#
# This file is part of the resolvconf package.
#

set -e

echo_usage() { cat <<-EOF
	Usage: resolvconf COMMAND
	
	Manage nameserver information.
	
	Commands:
	  -a IFACE               Add DNS configuration for IFACE from stdin.
	  -d IFACE               Delete DNS configuration for IFACE.
	  -u                     Run update actions (e.g. regenerate resolv.conf).
	  -i                     List interfaces providing DNS configuration.
	  --list, -l             Show DNS configuration for each interface.
	  --enable-updates       Enable updates; run update actions if any are pending.
	  --disable-updates      Disable updates until re-enabled.
	  --updates-are-enabled  Return 0 if updates are enabled, 1 otherwise.
	  --version              Print the resolvconf version.
	  --help, -h             Print this help text.
	
	The IFACE argument may not contain spaces, slashes, an initial dot, an initial
	hyphen or an initial tilde. IFACE is conventionally formed from the interface
	name, a dot, and the name of the interface configuration program (e.g. dhclient)
	or the address family name (e.g. inet or inet6): eth0.dhclient, wlan0.inet6 etc.
	EOF
}

VERSION="Git snapshot"
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MYNAME="${0##*/}"
RUN_DIR=/run/resolvconf
ENABLE_UPDATES_FLAGFILE="${RUN_DIR}/enable-updates"
POSTPONED_UPDATE_FLAGFILE="${RUN_DIR}/postponed-update"

report_err() { echo "${MYNAME}: Error: $*" >&2 ; }

# Check arguments
CMD="$1"
case "$CMD" in
  -a|-d)
	IFACE="$2"
	if [ ! "$IFACE" ] ; then
		report_err "No interface name specified"
		echo_usage >&2
		exit 1
	fi
	report_iface_err() {
		report_err "$* not allowed in interface record name"
	}
	case "$IFACE" in
	  */*) report_iface_err "Slash" ; exit 1 ;;
	  *" "*) report_iface_err "Space" ; exit 1 ;;
	  .*) report_iface_err "Initial dot" ; exit 1 ;;
	  -*) report_iface_err "Initial hyphen" ; exit 1 ;;
	  \~*) report_iface_err "Initial tilde" ; exit 1 ;;
	esac
	;;
  -u|--enable-updates|--disable-updates|--updates-are-enabled)
  	if [ "$2" ] ; then
		report_err "The $CMD option does not take an argument"
		echo_usage >&2
		exit 1
	fi
	;;
 --version)
	echo "Debian resolvconf $VERSION"
	if [ -r /usr/share/doc/resolvconf/copyright ]
	then
		echo "Copyright: "
		sed -n '/Copyright:/,${ s/Copyright[: ]//g; /^$/d; /^License: /{p;q}; s/^ */  /; p}' \
			/usr/share/doc/resolvconf/copyright
	fi
	exit 0
	;;
 --help|-h)
	echo_usage >&2
	exit 0
	;;
 --list|-l)
	;;
  -i)
	echo $(/usr/lib/resolvconf/list-records)
	exit 0
	;;
  *)
	report_err "Command not recognized"
	echo_usage >&2
	exit 99
	;;
esac

[ -d "${RUN_DIR}/interface" ] || { mkdir -p "${RUN_DIR}/interface" ; }
[ -d "${RUN_DIR}/interface" ] || { report_err "${RUN_DIR}/interface either does not exist or is not a directory" ; exit 1 ; }
cd "${RUN_DIR}/interface"

{
# Make sure that we do not end up with a broken resolv.conf if resolvconf
# is invoked in parallel.
LOCK_WAIT_S=10
flock -w $LOCK_WAIT_S 9 ||
	{ report_err "Run lock held by another process for longer than $LOCK_WAIT_S seconds"; exit 1; }

update_and_exit()
{
	rm -f "$POSTPONED_UPDATE_FLAGFILE"
	exec run-parts ${1:+--arg="$1"} ${2:+--arg="$2"} /etc/resolvconf/update.d
}

# Handle commands that don't require normalized stdin

case "$CMD" in
  -d)
	if [ ! -e "$IFACE" ] ; then
		exit 0
	fi
	if [ ! -s "$IFACE" ] ; then
		rm -f "$IFACE"
		exit 0
	fi
	rm -f "$IFACE"
	if [ -e "$ENABLE_UPDATES_FLAGFILE" ] ; then
		update_and_exit -d "$IFACE"
	else
		: >| "$POSTPONED_UPDATE_FLAGFILE"
		exit 0
	fi
	;;
  --list|-l)
	for f in $(/usr/lib/resolvconf/list-records)
	do
		echo "# resolv.conf from $f"
		cat $f
	done
	exit 0
	;;
  -u)
	if [ -e "$ENABLE_UPDATES_FLAGFILE" ] ; then
		update_and_exit -u
	else
		: >| "$POSTPONED_UPDATE_FLAGFILE"
		exit 0
	fi
	;;
  --enable-updates)
	: >| "$ENABLE_UPDATES_FLAGFILE" || exit 1
	if [ -e "$POSTPONED_UPDATE_FLAGFILE" ] ; then
		(update_and_exit -u) || :
	fi
	exit 0
	;;
  --disable-updates)
	rm -f "$POSTPONED_UPDATE_FLAGFILE" || :
	rm -f "$ENABLE_UPDATES_FLAGFILE" || exit 1
	exit 0
	;;
  --updates-are-enabled)
	if [ -e "$ENABLE_UPDATES_FLAGFILE" ] ; then
		exit 0
	else
		exit 1
	fi
	;;
esac

case "$CMD" in
  -a)
	OLD_CONTENT=""
	[ -f "$IFACE" ] && OLD_CONTENT="$(cat "$IFACE")"
	NEW_CONTENT="$(/usr/lib/resolvconf/normalize-resolvconf)"
	# Proceed only if content has changed. The test here can't
	# eliminate 100% of redundant invocations of update scripts
	# because we don't do any locking; however it certainly does
	# eliminate most of them.
	if [ "$NEW_CONTENT" = "$OLD_CONTENT" ] ; then 
		exit 0
	fi
	IFACE_TMPFILE="${IFACE}_new.$$"
	cleanup() { rm -f "$IFACE_TMPFILE" ; }
	trap cleanup EXIT
	echo "$NEW_CONTENT" > "$IFACE_TMPFILE"
	mv -f "$IFACE_TMPFILE" "$IFACE"

	if [ -e "$ENABLE_UPDATES_FLAGFILE" ] ; then
		update_and_exit -a "$IFACE"
	else
		: >| "$POSTPONED_UPDATE_FLAGFILE"
		exit 0
	fi
	;;
esac

# Don't reach here
exit 99

} 9>"${RUN_DIR}/run-lock"
