2 # Copyright (c) 2007-2019 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.9.2"
29 SYSCONFDIR
=@SYSCONFDIR@
30 LIBEXECDIR
=@LIBEXECDIR@
33 RESTARTCMD
=@RESTARTCMD@
35 if [ "$1" = "--version" ]; then
36 echo "openresolv $OPENRESOLV_VERSION"
37 echo "Copyright (c) 2007-2016 Roy Marples"
41 # Disregard dhcpcd setting
42 unset interface_order state_dir
44 # If you change this, change the test in VFLAG and libc.in as well
45 local_nameservers
="127.* 0.0.0.0 255.255.255.255 ::1"
47 dynamic_order
="tap[0-9]* tun[0-9]* vpn vpn[0-9]* ppp[0-9]* ippp[0-9]*"
48 interface_order
="lo lo[0-9]*"
49 name_server_blacklist
="0.0.0.0"
51 # Support original resolvconf configuration layout
52 # as well as the openresolv config file
53 if [ -f "$SYSCONFDIR"/resolvconf.conf
]; then
54 .
"$SYSCONFDIR"/resolvconf.conf
55 [ -n "$state_dir" ] && VARDIR
="$state_dir"
56 elif [ -d "$SYSCONFDIR/resolvconf" ]; then
57 SYSCONFDIR
="$SYSCONFDIR/resolvconf"
58 if [ -f "$SYSCONFDIR"/interface-order
]; then
59 interface_order
="$(cat "$SYSCONFDIR"/interface-order)"
62 IFACEDIR
="$VARDIR/interfaces"
63 METRICDIR
="$VARDIR/metrics"
64 PRIVATEDIR
="$VARDIR/private"
65 EXCLUSIVEDIR
="$VARDIR/exclusive"
66 LOCKDIR
="$VARDIR/lock"
83 Usage: ${RESOLVCONF##*/} [options] command [argument]
85 Inform the system about any DNS updates.
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
95 -l [\$PATTERN] Show DNS information, optionally from interfaces
96 that match the specified pattern
98 -u Run updates from our current DNS information
99 --version Echo the ${RESOLVCONF##*/} version
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
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
115 -V [\$PATTERN] Same as -v, but only uses configuration in
116 $SYSCONFDIR/resolvconf.conf
118 [ -z "$1" ] && exit 0
123 # Strip any trailing dot from each name as a FQDN does not belong
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.
128 # Also strip any comments denoted by #.
136 printf "%s%s" "$space${word%.}"
147 # Public interfaces override private ones.
148 for p
in $public_interfaces; do
150 "$p"|
"$p":*) return 1;;
154 if [ -e "$PRIVATEDIR/$iface" ]; then
158 for p
in $private_interfaces; do
160 "$p"|
"$p":*) return 0;;
164 # Not a private interface
168 # Parse resolv.conf's and make variables
169 # for domain name servers, search name servers and global nameservers
179 while read -r line
; do
180 stripped_line
="$(resolv_strip ${line#* })"
182 "# resolv.conf from "*)
184 iface
="${line#\# resolv.conf from *}"
186 if private_iface
"$iface"; then
195 for l
in $local_nameservers; do
196 case "$stripped_line" in
204 echo "LOCALNAMESERVERS=\"\$LOCALNAMESERVERS $stripped_line\""
206 ns
="$ns$stripped_line "
210 search
="$stripped_line"
211 if [ -z "$domain" ]; then
213 echo "DOMAIN=\"$domain\""
217 search
="$stripped_line"
220 [ -n "$line" ] && continue
221 if [ -n "$ns" ] && [ -n "$search" ]; then
224 newns
="$newns${newns:+,}$n"
228 ds
="$ds${ds:+ }$d:$newns"
230 echo "DOMAINS=\"\$DOMAINS $ds\""
232 echo "SEARCH=\"\$SEARCH $search\""
234 echo "NAMESERVERS=\"\$NAMESERVERS $ns\""
247 while [ -n "$1" ]; do
250 *) result
="$result $1";;
268 while [ -n "$2" ]; do
279 [ -n "$f" ] ||
continue
281 if [ ! -d "$d" ]; then
282 if type install >/dev
/null
2>&1; then
283 install -d "$d" || e
=$?
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.
300 [ -n "$RESTARTCMD" ] && return 0
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.
307 if [ -x /bin
/systemctl
] && [ -S /run
/systemd
/private
]; then
309 if /bin/systemctl --quiet is-active $1.service
311 /bin/systemctl restart $1.service
313 elif [ -x /usr
/bin
/systemctl
] && [ -S /run
/systemd
/private
]; then
315 if /usr/bin/systemctl --quiet is-active $1.service
317 /usr/bin/systemctl restart $1.service
319 elif [ -x /sbin
/rc-service
] &&
320 { [ -s /libexec
/rc
/init.d
/softlevel
] ||
321 [ -s /run
/openrc
/softlevel
]; }
323 RESTARTCMD
='/sbin/rc-service -i $1 -- -Ds restart'
324 elif [ -x /usr
/sbin
/invoke-rc.d
]; then
327 if /usr/sbin/invoke-rc.d --quiet $1 status >/dev/null 2>&1
329 /usr/sbin/invoke-rc.d $1 restart
331 elif [ -x /sbin
/service
]; then
335 if /sbin/service $1; then
336 /sbin/service $1 restart
338 elif [ -x /usr
/sbin
/service
]; then
341 if /usr/sbin/service \$1 $status >/dev/null 2>&1
343 /usr/sbin/service \$1 restart
345 elif [ -x /bin
/sv
]; then
346 RESTARTCMD
='/bin/sv status $1 >/dev/null 2>&1 &&
347 /bin/sv try-restart $1'
348 elif [ -x /usr
/bin
/sv
]; then
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
354 if [ -e /var/run/daemons/$1 ]
358 elif [ -e /etc
/slackware-version
] && [ -d /etc
/rc.d
]; then
360 if /etc/rc.d/rc.$1 status >/dev/null 2>&1
362 /etc/rc.d/rc.$1 restart
364 elif [ -e /etc
/rc.d
/rc.subr
] && [ -d /etc
/rc.d
]; then
367 if /etc/rc.d/$1 check >/dev/null 2>&1
372 for x
in /etc
/init.d
/rc.d
/etc
/rc.d
/etc
/init.d
; do
373 [ -d $x ] ||
continue
375 if $x/\$1 $status >/dev/null 2>&1
383 if [ -z "$RESTARTCMD" ]; then
384 if [ "$_NOINIT_WARNED" != true
]; then
385 warn
"could not detect a useable init system"
398 [ -n "$1" ] && [ -f "$IFACEDIR/$1" ] ||
return 1
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
405 if [ -n "$line" ]; then
406 # We need to set IFS here to preserve any whitespace
408 printf "%s\n" "$line"
410 done < "$IFACEDIR/$1"
416 [ -d "$IFACEDIR" ] ||
return 0
425 case "$IF_EXCLUSIVE" in
426 [Yy
][Ee
][Ss
]|
[Tt
][Rr
][Uu
][Ee
]|
[Oo
][Nn
]|
1)
428 if [ -d "$EXCLUSIVEDIR" ]; then
438 for i
in $inclusive_interfaces; do
439 if [ -f "$i" ] && [ "$list" = "$i" ]; then
448 # If we have an interface ordering list, then use that.
449 # It works by just using pathname expansion in the interface directory.
452 $force || report
=true
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"
461 for i
in $dynamic_order; do
462 if [ -e "$i" ] && ! [ -e "$METRICDIR/"*" $i" ]; then
465 for ii
in "$i":* "$i".
*; do
466 if [ -f "$ii" ] && ! [ -e "$METRICDIR/"*" $ii" ]
472 # Interfaces have an implicit metric of 0 if not specified.
474 if [ -f "$i" ] && ! [ -e "$METRICDIR/"*" $i" ]; then
478 if [ -d "$METRICDIR" ]; then
481 [ -f "$i" ] && list
="$list ${i#* }"
488 for i
in $
(uniqify
$list); do
489 # Only list interfaces which we really have
490 if ! [ -f "$i" ]; then
492 echo "No resolv.conf for interface $i" >&2
498 if [ "$cmd" = i
] ||
[ "$cmd" = "-i" ]; then
501 echo_resolv
"$i" && echo
503 [ $?
= 0 ] && [ "$retval" = 1 ] && retval
=0
505 [ "$cmd" = i
] ||
[ "$cmd" = "-i" ] && echo
511 [ -z "$2" ] && return 0
527 retval
=$
(($retval + 1))
539 echo "# Generated by resolvconf"
540 if [ -n "$search_domains" ]; then
541 echo "search $search_domains"
543 for n
in $name_servers; do
551 echo "# Generated by resolvconf"
552 if [ -n "$search_domains_append" ]; then
553 echo "search $search_domains_append"
555 for n
in $name_servers_append; do
563 while read -r keyword value
; do
564 for r
in $replace; do
579 for sub
in $value; do
580 for r
in $replace_sub; do
594 val
="$val${val:+ }$sub"
596 printf "%s %s\n" "$keyword" "$val"
609 if [ -n "${name_servers}${search_domains}" ]; then
610 eval "$(echo_prepend | parse_resolv)"
612 if [ -z "$VFLAG" ]; then
614 list_resolv
-i "$@" >/dev
/null || IF_EXCLUSIVE
=0
615 eval "$(list_resolv -l "$@
" | replace | parse_resolv)"
617 if [ -n "${name_servers_append}${search_domains_append}" ]; then
618 eval "$(echo_append | parse_resolv)"
621 # Ensure that we only list each domain once
623 for d
in $DOMAINS; do
625 list_remove domain_blacklist
"$dn" >/dev
/null ||
continue
626 case " $newdomains" in
627 *" ${dn}:"*) continue;;
630 for nd
in $DOMAINS; do
631 if [ "$dn" = "${nd%%:*}" ]; then
633 while [ -n "$ns" ]; do
636 *) list_remove name_server_blacklist \
637 "${ns%%,*}" >/dev
/null \
638 && newns
="$newns${newns:+,}${ns%%,*}";;
640 [ "$ns" = "${ns#*,}" ] && break
645 if [ -n "$newns" ]; then
646 newdomains
="$newdomains${newdomains:+ }$dn:$newns"
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'"
665 while getopts a
:Dd
:fhIilm
:pRruvVx OPT
; do
669 m
) IF_METRIC
="$OPTARG";;
673 if [ "$local_nameservers" = \
674 "127.* 0.0.0.0 255.255.255.255 ::1" ]
681 *) cmd
="$OPT"; iface
="$OPTARG";;
684 shift $
(($OPTIND - 1))
685 args
="$iface${iface:+ }$*"
687 # -I inits the state dir
688 if [ "$cmd" = I
]; then
689 if [ -d "$VARDIR" ]; then
695 # -D ensures that the listed config file base dirs exist
696 if [ "$cmd" = D
]; then
701 # -l lists our resolv files, optionally for a specific interface
702 if [ "$cmd" = l
] ||
[ "$cmd" = i
]; then
703 list_resolv
"$cmd" "$args"
707 # Restart a service or echo the command to restart a service
708 if [ "$cmd" = r
] ||
[ "$cmd" = R
]; then
709 detect_init ||
exit 1
710 if [ "$cmd" = r
]; then
715 sed -e '/^$/d' -e 's/^ //g'
720 # Not normally needed, but subscribers should be able to run independently
721 if [ "$cmd" = v
] ||
[ -n "$VFLAG" ]; then
726 # Test that we have valid options
727 if [ "$cmd" = a
] ||
[ "$cmd" = d
]; then
728 if [ -z "$iface" ]; then
729 usage
"Interface not specified"
731 elif [ "$cmd" != u
]; then
732 [ -n "$cmd" ] && [ "$cmd" != h
] && usage
"Unknown option $cmd"
736 if [ "$cmd" = a
]; then
737 for x
in '/' \\ ' ' '*'; do
739 *[$x]*) error_exit
"$x not allowed in interface name";;
742 for x
in '.' '-' '~'; do
745 "$x not allowed at start of interface name";;
748 [ "$cmd" = a
] && [ -t 0 ] && error_exit
"No file given via stdin"
751 if [ ! -d "$VARDIR" ]; then
752 if [ -L "$VARDIR" ]; then
753 dir
="$(readlink "$VARDIR")"
754 # link maybe relative
756 if ! mkdir
-m 0755 -p "$dir"; then
757 error_exit
"Failed to create needed" \
761 if ! mkdir
-m 0755 -p "$VARDIR"; then
762 error_exit
"Failed to create needed" \
768 if [ ! -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
776 warn
"No resolv.conf for interface $i"
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}
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"
799 pid
=$
(cat "$LOCKDIR/pid")
800 if ! kill -0 "$pid"; then
801 warn
"clearing stale lock pid $pid"
805 lock_timeout
=$
(($lock_timeout - 1))
806 if [ "$lock_timeout" -le 0 ]; then
807 error_exit
"timed out waiting for lock from pid $pid"
814 # Read resolv.conf from stdin
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")" ]
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"
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"
841 newmetric
="$METRICDIR/$IF_METRIC $iface"
843 rm -f "$METRICDIR/"*" $iface"
844 [ "$oldmetric" != "$newmetric" ] &&
845 [ "$oldmetric" != "$METRICDIR/* $iface" ] &&
847 [ -n "$newmetric" ] && echo " " >"$newmetric"
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"
855 [ -e "$PRIVATEDIR/$iface" ] || changed
=true
856 [ -d "$PRIVATEDIR" ] && echo " " >"$PRIVATEDIR/$iface"
859 if [ -e "$PRIVATEDIR/$iface" ]; then
860 rm -f "$PRIVATEDIR/$iface"
867 for x
in "$EXCLUSIVEDIR/"*" $iface"; do
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"
883 if [ "${x#* }" != "$iface" ]; then
884 if [ "$x" = "${x% *}" ]; then
889 if [ "$x" = "0000000" ]; then
890 warn
"exclusive underflow"
894 if [ -d "$EXCLUSIVEDIR" ]; then
895 echo " " >"$EXCLUSIVEDIR/$x $iface"
901 if [ -f "$oldexcl" ]; then
908 if $changedfile; then
909 printf "%s\n" "$resolv" >"$IFACEDIR/$iface" ||
exit $?
910 elif ! $changed; then
913 unset changed changedfile oldmetric newmetric x oldexcl
917 # Delete any existing information about the interface
923 elif ! ${force}; then
924 warn
"No resolv.conf for interface $i"
926 rm -f "$i" "$METRICDIR/"*" $i" \
928 "$EXCLUSIVEDIR/"*" $i" ||
exit $?
930 if ! ${changed}; then
931 # Set the return code based on the forced flag
939 case "${resolvconf:-YES}" in
940 [Yy
][Ee
][Ss
]|
[Tt
][Rr
][Uu
][Ee
]|
[Oo
][Nn
]|
1) ;;
944 # Try and detect a suitable init system for our scripts
946 export RESTARTCMD RCDIR _NOINIT_WARNED
949 export RESOLVCONF DOMAINS SEARCH NAMESERVERS LOCALNAMESERVERS
950 : ${list_resolv:=list_resolv -l}
953 # Run scripts in the same directory resolvconf is run from
954 # in case any scripts accidentally dump files in the wrong place.
956 for 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) ;;
963 if [ -x "$script" ]; then
964 "$script" "$cmd" "$iface"
966 (set -- "$cmd" "$iface"; .
"$script")
968 retval
=$
(($retval + $?
))