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.9.0"
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 strip_trailing_dots()
133 printf "$d%s" "${n%.}"
146 # Public interfaces override private ones.
147 for p in $public_interfaces; do
149 "$p"|"$p":*) return 1;;
153 if [ -e "$PRIVATEDIR/$iface" ]; then
157 for p in $private_interfaces; do
159 "$p"|"$p":*) return 0;;
163 # Not a private interface
167 # Parse resolv.conf's and make variables
168 # for domain name servers, search name servers and global nameservers
171 local line= ns= ds= search= d= n= newns=
172 local new=true iface= private=false p= domain= l= islocal=
176 while read -r line; do
178 "# resolv.conf from "*)
180 iface="${line#\# resolv.conf from *}"
182 if private_iface "$iface"; then
191 for l in $local_nameservers; do
195 echo "LOCALNAMESERVERS=\"\$LOCALNAMESERVERS ${line#* }\""
200 $islocal || ns="$ns${line#* } "
203 search="$(strip_trailing_dots ${line#* })"
204 if [ -z "$domain" ]; then
206 echo "DOMAIN=\"$domain\""
210 search="$(strip_trailing_dots ${line#* })"
213 [ -n "$line" ] && continue
214 if [ -n "$ns" -a -n "$search" ]; then
217 newns="$newns${newns:+,}$n"
221 ds="$ds${ds:+ }$d:$newns"
223 echo "DOMAINS=\"\$DOMAINS $ds\""
225 echo "SEARCH=\"\$SEARCH $search\""
227 echo "NAMESERVERS=\"\$NAMESERVERS $ns\""
240 while [ -n "$1" ]; do
243 *) result="$result $1";;
252 local dir= OIFS="$IFS"
261 while [ -n "$2" ]; do
272 [ -n "$f" ] || continue
274 if [ ! -d "$d" ]; then
275 if type install >/dev/null 2>&1; then
276 install -d "$d" || e=$?
285 # With the advent of alternative init systems, it's possible to have
286 # more than one installed. So we need to try and guess what one we're
287 # using unless overriden by configure.
288 # Note that restarting a service is a last resort - the subscribers
289 # should make a reasonable attempt to reconfigre the service via some
290 # method, normally SIGHUP.
293 [ -n "$RESTARTCMD" ] && return 0
295 # Detect the running init system.
296 # As systemd and OpenRC can be installed on top of legacy init
297 # systems we try to detect them first.
298 local status="@STATUSARG@"
300 if [ -x /bin/systemctl -a -S /run/systemd/private ]; then
301 RESTARTCMD="if /bin/systemctl --quiet is-active \$1.service; then
302 /bin/systemctl restart \$1.service;
304 elif [ -x /usr/bin/systemctl -a -S /run/systemd/private ]; then
305 RESTARTCMD="if /usr/bin/systemctl --quiet is-active \$1.service; then
306 /usr/bin/systemctl restart \$1.service;
308 elif [ -x /sbin/rc-service -a \
309 -s /libexec/rc/init.d/softlevel -o -s /run/openrc/softlevel ]
311 RESTARTCMD="/sbin/rc-service -i \$1 -- -Ds restart"
312 elif [ -x /usr/sbin/invoke-rc.d ]; then
314 RESTARTCMD="if /usr/sbin/invoke-rc.d --quiet \$1 status 1>/dev/null 2>&1; then
315 /usr/sbin/invoke-rc.d \$1 restart;
317 elif [ -x /sbin/service ]; then
320 RESTARTCMD="if /sbin/service \$1; then
321 /sbin/service \$1 restart;
323 elif [ -x /usr/sbin/service ]; then
325 RESTARTCMD="if /usr/sbin/service \$1 $status 1>/dev/null 2>&1; then
326 /usr/sbin/service \$1 restart;
328 elif [ -x /bin/sv ]; then
329 RESTARTCMD="/bin/sv status \$1 >/dev/null 2>&1 && /bin/sv try-restart \$1"
330 elif [ -x /usr/bin/sv ]; then
331 RESTARTCMD="/usr/bin/sv status \$1 >/dev/null 2>&1 && /usr/bin/sv try-restart \$1"
332 elif [ -e /etc/arch-release -a -d /etc/rc.d ]; then
334 RESTARTCMD="if [ -e /var/run/daemons/\$1 ]; then
335 /etc/rc.d/\$1 restart;
337 elif [ -e /etc/slackware-version -a -d /etc/rc.d ]; then
338 RESTARTCMD="if /etc/rc.d/rc.\$1 status 1>/dev/null 2>&1; then
339 /etc/rc.d/rc.\$1 restart;
341 elif [ -e /etc/rc.d/rc.subr -a -d /etc/rc.d ]; then
343 RESTARTCMD="if /etc/rc.d/\$1 check 1>/dev/null 2>&1; then
344 /etc/rc.d/\$1 restart;
347 for x in /etc/init.d/rc.d /etc/rc.d /etc/init.d; do
348 [ -d $x ] || continue
349 RESTARTCMD="if $x/\$1 $status 1>/dev/null 2>&1; then
356 if [ -z "$RESTARTCMD" ]; then
357 if [ "$NOINIT_WARNED" != true ]; then
358 warn "could not detect a useable init system"
369 local line= OIFS="$IFS"
371 [ -n "$1" -a -f "$IFACEDIR/$1" ] || return 1
372 echo "# resolv.conf from $1"
373 # Our variable maker works of the fact each resolv.conf per interface
374 # is separated by blank lines.
375 # So we remove them when echoing them.
376 while read -r line; do
378 if [ -n "$line" ]; then
379 # We need to set IFS here to preserve any whitespace
381 printf "%s\n" "$line"
383 done < "$IFACEDIR/$1"
389 [ -d "$IFACEDIR" ] || return 0
391 local report=false list= retval=0 cmd="$1" excl=
394 case "$IF_EXCLUSIVE" in
395 [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1)
396 if [ -d "$EXCLUSIVEDIR" ]; then
407 for i in $inclusive_interfaces; do
408 if [ -f "$i" -a "$list" = "$i" ]; then
420 # If we have an interface ordering list, then use that.
421 # It works by just using pathname expansion in the interface directory.
424 $force || report=true
427 for i in $interface_order; do
428 [ -f "$i" ] && list="$list $i"
429 for ii in "$i":* "$i".*; do
430 [ -f "$ii" ] && list="$list $ii"
433 for i in $dynamic_order; do
434 if [ -e "$i" -a ! -e "$METRICDIR/"*" $i" ]; then
437 for ii in "$i":* "$i".*; do
438 if [ -f "$ii" -a ! -e "$METRICDIR/"*" $ii" ]; then
443 if [ -d "$METRICDIR" ]; then
446 [ -f "$i" ] && list="$list ${i#* }"
454 for i in $(uniqify $list); do
455 # Only list interfaces which we really have
456 if ! [ -f "$i" ]; then
458 echo "No resolv.conf for interface $i" >&2
464 if [ "$cmd" = i -o "$cmd" = "-i" ]; then
467 echo_resolv "$i" && echo
469 [ $? = 0 -a "$retval" = 1 ] && retval=0
471 [ "$cmd" = i -o "$cmd" = "-i" ] && echo
476 local list= e= l= result= found= retval=0
478 [ -z "$2" ] && return 0
492 retval=$(($retval + 1))
504 echo "# Generated by resolvconf"
505 if [ -n "$search_domains" ]; then
506 echo "search $search_domains"
508 for n in $name_servers; do
516 echo "# Generated by resolvconf"
517 if [ -n "$search_domains_append" ]; then
518 echo "search $search_domains_append"
520 for n in $name_servers_append; do
528 local r= k= f= v= val= sub=
530 while read -r keyword value; do
531 for r in $replace; do
546 for sub in $value; do
547 for r in $replace_sub; do
561 val="$val${val:+ }$sub"
563 printf "%s %s\n" "$keyword" "$val"
569 local newdomains= d= dn= newns= ns=
578 if [ -n "$name_servers" -o -n "$search_domains" ]; then
579 eval "$(echo_prepend | parse_resolv)"
581 if [ -z "$VFLAG" ]; then
583 list_resolv -i "$@" >/dev/null || IF_EXCLUSIVE=0
584 eval "$(list_resolv -l "$@" | replace | parse_resolv)"
586 if [ -n "$name_servers_append" -o -n "$search_domains_append" ]; then
587 eval "$(echo_append | parse_resolv)"
590 # Ensure that we only list each domain once
591 for d in $DOMAINS; do
593 list_remove domain_blacklist "$dn" >/dev/null || continue
594 case " $newdomains" in
595 *" ${dn}:"*) continue;;
598 for nd in $DOMAINS; do
599 if [ "$dn" = "${nd%%:*}" ]; then
601 while [ -n "$ns" ]; do
604 *) list_remove name_server_blacklist \
605 "${ns%%,*}" >/dev/null \
606 && newns="$newns${newns:+,}${ns%%,*}";;
608 [ "$ns" = "${ns#*,}" ] && break
613 if [ -n "$newns" ]; then
614 newdomains="$newdomains${newdomains:+ }$dn:$newns"
617 DOMAIN="$(list_remove domain_blacklist $DOMAIN)"
618 SEARCH="$(uniqify $SEARCH)"
619 SEARCH="$(list_remove domain_blacklist $SEARCH)"
620 NAMESERVERS="$(uniqify $NAMESERVERS)"
621 NAMESERVERS="$(list_remove name_server_blacklist $NAMESERVERS)"
622 LOCALNAMESERVERS="$(uniqify $LOCALNAMESERVERS)"
623 LOCALNAMESERVERS="$(list_remove name_server_blacklist $LOCALNAMESERVERS)"
624 echo "DOMAIN='$DOMAIN'"
625 echo "SEARCH='$SEARCH'"
626 echo "NAMESERVERS='$NAMESERVERS'"
627 echo "LOCALNAMESERVERS='$LOCALNAMESERVERS'"
628 echo "DOMAINS='$newdomains'"
633 while getopts a:Dd:fhIilm:pRruvVx OPT; do
637 m) IF_METRIC="$OPTARG";;
641 if [ "$local_nameservers" = \
642 "127.* 0.0.0.0 255.255.255.255 ::1" ]
649 *) cmd="$OPT"; iface="$OPTARG";;
652 shift $(($OPTIND - 1))
653 args="$iface${iface:+ }$*"
655 # -I inits the state dir
656 if [ "$cmd" = I ]; then
657 if [ -d "$VARDIR" ]; then
663 # -D ensures that the listed config file base dirs exist
664 if [ "$cmd" = D ]; then
669 # -l lists our resolv files, optionally for a specific interface
670 if [ "$cmd" = l -o "$cmd" = i ]; then
671 list_resolv "$cmd" "$args"
675 # Restart a service or echo the command to restart a service
676 if [ "$cmd" = r -o "$cmd" = R ]; then
677 detect_init || exit 1
678 if [ "$cmd" = r ]; then
687 # Not normally needed, but subscribers should be able to run independently
688 if [ "$cmd" = v -o -n "$VFLAG" ]; then
693 # Test that we have valid options
694 if [ "$cmd" = a -o "$cmd" = d ]; then
695 if [ -z "$iface" ]; then
696 usage "Interface not specified"
698 elif [ "$cmd" != u ]; then
699 [ -n "$cmd" -a "$cmd" != h ] && usage "Unknown option $cmd"
703 if [ "$cmd" = a ]; then
704 for x in '/' \\ ' ' '*'; do
706 *[$x]*) error_exit "$x not allowed in interface name";;
709 for x in '.' '-' '~'; do
712 "$x not allowed at start of interface name";;
715 [ "$cmd" = a -a -t 0 ] && error_exit "No file given via stdin"
718 if [ ! -d "$VARDIR" ]; then
719 if [ -L "$VARDIR" ]; then
720 dir="$(readlink "$VARDIR")"
721 # link maybe relative
723 if ! mkdir -m 0755 -p "$dir"; then
724 error_exit "Failed to create needed" \
728 if ! mkdir -m 0755 -p "$VARDIR"; then
729 error_exit "Failed to create needed" \
735 if [ ! -d "$IFACEDIR" ]; then
736 mkdir -m 0755 -p "$IFACEDIR" || \
737 error_exit "Failed to create needed directory $IFACEDIR"
738 if [ "$cmd" = d ]; then
739 # Provide the same error messages as below
743 warn "No resolv.conf for interface $i"
751 # An interface was added, changed, deleted or a general update was called.
752 # Due to exclusivity we need to ensure that this is an atomic operation.
753 # Our subscribers *may* need this as well if the init system is sub par.
754 # As such we spinlock at this point as best we can.
755 # We don't use flock(1) because it's not widely available and normally resides
756 # in /usr which we do our very best to operate without.
757 [ -w "$VARDIR" ] || error_exit "Cannot write to $LOCKDIR"
758 : ${lock_timeout:=10}
760 if mkdir "$LOCKDIR" 2>/dev/null; then
761 trap 'rm -rf "$LOCKDIR";' EXIT
762 trap 'rm -rf "$LOCKDIR"; exit 1' INT QUIT ABRT SEGV ALRM TERM
763 echo $$ >"$LOCKDIR/pid"
766 pid=$(cat "$LOCKDIR/pid")
767 if ! kill -0 "$pid"; then
768 warn "clearing stale lock pid $pid"
772 lock_timeout=$(($lock_timeout - 1))
773 if [ "$lock_timeout" -le 0 ]; then
774 error_exit "timed out waiting for lock from pid $pid"
781 # Read resolv.conf from stdin
785 # If what we are given matches what we have, then do nothing
786 if [ -e "$IFACEDIR/$iface" ]; then
787 if [ "$(echo "$resolv")" != \
788 "$(cat "$IFACEDIR/$iface")" ]
798 # Set metric and private before creating the interface resolv.conf file
799 # to ensure that it will have the correct flags
800 [ ! -d "$METRICDIR" ] && mkdir "$METRICDIR"
801 oldmetric="$METRICDIR/"*" $iface"
803 if [ -n "$IF_METRIC" ]; then
804 # Pad metric to 6 characters, so 5 is less than 10
805 while [ ${#IF_METRIC} -le 6 ]; do
806 IF_METRIC="0$IF_METRIC"
808 newmetric="$METRICDIR/$IF_METRIC $iface"
810 rm -f "$METRICDIR/"*" $iface"
811 [ "$oldmetric" != "$newmetric" -a \
812 "$oldmetric" != "$METRICDIR/* $iface" ] &&
814 [ -n "$newmetric" ] && echo " " >"$newmetric"
816 case "$IF_PRIVATE" in
817 [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1)
818 if [ ! -d "$PRIVATEDIR" ]; then
819 [ -e "$PRIVATEDIR" ] && rm "$PRIVATEDIR"
822 [ -e "$PRIVATEDIR/$iface" ] || changed=true
823 [ -d "$PRIVATEDIR" ] && echo " " >"$PRIVATEDIR/$iface"
826 if [ -e "$PRIVATEDIR/$iface" ]; then
827 rm -f "$PRIVATEDIR/$iface"
834 for x in "$EXCLUSIVEDIR/"*" $iface"; do
840 case "$IF_EXCLUSIVE" in
841 [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1)
842 if [ ! -d "$EXCLUSIVEDIR" ]; then
843 [ -e "$EXCLUSIVEDIR" ] && rm "$EXCLUSIVEDIR"
844 mkdir "$EXCLUSIVEDIR"
850 if [ "${x#* }" != "$iface" ]; then
851 if [ "$x" = "${x% *}" ]; then
856 if [ "$x" = "0000000" ]; then
857 warn "exclusive underflow"
861 if [ -d "$EXCLUSIVEDIR" ]; then
862 echo " " >"$EXCLUSIVEDIR/$x $iface"
868 if [ -f "$oldexcl" ]; then
875 if $changedfile; then
876 printf "%s\n" "$resolv" >"$IFACEDIR/$iface" || exit $?
877 elif ! $changed; then
880 unset changed changedfile oldmetric newmetric x oldexcl
884 # Delete any existing information about the interface
890 elif ! ${force}; then
891 warn "No resolv.conf for interface $i"
893 rm -f "$i" "$METRICDIR/"*" $i" \
895 "$EXCLUSIVEDIR/"*" $i" || exit $?
897 if ! ${changed}; then
898 # Set the return code based on the forced flag
906 case "${resolvconf:-YES}" in
907 [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) ;;
911 # Try and detect a suitable init system for our scripts
913 export RESTARTCMD RCDIR _NOINIT_WARNED
916 export RESOLVCONF DOMAINS SEARCH NAMESERVERS LOCALNAMESERVERS
917 : ${list_resolv:=list_resolv -l}
920 # Run scripts in the same directory resolvconf is run from
921 # in case any scripts accidentally dump files in the wrong place.
923 for script in "$LIBEXECDIR"/*; do
924 if [ -f "$script" ]; then
925 eval script_enabled="\$${script##*/}"
926 case "${script_enabled:-YES}" in
927 [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) ;;
930 if [ -x "$script" ]; then
931 "$script" "$cmd" "$iface"
933 (set -- "$cmd" "$iface"; . "$script")
935 retval=$(($retval + $?))