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