resolvconf.in revision 304515
1#!/bin/sh
2# Copyright (c) 2007-2016 Roy Marples
3# All rights reserved
4
5# Redistribution and use in source and binary forms, with or without
6# modification, are permitted provided that the following conditions
7# are met:
8#     * Redistributions of source code must retain the above copyright
9#       notice, this list of conditions and the following disclaimer.
10#     * Redistributions in binary form must reproduce the above
11#       copyright notice, this list of conditions and the following
12#       disclaimer in the documentation and/or other materials provided
13#       with the distribution.
14#
15# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
19# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
21# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
27RESOLVCONF="$0"
28OPENRESOLV_VERSION="3.8.1"
29SYSCONFDIR=@SYSCONFDIR@
30LIBEXECDIR=@LIBEXECDIR@
31VARDIR=@VARDIR@
32RCDIR=@RCDIR@
33RESTARTCMD=@RESTARTCMD@
34
35# Disregard dhcpcd setting
36unset interface_order state_dir
37
38# If you change this, change the test in VFLAG and libc.in as well
39local_nameservers="127.* 0.0.0.0 255.255.255.255 ::1"
40
41dynamic_order="tap[0-9]* tun[0-9]* vpn vpn[0-9]* ppp[0-9]* ippp[0-9]*"
42interface_order="lo lo[0-9]*"
43name_server_blacklist="0.0.0.0"
44
45# Support original resolvconf configuration layout
46# as well as the openresolv config file
47if [ -f "$SYSCONFDIR"/resolvconf.conf ]; then
48	. "$SYSCONFDIR"/resolvconf.conf
49	[ -n "$state_dir" ] && VARDIR="$state_dir"
50elif [ -d "$SYSCONFDIR/resolvconf" ]; then
51	SYSCONFDIR="$SYSCONFDIR/resolvconf"
52	if [ -f "$SYSCONFDIR"/interface-order ]; then
53		interface_order="$(cat "$SYSCONFDIR"/interface-order)"
54	fi
55fi
56IFACEDIR="$VARDIR/interfaces"
57METRICDIR="$VARDIR/metrics"
58PRIVATEDIR="$VARDIR/private"
59EXCLUSIVEDIR="$VARDIR/exclusive"
60LOCKDIR="$VARDIR/lock"
61_PWD="$PWD"
62
63warn()
64{
65	echo "$*" >&2
66}
67
68error_exit()
69{
70	echo "$*" >&2
71	exit 1
72}
73
74usage()
75{
76	cat <<-EOF
77	Usage: ${RESOLVCONF##*/} [options] command [argument]
78
79	Inform the system about any DNS updates.
80
81	Commands:
82	  -a \$INTERFACE    Add DNS information to the specified interface
83	                   (DNS supplied via stdin in resolv.conf format)
84	  -d \$INTERFACE    Delete DNS information from the specified interface
85	  -h               Show this help cruft
86	  -i [\$PATTERN]    Show interfaces that have supplied DNS information
87                   optionally from interfaces that match the specified
88                   pattern
89	  -l [\$PATTERN]    Show DNS information, optionally from interfaces
90	                   that match the specified pattern
91
92	  -u               Run updates from our current DNS information
93
94	Options:
95	  -f               Ignore non existent interfaces
96	  -m metric        Give the added DNS information a metric
97	  -p               Mark the interface as private
98	  -x               Mark the interface as exclusive
99
100	Subscriber and System Init Commands:
101	  -I               Init the state dir
102	  -r \$SERVICE      Restart the system service
103	                   (restarting a non-existent or non-running service
104	                    should have no output and return 0)
105	  -R               Show the system service restart command
106	  -v [\$PATTERN]    echo NEWDOMAIN, NEWSEARCH and NEWNS variables to
107	  		   the console
108	  -V [\$PATTERN]    Same as -v, but only uses configuration in
109	                   $SYSCONFDIR/resolvconf.conf
110	EOF
111	[ -z "$1" ] && exit 0
112	echo
113	error_exit "$*"
114}
115
116# Strip any trailing dot from each name as a FQDN does not belong
117# in resolv.conf(5)
118# If you think otherwise, capture a DNS trace and you'll see libc
119# will strip it regardless.
120# This also solves setting up duplicate zones in our subscribers.
121strip_trailing_dots()
122{
123	local n= d=
124
125	for n; do
126		printf "$d%s" "${n%.}"
127		d=" "
128	done
129	printf "\n"
130}
131
132# Parse resolv.conf's and make variables
133# for domain name servers, search name servers and global nameservers
134parse_resolv()
135{
136	local line= ns= ds= search= d= n= newns=
137	local new=true iface= private=false p= domain= l= islocal=
138
139	newns=
140
141	while read -r line; do
142		case "$line" in
143		"# resolv.conf from "*)
144			if ${new}; then
145				iface="${line#\# resolv.conf from *}"
146				new=false
147				if [ -e "$PRIVATEDIR/$iface" ]; then
148					private=true
149				else
150					# Allow expansion
151					cd "$IFACEDIR"
152					private=false
153					for p in $private_interfaces; do
154						case "$iface" in
155						"$p"|"$p":*)
156							private=true
157							break
158							;;
159						esac
160					done
161				fi
162			fi
163			;;
164		"nameserver "*)
165			islocal=false
166			for l in $local_nameservers; do
167				case "${line#* }" in
168				$l)
169					islocal=true
170					echo "LOCALNAMESERVERS=\"\$LOCALNAMESERVERS ${line#* }\""
171					break
172					;;
173				esac
174			done
175			$islocal || ns="$ns${line#* } "
176			;;
177		"domain "*)
178			search="$(strip_trailing_dots ${line#* })"
179			if [ -z "$domain" ]; then
180				domain="$search"
181				echo "DOMAIN=\"$domain\""
182			fi
183			;;
184		"search "*)
185			search="$(strip_trailing_dots ${line#* })"
186			;;
187		*)
188			[ -n "$line" ] && continue
189			if [ -n "$ns" -a -n "$search" ]; then
190				newns=
191				for n in $ns; do
192					newns="$newns${newns:+,}$n"
193				done
194				ds=
195				for d in $search; do
196					ds="$ds${ds:+ }$d:$newns"
197				done
198				echo "DOMAINS=\"\$DOMAINS $ds\""
199			fi
200			echo "SEARCH=\"\$SEARCH $search\""
201			if ! $private; then
202				echo "NAMESERVERS=\"\$NAMESERVERS $ns\""
203			fi
204			ns=
205			search=
206			new=true
207			;;
208		esac
209	done
210}
211
212uniqify()
213{
214	local result=
215	while [ -n "$1" ]; do
216		case " $result " in
217		*" $1 "*);;
218		*) result="$result $1";;
219		esac
220		shift
221	done
222	echo "${result# *}"
223}
224
225dirname()
226{
227	local dir= OIFS="$IFS"
228	local IFS=/
229	set -- $@
230	IFS="$OIFS"
231	if [ -n "$1" ]; then
232		printf %s .
233	else
234		shift
235	fi
236	while [ -n "$2" ]; do
237		printf "/%s" "$1"
238		shift
239	done
240	printf "\n"
241}
242
243config_mkdirs()
244{
245	local e=0 f d
246	for f; do
247		[ -n "$f" ] || continue
248		d="$(dirname "$f")"
249		if [ ! -d "$d" ]; then
250			if type install >/dev/null 2>&1; then
251				install -d "$d" || e=$?
252			else
253				mkdir "$d" || e=$?
254			fi
255		fi
256	done
257	return $e
258}
259
260# With the advent of alternative init systems, it's possible to have
261# more than one installed. So we need to try and guess what one we're
262# using unless overriden by configure.
263# Note that restarting a service is a last resort - the subscribers
264# should make a reasonable attempt to reconfigre the service via some
265# method, normally SIGHUP.
266detect_init()
267{
268	[ -n "$RESTARTCMD" ] && return 0
269
270	# Detect the running init system.
271	# As systemd and OpenRC can be installed on top of legacy init
272	# systems we try to detect them first.
273	local status="@STATUSARG@"
274	: ${status:=status}
275	if [ -x /bin/systemctl -a -S /run/systemd/private ]; then
276		RESTARTCMD="if /bin/systemctl --quiet is-active \$1.service; then
277	/bin/systemctl restart \$1.service;
278fi"
279	elif [ -x /usr/bin/systemctl -a -S /run/systemd/private ]; then
280		RESTARTCMD="if /usr/bin/systemctl --quiet is-active \$1.service; then
281	/usr/bin/systemctl restart \$1.service;
282fi"
283	elif [ -x /sbin/rc-service -a \
284	    -s /libexec/rc/init.d/softlevel -o -s /run/openrc/softlevel ]
285	then
286		RESTARTCMD="/sbin/rc-service -i \$1 -- -Ds restart"
287	elif [ -x /usr/sbin/invoke-rc.d ]; then
288		RCDIR=/etc/init.d
289		RESTARTCMD="if /usr/sbin/invoke-rc.d --quiet \$1 status 1>/dev/null 2>&1; then
290	/usr/sbin/invoke-rc.d \$1 restart;
291fi"
292	elif [ -x /sbin/service ]; then
293		# Old RedHat
294		RCDIR=/etc/init.d
295		RESTARTCMD="if /sbin/service \$1; then
296	/sbin/service \$1 restart;
297fi"
298	elif [ -x /usr/sbin/service ]; then
299		# Could be FreeBSD
300		RESTARTCMD="if /usr/sbin/service \$1 $status 1>/dev/null 2>&1; then
301	/usr/sbin/service \$1 restart;
302fi"
303	elif [ -x /bin/sv ]; then
304		RESTARTCMD="/bin/sv try-restart \$1"
305	elif [ -x /usr/bin/sv ]; then
306		RESTARTCMD="/usr/bin/sv try-restart \$1"
307	elif [ -e /etc/arch-release -a -d /etc/rc.d ]; then
308		RCDIR=/etc/rc.d
309		RESTARTCMD="if [ -e /var/run/daemons/\$1 ]; then
310	/etc/rc.d/\$1 restart;
311fi"
312	elif [ -e /etc/slackware-version -a -d /etc/rc.d ]; then
313		RESTARTCMD="if /etc/rc.d/rc.\$1 status 1>/dev/null 2>&1; then
314	/etc/rc.d/rc.\$1 restart;
315fi"
316	elif [ -e /etc/rc.d/rc.subr -a -d /etc/rc.d ]; then
317		# OpenBSD
318		RESTARTCMD="if /etc/rc.d/\$1 check 1>/dev/null 2>&1; then
319	/etc/rc.d/\$1 restart;
320fi"
321	else
322		for x in /etc/init.d/rc.d /etc/rc.d /etc/init.d; do
323			[ -d $x ] || continue
324			RESTARTCMD="if $x/\$1 $status 1>/dev/null 2>&1; then
325	$x/\$1 restart;
326fi"
327			break
328		done
329	fi
330
331	if [ -z "$RESTARTCMD" ]; then
332		if [ "$NOINIT_WARNED" != true ]; then
333			warn "could not detect a useable init system"
334			_NOINIT_WARNED=true
335		fi
336		return 1
337	fi
338	_NOINIT_WARNED=
339	return 0
340}
341
342echo_resolv()
343{
344	local line= OIFS="$IFS"
345
346	[ -n "$1" -a -f "$IFACEDIR/$1" ] || return 1
347	echo "# resolv.conf from $1"
348	# Our variable maker works of the fact each resolv.conf per interface
349	# is separated by blank lines.
350	# So we remove them when echoing them.
351	while read -r line; do
352		IFS="$OIFS"
353		if [ -n "$line" ]; then
354			# We need to set IFS here to preserve any whitespace
355			IFS=''
356			printf "%s\n" "$line"
357		fi
358	done < "$IFACEDIR/$1"
359	IFS="$OIFS"
360}
361
362list_resolv()
363{
364	[ -d "$IFACEDIR" ] || return 0
365
366	local report=false list= retval=0 cmd="$1" excl=
367	shift
368
369	case "$IF_EXCLUSIVE" in
370	[Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1)
371		if [ -d "$EXCLUSIVEDIR" ]; then
372			cd "$EXCLUSIVEDIR"
373			for i in *; do
374				if [ -f "$i" ]; then
375					list="${i#* }"
376					break
377				fi
378			done
379		fi
380		excl=true
381		;;
382	*)
383		excl=false
384		;;
385	esac
386
387	# If we have an interface ordering list, then use that.
388	# It works by just using pathname expansion in the interface directory.
389	if [ -n "$1" ]; then
390		list="$*"
391		$force || report=true
392	elif ! $excl; then
393		cd "$IFACEDIR"
394		for i in $interface_order; do
395			[ -f "$i" ] && list="$list $i"
396			for ii in "$i":* "$i".*; do
397				[ -f "$ii" ] && list="$list $ii"
398			done
399		done
400		for i in $dynamic_order; do
401			if [ -e "$i" -a ! -e "$METRICDIR/"*" $i" ]; then
402				list="$list $i"
403			fi
404			for ii in "$i":* "$i".*; do
405				if [ -f "$ii" -a ! -e "$METRICDIR/"*" $ii" ]; then
406					list="$list $ii"
407				fi
408			done
409		done
410		if [ -d "$METRICDIR" ]; then
411			cd "$METRICDIR"
412			for i in *; do
413				[ -f "$i" ] && list="$list ${i#* }"
414			done
415		fi
416		list="$list *"
417	fi
418
419	cd "$IFACEDIR"
420	retval=1
421	excl=true
422	for i in $(uniqify $list); do
423		# Only list interfaces which we really have
424		if ! [ -f "$i" ]; then
425			if $report; then
426				echo "No resolv.conf for interface $i" >&2
427				retval=2
428			fi
429			continue
430		fi
431		
432		if [ "$cmd" = i -o "$cmd" = "-i" ]; then
433			printf %s "$i "
434		else
435			echo_resolv "$i"
436			echo
437		fi
438		[ $? = 0 -a "$retval" = 1 ] && retval=0
439	done
440	[ "$cmd" = i -o "$cmd" = "-i" ] && echo
441	return $retval
442}
443
444list_remove() {
445	local list= e= l= result= found= retval=0
446
447	[ -z "$2" ] && return 0
448	eval list=\"\$$1\"
449	shift
450
451	set -f
452	for e; do
453		found=false
454		for l in $list; do
455			case "$e" in
456			$l) found=true;;
457			esac
458			$found && break
459		done
460		if $found; then
461			retval=$(($retval + 1))
462		else
463			result="$result $e"
464		fi
465	done
466	set +f
467	echo "${result# *}"
468	return $retval
469}
470
471echo_prepend()
472{
473	echo "# Generated by resolvconf"
474	if [ -n "$search_domains" ]; then
475		echo "search $search_domains"
476	fi
477	for n in $name_servers; do
478		echo "nameserver $n"
479	done
480	echo
481}
482
483echo_append()
484{
485	echo "# Generated by resolvconf"
486	if [ -n "$search_domains_append" ]; then
487		echo "search $search_domains_append"
488	fi
489	for n in $name_servers_append; do
490		echo "nameserver $n"
491	done
492	echo
493}
494
495replace()
496{
497	local r= k= f= v= val= sub=
498
499	while read -r keyword value; do
500		for r in $replace; do
501			k="${r%%/*}"
502			r="${r#*/}"
503			f="${r%%/*}"
504			r="${r#*/}"
505			v="${r%%/*}"
506			case "$keyword" in
507			$k)
508				case "$value" in
509				$f) value="$v";;
510				esac
511				;;
512			esac
513		done
514		val=
515		for sub in $value; do
516			for r in $replace_sub; do
517				k="${r%%/*}"
518				r="${r#*/}"
519				f="${r%%/*}"
520				r="${r#*/}"
521				v="${r%%/*}"
522				case "$keyword" in
523				$k)
524					case "$sub" in
525					$f) sub="$v";;
526					esac
527					;;
528				esac
529			done
530			val="$val${val:+ }$sub"
531		done
532		printf "%s %s\n" "$keyword" "$val"
533	done
534}
535
536make_vars()
537{
538	local newdomains= d= dn= newns= ns=
539
540	# Clear variables
541	DOMAIN=
542	DOMAINS=
543	SEARCH=
544	NAMESERVERS=
545	LOCALNAMESERVERS=
546	
547	if [ -n "$name_servers" -o -n "$search_domains" ]; then
548		eval "$(echo_prepend | parse_resolv)"
549	fi
550	if [ -z "$VFLAG" ]; then
551		IF_EXCLUSIVE=1
552		list_resolv -i "$@" >/dev/null || IF_EXCLUSIVE=0
553		eval "$(list_resolv -l "$@" | replace | parse_resolv)"
554	fi
555	if [ -n "$name_servers_append" -o -n "$search_domains_append" ]; then
556		eval "$(echo_append | parse_resolv)"
557	fi
558
559	# Ensure that we only list each domain once
560	for d in $DOMAINS; do
561		dn="${d%%:*}"
562		list_remove domain_blacklist "$dn" >/dev/null || continue
563		case " $newdomains" in
564		*" ${dn}:"*) continue;;
565		esac
566		newns=
567		for nd in $DOMAINS; do
568			if [ "$dn" = "${nd%%:*}" ]; then
569				ns="${nd#*:}"
570				while [ -n "$ns" ]; do
571					case ",$newns," in
572					*,${ns%%,*},*) ;;
573					*) list_remove name_server_blacklist \
574						"${ns%%,*}" >/dev/null \
575					&& newns="$newns${newns:+,}${ns%%,*}";;
576					esac
577					[ "$ns" = "${ns#*,}" ] && break
578					ns="${ns#*,}"
579				done
580			fi
581		done
582		if [ -n "$newns" ]; then
583			newdomains="$newdomains${newdomains:+ }$dn:$newns"
584		fi
585	done
586	DOMAIN="$(list_remove domain_blacklist $DOMAIN)"
587	SEARCH="$(uniqify $SEARCH)"
588	SEARCH="$(list_remove domain_blacklist $SEARCH)"
589	NAMESERVERS="$(uniqify $NAMESERVERS)"
590	NAMESERVERS="$(list_remove name_server_blacklist $NAMESERVERS)"
591	LOCALNAMESERVERS="$(uniqify $LOCALNAMESERVERS)"
592	LOCALNAMESERVERS="$(list_remove name_server_blacklist $LOCALNAMESERVERS)"
593	echo "DOMAIN='$DOMAIN'"
594	echo "SEARCH='$SEARCH'"
595	echo "NAMESERVERS='$NAMESERVERS'"
596	echo "LOCALNAMESERVERS='$LOCALNAMESERVERS'"
597	echo "DOMAINS='$newdomains'"
598}
599
600force=false
601VFLAG=
602while getopts a:Dd:fhIilm:pRruvVx OPT; do
603	case "$OPT" in
604	f) force=true;;
605	h) usage;;
606	m) IF_METRIC="$OPTARG";;
607	p) IF_PRIVATE=1;;
608	V)
609		VFLAG=1
610		if [ "$local_nameservers" = \
611		    "127.* 0.0.0.0 255.255.255.255 ::1" ]
612		then
613			local_nameservers=
614		fi
615		;;
616	x) IF_EXCLUSIVE=1;;
617	'?') ;;
618	*) cmd="$OPT"; iface="$OPTARG";;
619	esac
620done
621shift $(($OPTIND - 1))
622args="$iface${iface:+ }$*"
623
624# -I inits the state dir
625if [ "$cmd" = I ]; then
626	if [ -d "$VARDIR" ]; then
627		rm -rf "$VARDIR"/*
628	fi
629	exit $?
630fi
631
632# -D ensures that the listed config file base dirs exist
633if [ "$cmd" = D ]; then
634	config_mkdirs "$@"
635	exit $?
636fi
637
638# -l lists our resolv files, optionally for a specific interface
639if [ "$cmd" = l -o "$cmd" = i ]; then
640	list_resolv "$cmd" "$args"
641	exit $?
642fi
643
644# Restart a service or echo the command to restart a service
645if [ "$cmd" = r -o "$cmd" = R ]; then
646	detect_init || exit 1
647	if [ "$cmd" = r ]; then
648		set -- $args
649		eval $RESTARTCMD
650	else
651		echo "$RESTARTCMD"
652	fi
653	exit $?
654fi
655
656# Not normally needed, but subscribers should be able to run independently
657if [ "$cmd" = v -o -n "$VFLAG" ]; then
658	make_vars "$iface"
659	exit $?
660fi
661
662# Test that we have valid options
663if [ "$cmd" = a -o "$cmd" = d ]; then
664	if [ -z "$iface" ]; then
665		usage "Interface not specified"
666	fi
667elif [ "$cmd" != u ]; then
668	[ -n "$cmd" -a "$cmd" != h ] && usage "Unknown option $cmd"
669	usage
670fi
671
672if [ "$cmd" = a ]; then
673	for x in '/' \\ ' ' '*'; do
674		case "$iface" in
675		*[$x]*) error_exit "$x not allowed in interface name";;
676		esac
677	done
678	for x in '.' '-' '~'; do
679		case "$iface" in
680		[$x]*) error_exit \
681			"$x not allowed at start of interface name";;
682		esac
683	done
684	[ "$cmd" = a -a -t 0 ] && error_exit "No file given via stdin"
685fi
686
687if [ ! -d "$VARDIR" ]; then
688	if [ -L "$VARDIR" ]; then
689		dir="$(readlink "$VARDIR")"
690		# link maybe relative
691		cd "${VARDIR%/*}"
692		if ! mkdir -m 0755 -p "$dir"; then
693			error_exit "Failed to create needed" \
694				"directory $dir"
695		fi
696	else
697		if ! mkdir -m 0755 -p "$VARDIR"; then
698			error_exit "Failed to create needed" \
699				"directory $VARDIR"
700		fi
701	fi
702fi
703
704if [ ! -d "$IFACEDIR" ]; then
705	mkdir -m 0755 -p "$IFACEDIR" || \
706		error_exit "Failed to create needed directory $IFACEDIR"
707	if [ "$cmd" = d ]; then
708		# Provide the same error messages as below
709		if ! ${force}; then
710			cd "$IFACEDIR"
711			for i in $args; do
712				warn "No resolv.conf for interface $i"
713			done
714		fi
715		${force}
716		exit $?
717	fi
718fi
719
720# An interface was added, changed, deleted or a general update was called.
721# Due to exclusivity we need to ensure that this is an atomic operation.
722# Our subscribers *may* need this as well if the init system is sub par.
723# As such we spinlock at this point as best we can.
724# We don't use flock(1) because it's not widely available and normally resides
725# in /usr which we do our very best to operate without.
726[ -w "$VARDIR" ] || error_exit "Cannot write to $LOCKDIR"
727: ${lock_timeout:=10}
728while true; do
729	if mkdir "$LOCKDIR" 2>/dev/null; then
730		trap 'rm -rf "$LOCKDIR";' EXIT
731		trap 'rm -rf "$LOCKDIR"; exit 1' INT QUIT ABRT SEGV ALRM TERM
732		echo $$ >"$LOCKDIR/pid"
733		break
734	fi
735	pid=$(cat "$LOCKDIR/pid")
736	if ! kill -0 "$pid"; then
737		warn "clearing stale lock pid $pid"
738		rm -rf "$LOCKDIR"
739		continue
740	fi
741	lock_timeout=$(($lock_timeout - 1))
742	if [ "$lock_timeout" -le 0 ]; then
743		error_exit "timed out waiting for lock from pid $pid"
744	fi
745	sleep 1
746done
747
748case "$cmd" in
749a)
750	# Read resolv.conf from stdin
751	resolv="$(cat)"
752	changed=false
753	changedfile=false
754	# If what we are given matches what we have, then do nothing
755	if [ -e "$IFACEDIR/$iface" ]; then
756		if [ "$(echo "$resolv")" != \
757			"$(cat "$IFACEDIR/$iface")" ]
758		then
759			changed=true
760			changedfile=true
761		fi
762	else
763		changed=true
764		changedfile=true
765	fi
766
767	# Set metric and private before creating the interface resolv.conf file
768	# to ensure that it will have the correct flags
769	[ ! -d "$METRICDIR" ] && mkdir "$METRICDIR"
770	oldmetric="$METRICDIR/"*" $iface"
771	newmetric=
772	if [ -n "$IF_METRIC" ]; then
773		# Pad metric to 6 characters, so 5 is less than 10
774		while [ ${#IF_METRIC} -le 6 ]; do
775			IF_METRIC="0$IF_METRIC"
776		done
777		newmetric="$METRICDIR/$IF_METRIC $iface"
778	fi
779	rm -f "$METRICDIR/"*" $iface"
780	[ "$oldmetric" != "$newmetric" -a \
781	    "$oldmetric" != "$METRICDIR/* $iface" ] &&
782		changed=true
783	[ -n "$newmetric" ] && echo " " >"$newmetric"
784
785	case "$IF_PRIVATE" in
786	[Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1)
787		if [ ! -d "$PRIVATEDIR" ]; then
788			[ -e "$PRIVATEDIR" ] && rm "$PRIVATEDIR"
789			mkdir "$PRIVATEDIR"
790		fi
791		[ -e "$PRIVATEDIR/$iface" ] || changed=true
792		[ -d "$PRIVATEDIR" ] && echo " " >"$PRIVATEDIR/$iface"
793		;;
794	*)
795		if [ -e "$PRIVATEDIR/$iface" ]; then
796			rm -f "$PRIVATEDIR/$iface"
797			changed=true
798		fi
799		;;
800	esac
801
802	oldexcl=
803	for x in "$EXCLUSIVEDIR/"*" $iface"; do
804		if [ -f "$x" ]; then
805			oldexcl="$x"
806			break
807		fi
808	done
809	case "$IF_EXCLUSIVE" in
810	[Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1)
811		if [ ! -d "$EXCLUSIVEDIR" ]; then
812			[ -e "$EXCLUSIVEDIR" ] && rm "$EXCLUSIVEDIR"
813			mkdir "$EXCLUSIVEDIR"
814		fi
815		cd "$EXCLUSIVEDIR"
816		for x in *; do
817			[ -f "$x" ] && break
818		done
819		if [ "${x#* }" != "$iface" ]; then
820			if [ "$x" = "${x% *}" ]; then
821				x=10000000
822			else
823				x="${x% *}"
824			fi
825			if [ "$x" = "0000000" ]; then
826				warn "exclusive underflow"
827			else
828				x=$(($x - 1))
829			fi
830			if [ -d "$EXCLUSIVEDIR" ]; then
831				echo " " >"$EXCLUSIVEDIR/$x $iface"
832			fi
833			changed=true
834		fi
835		;;
836	*)
837		if [ -f "$oldexcl" ]; then
838			rm -f "$oldexcl"
839			changed=true
840		fi
841		;;
842	esac
843
844	if $changedfile; then
845		printf "%s\n" "$resolv" >"$IFACEDIR/$iface" || exit $?
846	elif ! $changed; then
847		exit 0
848	fi
849	unset changed changedfile oldmetric newmetric x oldexcl
850	;;
851
852d)
853	# Delete any existing information about the interface
854	cd "$IFACEDIR"
855	changed=false
856	for i in $args; do
857		if [ -e "$i" ]; then
858			changed=true
859		elif ! ${force}; then
860			warn "No resolv.conf for interface $i"
861		fi
862		rm -f "$i" "$METRICDIR/"*" $i" \
863			"$PRIVATEDIR/$i" \
864			"$EXCLUSIVEDIR/"*" $i" || exit $?
865	done
866	if ! ${changed}; then
867		# Set the return code based on the forced flag
868		${force}
869		exit $?
870	fi
871	unset changed i
872	;;
873esac
874
875case "${resolvconf:-YES}" in
876[Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) ;;
877*) exit 0;;
878esac
879
880# Try and detect a suitable init system for our scripts
881detect_init
882export RESTARTCMD RCDIR _NOINIT_WARNED
883
884eval "$(make_vars)"
885export RESOLVCONF DOMAINS SEARCH NAMESERVERS LOCALNAMESERVERS
886: ${list_resolv:=list_resolv -l}
887retval=0
888
889# Run scripts in the same directory resolvconf is run from
890# in case any scripts accidentally dump files in the wrong place.
891cd "$_PWD"
892for script in "$LIBEXECDIR"/*; do
893	if [ -f "$script" ]; then
894		eval script_enabled="\$${script##*/}"
895		case "${script_enabled:-YES}" in
896		[Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) ;;
897		*) continue;;
898		esac
899		if [ -x "$script" ]; then
900			"$script" "$cmd" "$iface"
901		else
902			(set -- "$cmd" "$iface"; . "$script")
903		fi
904		retval=$(($retval + $?))
905	fi
906done
907exit $retval
908