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