etc/services - sync with NetBSD-8
[minix.git] / external / bsd / openresolv / dist / resolvconf.in
blob3b2b0f53fd835538097b3bce8bec0101589e8580
1 #!/bin/sh
2 # Copyright (c) 2007-2015 Roy Marples
3 # All rights reserved
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.
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.
27 RESOLVCONF="$0"
28 SYSCONFDIR=@SYSCONFDIR@
29 LIBEXECDIR=@LIBEXECDIR@
30 VARDIR=@VARDIR@
32 # Disregard dhcpcd setting
33 unset interface_order state_dir
35 # If you change this, change the test in VFLAG and libc.in as well
36 local_nameservers="127.* 0.0.0.0 255.255.255.255 ::1"
38 dynamic_order="tap[0-9]* tun[0-9]* vpn vpn[0-9]* ppp[0-9]* ippp[0-9]*"
39 interface_order="lo lo[0-9]*"
40 name_server_blacklist="0.0.0.0"
42 # Support original resolvconf configuration layout
43 # as well as the openresolv config file
44 if [ -f "$SYSCONFDIR"/resolvconf.conf ]; then
45 . "$SYSCONFDIR"/resolvconf.conf
46 [ -n "$state_dir" ] && VARDIR="$state_dir"
47 elif [ -d "$SYSCONFDIR/resolvconf" ]; then
48 SYSCONFDIR="$SYSCONFDIR/resolvconf"
49 if [ -f "$SYSCONFDIR"/interface-order ]; then
50 interface_order="$(cat "$SYSCONFDIR"/interface-order)"
53 TMPDIR="$VARDIR/tmp"
54 IFACEDIR="$VARDIR/interfaces"
55 METRICDIR="$VARDIR/metrics"
56 PRIVATEDIR="$VARDIR/private"
57 EXCLUSIVEDIR="$VARDIR/exclusive"
58 LOCKDIR="$VARDIR/lock"
60 warn()
62 echo "$*" >&2
65 error_exit()
67 echo "$*" >&2
68 exit 1
71 usage()
73 cat <<-EOF
74 Usage: ${RESOLVCONF##*/} [options]
76 Inform the system about any DNS updates.
78 Options:
79 -a \$INTERFACE Add DNS information to the specified interface
80 (DNS supplied via stdin in resolv.conf format)
81 -m metric Give the added DNS information a metric
82 -p Mark the interface as private
83 -x Mark the interface as exclusive
84 -d \$INTERFACE Delete DNS information from the specified interface
85 -f Ignore non existant interfaces
86 -I Init the state dir
87 -u Run updates from our current DNS information
88 -l [\$PATTERN] Show DNS information, optionally from interfaces
89 that match the specified pattern
90 -i [\$PATTERN] Show interfaces that have supplied DNS information
91 optionally from interfaces that match the specified
92 pattern
93 -v [\$PATTERN] echo NEWDOMAIN, NEWSEARCH and NEWNS variables to
94 the console
95 -h Show this help cruft
96 EOF
97 [ -z "$1" ] && exit 0
98 echo
99 error_exit "$*"
102 echo_resolv()
104 local line= OIFS="$IFS"
106 [ -n "$1" -a -f "$IFACEDIR/$1" ] || return 1
107 echo "# resolv.conf from $1"
108 # Our variable maker works of the fact each resolv.conf per interface
109 # is separated by blank lines.
110 # So we remove them when echoing them.
111 while read -r line; do
112 IFS="$OIFS"
113 if [ -n "$line" ]; then
114 # We need to set IFS here to preserve any whitespace
115 IFS=''
116 printf "%s\n" "$line"
118 done < "$IFACEDIR/$1"
119 echo
120 IFS="$OIFS"
123 # Parse resolv.conf's and make variables
124 # for domain name servers, search name servers and global nameservers
125 parse_resolv()
127 local line= ns= ds= search= d= n= newns=
128 local new=true iface= private=false p= domain= l= islocal=
130 newns=
132 while read -r line; do
133 case "$line" in
134 "# resolv.conf from "*)
135 if ${new}; then
136 iface="${line#\# resolv.conf from *}"
137 new=false
138 if [ -e "$PRIVATEDIR/$iface" ]; then
139 private=true
140 else
141 # Allow expansion
142 cd "$IFACEDIR"
143 private=false
144 for p in $private_interfaces; do
145 case "$iface" in
146 "$p"|"$p":*) private=true; break;;
147 esac
148 done
152 "nameserver "*)
153 islocal=false
154 for l in $local_nameservers; do
155 case "${line#* }" in
157 islocal=true
158 echo "LOCALNAMESERVERS=\"\$LOCALNAMESERVERS ${line#* }\""
159 break
161 esac
162 done
163 $islocal || ns="$ns${line#* } "
165 "domain "*)
166 if [ -z "$domain" ]; then
167 domain="${line#* }"
168 echo "DOMAIN=\"$domain\""
170 search="${line#* }"
172 "search "*)
173 search="${line#* }"
176 [ -n "$line" ] && continue
177 if [ -n "$ns" -a -n "$search" ]; then
178 newns=
179 for n in $ns; do
180 newns="$newns${newns:+,}$n"
181 done
183 for d in $search; do
184 ds="$ds${ds:+ }$d:$newns"
185 done
186 echo "DOMAINS=\"\$DOMAINS $ds\""
188 echo "SEARCH=\"\$SEARCH $search\""
189 if ! $private; then
190 echo "NAMESERVERS=\"\$NAMESERVERS $ns\""
193 search=
194 new=true
196 esac
197 done
200 uniqify()
202 local result=
203 while [ -n "$1" ]; do
204 case " $result " in
205 *" $1 "*);;
206 *) result="$result $1";;
207 esac
208 shift
209 done
210 echo "${result# *}"
213 dirname()
215 local dir= OIFS="$IFS"
216 local IFS=/
217 set -- $@
218 IFS="$OIFS"
219 if [ -n "$1" ]; then
220 printf %s .
221 else
222 shift
224 while [ -n "$2" ]; do
225 printf "/%s" "$1"
226 shift
227 done
228 printf "\n"
231 config_mkdirs()
233 local e=0 f d
234 for f; do
235 [ -n "$f" ] || continue
236 d="$(dirname "$f")"
237 if [ ! -d "$d" ]; then
238 if type install >/dev/null 2>&1; then
239 install -d "$d" || e=$?
240 else
241 mkdir "$d" || e=$?
244 done
245 return $e
248 list_resolv()
250 [ -d "$IFACEDIR" ] || return 0
252 local report=false list= retval=0 cmd="$1" excl=
253 shift
255 case "$IF_EXCLUSIVE" in
256 [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1)
257 if [ -d "$EXCLUSIVEDIR" ]; then
258 cd "$EXCLUSIVEDIR"
259 for i in *; do
260 if [ -f "$i" ]; then
261 list="${i#* }"
262 break
264 done
266 excl=true
269 excl=false
271 esac
273 # If we have an interface ordering list, then use that.
274 # It works by just using pathname expansion in the interface directory.
275 if [ -n "$1" ]; then
276 list="$*"
277 $force || report=true
278 elif ! $excl; then
279 cd "$IFACEDIR"
280 for i in $interface_order; do
281 [ -f "$i" ] && list="$list $i"
282 for ii in "$i":* "$i".*; do
283 [ -f "$ii" ] && list="$list $ii"
284 done
285 done
286 for i in $dynamic_order; do
287 if [ -e "$i" -a ! -e "$METRICDIR/"*" $i" ]; then
288 list="$list $i"
290 for ii in "$i":* "$i".*; do
291 if [ -f "$ii" -a ! -e "$METRICDIR/"*" $ii" ]; then
292 list="$list $ii"
294 done
295 done
296 if [ -d "$METRICDIR" ]; then
297 cd "$METRICDIR"
298 for i in *; do
299 [ -f "$i" ] && list="$list ${i#* }"
300 done
302 list="$list *"
305 cd "$IFACEDIR"
306 retval=1
307 for i in $(uniqify $list); do
308 # Only list interfaces which we really have
309 if ! [ -f "$i" ]; then
310 if $report; then
311 echo "No resolv.conf for interface $i" >&2
312 retval=2
314 continue
317 if [ "$cmd" = i -o "$cmd" = "-i" ]; then
318 printf %s "$i "
319 else
320 echo_resolv "$i"
322 [ $? = 0 -a "$retval" = 1 ] && retval=0
323 done
324 [ "$cmd" = i -o "$cmd" = "-i" ] && echo
325 return $retval
328 list_remove() {
329 local list= e= l= result= found= retval=0
331 [ -z "$2" ] && return 0
332 eval list=\"\$$1\"
333 shift
335 set -f
336 for e; do
337 found=false
338 for l in $list; do
339 case "$e" in
340 $l) found=true;;
341 esac
342 $found && break
343 done
344 if $found; then
345 retval=$(($retval + 1))
346 else
347 result="$result $e"
349 done
350 set +f
351 echo "${result# *}"
352 return $retval
355 echo_prepend()
357 echo "# Generated by resolvconf"
358 if [ -n "$search_domains" ]; then
359 echo "search $search_domains"
361 for n in $name_servers; do
362 echo "nameserver $n"
363 done
364 echo
367 echo_append()
369 echo "# Generated by resolvconf"
370 if [ -n "$search_domains_append" ]; then
371 echo "search $search_domains_append"
373 for n in $name_servers_append; do
374 echo "nameserver $n"
375 done
376 echo
379 replace()
381 local r= k= f= v= val= sub=
383 while read -r keyword value; do
384 for r in $replace; do
385 k="${r%%/*}"
386 r="${r#*/}"
387 f="${r%%/*}"
388 r="${r#*/}"
389 v="${r%%/*}"
390 case "$keyword" in
392 case "$value" in
393 $f) value="$v";;
394 esac
396 esac
397 done
398 val=
399 for sub in $value; do
400 for r in $replace_sub; do
401 k="${r%%/*}"
402 r="${r#*/}"
403 f="${r%%/*}"
404 r="${r#*/}"
405 v="${r%%/*}"
406 case "$keyword" in
408 case "$sub" in
409 $f) sub="$v";;
410 esac
412 esac
413 done
414 val="$val${val:+ }$sub"
415 done
416 printf "%s %s\n" "$keyword" "$val"
417 done
420 make_vars()
422 local newdomains= d= dn= newns= ns=
424 # Clear variables
425 DOMAIN=
426 DOMAINS=
427 SEARCH=
428 NAMESERVERS=
429 LOCALNAMESERVERS=
431 if [ -n "$name_servers" -o -n "$search_domains" ]; then
432 eval "$(echo_prepend | parse_resolv)"
434 if [ -z "$VFLAG" ]; then
435 IF_EXCLUSIVE=1
436 list_resolv -i "$@" >/dev/null || IF_EXCLUSIVE=0
437 eval "$(list_resolv -l "$@" | replace | parse_resolv)"
439 if [ -n "$name_servers_append" -o -n "$search_domains_append" ]; then
440 eval "$(echo_append | parse_resolv)"
443 # Ensure that we only list each domain once
444 for d in $DOMAINS; do
445 dn="${d%%:*}"
446 list_remove domain_blacklist "$dn" >/dev/null || continue
447 case " $newdomains" in
448 *" ${dn}:"*) continue;;
449 esac
450 newns=
451 for nd in $DOMAINS; do
452 if [ "$dn" = "${nd%%:*}" ]; then
453 ns="${nd#*:}"
454 while [ -n "$ns" ]; do
455 case ",$newns," in
456 *,${ns%%,*},*) ;;
457 *) list_remove name_server_blacklist \
458 "${ns%%,*}" >/dev/null \
459 && newns="$newns${newns:+,}${ns%%,*}";;
460 esac
461 [ "$ns" = "${ns#*,}" ] && break
462 ns="${ns#*,}"
463 done
465 done
466 if [ -n "$newns" ]; then
467 newdomains="$newdomains${newdomains:+ }$dn:$newns"
469 done
470 DOMAIN="$(list_remove domain_blacklist $DOMAIN)"
471 SEARCH="$(uniqify $SEARCH)"
472 SEARCH="$(list_remove domain_blacklist $SEARCH)"
473 NAMESERVERS="$(uniqify $NAMESERVERS)"
474 NAMESERVERS="$(list_remove name_server_blacklist $NAMESERVERS)"
475 LOCALNAMESERVERS="$(uniqify $LOCALNAMESERVERS)"
476 LOCALNAMESERVERS="$(list_remove name_server_blacklist $LOCALNAMESERVERS)"
477 echo "DOMAIN='$DOMAIN'"
478 echo "SEARCH='$SEARCH'"
479 echo "NAMESERVERS='$NAMESERVERS'"
480 echo "LOCALNAMESERVERS='$LOCALNAMESERVERS'"
481 echo "DOMAINS='$newdomains'"
484 force=false
485 VFLAG=
486 while getopts a:Dd:fhIilm:puvVx OPT; do
487 case "$OPT" in
488 f) force=true;;
489 h) usage;;
490 m) IF_METRIC="$OPTARG";;
491 p) IF_PRIVATE=1;;
493 VFLAG=1
494 if [ "$local_nameservers" = \
495 "127.* 0.0.0.0 255.255.255.255 ::1" ]
496 then
497 local_nameservers=
500 x) IF_EXCLUSIVE=1;;
501 '?') ;;
502 *) cmd="$OPT"; iface="$OPTARG";;
503 esac
504 done
505 shift $(($OPTIND - 1))
506 args="$iface${iface:+ }$*"
508 # -I inits the state dir
509 if [ "$cmd" = I ]; then
510 if [ -d "$VARDIR" ]; then
511 rm -rf "$VARDIR"/*
513 exit $?
516 # -D ensures that the listed config file base dirs exist
517 if [ "$cmd" = D ]; then
518 config_mkdirs "$@"
519 exit $?
522 # -l lists our resolv files, optionally for a specific interface
523 if [ "$cmd" = l -o "$cmd" = i ]; then
524 list_resolv "$cmd" "$args"
525 exit $?
528 # Not normally needed, but subscribers should be able to run independently
529 if [ "$cmd" = v -o -n "$VFLAG" ]; then
530 make_vars "$iface"
531 exit $?
534 # Test that we have valid options
535 if [ "$cmd" = a -o "$cmd" = d ]; then
536 if [ -z "$iface" ]; then
537 usage "Interface not specified"
539 elif [ "$cmd" != u ]; then
540 [ -n "$cmd" -a "$cmd" != h ] && usage "Unknown option $cmd"
541 usage
544 if [ "$cmd" = a ]; then
545 for x in '/' \\ ' ' '*'; do
546 case "$iface" in
547 *[$x]*) error_exit "$x not allowed in interface name";;
548 esac
549 done
550 for x in '.' '-' '~'; do
551 case "$iface" in
552 [$x]*) error_exit \
553 "$x not allowed at start of interface name";;
554 esac
555 done
556 [ "$cmd" = a -a -t 0 ] && error_exit "No file given via stdin"
559 if [ ! -d "$VARDIR" ]; then
560 if [ -L "$VARDIR" ]; then
561 dir="$(readlink "$VARDIR")"
562 # link maybe relative
563 cd "${VARDIR%/*}"
564 if ! mkdir -m 0755 -p "$dir"; then
565 error_exit "Failed to create needed" \
566 "directory $dir"
568 else
569 if ! mkdir -m 0755 -p "$VARDIR"; then
570 error_exit "Failed to create needed" \
571 "directory $VARDIR"
576 if [ ! -d "$IFACEDIR" ]; then
577 mkdir -m 0755 -p "$IFACEDIR" || \
578 error_exit "Failed to create needed directory $IFACEDIR"
579 if [ "$cmd" = d ]; then
580 # Provide the same error messages as below
581 if ! ${force}; then
582 cd "$IFACEDIR"
583 for i in $args; do
584 warn "No resolv.conf for interface $i"
585 done
587 ${force}
588 exit $?
592 # An interface was added, changed, deleted or a general update was called.
593 # Due to exclusivity we need to ensure that this is an atomic operation.
594 # Our subscribers *may* need this as well if the init system is sub par.
595 # As such we spinlock at this point as best we can.
596 # We don't use flock(1) because it's not widely available and normally resides
597 # in /usr which we do our very best to operate without.
598 [ -w "$VARDIR" ] || error_exit "Cannot write to $LOCKDIR"
599 : ${lock_timeout:=10}
600 while true; do
601 if mkdir "$LOCKDIR" 2>/dev/null; then
602 trap 'rm -rf "$LOCKDIR";' EXIT
603 trap 'rm -rf "$LOCKDIR"; exit 1' INT QUIT ABRT SEGV ALRM TERM
604 echo $$ >"$LOCKDIR/pid"
605 break
607 pid=$(cat "$LOCKDIR/pid")
608 if ! kill -0 "$pid"; then
609 warn "clearing stale lock pid $pid"
610 rm -rf "$LOCKDIR"
611 continue
613 lock_timeout=$(($lock_timeout - 1))
614 if [ "$lock_timeout" -le 0 ]; then
615 error_exit "timed out waiting for lock from pid $pid"
617 sleep 1
618 done
620 case "$cmd" in
622 # Read resolv.conf from stdin
623 resolv="$(cat)"
624 changed=false
625 changedfile=false
626 # If what we are given matches what we have, then do nothing
627 if [ -e "$IFACEDIR/$iface" ]; then
628 if [ "$(echo "$resolv")" != \
629 "$(cat "$IFACEDIR/$iface")" ]
630 then
631 changed=true
632 changedfile=true
634 else
635 changed=true
636 changedfile=true
639 # Set metric and private before creating the interface resolv.conf file
640 # to ensure that it will have the correct flags
641 [ ! -d "$METRICDIR" ] && mkdir "$METRICDIR"
642 oldmetric="$METRICDIR/"*" $iface"
643 newmetric=
644 if [ -n "$IF_METRIC" ]; then
645 # Pad metric to 6 characters, so 5 is less than 10
646 while [ ${#IF_METRIC} -le 6 ]; do
647 IF_METRIC="0$IF_METRIC"
648 done
649 newmetric="$METRICDIR/$IF_METRIC $iface"
651 rm -f "$METRICDIR/"*" $iface"
652 [ "$oldmetric" != "$newmetric" -a \
653 "$oldmetric" != "$METRICDIR/* $iface" ] &&
654 changed=true
655 [ -n "$newmetric" ] && echo " " >"$newmetric"
657 case "$IF_PRIVATE" in
658 [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1)
659 if [ ! -d "$PRIVATEDIR" ]; then
660 [ -e "$PRIVATEDIR" ] && rm "$PRIVATEDIR"
661 mkdir "$PRIVATEDIR"
663 [ -e "$PRIVATEDIR/$iface" ] || changed=true
664 [ -d "$PRIVATEDIR" ] && echo " " >"$PRIVATEDIR/$iface"
667 if [ -e "$PRIVATEDIR/$iface" ]; then
668 rm -f "$PRIVATEDIR/$iface"
669 changed=true
672 esac
674 oldexcl=
675 for x in "$EXCLUSIVEDIR/"*" $iface"; do
676 if [ -f "$x" ]; then
677 oldexcl="$x"
678 break
680 done
681 case "$IF_EXCLUSIVE" in
682 [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1)
683 if [ ! -d "$EXCLUSIVEDIR" ]; then
684 [ -e "$EXCLUSIVEDIR" ] && rm "$EXCLUSIVEDIR"
685 mkdir "$EXCLUSIVEDIR"
687 cd "$EXCLUSIVEDIR"
688 for x in *; do
689 [ -f "$x" ] && break
690 done
691 if [ "${x#* }" != "$iface" ]; then
692 if [ "$x" = "${x% *}" ]; then
693 x=10000000
694 else
695 x="${x% *}"
697 if [ "$x" = "0000000" ]; then
698 warn "exclusive underflow"
699 else
700 x=$(($x - 1))
702 if [ -d "$EXCLUSIVEDIR" ]; then
703 echo " " >"$EXCLUSIVEDIR/$x $iface"
705 changed=true
709 if [ -f "$oldexcl" ]; then
710 rm -f "$oldexcl"
711 changed=true
714 esac
716 if $changedfile; then
717 printf "%s\n" "$resolv" >"$IFACEDIR/$iface" || exit $?
718 elif ! $changed; then
719 exit 0
721 unset changed changedfile oldmetric newmetric x oldexcl
725 # Delete any existing information about the interface
726 cd "$IFACEDIR"
727 changed=false
728 for i in $args; do
729 if [ -e "$i" ]; then
730 changed=true
731 elif ! ${force}; then
732 warn "No resolv.conf for interface $i"
734 rm -f "$i" "$METRICDIR/"*" $i" \
735 "$PRIVATEDIR/$i" \
736 "$EXCLUSIVEDIR/"*" $i" || exit $?
737 done
738 if ! ${changed}; then
739 # Set the return code based on the forced flag
740 ${force}
741 exit $?
743 unset changed i
745 esac
747 case "${resolvconf:-YES}" in
748 [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) ;;
749 *) exit 0;;
750 esac
752 eval "$(make_vars)"
753 export RESOLVCONF DOMAINS SEARCH NAMESERVERS LOCALNAMESERVERS
754 : ${list_resolv:=list_resolv -l}
755 retval=0
756 for script in "$LIBEXECDIR"/*; do
757 if [ -f "$script" ]; then
758 eval script_enabled="\$${script##*/}"
759 case "${script_enabled:-YES}" in
760 [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) ;;
761 *) continue;;
762 esac
763 if [ -x "$script" ]; then
764 "$script" "$cmd" "$iface"
765 else
766 (set -- "$cmd" "$iface"; . "$script")
768 retval=$(($retval + $?))
770 done
771 exit $retval