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