2 # Copyright (c) 2007-2016 Roy Marples
5 # Redistribution and use in source and binary forms, with or without
6 # modification, are permitted provided that the following conditions
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.
28 OPENRESOLV_VERSION="3.8.1"
29 SYSCONFDIR=@SYSCONFDIR@
30 LIBEXECDIR=@LIBEXECDIR@
33 RESTARTCMD=@RESTARTCMD@
35 # Disregard dhcpcd setting
36 unset interface_order state_dir
38 # If you change this, change the test in VFLAG and libc.in as well
39 local_nameservers="127.* 0.0.0.0 255.255.255.255 ::1"
41 dynamic_order="tap[0-9]* tun[0-9]* vpn vpn[0-9]* ppp[0-9]* ippp[0-9]*"
42 interface_order="lo lo[0-9]*"
43 name_server_blacklist="0.0.0.0"
45 # Support original resolvconf configuration layout
46 # as well as the openresolv config file
47 if [ -f "$SYSCONFDIR"/resolvconf.conf ]; then
48 . "$SYSCONFDIR"/resolvconf.conf
49 [ -n "$state_dir" ] && VARDIR="$state_dir"
50 elif [ -d "$SYSCONFDIR/resolvconf" ]; then
51 SYSCONFDIR="$SYSCONFDIR/resolvconf"
52 if [ -f "$SYSCONFDIR"/interface-order ]; then
53 interface_order="$(cat "$SYSCONFDIR"/interface-order)"
56 IFACEDIR="$VARDIR/interfaces"
57 METRICDIR="$VARDIR/metrics"
58 PRIVATEDIR="$VARDIR/private"
59 EXCLUSIVEDIR="$VARDIR/exclusive"
60 LOCKDIR="$VARDIR/lock"
77 Usage: ${RESOLVCONF##*/} [options] command [argument]
79 Inform the system about any DNS updates.
82 -a \$INTERFACE Add DNS information to the specified interface
83 (DNS supplied via stdin in resolv.conf format)
84 -d \$INTERFACE Delete DNS information from the specified interface
85 -h Show this help cruft
86 -i [\$PATTERN] Show interfaces that have supplied DNS information
87 optionally from interfaces that match the specified
89 -l [\$PATTERN] Show DNS information, optionally from interfaces
90 that match the specified pattern
92 -u Run updates from our current DNS information
95 -f Ignore non existent interfaces
96 -m metric Give the added DNS information a metric
97 -p Mark the interface as private
98 -x Mark the interface as exclusive
100 Subscriber and System Init Commands:
101 -I Init the state dir
102 -r \$SERVICE Restart the system service
103 (restarting a non-existent or non-running service
104 should have no output and return 0)
105 -R Show the system service restart command
106 -v [\$PATTERN] echo NEWDOMAIN, NEWSEARCH and NEWNS variables to
108 -V [\$PATTERN] Same as -v, but only uses configuration in
109 $SYSCONFDIR/resolvconf.conf
111 [ -z "$1" ] && exit 0
116 # Strip any trailing dot from each name as a FQDN does not belong
118 # If you think otherwise, capture a DNS trace and you'll see libc
119 # will strip it regardless.
120 # This also solves setting up duplicate zones in our subscribers.
121 strip_trailing_dots()
126 printf "$d%s" "${n%.}"
132 # Parse resolv.conf's and make variables
133 # for domain name servers, search name servers and global nameservers
136 local line= ns= ds= search= d= n= newns=
137 local new=true iface= private=false p= domain= l= islocal=
141 while read -r line; do
143 "# resolv.conf from "*)
145 iface="${line#\# resolv.conf from *}"
147 if [ -e "$PRIVATEDIR/$iface" ]; then
153 for p in $private_interfaces; do
166 for l in $local_nameservers; do
170 echo "LOCALNAMESERVERS=\"\$LOCALNAMESERVERS ${line#* }\""
175 $islocal || ns="$ns${line#* } "
178 search="$(strip_trailing_dots ${line#* })"
179 if [ -z "$domain" ]; then
181 echo "DOMAIN=\"$domain\""
185 search="$(strip_trailing_dots ${line#* })"
188 [ -n "$line" ] && continue
189 if [ -n "$ns" -a -n "$search" ]; then
192 newns="$newns${newns:+,}$n"
196 ds="$ds${ds:+ }$d:$newns"
198 echo "DOMAINS=\"\$DOMAINS $ds\""
200 echo "SEARCH=\"\$SEARCH $search\""
202 echo "NAMESERVERS=\"\$NAMESERVERS $ns\""
215 while [ -n "$1" ]; do
218 *) result="$result $1";;
227 local dir= OIFS="$IFS"
236 while [ -n "$2" ]; do
247 [ -n "$f" ] || continue
249 if [ ! -d "$d" ]; then
250 if type install >/dev/null 2>&1; then
251 install -d "$d" || e=$?
260 # With the advent of alternative init systems, it's possible to have
261 # more than one installed. So we need to try and guess what one we're
262 # using unless overriden by configure.
263 # Note that restarting a service is a last resort - the subscribers
264 # should make a reasonable attempt to reconfigre the service via some
265 # method, normally SIGHUP.
268 [ -n "$RESTARTCMD" ] && return 0
270 # Detect the running init system.
271 # As systemd and OpenRC can be installed on top of legacy init
272 # systems we try to detect them first.
273 local status="@STATUSARG@"
275 if [ -x /bin/systemctl -a -S /run/systemd/private ]; then
276 RESTARTCMD="if /bin/systemctl --quiet is-active \$1.service; then
277 /bin/systemctl restart \$1.service;
279 elif [ -x /usr/bin/systemctl -a -S /run/systemd/private ]; then
280 RESTARTCMD="if /usr/bin/systemctl --quiet is-active \$1.service; then
281 /usr/bin/systemctl restart \$1.service;
283 elif [ -x /sbin/rc-service -a \
284 -s /libexec/rc/init.d/softlevel -o -s /run/openrc/softlevel ]
286 RESTARTCMD="/sbin/rc-service -i \$1 -- -Ds restart"
287 elif [ -x /usr/sbin/invoke-rc.d ]; then
289 RESTARTCMD="if /usr/sbin/invoke-rc.d --quiet \$1 status 1>/dev/null 2>&1; then
290 /usr/sbin/invoke-rc.d \$1 restart;
292 elif [ -x /sbin/service ]; then
295 RESTARTCMD="if /sbin/service \$1; then
296 /sbin/service \$1 restart;
298 elif [ -x /usr/sbin/service ]; then
300 RESTARTCMD="if /usr/sbin/service \$1 $status 1>/dev/null 2>&1; then
301 /usr/sbin/service \$1 restart;
303 elif [ -x /bin/sv ]; then
304 RESTARTCMD="/bin/sv try-restart \$1"
305 elif [ -x /usr/bin/sv ]; then
306 RESTARTCMD="/usr/bin/sv try-restart \$1"
307 elif [ -e /etc/arch-release -a -d /etc/rc.d ]; then
309 RESTARTCMD="if [ -e /var/run/daemons/\$1 ]; then
310 /etc/rc.d/\$1 restart;
312 elif [ -e /etc/slackware-version -a -d /etc/rc.d ]; then
313 RESTARTCMD="if /etc/rc.d/rc.\$1 status 1>/dev/null 2>&1; then
314 /etc/rc.d/rc.\$1 restart;
316 elif [ -e /etc/rc.d/rc.subr -a -d /etc/rc.d ]; then
318 RESTARTCMD="if /etc/rc.d/\$1 check 1>/dev/null 2>&1; then
319 /etc/rc.d/\$1 restart;
322 for x in /etc/init.d/rc.d /etc/rc.d /etc/init.d; do
323 [ -d $x ] || continue
324 RESTARTCMD="if $x/\$1 $status 1>/dev/null 2>&1; then
331 if [ -z "$RESTARTCMD" ]; then
332 if [ "$NOINIT_WARNED" != true ]; then
333 warn "could not detect a useable init system"
344 local line= OIFS="$IFS"
346 [ -n "$1" -a -f "$IFACEDIR/$1" ] || return 1
347 echo "# resolv.conf from $1"
348 # Our variable maker works of the fact each resolv.conf per interface
349 # is separated by blank lines.
350 # So we remove them when echoing them.
351 while read -r line; do
353 if [ -n "$line" ]; then
354 # We need to set IFS here to preserve any whitespace
356 printf "%s\n" "$line"
358 done < "$IFACEDIR/$1"
364 [ -d "$IFACEDIR" ] || return 0
366 local report=false list= retval=0 cmd="$1" excl=
369 case "$IF_EXCLUSIVE" in
370 [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1)
371 if [ -d "$EXCLUSIVEDIR" ]; then
387 # If we have an interface ordering list, then use that.
388 # It works by just using pathname expansion in the interface directory.
391 $force || report=true
394 for i in $interface_order; do
395 [ -f "$i" ] && list="$list $i"
396 for ii in "$i":* "$i".*; do
397 [ -f "$ii" ] && list="$list $ii"
400 for i in $dynamic_order; do
401 if [ -e "$i" -a ! -e "$METRICDIR/"*" $i" ]; then
404 for ii in "$i":* "$i".*; do
405 if [ -f "$ii" -a ! -e "$METRICDIR/"*" $ii" ]; then
410 if [ -d "$METRICDIR" ]; then
413 [ -f "$i" ] && list="$list ${i#* }"
422 for i in $(uniqify $list); do
423 # Only list interfaces which we really have
424 if ! [ -f "$i" ]; then
426 echo "No resolv.conf for interface $i" >&2
432 if [ "$cmd" = i -o "$cmd" = "-i" ]; then
438 [ $? = 0 -a "$retval" = 1 ] && retval=0
440 [ "$cmd" = i -o "$cmd" = "-i" ] && echo
445 local list= e= l= result= found= retval=0
447 [ -z "$2" ] && return 0
461 retval=$(($retval + 1))
473 echo "# Generated by resolvconf"
474 if [ -n "$search_domains" ]; then
475 echo "search $search_domains"
477 for n in $name_servers; do
485 echo "# Generated by resolvconf"
486 if [ -n "$search_domains_append" ]; then
487 echo "search $search_domains_append"
489 for n in $name_servers_append; do
497 local r= k= f= v= val= sub=
499 while read -r keyword value; do
500 for r in $replace; do
515 for sub in $value; do
516 for r in $replace_sub; do
530 val="$val${val:+ }$sub"
532 printf "%s %s\n" "$keyword" "$val"
538 local newdomains= d= dn= newns= ns=
547 if [ -n "$name_servers" -o -n "$search_domains" ]; then
548 eval "$(echo_prepend | parse_resolv)"
550 if [ -z "$VFLAG" ]; then
552 list_resolv -i "$@" >/dev/null || IF_EXCLUSIVE=0
553 eval "$(list_resolv -l "$@" | replace | parse_resolv)"
555 if [ -n "$name_servers_append" -o -n "$search_domains_append" ]; then
556 eval "$(echo_append | parse_resolv)"
559 # Ensure that we only list each domain once
560 for d in $DOMAINS; do
562 list_remove domain_blacklist "$dn" >/dev/null || continue
563 case " $newdomains" in
564 *" ${dn}:"*) continue;;
567 for nd in $DOMAINS; do
568 if [ "$dn" = "${nd%%:*}" ]; then
570 while [ -n "$ns" ]; do
573 *) list_remove name_server_blacklist \
574 "${ns%%,*}" >/dev/null \
575 && newns="$newns${newns:+,}${ns%%,*}";;
577 [ "$ns" = "${ns#*,}" ] && break
582 if [ -n "$newns" ]; then
583 newdomains="$newdomains${newdomains:+ }$dn:$newns"
586 DOMAIN="$(list_remove domain_blacklist $DOMAIN)"
587 SEARCH="$(uniqify $SEARCH)"
588 SEARCH="$(list_remove domain_blacklist $SEARCH)"
589 NAMESERVERS="$(uniqify $NAMESERVERS)"
590 NAMESERVERS="$(list_remove name_server_blacklist $NAMESERVERS)"
591 LOCALNAMESERVERS="$(uniqify $LOCALNAMESERVERS)"
592 LOCALNAMESERVERS="$(list_remove name_server_blacklist $LOCALNAMESERVERS)"
593 echo "DOMAIN='$DOMAIN'"
594 echo "SEARCH='$SEARCH'"
595 echo "NAMESERVERS='$NAMESERVERS'"
596 echo "LOCALNAMESERVERS='$LOCALNAMESERVERS'"
597 echo "DOMAINS='$newdomains'"
602 while getopts a:Dd:fhIilm:pRruvVx OPT; do
606 m) IF_METRIC="$OPTARG";;
610 if [ "$local_nameservers" = \
611 "127.* 0.0.0.0 255.255.255.255 ::1" ]
618 *) cmd="$OPT"; iface="$OPTARG";;
621 shift $(($OPTIND - 1))
622 args="$iface${iface:+ }$*"
624 # -I inits the state dir
625 if [ "$cmd" = I ]; then
626 if [ -d "$VARDIR" ]; then
632 # -D ensures that the listed config file base dirs exist
633 if [ "$cmd" = D ]; then
638 # -l lists our resolv files, optionally for a specific interface
639 if [ "$cmd" = l -o "$cmd" = i ]; then
640 list_resolv "$cmd" "$args"
644 # Restart a service or echo the command to restart a service
645 if [ "$cmd" = r -o "$cmd" = R ]; then
646 detect_init || exit 1
647 if [ "$cmd" = r ]; then
656 # Not normally needed, but subscribers should be able to run independently
657 if [ "$cmd" = v -o -n "$VFLAG" ]; then
662 # Test that we have valid options
663 if [ "$cmd" = a -o "$cmd" = d ]; then
664 if [ -z "$iface" ]; then
665 usage "Interface not specified"
667 elif [ "$cmd" != u ]; then
668 [ -n "$cmd" -a "$cmd" != h ] && usage "Unknown option $cmd"
672 if [ "$cmd" = a ]; then
673 for x in '/' \\ ' ' '*'; do
675 *[$x]*) error_exit "$x not allowed in interface name";;
678 for x in '.' '-' '~'; do
681 "$x not allowed at start of interface name";;
684 [ "$cmd" = a -a -t 0 ] && error_exit "No file given via stdin"
687 if [ ! -d "$VARDIR" ]; then
688 if [ -L "$VARDIR" ]; then
689 dir="$(readlink "$VARDIR")"
690 # link maybe relative
692 if ! mkdir -m 0755 -p "$dir"; then
693 error_exit "Failed to create needed" \
697 if ! mkdir -m 0755 -p "$VARDIR"; then
698 error_exit "Failed to create needed" \
704 if [ ! -d "$IFACEDIR" ]; then
705 mkdir -m 0755 -p "$IFACEDIR" || \
706 error_exit "Failed to create needed directory $IFACEDIR"
707 if [ "$cmd" = d ]; then
708 # Provide the same error messages as below
712 warn "No resolv.conf for interface $i"
720 # An interface was added, changed, deleted or a general update was called.
721 # Due to exclusivity we need to ensure that this is an atomic operation.
722 # Our subscribers *may* need this as well if the init system is sub par.
723 # As such we spinlock at this point as best we can.
724 # We don't use flock(1) because it's not widely available and normally resides
725 # in /usr which we do our very best to operate without.
726 [ -w "$VARDIR" ] || error_exit "Cannot write to $LOCKDIR"
727 : ${lock_timeout:=10}
729 if mkdir "$LOCKDIR" 2>/dev/null; then
730 trap 'rm -rf "$LOCKDIR";' EXIT
731 trap 'rm -rf "$LOCKDIR"; exit 1' INT QUIT ABRT SEGV ALRM TERM
732 echo $$ >"$LOCKDIR/pid"
735 pid=$(cat "$LOCKDIR/pid")
736 if ! kill -0 "$pid"; then
737 warn "clearing stale lock pid $pid"
741 lock_timeout=$(($lock_timeout - 1))
742 if [ "$lock_timeout" -le 0 ]; then
743 error_exit "timed out waiting for lock from pid $pid"
750 # Read resolv.conf from stdin
754 # If what we are given matches what we have, then do nothing
755 if [ -e "$IFACEDIR/$iface" ]; then
756 if [ "$(echo "$resolv")" != \
757 "$(cat "$IFACEDIR/$iface")" ]
767 # Set metric and private before creating the interface resolv.conf file
768 # to ensure that it will have the correct flags
769 [ ! -d "$METRICDIR" ] && mkdir "$METRICDIR"
770 oldmetric="$METRICDIR/"*" $iface"
772 if [ -n "$IF_METRIC" ]; then
773 # Pad metric to 6 characters, so 5 is less than 10
774 while [ ${#IF_METRIC} -le 6 ]; do
775 IF_METRIC="0$IF_METRIC"
777 newmetric="$METRICDIR/$IF_METRIC $iface"
779 rm -f "$METRICDIR/"*" $iface"
780 [ "$oldmetric" != "$newmetric" -a \
781 "$oldmetric" != "$METRICDIR/* $iface" ] &&
783 [ -n "$newmetric" ] && echo " " >"$newmetric"
785 case "$IF_PRIVATE" in
786 [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1)
787 if [ ! -d "$PRIVATEDIR" ]; then
788 [ -e "$PRIVATEDIR" ] && rm "$PRIVATEDIR"
791 [ -e "$PRIVATEDIR/$iface" ] || changed=true
792 [ -d "$PRIVATEDIR" ] && echo " " >"$PRIVATEDIR/$iface"
795 if [ -e "$PRIVATEDIR/$iface" ]; then
796 rm -f "$PRIVATEDIR/$iface"
803 for x in "$EXCLUSIVEDIR/"*" $iface"; do
809 case "$IF_EXCLUSIVE" in
810 [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1)
811 if [ ! -d "$EXCLUSIVEDIR" ]; then
812 [ -e "$EXCLUSIVEDIR" ] && rm "$EXCLUSIVEDIR"
813 mkdir "$EXCLUSIVEDIR"
819 if [ "${x#* }" != "$iface" ]; then
820 if [ "$x" = "${x% *}" ]; then
825 if [ "$x" = "0000000" ]; then
826 warn "exclusive underflow"
830 if [ -d "$EXCLUSIVEDIR" ]; then
831 echo " " >"$EXCLUSIVEDIR/$x $iface"
837 if [ -f "$oldexcl" ]; then
844 if $changedfile; then
845 printf "%s\n" "$resolv" >"$IFACEDIR/$iface" || exit $?
846 elif ! $changed; then
849 unset changed changedfile oldmetric newmetric x oldexcl
853 # Delete any existing information about the interface
859 elif ! ${force}; then
860 warn "No resolv.conf for interface $i"
862 rm -f "$i" "$METRICDIR/"*" $i" \
864 "$EXCLUSIVEDIR/"*" $i" || exit $?
866 if ! ${changed}; then
867 # Set the return code based on the forced flag
875 case "${resolvconf:-YES}" in
876 [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) ;;
880 # Try and detect a suitable init system for our scripts
882 export RESTARTCMD RCDIR _NOINIT_WARNED
885 export RESOLVCONF DOMAINS SEARCH NAMESERVERS LOCALNAMESERVERS
886 : ${list_resolv:=list_resolv -l}
889 # Run scripts in the same directory resolvconf is run from
890 # in case any scripts accidentally dump files in the wrong place.
892 for script in "$LIBEXECDIR"/*; do
893 if [ -f "$script" ]; then
894 eval script_enabled="\$${script##*/}"
895 case "${script_enabled:-YES}" in
896 [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) ;;
899 if [ -x "$script" ]; then
900 "$script" "$cmd" "$iface"
902 (set -- "$cmd" "$iface"; . "$script")
904 retval=$(($retval + $?))