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