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