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