1#!/bin/sh 2# Copyright (c) 2007-2023 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.13.2" 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 overridden by configure. 294# Note that restarting a service is a last resort - the subscribers 295# should make a reasonable attempt to reconfigure 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 /usr/bin/s6-rc ] && [ -x /usr/bin/s6-svc ]; then 331 RESTARTCMD=' 332 if s6-rc -a list | grep -qFx $1-srv 333 then 334 s6-svc -r /run/service/$1-srv 335 fi' 336 elif [ -x /sbin/service ]; then 337 # Old RedHat 338 RCDIR=/etc/init.d 339 RESTARTCMD=' 340 if /sbin/service $1; then 341 /sbin/service $1 restart 342 fi' 343 elif [ -x /usr/sbin/service ]; then 344 # Could be FreeBSD 345 RESTARTCMD=" 346 if /usr/sbin/service \$1 $status >/dev/null 2>&1 347 then 348 /usr/sbin/service \$1 restart 349 fi" 350 elif [ -x /bin/sv ]; then 351 RESTARTCMD='/bin/sv status $1 >/dev/null 2>&1 && 352 /bin/sv try-restart $1' 353 elif [ -x /usr/bin/sv ]; then 354 RESTARTCMD='/usr/bin/sv status $1 >/dev/null 2>&1 && 355 /usr/bin/sv try-restart $1' 356 elif [ -e /etc/arch-release ] && [ -d /etc/rc.d ]; then 357 RCDIR=/etc/rc.d 358 RESTARTCMD=' 359 if [ -e /var/run/daemons/$1 ] 360 then 361 /etc/rc.d/$1 restart 362 fi' 363 elif [ -e /etc/slackware-version ] && [ -d /etc/rc.d ]; then 364 RESTARTCMD=' 365 if /etc/rc.d/rc.$1 status >/dev/null 2>&1 366 then 367 /etc/rc.d/rc.$1 restart 368 fi' 369 elif [ -e /etc/rc.d/rc.subr ] && [ -d /etc/rc.d ]; then 370 # OpenBSD 371 RESTARTCMD=' 372 if /etc/rc.d/$1 check >/dev/null 2>&1 373 then 374 /etc/rc.d/$1 restart 375 fi' 376 elif [ -d /etc/dinit.d ] && command -v dinitctl >/dev/null 2>&1; then 377 RESTARTCMD='dinitctl --quiet restart --ignore-unstarted $1' 378 else 379 for x in /etc/init.d/rc.d /etc/rc.d /etc/init.d; do 380 [ -d $x ] || continue 381 RESTARTCMD=" 382 if $x/\$1 $status >/dev/null 2>&1 383 then 384 $x/\$1 restart 385 fi" 386 break 387 done 388 fi 389 390 if [ -z "$RESTARTCMD" ]; then 391 if [ "$_NOINIT_WARNED" != true ]; then 392 warn "could not detect a useable init system" 393 _NOINIT_WARNED=true 394 fi 395 return 1 396 fi 397 _NOINIT_WARNED= 398 return 0 399} 400 401echo_resolv() 402{ 403 OIFS="$IFS" 404 405 [ -n "$1" ] && [ -f "$IFACEDIR/$1" ] || return 1 406 echo "# resolv.conf from $1" 407 # Our variable maker works of the fact each resolv.conf per interface 408 # is separated by blank lines. 409 # So we remove them when echoing them. 410 while read -r line; do 411 IFS="$OIFS" 412 if [ -n "$line" ]; then 413 # We need to set IFS here to preserve any whitespace 414 IFS='' 415 printf "%s\n" "$line" 416 fi 417 done < "$IFACEDIR/$1" 418 IFS="$OIFS" 419} 420 421deprecated_interface() 422{ 423 [ -d "$DEPRECATEDDIR" ] || return 1 424 425 cd "$DEPRECATEDDIR" 426 for da; do 427 for daf in *; do 428 [ -f "$daf" ] || continue 429 case "$da" in 430 $daf) return 0;; 431 esac 432 done 433 done 434 return 1 435} 436 437list_resolv() 438{ 439 [ -d "$IFACEDIR" ] || return 0 440 441 cmd="$1" 442 shift 443 pattern_specified="$1" 444 445 excl=false 446 list= 447 report=false 448 retval=0 449 450 case "$IF_EXCLUSIVE" in 451 [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) 452 excl=true 453 if [ -d "$EXCLUSIVEDIR" ]; then 454 cd "$EXCLUSIVEDIR" 455 for i in *; do 456 if [ -f "$i" ]; then 457 list="${i#* }" 458 break 459 fi 460 done 461 fi 462 cd "$IFACEDIR" 463 for i in $inclusive_interfaces; do 464 if [ -f "$i" ] && [ "$list" = "$i" ]; then 465 list= 466 excl=false 467 break 468 fi 469 done 470 ;; 471 esac 472 473 # If we have an interface ordering list, then use that. 474 # It works by just using pathname expansion in the interface directory. 475 if [ -n "$pattern_specified" ]; then 476 list="$*" 477 $force || report=true 478 elif ! $excl; then 479 cd "$IFACEDIR" 480 481 for i in $interface_order; do 482 [ -f "$i" ] && list="$list $i" 483 for ii in "$i":* "$i".*; do 484 [ -f "$ii" ] && list="$list $ii" 485 done 486 done 487 488 for i in $dynamic_order; do 489 if [ -e "$i" ] && ! [ -e "$METRICDIR/"*" $i" ]; then 490 list="$list $i" 491 fi 492 for ii in "$i":* "$i".*; do 493 if [ -f "$ii" ] && ! [ -e "$METRICDIR/"*" $ii" ] 494 then 495 list="$list $ii" 496 fi 497 done 498 done 499 500 # Interfaces have an implicit metric of 0 if not specified. 501 for i in *; do 502 if [ -f "$i" ] && ! [ -e "$METRICDIR/"*" $i" ]; then 503 list="$list $i" 504 fi 505 done 506 507 if [ -d "$METRICDIR" ]; then 508 cd "$METRICDIR" 509 for i in *; do 510 [ -f "$i" ] && list="$list ${i#* }" 511 done 512 fi 513 514 # Move deprecated interfaces to the back 515 active= 516 deprecated= 517 for i in $list; do 518 if deprecated_interface "$i"; then 519 deprecated="$deprecated $i" 520 else 521 active="$active $i" 522 fi 523 done 524 list="$active $deprecated" 525 fi 526 527 cd "$IFACEDIR" 528 if $excl || [ -n "$pattern_specified" ]; then 529 retval=1 530 else 531 retval=0 532 fi 533 for i in $(uniqify $list); do 534 # Only list interfaces which we really have 535 if ! [ -f "$i" ]; then 536 if $report; then 537 echo "No resolv.conf for interface $i" >&2 538 retval=2 539 fi 540 continue 541 fi 542 543 if ! $ALLIFACES; then 544 if [ -n "$allow_interfaces" ]; then 545 x=false 546 for j in $allow_interfaces; do 547 if [ "$i" = "$j" ]; then 548 x=true 549 fi 550 done 551 $x || continue 552 fi 553 for j in $deny_interfaces; do 554 if [ "$i" = "$j" ]; then 555 continue 2 556 fi 557 done 558 fi 559 560 if [ "$cmd" = i ] || [ "$cmd" = "-i" ]; then 561 printf %s "$i " 562 else 563 echo_resolv "$i" && echo 564 fi 565 [ $? = 0 ] && [ "$retval" = 1 ] && retval=0 566 done 567 [ "$cmd" = i ] || [ "$cmd" = "-i" ] && echo 568 return $retval 569} 570 571list_remove() 572{ 573 [ -z "$2" ] && return 0 574 eval list=\"\$$1\" 575 shift 576 result= 577 retval=0 578 579 set -f 580 for e; do 581 found=false 582 for l in $list; do 583 case "$e" in 584 $l) found=true;; 585 esac 586 $found && break 587 done 588 if $found; then 589 retval=$(($retval + 1)) 590 else 591 result="$result $e" 592 fi 593 done 594 set +f 595 echo "${result# *}" 596 return $retval 597} 598 599echo_prepend() 600{ 601 echo "# Generated by resolvconf" 602 if [ -n "$search_domains" ]; then 603 echo "search $search_domains" 604 fi 605 for n in $name_servers; do 606 echo "nameserver $n" 607 done 608 echo 609} 610 611echo_append() 612{ 613 echo "# Generated by resolvconf" 614 if [ -n "$search_domains_append" ]; then 615 echo "search $search_domains_append" 616 fi 617 for n in $name_servers_append; do 618 echo "nameserver $n" 619 done 620 echo 621} 622 623replace() 624{ 625 while read -r keyword value; do 626 for r in $replace; do 627 k="${r%%/*}" 628 r="${r#*/}" 629 f="${r%%/*}" 630 r="${r#*/}" 631 v="${r%%/*}" 632 case "$keyword" in 633 $k) 634 case "$value" in 635 $f) value="$v";; 636 esac 637 ;; 638 esac 639 done 640 val= 641 for sub in $value; do 642 for r in $replace_sub; do 643 k="${r%%/*}" 644 r="${r#*/}" 645 f="${r%%/*}" 646 r="${r#*/}" 647 v="${r%%/*}" 648 case "$keyword" in 649 $k) 650 case "$sub" in 651 $f) sub="$v";; 652 esac 653 ;; 654 esac 655 done 656 val="$val${val:+ }$sub" 657 done 658 printf "%s %s\n" "$keyword" "$val" 659 done 660} 661 662make_vars() 663{ 664 # Clear variables 665 DOMAIN= 666 DOMAINS= 667 SEARCH= 668 NAMESERVERS= 669 LOCALNAMESERVERS= 670 671 if [ -n "${name_servers}${search_domains}" ]; then 672 eval "$(echo_prepend | parse_resolv)" 673 fi 674 if [ -z "$VFLAG" ]; then 675 IF_EXCLUSIVE=1 676 list_resolv -i "$@" >/dev/null || IF_EXCLUSIVE=0 677 eval "$(list_resolv -l "$@" | replace | parse_resolv)" 678 fi 679 if [ -n "${name_servers_append}${search_domains_append}" ]; then 680 eval "$(echo_append | parse_resolv)" 681 fi 682 683 # Ensure that we only list each domain once 684 newdomains= 685 for d in $DOMAINS; do 686 dn="${d%%:*}" 687 list_remove domain_blacklist "$dn" >/dev/null || continue 688 case " $newdomains" in 689 *" ${dn}:"*) continue;; 690 esac 691 newns= 692 for nd in $DOMAINS; do 693 if [ "$dn" = "${nd%%:*}" ]; then 694 ns="${nd#*:}" 695 while [ -n "$ns" ]; do 696 case ",$newns," in 697 *,${ns%%,*},*) ;; 698 *) list_remove name_server_blacklist \ 699 "${ns%%,*}" >/dev/null \ 700 && newns="$newns${newns:+,}${ns%%,*}";; 701 esac 702 [ "$ns" = "${ns#*,}" ] && break 703 ns="${ns#*,}" 704 done 705 fi 706 done 707 if [ -n "$newns" ]; then 708 newdomains="$newdomains${newdomains:+ }$dn:$newns" 709 fi 710 done 711 DOMAIN="$(list_remove domain_blacklist $DOMAIN)" 712 SEARCH="$(uniqify $SEARCH)" 713 SEARCH="$(list_remove domain_blacklist $SEARCH)" 714 NAMESERVERS="$(uniqify $NAMESERVERS)" 715 NAMESERVERS="$(list_remove name_server_blacklist $NAMESERVERS)" 716 LOCALNAMESERVERS="$(uniqify $LOCALNAMESERVERS)" 717 LOCALNAMESERVERS="$(list_remove name_server_blacklist $LOCALNAMESERVERS)" 718 echo "DOMAIN='$DOMAIN'" 719 echo "SEARCH='$SEARCH'" 720 echo "NAMESERVERS='$NAMESERVERS'" 721 echo "LOCALNAMESERVERS='$LOCALNAMESERVERS'" 722 echo "DOMAINS='$newdomains'" 723} 724 725force=false 726VFLAG= 727while getopts a:C:c:Dd:fhIilm:pRruvVx OPT; do 728 case "$OPT" in 729 f) force=true;; 730 h) usage;; 731 m) IF_METRIC="$OPTARG";; 732 p) IF_PRIVATE=1;; 733 V) 734 VFLAG=1 735 if [ "$local_nameservers" = \ 736 "127.* 0.0.0.0 255.255.255.255 ::1" ] 737 then 738 local_nameservers= 739 fi 740 ;; 741 x) IF_EXCLUSIVE=1;; 742 '?') exit 1;; 743 *) cmd="$OPT"; iface="$OPTARG";; 744 esac 745done 746shift $(($OPTIND - 1)) 747args="$iface${iface:+ }$*" 748 749# -I inits the state dir 750if [ "$cmd" = I ]; then 751 if [ -d "$VARDIR" ]; then 752 rm -rf "$VARDIR"/* 753 fi 754 exit $? 755fi 756 757# -D ensures that the listed config file base dirs exist 758if [ "$cmd" = D ]; then 759 config_mkdirs "$@" 760 exit $? 761fi 762 763# -l lists our resolv files, optionally for a specific interface 764if [ "$cmd" = l ] || [ "$cmd" = i ]; then 765 ALLIFACES=true 766 list_resolv "$cmd" "$args" 767 exit $? 768fi 769ALLIFACES=false 770 771# Restart a service or echo the command to restart a service 772if [ "$cmd" = r ] || [ "$cmd" = R ]; then 773 detect_init || exit 1 774 if [ "$cmd" = r ]; then 775 set -- $args 776 eval "$RESTARTCMD" 777 else 778 echo "$RESTARTCMD" | 779 sed -e '/^$/d' -e 's/^ //g' 780 fi 781 exit $? 782fi 783 784# Not normally needed, but subscribers should be able to run independently 785if [ "$cmd" = v ] || [ -n "$VFLAG" ]; then 786 make_vars "$iface" 787 exit $? 788fi 789 790# Test that we have valid options 791case "$cmd" in 792a|d|C|c) 793 if [ -z "$iface" ]; then 794 error_exit "Interface not specified" 795 fi 796 ;; 797u) ;; 798*) 799 if [ -n "$cmd" ] && [ "$cmd" != h ]; then 800 error_exit "Unknown option $cmd" 801 fi 802 usage 803 ;; 804esac 805 806if [ "$cmd" = a ]; then 807 for x in '/' \\ ' ' '*'; do 808 case "$iface" in 809 *[$x]*) error_exit "$x not allowed in interface name";; 810 esac 811 done 812 for x in '.' '-' '~'; do 813 case "$iface" in 814 [$x]*) error_exit \ 815 "$x not allowed at start of interface name";; 816 esac 817 done 818 [ "$cmd" = a ] && [ -t 0 ] && error_exit "No file given via stdin" 819fi 820 821if [ ! -d "$VARDIR" ]; then 822 if [ -L "$VARDIR" ]; then 823 dir="$(readlink "$VARDIR")" 824 # link maybe relative 825 cd "${VARDIR%/*}" 826 if ! mkdir -m 0755 -p "$dir"; then 827 error_exit "Failed to create needed" \ 828 "directory $dir" 829 fi 830 else 831 if ! mkdir -m 0755 -p "$VARDIR"; then 832 error_exit "Failed to create needed" \ 833 "directory $VARDIR" 834 fi 835 fi 836fi 837 838if [ ! -d "$IFACEDIR" ]; then 839 mkdir -m 0755 -p "$IFACEDIR" || \ 840 error_exit "Failed to create needed directory $IFACEDIR" 841 if [ "$cmd" = d ]; then 842 # Provide the same error messages as below 843 if ! ${force}; then 844 cd "$IFACEDIR" 845 for i in $args; do 846 warn "No resolv.conf for interface $i" 847 done 848 fi 849 ${force} 850 exit $? 851 fi 852fi 853 854# An interface was added, changed, deleted or a general update was called. 855# Due to exclusivity we need to ensure that this is an atomic operation. 856# Our subscribers *may* need this as well if the init system is sub par. 857# As such we spinlock at this point as best we can. 858# We don't use flock(1) because it's not widely available and normally resides 859# in /usr which we do our very best to operate without. 860[ -w "$VARDIR" ] || error_exit "Cannot write to $LOCKDIR" 861: ${lock_timeout:=10} 862: ${clear_nopids:=5} 863have_pid=false 864had_pid=false 865while true; do 866 if mkdir "$LOCKDIR" 2>/dev/null; then 867 trap 'rm -rf "$LOCKDIR";' EXIT 868 trap 'rm -rf "$LOCKDIR"; exit 1' INT QUIT ABRT SEGV ALRM TERM 869 echo $$ >"$LOCKDIR/pid" 870 break 871 fi 872 pid=$(cat "$LOCKDIR/pid" 2>/dev/null) 873 if [ "$pid" -gt 0 ] 2>/dev/null; then 874 have_pid=true 875 had_pid=true 876 else 877 have_pid=false 878 clear_nopids=$(($clear_nopids - 1)) 879 if [ "$clear_nopids" -le 0 ]; then 880 warn "not seen a pid, clearing lock directory" 881 rm -rf "$LOCKDIR" 882 else 883 lock_timeout=$(($lock_timeout - 1)) 884 sleep 1 885 fi 886 continue 887 fi 888 if $have_pid && ! kill -0 "$pid"; then 889 warn "clearing stale lock pid $pid" 890 rm -rf "$LOCKDIR" 891 continue 892 fi 893 lock_timeout=$(($lock_timeout - 1)) 894 if [ "$lock_timeout" -le 0 ]; then 895 if $have_pid; then 896 error_exit "timed out waiting for lock from pid $pid" 897 else 898 if $had_pid; then 899 error_exit "timed out waiting for lock" \ 900 "from some pids" 901 else 902 error_exit "timed out waiting for lock" 903 fi 904 fi 905 fi 906 sleep 1 907done 908unset have_pid had_pid clear_nopids 909 910case "$cmd" in 911a) 912 # Read resolv.conf from stdin 913 resolv="$(cat)" 914 changed=false 915 changedfile=false 916 # If what we are given matches what we have, then do nothing 917 if [ -e "$IFACEDIR/$iface" ]; then 918 if [ "$(echo "$resolv")" != \ 919 "$(cat "$IFACEDIR/$iface")" ] 920 then 921 changed=true 922 changedfile=true 923 fi 924 else 925 changed=true 926 changedfile=true 927 fi 928 929 # Set metric and private before creating the interface resolv.conf file 930 # to ensure that it will have the correct flags 931 [ ! -d "$METRICDIR" ] && mkdir "$METRICDIR" 932 oldmetric="$METRICDIR/"*" $iface" 933 newmetric= 934 if [ -n "$IF_METRIC" ]; then 935 # Pad metric to 6 characters, so 5 is less than 10 936 while [ ${#IF_METRIC} -le 6 ]; do 937 IF_METRIC="0$IF_METRIC" 938 done 939 newmetric="$METRICDIR/$IF_METRIC $iface" 940 fi 941 rm -f "$METRICDIR/"*" $iface" 942 [ "$oldmetric" != "$newmetric" ] && 943 [ "$oldmetric" != "$METRICDIR/* $iface" ] && 944 changed=true 945 [ -n "$newmetric" ] && echo " " >"$newmetric" 946 947 case "$IF_PRIVATE" in 948 [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) 949 if [ ! -d "$PRIVATEDIR" ]; then 950 [ -e "$PRIVATEDIR" ] && rm "$PRIVATEDIR" 951 mkdir "$PRIVATEDIR" 952 fi 953 [ -e "$PRIVATEDIR/$iface" ] || changed=true 954 [ -d "$PRIVATEDIR" ] && echo " " >"$PRIVATEDIR/$iface" 955 ;; 956 *) 957 if [ -e "$PRIVATEDIR/$iface" ]; then 958 rm -f "$PRIVATEDIR/$iface" 959 changed=true 960 fi 961 ;; 962 esac 963 964 oldexcl= 965 for x in "$EXCLUSIVEDIR/"*" $iface"; do 966 if [ -f "$x" ]; then 967 oldexcl="$x" 968 break 969 fi 970 done 971 case "$IF_EXCLUSIVE" in 972 [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) 973 if [ ! -d "$EXCLUSIVEDIR" ]; then 974 [ -e "$EXCLUSIVEDIR" ] && rm "$EXCLUSIVEDIR" 975 mkdir "$EXCLUSIVEDIR" 976 fi 977 cd "$EXCLUSIVEDIR" 978 for x in *; do 979 [ -f "$x" ] && break 980 done 981 if [ "${x#* }" != "$iface" ]; then 982 if [ "$x" = "${x% *}" ]; then 983 x=10000000 984 else 985 x="${x% *}" 986 fi 987 if [ "$x" = "0000000" ]; then 988 warn "exclusive underflow" 989 else 990 x=$(($x - 1)) 991 fi 992 if [ -d "$EXCLUSIVEDIR" ]; then 993 echo " " >"$EXCLUSIVEDIR/$x $iface" 994 fi 995 changed=true 996 fi 997 ;; 998 *) 999 if [ -f "$oldexcl" ]; then 1000 rm -f "$oldexcl" 1001 changed=true 1002 fi 1003 ;; 1004 esac 1005 1006 if $changedfile; then 1007 printf "%s\n" "$resolv" >"$IFACEDIR/$iface" || exit $? 1008 elif ! $changed; then 1009 exit 0 1010 fi 1011 unset changed changedfile oldmetric newmetric x oldexcl 1012 ;; 1013 1014d) 1015 # Delete any existing information about the interface 1016 cd "$IFACEDIR" 1017 changed=false 1018 for i in $args; do 1019 if [ -e "$i" ]; then 1020 changed=true 1021 elif ! ${force}; then 1022 warn "No resolv.conf for interface $i" 1023 fi 1024 rm -f "$i" "$METRICDIR/"*" $i" \ 1025 "$PRIVATEDIR/$i" \ 1026 "$EXCLUSIVEDIR/"*" $i" || exit $? 1027 done 1028 1029 if ! $changed; then 1030 # Set the return code based on the forced flag 1031 $force 1032 exit $? 1033 fi 1034 unset changed i 1035 ;; 1036 1037C) 1038 # Mark interface as deprecated 1039 [ ! -d "$DEPRECATEDDIR" ] && mkdir "$DEPRECATEDDIR" 1040 cd "$DEPRECATEDDIR" 1041 changed=false 1042 for i in $args; do 1043 if [ ! -e "$i" ]; then 1044 changed=true 1045 echo " " >"$i" || exit $? 1046 fi 1047 done 1048 $changed || exit 0 1049 unset changed i 1050 ;; 1051 1052c) 1053 # Mark interface as active 1054 if [ -d "$DEPRECATEDDIR" ]; then 1055 cd "$DEPRECATEDDIR" 1056 changed=false 1057 for i in $args; do 1058 if [ -e "$i" ]; then 1059 changed=true 1060 rm "$i" || exit $? 1061 fi 1062 done 1063 $changed || exit 0 1064 unset changed i 1065 fi 1066 ;; 1067esac 1068 1069case "${resolvconf:-YES}" in 1070[Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) ;; 1071*) exit 0;; 1072esac 1073 1074# Try and detect a suitable init system for our scripts 1075detect_init 1076export RESTARTCMD RCDIR _NOINIT_WARNED 1077 1078eval "$(make_vars)" 1079export RESOLVCONF DOMAINS SEARCH NAMESERVERS LOCALNAMESERVERS 1080: ${list_resolv:=list_resolv -l} 1081retval=0 1082 1083# Run scripts in the same directory resolvconf is run from 1084# in case any scripts accidentally dump files in the wrong place. 1085cd "$_PWD" 1086for script in "$LIBEXECDIR"/*; do 1087 if [ -f "$script" ]; then 1088 eval script_enabled="\$${script##*/}" 1089 case "${script_enabled:-YES}" in 1090 [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) ;; 1091 *) continue;; 1092 esac 1093 if [ -x "$script" ]; then 1094 "$script" "$cmd" "$iface" 1095 else 1096 (set -- "$cmd" "$iface"; . "$script") 1097 fi 1098 retval=$(($retval + $?)) 1099 fi 1100done 1101exit $retval 1102