Home | History | Annotate | Line # | Download | only in dist
resolvconf.in revision 1.1
      1  1.1  roy #!/bin/sh
      2  1.1  roy # Copyright (c) 2007-2009 Roy Marples
      3  1.1  roy # All rights reserved
      4  1.1  roy 
      5  1.1  roy # Redistribution and use in source and binary forms, with or without
      6  1.1  roy # modification, are permitted provided that the following conditions
      7  1.1  roy # are met:
      8  1.1  roy #     * Redistributions of source code must retain the above copyright
      9  1.1  roy #       notice, this list of conditions and the following disclaimer.
     10  1.1  roy #     * Redistributions in binary form must reproduce the above
     11  1.1  roy #       copyright notice, this list of conditions and the following
     12  1.1  roy #       disclaimer in the documentation and/or other materials provided
     13  1.1  roy #       with the distribution.
     14  1.1  roy #
     15  1.1  roy # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     16  1.1  roy # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     17  1.1  roy # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     18  1.1  roy # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     19  1.1  roy # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     20  1.1  roy # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     21  1.1  roy # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     22  1.1  roy # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     23  1.1  roy # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     24  1.1  roy # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     25  1.1  roy # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     26  1.1  roy 
     27  1.1  roy RESOLVCONF="$0"
     28  1.1  roy SYSCONFDIR=@SYSCONFDIR@
     29  1.1  roy LIBEXECDIR=@LIBEXECDIR@
     30  1.1  roy VARDIR=@VARDIR@
     31  1.1  roy # Support original resolvconf configuration layout
     32  1.1  roy # as well as the openresolv config file
     33  1.1  roy if [ -f "$SYSCONFDIR"/resolvconf.conf ]; then
     34  1.1  roy 	. "$SYSCONFDIR"/resolvconf.conf
     35  1.1  roy 	[ -n "$state_dir" ] && VARDIR="$state_dir"
     36  1.1  roy elif [ -d "$SYSCONFDIR/resolvconf" ]; then
     37  1.1  roy 	SYSCONFDIR="$SYSCONFDIR/resolvconf"
     38  1.1  roy 	if [ -f "$SYSCONFDIR"/interface-order ]; then
     39  1.1  roy 		interface_order="$(cat "$SYSCONFDIR"/interface-order)"
     40  1.1  roy 	fi
     41  1.1  roy fi
     42  1.1  roy IFACEDIR="$VARDIR/interfaces"
     43  1.1  roy METRICDIR="$VARDIR/metrics"
     44  1.1  roy PRIVATEDIR="$VARDIR/private"
     45  1.1  roy 
     46  1.1  roy : ${dynamic_order:=tap[0-9]* tun[0-9]* vpn vpn[0-9]* ppp[0-9]* ippp[0-9]*}
     47  1.1  roy : ${interface_order:=lo lo[0-9]*}
     48  1.1  roy 
     49  1.1  roy error_exit()
     50  1.1  roy {
     51  1.1  roy 	echo "$*" >&2
     52  1.1  roy 	exit 1
     53  1.1  roy }
     54  1.1  roy 
     55  1.1  roy usage()
     56  1.1  roy {
     57  1.1  roy 	cat <<-EOF
     58  1.1  roy 	Usage: ${RESOLVCONF##*/} [options]
     59  1.1  roy 
     60  1.1  roy 	Inform the system about any DNS updates.
     61  1.1  roy 
     62  1.1  roy 	Options:
     63  1.1  roy 	  -a \$INTERFACE    Add DNS information to the specified interface
     64  1.1  roy 	                   (DNS supplied via stdin in resolv.conf format)
     65  1.1  roy 	  -m metric        Give the added DNS information a metric
     66  1.1  roy 	  -p               Mark the interface as private
     67  1.1  roy 	  -d \$INTERFACE    Delete DNS information from the specified interface
     68  1.1  roy 	  -f               Ignore non existant interfaces
     69  1.1  roy 	  -I               Init the state dir
     70  1.1  roy 	  -u               Run updates from our current DNS information
     71  1.1  roy 	  -l [\$PATTERN]    Show DNS information, optionally from interfaces
     72  1.1  roy 	                   that match the specified pattern
     73  1.1  roy 	  -i [\$PATTERN]    Show interfaces that have supplied DNS information
     74  1.1  roy                    optionally from interfaces that match the specified
     75  1.1  roy                    pattern
     76  1.1  roy 	  -v [\$PATTERN]    echo NEWDOMAIN, NEWSEARCH and NEWNS variables to
     77  1.1  roy 	  		   the console
     78  1.1  roy 	  -h               Show this help cruft
     79  1.1  roy 	EOF
     80  1.1  roy 	[ -z "$1" ] && exit 0
     81  1.1  roy 	echo
     82  1.1  roy 	error_exit "$*"
     83  1.1  roy }
     84  1.1  roy 
     85  1.1  roy echo_resolv()
     86  1.1  roy {
     87  1.1  roy 	local line=
     88  1.1  roy 	[ -n "$1" -a -e "$IFACEDIR/$1" ] || return 1
     89  1.1  roy 	echo "# resolv.conf from $1"
     90  1.1  roy 	# Our variable maker works of the fact each resolv.conf per interface
     91  1.1  roy 	# is separated by blank lines.
     92  1.1  roy 	# So we remove them when echoing them.
     93  1.1  roy 	while read line; do
     94  1.1  roy 		[ -n "$line" ] && echo "$line"
     95  1.1  roy 	done < "$IFACEDIR/$1"
     96  1.1  roy 	echo
     97  1.1  roy }
     98  1.1  roy 
     99  1.1  roy # Parse resolv.conf's and make variables
    100  1.1  roy # for domain name servers, search name servers and global nameservers
    101  1.1  roy parse_resolv()
    102  1.1  roy {
    103  1.1  roy 	local line= ns= ds= search= d= n= newns=
    104  1.1  roy 	local new=true iface= private=false
    105  1.1  roy 
    106  1.1  roy 	echo "DOMAINS="
    107  1.1  roy 	echo "SEARCH="
    108  1.1  roy 	echo "NAMESERVERS="
    109  1.1  roy 
    110  1.1  roy 	while read line; do
    111  1.1  roy 		case "$line" in
    112  1.1  roy 		"# resolv.conf from "*)
    113  1.1  roy 			if ${new}; then
    114  1.1  roy 				iface="${line#\# resolv.conf from *}"
    115  1.1  roy 				new=false
    116  1.1  roy 				case " $private_interfaces " in
    117  1.1  roy 				*" $iface "*)
    118  1.1  roy 					private=true
    119  1.1  roy 					;;
    120  1.1  roy 				*)
    121  1.1  roy 					if [ -e "$PRIVATEDIR/$iface" ]; then
    122  1.1  roy 						private=true
    123  1.1  roy 					else
    124  1.1  roy 						private=false
    125  1.1  roy 					fi
    126  1.1  roy 					;;
    127  1.1  roy 				esac
    128  1.1  roy 			fi
    129  1.1  roy 			;;
    130  1.1  roy 		"nameserver "*)
    131  1.1  roy 			case "${line#* }" in
    132  1.1  roy 			127.*|0.0.0.0|255.255.255.255) continue;;
    133  1.1  roy 			esac
    134  1.1  roy 			ns="$ns${line#* } "
    135  1.1  roy 			;;
    136  1.1  roy 		"domain "*|"search "*)
    137  1.1  roy 			search="${line#* }"
    138  1.1  roy 			;;
    139  1.1  roy 		*)
    140  1.1  roy 			[ -n "$line" ] && continue
    141  1.1  roy 			if [ -n "$ns" -a -n "$search" ]; then
    142  1.1  roy 				newns=
    143  1.1  roy 				for n in $ns; do
    144  1.1  roy 					newns="$newns${newns:+,}$n"
    145  1.1  roy 				done
    146  1.1  roy 				ds=
    147  1.1  roy 				for d in $search; do
    148  1.1  roy 					ds="$ds${ds:+ }$d:$newns"
    149  1.1  roy 				done
    150  1.1  roy 				echo "DOMAINS=\"\$DOMAINS $ds\""
    151  1.1  roy 			fi
    152  1.1  roy 			echo "SEARCH=\"\$SEARCH $search\""
    153  1.1  roy 			if ! $private; then
    154  1.1  roy 				echo "NAMESERVERS=\"\$NAMESERVERS $ns\""
    155  1.1  roy 			fi
    156  1.1  roy 			ns=
    157  1.1  roy 			search=
    158  1.1  roy 			new=true
    159  1.1  roy 			;;
    160  1.1  roy 		esac
    161  1.1  roy 	done
    162  1.1  roy }
    163  1.1  roy 
    164  1.1  roy uniqify()
    165  1.1  roy {
    166  1.1  roy 	local result=
    167  1.1  roy 	while [ -n "$1" ]; do
    168  1.1  roy 		case " $result " in
    169  1.1  roy 		*" $1 "*);;
    170  1.1  roy 		*) result="$result $1";;
    171  1.1  roy 		esac
    172  1.1  roy 		shift
    173  1.1  roy 	done
    174  1.1  roy 	echo "${result# *}"
    175  1.1  roy }
    176  1.1  roy 
    177  1.1  roy list_resolv()
    178  1.1  roy {
    179  1.1  roy 	[ -d "$IFACEDIR" ] || return 0
    180  1.1  roy 
    181  1.1  roy 	local report=false list= retval=0 cmd="$1"
    182  1.1  roy 	shift
    183  1.1  roy 
    184  1.1  roy 	# If we have an interface ordering list, then use that.
    185  1.1  roy 	# It works by just using pathname expansion in the interface directory.
    186  1.1  roy 	if [ -n "$1" ]; then
    187  1.1  roy 		list="$@"
    188  1.1  roy 		$force || report=true
    189  1.1  roy 	else
    190  1.1  roy 		cd "$IFACEDIR"
    191  1.1  roy 		for i in $interface_order; do
    192  1.1  roy 			[ -e "$i" ] && list="$list $i"
    193  1.1  roy 		done
    194  1.1  roy 		for i in $dynamic_order; do
    195  1.1  roy 			if [ -e "$i" -a ! -e "$METRICDIR/"*" $i" ]; then
    196  1.1  roy 				list="$list $i"
    197  1.1  roy 			fi
    198  1.1  roy 		done
    199  1.1  roy 		if [ -d "$METRICDIR" ]; then
    200  1.1  roy 			cd "$METRICDIR"
    201  1.1  roy 			for i in *; do
    202  1.1  roy 				list="$list ${i#* }"
    203  1.1  roy 			done
    204  1.1  roy 		fi
    205  1.1  roy 		list="$list *"
    206  1.1  roy 	fi
    207  1.1  roy 
    208  1.1  roy 	cd "$IFACEDIR"
    209  1.1  roy 	for i in $(uniqify $list); do
    210  1.1  roy 		# Only list interfaces which we really have
    211  1.1  roy 		if ! [ -e "$i" ]; then
    212  1.1  roy 			if $report; then
    213  1.1  roy 				echo "No resolv.conf for interface $i" >&2
    214  1.1  roy 				retval=$(($retval + 1))
    215  1.1  roy 			fi
    216  1.1  roy 			continue
    217  1.1  roy 		fi
    218  1.1  roy 		
    219  1.1  roy 		if [ "$cmd" = i -o "$cmd" = "-i" ]; then
    220  1.1  roy 			printf "$i "
    221  1.1  roy 		else
    222  1.1  roy 			echo_resolv "$i"
    223  1.1  roy 		fi
    224  1.1  roy 	done
    225  1.1  roy 	[ "$cmd" = i -o "$cmd" = "-i" ] && echo
    226  1.1  roy 	return $retval
    227  1.1  roy }
    228  1.1  roy 
    229  1.1  roy make_vars()
    230  1.1  roy {
    231  1.1  roy 	eval "$(list_resolv -l "$@" | parse_resolv)"
    232  1.1  roy 
    233  1.1  roy 	# Ensure that we only list each domain once
    234  1.1  roy 	newdomains=
    235  1.1  roy 	for d in $DOMAINS; do
    236  1.1  roy 		dn="${d%%:*}"
    237  1.1  roy 		case " $newdomains" in
    238  1.1  roy 		*" ${dn}:"*) continue;;
    239  1.1  roy 		esac
    240  1.1  roy 		newdomains="$newdomains${newdomains:+ }$dn:"
    241  1.1  roy 		newns=
    242  1.1  roy 		for nd in $DOMAINS; do
    243  1.1  roy 			if [ "$dn" = "${nd%%:*}" ]; then
    244  1.1  roy 				ns="${nd#*:}"
    245  1.1  roy 				while [ -n "$ns" ]; do
    246  1.1  roy 					case ",$newns," in
    247  1.1  roy 					*,${ns%%,*},*) ;;
    248  1.1  roy 					*) newns="$newns${newns:+,}${ns%%,*}";;
    249  1.1  roy 					esac
    250  1.1  roy 					[ "$ns" = "${ns#*,}" ] && break
    251  1.1  roy 					ns="${ns#*,}"
    252  1.1  roy 				done
    253  1.1  roy 			fi
    254  1.1  roy 		done
    255  1.1  roy 		newdomains="$newdomains$newns"
    256  1.1  roy 	done
    257  1.1  roy 	echo "DOMAINS='$newdomains'"
    258  1.1  roy 	echo "SEARCH='$(uniqify $SEARCH)'"
    259  1.1  roy 	echo "NAMESERVERS='$(uniqify $NAMESERVERS)'"
    260  1.1  roy }
    261  1.1  roy 
    262  1.1  roy force=false
    263  1.1  roy while getopts a:d:fhilm:puv OPT; do
    264  1.1  roy 	case "$OPT" in
    265  1.1  roy 	f) force=true;;
    266  1.1  roy 	h) usage;;
    267  1.1  roy 	m) IF_METRIC="$OPTARG";;
    268  1.1  roy 	p) IF_PRIVATE=1;;
    269  1.1  roy 	'?') ;;
    270  1.1  roy 	*) cmd="$OPT"; iface="$OPTARG";;
    271  1.1  roy 	esac
    272  1.1  roy done
    273  1.1  roy shift $(($OPTIND - 1))
    274  1.1  roy args="$iface${iface:+ }$@"
    275  1.1  roy 
    276  1.1  roy # -I inits the state dir
    277  1.1  roy if [ "$cmd" = I ]; then
    278  1.1  roy 	if [ -d "$VARDIR" ]; then
    279  1.1  roy 		rm -rf "$VARDIR"/*
    280  1.1  roy 	fi
    281  1.1  roy 	exit $?
    282  1.1  roy fi
    283  1.1  roy 
    284  1.1  roy # -l lists our resolv files, optionally for a specific interface
    285  1.1  roy if [ "$cmd" = l -o "$cmd" = i ]; then
    286  1.1  roy 	list_resolv "$cmd" "$args"
    287  1.1  roy 	exit $?
    288  1.1  roy fi
    289  1.1  roy 
    290  1.1  roy # Not normally needed, but subscribers should be able to run independently
    291  1.1  roy if [ "$cmd" = v ]; then
    292  1.1  roy 	make_vars "$iface"
    293  1.1  roy 	exit $?
    294  1.1  roy fi
    295  1.1  roy 
    296  1.1  roy # Test that we have valid options
    297  1.1  roy if [ "$cmd" = a -o "$cmd" = d ]; then
    298  1.1  roy 	if [ -z "$iface" ]; then
    299  1.1  roy 		usage "Interface not specified"
    300  1.1  roy 	fi
    301  1.1  roy elif [ "$cmd" != u ]; then
    302  1.1  roy 	[ -n "$cmd" -a "$cmd" != h ] && usage "Unknown option $cmd"
    303  1.1  roy 	usage
    304  1.1  roy fi
    305  1.1  roy if [ "$cmd" = a ]; then
    306  1.1  roy 	for x in '/' \\ ' ' '*'; do
    307  1.1  roy 		case "$iface" in
    308  1.1  roy 		*[$x]*) error_exit "$x not allowed in interface name";;
    309  1.1  roy 		esac
    310  1.1  roy 	done
    311  1.1  roy 	for x in '.' '-' '~'; do
    312  1.1  roy 		case "$iface" in
    313  1.1  roy 		[$x]*) error_exit \
    314  1.1  roy 			"$x not allowed at start of interface name";;
    315  1.1  roy 		esac
    316  1.1  roy 	done
    317  1.1  roy 	[ "$cmd" = a -a -t 0 ] && error_exit "No file given via stdin"
    318  1.1  roy fi
    319  1.1  roy 
    320  1.1  roy if [ ! -d "$IFACEDIR" ]; then
    321  1.1  roy 	if [ ! -d "$VARDIR" ]; then
    322  1.1  roy 		if [ -L "$VARDIR" ]; then
    323  1.1  roy 			dir="$(readlink "$VARDIR")"
    324  1.1  roy 			# link maybe relative
    325  1.1  roy 			cd "${VARDIR%/*}"
    326  1.1  roy 			if ! mkdir -m 0755 -p "$dir"; then
    327  1.1  roy 				error_exit "Failed to create needed" \
    328  1.1  roy 					"directory $dir"
    329  1.1  roy 			fi
    330  1.1  roy 		else
    331  1.1  roy 			if ! mkdir -m 0755 -p "$VARDIR"; then
    332  1.1  roy 				error_exit "Failed to create needed" \
    333  1.1  roy 					"directory $VARDIR"
    334  1.1  roy 			fi
    335  1.1  roy 		fi
    336  1.1  roy 	fi
    337  1.1  roy 	mkdir -m 0755 -p "$IFACEDIR" || \
    338  1.1  roy 		error_exit "Failed to create needed directory $IFACEDIR"
    339  1.1  roy else
    340  1.1  roy 	# Delete any existing information about the interface
    341  1.1  roy 	if [ "$cmd" = d ]; then
    342  1.1  roy 		cd "$IFACEDIR"
    343  1.1  roy 		for i in $args; do
    344  1.1  roy 			if [ "$cmd" = d -a ! -e "$i" ]; then
    345  1.1  roy 				$force && continue
    346  1.1  roy 				error_exit "No resolv.conf for" \
    347  1.1  roy 					"interface $i"
    348  1.1  roy 			fi
    349  1.1  roy 			rm -f "$i" "$METRICDIR/"*" $i" \
    350  1.1  roy 				"$PRIVATEDIR/$i" || exit $?
    351  1.1  roy 		done
    352  1.1  roy 	fi
    353  1.1  roy fi
    354  1.1  roy 
    355  1.1  roy if [ "$cmd" = a ]; then
    356  1.1  roy 	# Read resolv.conf from stdin
    357  1.1  roy 	resolv="$(cat)\n"
    358  1.1  roy 	# If what we are given matches what we have, then do nothing
    359  1.1  roy 	if [ -e "$IFACEDIR/$iface" ]; then
    360  1.1  roy 		if [ "$(printf "$resolv")" = \
    361  1.1  roy 			"$(cat "$IFACEDIR/$iface")" ]
    362  1.1  roy 		then
    363  1.1  roy 			exit 0
    364  1.1  roy 		fi
    365  1.1  roy 		rm "$IFACEDIR/$iface"
    366  1.1  roy 	fi
    367  1.1  roy 	printf "$resolv" >"$IFACEDIR/$iface" || exit $?
    368  1.1  roy 	[ ! -d "$METRICDIR" ] && mkdir "$METRICDIR"
    369  1.1  roy 	rm -f "$METRICDIR/"*" $iface"
    370  1.1  roy 	if [ -n "$IF_METRIC" ]; then
    371  1.1  roy 		# Pad metric to 6 characters, so 5 is less than 10
    372  1.1  roy 		while [ ${#IF_METRIC} -le 6 ]; do
    373  1.1  roy 			IF_METRIC="0$IF_METRIC"
    374  1.1  roy 		done
    375  1.1  roy 		echo " " >"$METRICDIR/$IF_METRIC $iface"
    376  1.1  roy 	fi
    377  1.1  roy 	case "$IF_PRIVATE" in
    378  1.1  roy 	[Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1)
    379  1.1  roy 		if [ ! -d "$PRIVATEDIR" ]; then
    380  1.1  roy 			[ -e "$PRIVATEDIR" ] && rm "$PRIVATEDIR"
    381  1.1  roy 			mkdir "$PRIVATEDIR"
    382  1.1  roy 		fi
    383  1.1  roy 		[ -d "$PRIVATEDIR" ] && echo " " >"$PRIVATEDIR/$iface"
    384  1.1  roy 		;;
    385  1.1  roy 	*)
    386  1.1  roy 		if [ -e "$PRIVATEDIR/$iface" ]; then
    387  1.1  roy 			rm -f "$PRIVATEDIR/$iface"
    388  1.1  roy 		fi
    389  1.1  roy 		;;
    390  1.1  roy 	esac
    391  1.1  roy fi
    392  1.1  roy 
    393  1.1  roy eval "$(make_vars)"
    394  1.1  roy : ${list_resolv:=list_resolv -l}
    395  1.1  roy retval=0
    396  1.1  roy for script in "$LIBEXECDIR"/*; do
    397  1.1  roy 	[ -f "$script" ] || continue
    398  1.1  roy 	( . "$script" "$cmd" "$iface" )
    399  1.1  roy 	retval=$(($retval + $?))
    400  1.1  roy done
    401  1.1  roy exit $retval
    402