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