1219732Sume#!/bin/sh
2282746Sgjb# Copyright (c) 2007-2015 Roy Marples
3219732Sume# All rights reserved
4219732Sume
5219732Sume# Redistribution and use in source and binary forms, with or without
6219732Sume# modification, are permitted provided that the following conditions
7219732Sume# are met:
8219732Sume#     * Redistributions of source code must retain the above copyright
9219732Sume#       notice, this list of conditions and the following disclaimer.
10219732Sume#     * Redistributions in binary form must reproduce the above
11219732Sume#       copyright notice, this list of conditions and the following
12219732Sume#       disclaimer in the documentation and/or other materials provided
13219732Sume#       with the distribution.
14219732Sume#
15219732Sume# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16219732Sume# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17219732Sume# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18219732Sume# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
19219732Sume# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20219732Sume# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
21219732Sume# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22219732Sume# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23219732Sume# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24219732Sume# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25219732Sume# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26219732Sume
27219732SumeRESOLVCONF="$0"
28219732SumeSYSCONFDIR=@SYSCONFDIR@
29219732SumeLIBEXECDIR=@LIBEXECDIR@
30219732SumeVARDIR=@VARDIR@
31282746Sgjb
32282746Sgjb# Disregard dhcpcd setting
33282746Sgjbunset interface_order state_dir
34282746Sgjb
35282746Sgjb# If you change this, change the test in VFLAG and libc.in as well
36282746Sgjblocal_nameservers="127.* 0.0.0.0 255.255.255.255 ::1"
37282746Sgjb
38282746Sgjbdynamic_order="tap[0-9]* tun[0-9]* vpn vpn[0-9]* ppp[0-9]* ippp[0-9]*"
39282746Sgjbinterface_order="lo lo[0-9]*"
40282746Sgjbname_server_blacklist="0.0.0.0"
41282746Sgjb
42219732Sume# Support original resolvconf configuration layout
43219732Sume# as well as the openresolv config file
44219732Sumeif [ -f "$SYSCONFDIR"/resolvconf.conf ]; then
45219732Sume	. "$SYSCONFDIR"/resolvconf.conf
46219732Sume	[ -n "$state_dir" ] && VARDIR="$state_dir"
47219732Sumeelif [ -d "$SYSCONFDIR/resolvconf" ]; then
48219732Sume	SYSCONFDIR="$SYSCONFDIR/resolvconf"
49219732Sume	if [ -f "$SYSCONFDIR"/interface-order ]; then
50219732Sume		interface_order="$(cat "$SYSCONFDIR"/interface-order)"
51219732Sume	fi
52219732Sumefi
53282746SgjbTMPDIR="$VARDIR/tmp"
54219732SumeIFACEDIR="$VARDIR/interfaces"
55219732SumeMETRICDIR="$VARDIR/metrics"
56219732SumePRIVATEDIR="$VARDIR/private"
57282746SgjbEXCLUSIVEDIR="$VARDIR/exclusive"
58282746SgjbLOCKDIR="$VARDIR/lock"
59219732Sume
60282746Sgjbwarn()
61282746Sgjb{
62282746Sgjb	echo "$*" >&2
63282746Sgjb}
64219732Sume
65219732Sumeerror_exit()
66219732Sume{
67219732Sume	echo "$*" >&2
68219732Sume	exit 1
69219732Sume}
70219732Sume
71219732Sumeusage()
72219732Sume{
73219732Sume	cat <<-EOF
74219732Sume	Usage: ${RESOLVCONF##*/} [options]
75219732Sume
76219732Sume	Inform the system about any DNS updates.
77219732Sume
78219732Sume	Options:
79219732Sume	  -a \$INTERFACE    Add DNS information to the specified interface
80219732Sume	                   (DNS supplied via stdin in resolv.conf format)
81219732Sume	  -m metric        Give the added DNS information a metric
82219732Sume	  -p               Mark the interface as private
83282746Sgjb	  -x               Mark the interface as exclusive
84219732Sume	  -d \$INTERFACE    Delete DNS information from the specified interface
85219732Sume	  -f               Ignore non existant interfaces
86219732Sume	  -I               Init the state dir
87219732Sume	  -u               Run updates from our current DNS information
88219732Sume	  -l [\$PATTERN]    Show DNS information, optionally from interfaces
89219732Sume	                   that match the specified pattern
90219732Sume	  -i [\$PATTERN]    Show interfaces that have supplied DNS information
91219732Sume                   optionally from interfaces that match the specified
92219732Sume                   pattern
93219732Sume	  -v [\$PATTERN]    echo NEWDOMAIN, NEWSEARCH and NEWNS variables to
94219732Sume	  		   the console
95219732Sume	  -h               Show this help cruft
96219732Sume	EOF
97219732Sume	[ -z "$1" ] && exit 0
98219732Sume	echo
99219732Sume	error_exit "$*"
100219732Sume}
101219732Sume
102219732Sumeecho_resolv()
103219732Sume{
104282746Sgjb	local line= OIFS="$IFS"
105282746Sgjb
106282746Sgjb	[ -n "$1" -a -f "$IFACEDIR/$1" ] || return 1
107219732Sume	echo "# resolv.conf from $1"
108219732Sume	# Our variable maker works of the fact each resolv.conf per interface
109219732Sume	# is separated by blank lines.
110219732Sume	# So we remove them when echoing them.
111282746Sgjb	while read -r line; do
112282746Sgjb		IFS="$OIFS"
113282746Sgjb		if [ -n "$line" ]; then
114282746Sgjb			# We need to set IFS here to preserve any whitespace
115282746Sgjb			IFS=''
116282746Sgjb			printf "%s\n" "$line"
117282746Sgjb		fi
118219732Sume	done < "$IFACEDIR/$1"
119219732Sume	echo
120282746Sgjb	IFS="$OIFS"
121219732Sume}
122219732Sume
123219732Sume# Parse resolv.conf's and make variables
124219732Sume# for domain name servers, search name servers and global nameservers
125219732Sumeparse_resolv()
126219732Sume{
127219732Sume	local line= ns= ds= search= d= n= newns=
128282746Sgjb	local new=true iface= private=false p= domain= l= islocal=
129219732Sume
130219732Sume	newns=
131219732Sume
132282746Sgjb	while read -r line; do
133219732Sume		case "$line" in
134219732Sume		"# resolv.conf from "*)
135219732Sume			if ${new}; then
136219732Sume				iface="${line#\# resolv.conf from *}"
137219732Sume				new=false
138219732Sume				if [ -e "$PRIVATEDIR/$iface" ]; then
139219732Sume					private=true
140219732Sume				else
141219732Sume					# Allow expansion
142219732Sume					cd "$IFACEDIR"
143219732Sume					private=false
144219732Sume					for p in $private_interfaces; do
145282746Sgjb						case "$iface" in
146282746Sgjb						"$p"|"$p":*) private=true; break;;
147282746Sgjb						esac
148219732Sume					done
149219732Sume				fi
150219732Sume			fi
151219732Sume			;;
152219732Sume		"nameserver "*)
153282746Sgjb			islocal=false
154282746Sgjb			for l in $local_nameservers; do
155282746Sgjb				case "${line#* }" in
156282746Sgjb				$l)
157282746Sgjb					islocal=true
158282746Sgjb					echo "LOCALNAMESERVERS=\"\$LOCALNAMESERVERS ${line#* }\""
159282746Sgjb					break
160282746Sgjb					;;
161282746Sgjb				esac
162282746Sgjb			done
163282746Sgjb			$islocal || ns="$ns${line#* } "
164219732Sume			;;
165282746Sgjb		"domain "*)
166282746Sgjb			if [ -z "$domain" ]; then
167282746Sgjb				domain="${line#* }"
168282746Sgjb				echo "DOMAIN=\"$domain\""
169282746Sgjb			fi
170219732Sume			search="${line#* }"
171219732Sume			;;
172282746Sgjb		"search "*)
173282746Sgjb			search="${line#* }"
174282746Sgjb			;;
175219732Sume		*)
176219732Sume			[ -n "$line" ] && continue
177219732Sume			if [ -n "$ns" -a -n "$search" ]; then
178219732Sume				newns=
179219732Sume				for n in $ns; do
180219732Sume					newns="$newns${newns:+,}$n"
181219732Sume				done
182219732Sume				ds=
183219732Sume				for d in $search; do
184219732Sume					ds="$ds${ds:+ }$d:$newns"
185219732Sume				done
186219732Sume				echo "DOMAINS=\"\$DOMAINS $ds\""
187219732Sume			fi
188219732Sume			echo "SEARCH=\"\$SEARCH $search\""
189219732Sume			if ! $private; then
190219732Sume				echo "NAMESERVERS=\"\$NAMESERVERS $ns\""
191219732Sume			fi
192219732Sume			ns=
193219732Sume			search=
194219732Sume			new=true
195219732Sume			;;
196219732Sume		esac
197219732Sume	done
198219732Sume}
199219732Sume
200219732Sumeuniqify()
201219732Sume{
202219732Sume	local result=
203219732Sume	while [ -n "$1" ]; do
204219732Sume		case " $result " in
205219732Sume		*" $1 "*);;
206219732Sume		*) result="$result $1";;
207219732Sume		esac
208219732Sume		shift
209219732Sume	done
210219732Sume	echo "${result# *}"
211219732Sume}
212219732Sume
213282746Sgjbdirname()
214282746Sgjb{
215282746Sgjb	local dir= OIFS="$IFS"
216282746Sgjb	local IFS=/
217282746Sgjb	set -- $@
218282746Sgjb	IFS="$OIFS"
219282746Sgjb	if [ -n "$1" ]; then
220282746Sgjb		printf %s .
221282746Sgjb	else
222282746Sgjb		shift
223282746Sgjb	fi
224282746Sgjb	while [ -n "$2" ]; do
225282746Sgjb		printf "/%s" "$1"
226282746Sgjb		shift
227282746Sgjb	done
228282746Sgjb	printf "\n"
229282746Sgjb}
230282746Sgjb
231282746Sgjbconfig_mkdirs()
232282746Sgjb{
233282746Sgjb	local e=0 f d
234282746Sgjb	for f; do
235282746Sgjb		[ -n "$f" ] || continue
236282746Sgjb		d="$(dirname "$f")"
237282746Sgjb		if [ ! -d "$d" ]; then
238282746Sgjb			if type install >/dev/null 2>&1; then
239282746Sgjb				install -d "$d" || e=$?
240282746Sgjb			else
241282746Sgjb				mkdir "$d" || e=$?
242282746Sgjb			fi
243282746Sgjb		fi
244282746Sgjb	done
245282746Sgjb	return $e
246282746Sgjb}
247282746Sgjb
248219732Sumelist_resolv()
249219732Sume{
250219732Sume	[ -d "$IFACEDIR" ] || return 0
251219732Sume
252282746Sgjb	local report=false list= retval=0 cmd="$1" excl=
253219732Sume	shift
254219732Sume
255282746Sgjb	case "$IF_EXCLUSIVE" in
256282746Sgjb	[Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1)
257282746Sgjb		if [ -d "$EXCLUSIVEDIR" ]; then
258282746Sgjb			cd "$EXCLUSIVEDIR"
259282746Sgjb			for i in *; do
260282746Sgjb				if [ -f "$i" ]; then
261282746Sgjb					list="${i#* }"
262282746Sgjb					break
263282746Sgjb				fi
264282746Sgjb			done
265282746Sgjb		fi
266282746Sgjb		excl=true
267282746Sgjb		;;
268282746Sgjb	*)
269282746Sgjb		excl=false
270282746Sgjb		;;
271282746Sgjb	esac
272282746Sgjb
273219732Sume	# If we have an interface ordering list, then use that.
274219732Sume	# It works by just using pathname expansion in the interface directory.
275219732Sume	if [ -n "$1" ]; then
276282746Sgjb		list="$*"
277219732Sume		$force || report=true
278282746Sgjb	elif ! $excl; then
279219732Sume		cd "$IFACEDIR"
280219732Sume		for i in $interface_order; do
281282746Sgjb			[ -f "$i" ] && list="$list $i"
282282746Sgjb			for ii in "$i":* "$i".*; do
283282746Sgjb				[ -f "$ii" ] && list="$list $ii"
284282746Sgjb			done
285219732Sume		done
286219732Sume		for i in $dynamic_order; do
287219732Sume			if [ -e "$i" -a ! -e "$METRICDIR/"*" $i" ]; then
288219732Sume				list="$list $i"
289219732Sume			fi
290282746Sgjb			for ii in "$i":* "$i".*; do
291282746Sgjb				if [ -f "$ii" -a ! -e "$METRICDIR/"*" $ii" ]; then
292282746Sgjb					list="$list $ii"
293282746Sgjb				fi
294282746Sgjb			done
295219732Sume		done
296219732Sume		if [ -d "$METRICDIR" ]; then
297219732Sume			cd "$METRICDIR"
298219732Sume			for i in *; do
299282746Sgjb				[ -f "$i" ] && list="$list ${i#* }"
300219732Sume			done
301219732Sume		fi
302219732Sume		list="$list *"
303219732Sume	fi
304219732Sume
305219732Sume	cd "$IFACEDIR"
306282746Sgjb	retval=1
307219732Sume	for i in $(uniqify $list); do
308219732Sume		# Only list interfaces which we really have
309282746Sgjb		if ! [ -f "$i" ]; then
310219732Sume			if $report; then
311219732Sume				echo "No resolv.conf for interface $i" >&2
312282746Sgjb				retval=2
313219732Sume			fi
314219732Sume			continue
315219732Sume		fi
316219732Sume		
317219732Sume		if [ "$cmd" = i -o "$cmd" = "-i" ]; then
318282746Sgjb			printf %s "$i "
319219732Sume		else
320219732Sume			echo_resolv "$i"
321219732Sume		fi
322282746Sgjb		[ $? = 0 -a "$retval" = 1 ] && retval=0
323219732Sume	done
324219732Sume	[ "$cmd" = i -o "$cmd" = "-i" ] && echo
325219732Sume	return $retval
326219732Sume}
327219732Sume
328282746Sgjblist_remove() {
329282746Sgjb	local list= e= l= result= found= retval=0
330282746Sgjb
331282746Sgjb	[ -z "$2" ] && return 0
332282746Sgjb	eval list=\"\$$1\"
333282746Sgjb	shift
334282746Sgjb
335282746Sgjb	set -f
336282746Sgjb	for e; do
337282746Sgjb		found=false
338282746Sgjb		for l in $list; do
339282746Sgjb			case "$e" in
340282746Sgjb			$l) found=true;;
341282746Sgjb			esac
342282746Sgjb			$found && break
343282746Sgjb		done
344282746Sgjb		if $found; then
345282746Sgjb			retval=$(($retval + 1))
346282746Sgjb		else
347282746Sgjb			result="$result $e"
348282746Sgjb		fi
349282746Sgjb	done
350282746Sgjb	set +f
351282746Sgjb	echo "${result# *}"
352282746Sgjb	return $retval
353282746Sgjb}
354282746Sgjb
355282746Sgjbecho_prepend()
356282746Sgjb{
357282746Sgjb	echo "# Generated by resolvconf"
358282746Sgjb	if [ -n "$search_domains" ]; then
359282746Sgjb		echo "search $search_domains"
360282746Sgjb	fi
361282746Sgjb	for n in $name_servers; do
362282746Sgjb		echo "nameserver $n"
363282746Sgjb	done
364282746Sgjb	echo
365282746Sgjb}
366282746Sgjb
367282746Sgjbecho_append()
368282746Sgjb{
369282746Sgjb	echo "# Generated by resolvconf"
370282746Sgjb	if [ -n "$search_domains_append" ]; then
371282746Sgjb		echo "search $search_domains_append"
372282746Sgjb	fi
373282746Sgjb	for n in $name_servers_append; do
374282746Sgjb		echo "nameserver $n"
375282746Sgjb	done
376282746Sgjb	echo
377282746Sgjb}
378282746Sgjb
379282746Sgjbreplace()
380282746Sgjb{
381282746Sgjb	local r= k= f= v= val= sub=
382282746Sgjb
383282746Sgjb	while read -r keyword value; do
384282746Sgjb		for r in $replace; do
385282746Sgjb			k="${r%%/*}"
386282746Sgjb			r="${r#*/}"
387282746Sgjb			f="${r%%/*}"
388282746Sgjb			r="${r#*/}"
389282746Sgjb			v="${r%%/*}"
390282746Sgjb			case "$keyword" in
391282746Sgjb			$k)
392282746Sgjb				case "$value" in
393282746Sgjb				$f) value="$v";;
394282746Sgjb				esac
395282746Sgjb				;;
396282746Sgjb			esac
397282746Sgjb		done
398282746Sgjb		val=
399282746Sgjb		for sub in $value; do
400282746Sgjb			for r in $replace_sub; do
401282746Sgjb				k="${r%%/*}"
402282746Sgjb				r="${r#*/}"
403282746Sgjb				f="${r%%/*}"
404282746Sgjb				r="${r#*/}"
405282746Sgjb				v="${r%%/*}"
406282746Sgjb				case "$keyword" in
407282746Sgjb				$k)
408282746Sgjb					case "$sub" in
409282746Sgjb					$f) sub="$v";;
410282746Sgjb					esac
411282746Sgjb					;;
412282746Sgjb				esac
413282746Sgjb			done
414282746Sgjb			val="$val${val:+ }$sub"
415282746Sgjb		done
416282746Sgjb		printf "%s %s\n" "$keyword" "$val"
417282746Sgjb	done
418282746Sgjb}
419282746Sgjb
420219732Sumemake_vars()
421219732Sume{
422282746Sgjb	local newdomains= d= dn= newns= ns=
423219732Sume
424282746Sgjb	# Clear variables
425282746Sgjb	DOMAIN=
426282746Sgjb	DOMAINS=
427282746Sgjb	SEARCH=
428282746Sgjb	NAMESERVERS=
429282746Sgjb	LOCALNAMESERVERS=
430282746Sgjb	
431282746Sgjb	if [ -n "$name_servers" -o -n "$search_domains" ]; then
432282746Sgjb		eval "$(echo_prepend | parse_resolv)"
433282746Sgjb	fi
434282746Sgjb	if [ -z "$VFLAG" ]; then
435282746Sgjb		IF_EXCLUSIVE=1
436282746Sgjb		list_resolv -i "$@" >/dev/null || IF_EXCLUSIVE=0
437282746Sgjb		eval "$(list_resolv -l "$@" | replace | parse_resolv)"
438282746Sgjb	fi
439282746Sgjb	if [ -n "$name_servers_append" -o -n "$search_domains_append" ]; then
440282746Sgjb		eval "$(echo_append | parse_resolv)"
441282746Sgjb	fi
442282746Sgjb
443219732Sume	# Ensure that we only list each domain once
444219732Sume	for d in $DOMAINS; do
445219732Sume		dn="${d%%:*}"
446282746Sgjb		list_remove domain_blacklist "$dn" >/dev/null || continue
447219732Sume		case " $newdomains" in
448219732Sume		*" ${dn}:"*) continue;;
449219732Sume		esac
450219732Sume		newns=
451219732Sume		for nd in $DOMAINS; do
452219732Sume			if [ "$dn" = "${nd%%:*}" ]; then
453219732Sume				ns="${nd#*:}"
454219732Sume				while [ -n "$ns" ]; do
455219732Sume					case ",$newns," in
456219732Sume					*,${ns%%,*},*) ;;
457282746Sgjb					*) list_remove name_server_blacklist \
458282746Sgjb						"${ns%%,*}" >/dev/null \
459282746Sgjb					&& newns="$newns${newns:+,}${ns%%,*}";;
460219732Sume					esac
461219732Sume					[ "$ns" = "${ns#*,}" ] && break
462219732Sume					ns="${ns#*,}"
463219732Sume				done
464219732Sume			fi
465219732Sume		done
466282746Sgjb		if [ -n "$newns" ]; then
467282746Sgjb			newdomains="$newdomains${newdomains:+ }$dn:$newns"
468282746Sgjb		fi
469219732Sume	done
470282746Sgjb	DOMAIN="$(list_remove domain_blacklist $DOMAIN)"
471282746Sgjb	SEARCH="$(uniqify $SEARCH)"
472282746Sgjb	SEARCH="$(list_remove domain_blacklist $SEARCH)"
473282746Sgjb	NAMESERVERS="$(uniqify $NAMESERVERS)"
474282746Sgjb	NAMESERVERS="$(list_remove name_server_blacklist $NAMESERVERS)"
475282746Sgjb	LOCALNAMESERVERS="$(uniqify $LOCALNAMESERVERS)"
476282746Sgjb	LOCALNAMESERVERS="$(list_remove name_server_blacklist $LOCALNAMESERVERS)"
477282746Sgjb	echo "DOMAIN='$DOMAIN'"
478282746Sgjb	echo "SEARCH='$SEARCH'"
479282746Sgjb	echo "NAMESERVERS='$NAMESERVERS'"
480282746Sgjb	echo "LOCALNAMESERVERS='$LOCALNAMESERVERS'"
481219732Sume	echo "DOMAINS='$newdomains'"
482219732Sume}
483219732Sume
484219732Sumeforce=false
485282746SgjbVFLAG=
486282746Sgjbwhile getopts a:Dd:fhIilm:puvVx OPT; do
487219732Sume	case "$OPT" in
488219732Sume	f) force=true;;
489219732Sume	h) usage;;
490219732Sume	m) IF_METRIC="$OPTARG";;
491219732Sume	p) IF_PRIVATE=1;;
492282746Sgjb	V)
493282746Sgjb		VFLAG=1
494282746Sgjb		if [ "$local_nameservers" = \
495282746Sgjb		    "127.* 0.0.0.0 255.255.255.255 ::1" ]
496282746Sgjb		then
497282746Sgjb			local_nameservers=
498282746Sgjb		fi
499282746Sgjb		;;
500282746Sgjb	x) IF_EXCLUSIVE=1;;
501219732Sume	'?') ;;
502219732Sume	*) cmd="$OPT"; iface="$OPTARG";;
503219732Sume	esac
504219732Sumedone
505219732Sumeshift $(($OPTIND - 1))
506282746Sgjbargs="$iface${iface:+ }$*"
507219732Sume
508219732Sume# -I inits the state dir
509219732Sumeif [ "$cmd" = I ]; then
510219732Sume	if [ -d "$VARDIR" ]; then
511219732Sume		rm -rf "$VARDIR"/*
512219732Sume	fi
513219732Sume	exit $?
514219732Sumefi
515219732Sume
516282746Sgjb# -D ensures that the listed config file base dirs exist
517282746Sgjbif [ "$cmd" = D ]; then
518282746Sgjb	config_mkdirs "$@"
519282746Sgjb	exit $?
520282746Sgjbfi
521282746Sgjb
522219732Sume# -l lists our resolv files, optionally for a specific interface
523219732Sumeif [ "$cmd" = l -o "$cmd" = i ]; then
524219732Sume	list_resolv "$cmd" "$args"
525219732Sume	exit $?
526219732Sumefi
527219732Sume
528219732Sume# Not normally needed, but subscribers should be able to run independently
529282746Sgjbif [ "$cmd" = v -o -n "$VFLAG" ]; then
530219732Sume	make_vars "$iface"
531219732Sume	exit $?
532219732Sumefi
533219732Sume
534219732Sume# Test that we have valid options
535219732Sumeif [ "$cmd" = a -o "$cmd" = d ]; then
536219732Sume	if [ -z "$iface" ]; then
537219732Sume		usage "Interface not specified"
538219732Sume	fi
539219732Sumeelif [ "$cmd" != u ]; then
540219732Sume	[ -n "$cmd" -a "$cmd" != h ] && usage "Unknown option $cmd"
541219732Sume	usage
542219732Sumefi
543282746Sgjb
544219732Sumeif [ "$cmd" = a ]; then
545219732Sume	for x in '/' \\ ' ' '*'; do
546219732Sume		case "$iface" in
547219732Sume		*[$x]*) error_exit "$x not allowed in interface name";;
548219732Sume		esac
549219732Sume	done
550219732Sume	for x in '.' '-' '~'; do
551219732Sume		case "$iface" in
552219732Sume		[$x]*) error_exit \
553219732Sume			"$x not allowed at start of interface name";;
554219732Sume		esac
555219732Sume	done
556219732Sume	[ "$cmd" = a -a -t 0 ] && error_exit "No file given via stdin"
557219732Sumefi
558219732Sume
559282746Sgjbif [ ! -d "$VARDIR" ]; then
560282746Sgjb	if [ -L "$VARDIR" ]; then
561282746Sgjb		dir="$(readlink "$VARDIR")"
562282746Sgjb		# link maybe relative
563282746Sgjb		cd "${VARDIR%/*}"
564282746Sgjb		if ! mkdir -m 0755 -p "$dir"; then
565282746Sgjb			error_exit "Failed to create needed" \
566282746Sgjb				"directory $dir"
567219732Sume		fi
568282746Sgjb	else
569282746Sgjb		if ! mkdir -m 0755 -p "$VARDIR"; then
570282746Sgjb			error_exit "Failed to create needed" \
571282746Sgjb				"directory $VARDIR"
572282746Sgjb		fi
573219732Sume	fi
574282746Sgjbfi
575282746Sgjb
576282746Sgjbif [ ! -d "$IFACEDIR" ]; then
577219732Sume	mkdir -m 0755 -p "$IFACEDIR" || \
578219732Sume		error_exit "Failed to create needed directory $IFACEDIR"
579219732Sume	if [ "$cmd" = d ]; then
580282746Sgjb		# Provide the same error messages as below
581282746Sgjb		if ! ${force}; then
582282746Sgjb			cd "$IFACEDIR"
583282746Sgjb			for i in $args; do
584282746Sgjb				warn "No resolv.conf for interface $i"
585282746Sgjb			done
586282746Sgjb		fi
587282746Sgjb		${force}
588282746Sgjb		exit $?
589219732Sume	fi
590219732Sumefi
591219732Sume
592282746Sgjb# An interface was added, changed, deleted or a general update was called.
593282746Sgjb# Due to exclusivity we need to ensure that this is an atomic operation.
594282746Sgjb# Our subscribers *may* need this as well if the init system is sub par.
595282746Sgjb# As such we spinlock at this point as best we can.
596282746Sgjb# We don't use flock(1) because it's not widely available and normally resides
597282746Sgjb# in /usr which we do our very best to operate without.
598282746Sgjb[ -w "$VARDIR" ] || error_exit "Cannot write to $LOCKDIR"
599282746Sgjb: ${lock_timeout:=10}
600282746Sgjbwhile true; do
601282746Sgjb	if mkdir "$LOCKDIR" 2>/dev/null; then
602282746Sgjb		trap 'rm -rf "$LOCKDIR";' EXIT
603282746Sgjb		trap 'rm -rf "$LOCKDIR"; exit 1' INT QUIT ABRT SEGV ALRM TERM
604282746Sgjb		echo $$ >"$LOCKDIR/pid"
605282746Sgjb		break
606282746Sgjb	fi
607282746Sgjb	pid=$(cat "$LOCKDIR/pid")
608282746Sgjb	if ! kill -0 "$pid"; then
609282746Sgjb		warn "clearing stale lock pid $pid"
610282746Sgjb		rm -rf "$LOCKDIR"
611282746Sgjb		continue
612282746Sgjb	fi
613282746Sgjb	lock_timeout=$(($lock_timeout - 1))
614282746Sgjb	if [ "$lock_timeout" -le 0 ]; then
615282746Sgjb		error_exit "timed out waiting for lock from pid $pid"
616282746Sgjb	fi
617282746Sgjb	sleep 1
618282746Sgjbdone
619282746Sgjb
620282746Sgjbcase "$cmd" in
621282746Sgjba)
622219732Sume	# Read resolv.conf from stdin
623225524Shrs	resolv="$(cat)"
624282746Sgjb	changed=false
625282746Sgjb	changedfile=false
626219732Sume	# If what we are given matches what we have, then do nothing
627219732Sume	if [ -e "$IFACEDIR/$iface" ]; then
628282746Sgjb		if [ "$(echo "$resolv")" != \
629219732Sume			"$(cat "$IFACEDIR/$iface")" ]
630219732Sume		then
631282746Sgjb			changed=true
632282746Sgjb			changedfile=true
633219732Sume		fi
634282746Sgjb	else
635282746Sgjb		changed=true
636282746Sgjb		changedfile=true
637219732Sume	fi
638282746Sgjb
639282746Sgjb	# Set metric and private before creating the interface resolv.conf file
640282746Sgjb	# to ensure that it will have the correct flags
641219732Sume	[ ! -d "$METRICDIR" ] && mkdir "$METRICDIR"
642282746Sgjb	oldmetric="$METRICDIR/"*" $iface"
643282746Sgjb	newmetric=
644219732Sume	if [ -n "$IF_METRIC" ]; then
645219732Sume		# Pad metric to 6 characters, so 5 is less than 10
646219732Sume		while [ ${#IF_METRIC} -le 6 ]; do
647219732Sume			IF_METRIC="0$IF_METRIC"
648219732Sume		done
649282746Sgjb		newmetric="$METRICDIR/$IF_METRIC $iface"
650219732Sume	fi
651282746Sgjb	rm -f "$METRICDIR/"*" $iface"
652282746Sgjb	[ "$oldmetric" != "$newmetric" -a \
653282746Sgjb	    "$oldmetric" != "$METRICDIR/* $iface" ] &&
654282746Sgjb		changed=true
655282746Sgjb	[ -n "$newmetric" ] && echo " " >"$newmetric"
656282746Sgjb
657219732Sume	case "$IF_PRIVATE" in
658219732Sume	[Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1)
659219732Sume		if [ ! -d "$PRIVATEDIR" ]; then
660219732Sume			[ -e "$PRIVATEDIR" ] && rm "$PRIVATEDIR"
661219732Sume			mkdir "$PRIVATEDIR"
662219732Sume		fi
663282746Sgjb		[ -e "$PRIVATEDIR/$iface" ] || changed=true
664219732Sume		[ -d "$PRIVATEDIR" ] && echo " " >"$PRIVATEDIR/$iface"
665219732Sume		;;
666219732Sume	*)
667219732Sume		if [ -e "$PRIVATEDIR/$iface" ]; then
668219732Sume			rm -f "$PRIVATEDIR/$iface"
669282746Sgjb			changed=true
670219732Sume		fi
671219732Sume		;;
672219732Sume	esac
673219732Sume
674282746Sgjb	oldexcl=
675282746Sgjb	for x in "$EXCLUSIVEDIR/"*" $iface"; do
676282746Sgjb		if [ -f "$x" ]; then
677282746Sgjb			oldexcl="$x"
678282746Sgjb			break
679282746Sgjb		fi
680282746Sgjb	done
681282746Sgjb	case "$IF_EXCLUSIVE" in
682282746Sgjb	[Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1)
683282746Sgjb		if [ ! -d "$EXCLUSIVEDIR" ]; then
684282746Sgjb			[ -e "$EXCLUSIVEDIR" ] && rm "$EXCLUSIVEDIR"
685282746Sgjb			mkdir "$EXCLUSIVEDIR"
686282746Sgjb		fi
687282746Sgjb		cd "$EXCLUSIVEDIR"
688282746Sgjb		for x in *; do
689282746Sgjb			[ -f "$x" ] && break
690282746Sgjb		done
691282746Sgjb		if [ "${x#* }" != "$iface" ]; then
692282746Sgjb			if [ "$x" = "${x% *}" ]; then
693282746Sgjb				x=10000000
694282746Sgjb			else
695282746Sgjb				x="${x% *}"
696282746Sgjb			fi
697282746Sgjb			if [ "$x" = "0000000" ]; then
698282746Sgjb				warn "exclusive underflow"
699282746Sgjb			else
700282746Sgjb				x=$(($x - 1))
701282746Sgjb			fi
702282746Sgjb			if [ -d "$EXCLUSIVEDIR" ]; then
703282746Sgjb				echo " " >"$EXCLUSIVEDIR/$x $iface"
704282746Sgjb			fi
705282746Sgjb			changed=true
706282746Sgjb		fi
707282746Sgjb		;;
708282746Sgjb	*)
709282746Sgjb		if [ -f "$oldexcl" ]; then
710282746Sgjb			rm -f "$oldexcl"
711282746Sgjb			changed=true
712282746Sgjb		fi
713282746Sgjb		;;
714282746Sgjb	esac
715282746Sgjb
716282746Sgjb	if $changedfile; then
717282746Sgjb		printf "%s\n" "$resolv" >"$IFACEDIR/$iface" || exit $?
718282746Sgjb	elif ! $changed; then
719282746Sgjb		exit 0
720282746Sgjb	fi
721282746Sgjb	unset changed changedfile oldmetric newmetric x oldexcl
722282746Sgjb	;;
723282746Sgjb
724282746Sgjbd)
725282746Sgjb	# Delete any existing information about the interface
726282746Sgjb	cd "$IFACEDIR"
727282746Sgjb	changed=false
728282746Sgjb	for i in $args; do
729282746Sgjb		if [ -e "$i" ]; then
730282746Sgjb			changed=true
731282746Sgjb		elif ! ${force}; then
732282746Sgjb			warn "No resolv.conf for interface $i"
733282746Sgjb		fi
734282746Sgjb		rm -f "$i" "$METRICDIR/"*" $i" \
735282746Sgjb			"$PRIVATEDIR/$i" \
736282746Sgjb			"$EXCLUSIVEDIR/"*" $i" || exit $?
737282746Sgjb	done
738282746Sgjb	if ! ${changed}; then
739282746Sgjb		# Set the return code based on the forced flag
740282746Sgjb		${force}
741282746Sgjb		exit $?
742282746Sgjb	fi
743282746Sgjb	unset changed i
744282746Sgjb	;;
745282746Sgjbesac
746282746Sgjb
747282746Sgjbcase "${resolvconf:-YES}" in
748282746Sgjb[Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) ;;
749282746Sgjb*) exit 0;;
750282746Sgjbesac
751282746Sgjb
752219732Sumeeval "$(make_vars)"
753219732Sumeexport RESOLVCONF DOMAINS SEARCH NAMESERVERS LOCALNAMESERVERS
754219732Sume: ${list_resolv:=list_resolv -l}
755219732Sumeretval=0
756219732Sumefor script in "$LIBEXECDIR"/*; do
757219732Sume	if [ -f "$script" ]; then
758282746Sgjb		eval script_enabled="\$${script##*/}"
759282746Sgjb		case "${script_enabled:-YES}" in
760282746Sgjb		[Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) ;;
761282746Sgjb		*) continue;;
762282746Sgjb		esac
763219732Sume		if [ -x "$script" ]; then
764219732Sume			"$script" "$cmd" "$iface"
765219732Sume		else
766282746Sgjb			(set -- "$cmd" "$iface"; . "$script")
767219732Sume		fi
768219732Sume		retval=$(($retval + $?))
769219732Sume	fi
770219732Sumedone
771219732Sumeexit $retval
772