1219732Sume#!/bin/sh 2296190Spfg# Copyright (c) 2007-2016 Roy Marples 3219732Sume# All rights reserved 4219732Sume 5219732Sume# Redistribution and use in source and binary forms, with or without 6219732Sume# modification, are permitted provided that the following conditions 7219732Sume# are met: 8219732Sume# * Redistributions of source code must retain the above copyright 9219732Sume# notice, this list of conditions and the following disclaimer. 10219732Sume# * Redistributions in binary form must reproduce the above 11219732Sume# copyright notice, this list of conditions and the following 12219732Sume# disclaimer in the documentation and/or other materials provided 13219732Sume# with the distribution. 14219732Sume# 15219732Sume# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16219732Sume# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17219732Sume# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 18219732Sume# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 19219732Sume# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20219732Sume# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 21219732Sume# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22219732Sume# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23219732Sume# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24219732Sume# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25219732Sume# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26219732Sume 27219732SumeRESOLVCONF="$0" 28313980SpfgOPENRESOLV_VERSION="3.9.0" 29219732SumeSYSCONFDIR=@SYSCONFDIR@ 30219732SumeLIBEXECDIR=@LIBEXECDIR@ 31219732SumeVARDIR=@VARDIR@ 32304515SpfgRCDIR=@RCDIR@ 33304515SpfgRESTARTCMD=@RESTARTCMD@ 34282434Sgjb 35313980Spfgif [ "$1" = "--version" ]; then 36313980Spfg echo "openresolv $OPENRESOLV_VERSION" 37313980Spfg echo "Copyright (c) 2007-2016 Roy Marples" 38313980Spfg exit 0 39313980Spfgfi 40313980Spfg 41282434Sgjb# Disregard dhcpcd setting 42282434Sgjbunset interface_order state_dir 43282434Sgjb 44282434Sgjb# If you change this, change the test in VFLAG and libc.in as well 45282434Sgjblocal_nameservers="127.* 0.0.0.0 255.255.255.255 ::1" 46282434Sgjb 47282434Sgjbdynamic_order="tap[0-9]* tun[0-9]* vpn vpn[0-9]* ppp[0-9]* ippp[0-9]*" 48282434Sgjbinterface_order="lo lo[0-9]*" 49282434Sgjbname_server_blacklist="0.0.0.0" 50282434Sgjb 51219732Sume# Support original resolvconf configuration layout 52219732Sume# as well as the openresolv config file 53219732Sumeif [ -f "$SYSCONFDIR"/resolvconf.conf ]; then 54219732Sume . "$SYSCONFDIR"/resolvconf.conf 55219732Sume [ -n "$state_dir" ] && VARDIR="$state_dir" 56219732Sumeelif [ -d "$SYSCONFDIR/resolvconf" ]; then 57219732Sume SYSCONFDIR="$SYSCONFDIR/resolvconf" 58219732Sume if [ -f "$SYSCONFDIR"/interface-order ]; then 59219732Sume interface_order="$(cat "$SYSCONFDIR"/interface-order)" 60219732Sume fi 61219732Sumefi 62219732SumeIFACEDIR="$VARDIR/interfaces" 63219732SumeMETRICDIR="$VARDIR/metrics" 64219732SumePRIVATEDIR="$VARDIR/private" 65282434SgjbEXCLUSIVEDIR="$VARDIR/exclusive" 66282434SgjbLOCKDIR="$VARDIR/lock" 67296190Spfg_PWD="$PWD" 68219732Sume 69282434Sgjbwarn() 70282434Sgjb{ 71282434Sgjb echo "$*" >&2 72282434Sgjb} 73219732Sume 74219732Sumeerror_exit() 75219732Sume{ 76219732Sume echo "$*" >&2 77219732Sume exit 1 78219732Sume} 79219732Sume 80219732Sumeusage() 81219732Sume{ 82219732Sume cat <<-EOF 83304515Spfg Usage: ${RESOLVCONF##*/} [options] command [argument] 84219732Sume 85219732Sume Inform the system about any DNS updates. 86219732Sume 87304515Spfg Commands: 88219732Sume -a \$INTERFACE Add DNS information to the specified interface 89219732Sume (DNS supplied via stdin in resolv.conf format) 90304515Spfg -d \$INTERFACE Delete DNS information from the specified interface 91304515Spfg -h Show this help cruft 92304515Spfg -i [\$PATTERN] Show interfaces that have supplied DNS information 93304515Spfg optionally from interfaces that match the specified 94304515Spfg pattern 95304515Spfg -l [\$PATTERN] Show DNS information, optionally from interfaces 96304515Spfg that match the specified pattern 97304515Spfg 98304515Spfg -u Run updates from our current DNS information 99313980Spfg --version Echo the ${RESOLVCONF##*/} version 100304515Spfg 101304515Spfg Options: 102304515Spfg -f Ignore non existent interfaces 103219732Sume -m metric Give the added DNS information a metric 104219732Sume -p Mark the interface as private 105282434Sgjb -x Mark the interface as exclusive 106304515Spfg 107304515Spfg Subscriber and System Init Commands: 108219732Sume -I Init the state dir 109304515Spfg -r \$SERVICE Restart the system service 110304515Spfg (restarting a non-existent or non-running service 111304515Spfg should have no output and return 0) 112304515Spfg -R Show the system service restart command 113219732Sume -v [\$PATTERN] echo NEWDOMAIN, NEWSEARCH and NEWNS variables to 114219732Sume the console 115304515Spfg -V [\$PATTERN] Same as -v, but only uses configuration in 116304515Spfg $SYSCONFDIR/resolvconf.conf 117219732Sume EOF 118219732Sume [ -z "$1" ] && exit 0 119219732Sume echo 120219732Sume error_exit "$*" 121219732Sume} 122219732Sume 123296190Spfg# Strip any trailing dot from each name as a FQDN does not belong 124296190Spfg# in resolv.conf(5) 125296190Spfg# If you think otherwise, capture a DNS trace and you'll see libc 126296190Spfg# will strip it regardless. 127296190Spfg# This also solves setting up duplicate zones in our subscribers. 128296190Spfgstrip_trailing_dots() 129296190Spfg{ 130296351Spfg local n= d= 131296190Spfg 132296190Spfg for n; do 133296351Spfg printf "$d%s" "${n%.}" 134296351Spfg d=" " 135296190Spfg done 136296190Spfg printf "\n" 137296190Spfg} 138296190Spfg 139313980Spfgprivate_iface() 140313980Spfg{ 141313980Spfg local p 142313980Spfg 143313980Spfg # Allow expansion 144313980Spfg cd "$IFACEDIR" 145313980Spfg 146313980Spfg # Public interfaces override private ones. 147313980Spfg for p in $public_interfaces; do 148313980Spfg case "$iface" in 149313980Spfg "$p"|"$p":*) return 1;; 150313980Spfg esac 151313980Spfg done 152313980Spfg 153313980Spfg if [ -e "$PRIVATEDIR/$iface" ]; then 154313980Spfg return 0 155313980Spfg fi 156313980Spfg 157313980Spfg for p in $private_interfaces; do 158313980Spfg case "$iface" in 159313980Spfg "$p"|"$p":*) return 0;; 160313980Spfg esac 161313980Spfg done 162313980Spfg 163313980Spfg # Not a private interface 164313980Spfg return 1 165313980Spfg} 166313980Spfg 167219732Sume# Parse resolv.conf's and make variables 168219732Sume# for domain name servers, search name servers and global nameservers 169219732Sumeparse_resolv() 170219732Sume{ 171219732Sume local line= ns= ds= search= d= n= newns= 172282434Sgjb local new=true iface= private=false p= domain= l= islocal= 173219732Sume 174219732Sume newns= 175219732Sume 176282434Sgjb while read -r line; do 177219732Sume case "$line" in 178219732Sume "# resolv.conf from "*) 179219732Sume if ${new}; then 180219732Sume iface="${line#\# resolv.conf from *}" 181219732Sume new=false 182313980Spfg if private_iface "$iface"; then 183219732Sume private=true 184219732Sume else 185219732Sume private=false 186219732Sume fi 187219732Sume fi 188219732Sume ;; 189219732Sume "nameserver "*) 190282434Sgjb islocal=false 191282434Sgjb for l in $local_nameservers; do 192282434Sgjb case "${line#* }" in 193282434Sgjb $l) 194282434Sgjb islocal=true 195282434Sgjb echo "LOCALNAMESERVERS=\"\$LOCALNAMESERVERS ${line#* }\"" 196282434Sgjb break 197282434Sgjb ;; 198282434Sgjb esac 199282434Sgjb done 200282434Sgjb $islocal || ns="$ns${line#* } " 201219732Sume ;; 202282434Sgjb "domain "*) 203296190Spfg search="$(strip_trailing_dots ${line#* })" 204282434Sgjb if [ -z "$domain" ]; then 205296190Spfg domain="$search" 206282434Sgjb echo "DOMAIN=\"$domain\"" 207282434Sgjb fi 208219732Sume ;; 209282434Sgjb "search "*) 210296190Spfg search="$(strip_trailing_dots ${line#* })" 211282434Sgjb ;; 212219732Sume *) 213219732Sume [ -n "$line" ] && continue 214219732Sume if [ -n "$ns" -a -n "$search" ]; then 215219732Sume newns= 216219732Sume for n in $ns; do 217219732Sume newns="$newns${newns:+,}$n" 218219732Sume done 219219732Sume ds= 220219732Sume for d in $search; do 221219732Sume ds="$ds${ds:+ }$d:$newns" 222219732Sume done 223219732Sume echo "DOMAINS=\"\$DOMAINS $ds\"" 224219732Sume fi 225219732Sume echo "SEARCH=\"\$SEARCH $search\"" 226219732Sume if ! $private; then 227219732Sume echo "NAMESERVERS=\"\$NAMESERVERS $ns\"" 228219732Sume fi 229219732Sume ns= 230219732Sume search= 231219732Sume new=true 232219732Sume ;; 233219732Sume esac 234219732Sume done 235219732Sume} 236219732Sume 237219732Sumeuniqify() 238219732Sume{ 239219732Sume local result= 240219732Sume while [ -n "$1" ]; do 241219732Sume case " $result " in 242219732Sume *" $1 "*);; 243219732Sume *) result="$result $1";; 244219732Sume esac 245219732Sume shift 246219732Sume done 247219732Sume echo "${result# *}" 248219732Sume} 249219732Sume 250282434Sgjbdirname() 251282434Sgjb{ 252282434Sgjb local dir= OIFS="$IFS" 253282434Sgjb local IFS=/ 254282434Sgjb set -- $@ 255282434Sgjb IFS="$OIFS" 256282434Sgjb if [ -n "$1" ]; then 257282434Sgjb printf %s . 258282434Sgjb else 259282434Sgjb shift 260282434Sgjb fi 261282434Sgjb while [ -n "$2" ]; do 262282434Sgjb printf "/%s" "$1" 263282434Sgjb shift 264282434Sgjb done 265282434Sgjb printf "\n" 266282434Sgjb} 267282434Sgjb 268282434Sgjbconfig_mkdirs() 269282434Sgjb{ 270282434Sgjb local e=0 f d 271282434Sgjb for f; do 272282434Sgjb [ -n "$f" ] || continue 273282434Sgjb d="$(dirname "$f")" 274282434Sgjb if [ ! -d "$d" ]; then 275282434Sgjb if type install >/dev/null 2>&1; then 276282434Sgjb install -d "$d" || e=$? 277282434Sgjb else 278282434Sgjb mkdir "$d" || e=$? 279282434Sgjb fi 280282434Sgjb fi 281282434Sgjb done 282282434Sgjb return $e 283282434Sgjb} 284282434Sgjb 285304515Spfg# With the advent of alternative init systems, it's possible to have 286304515Spfg# more than one installed. So we need to try and guess what one we're 287304515Spfg# using unless overriden by configure. 288304515Spfg# Note that restarting a service is a last resort - the subscribers 289304515Spfg# should make a reasonable attempt to reconfigre the service via some 290304515Spfg# method, normally SIGHUP. 291304515Spfgdetect_init() 292304515Spfg{ 293304515Spfg [ -n "$RESTARTCMD" ] && return 0 294304515Spfg 295304515Spfg # Detect the running init system. 296304515Spfg # As systemd and OpenRC can be installed on top of legacy init 297304515Spfg # systems we try to detect them first. 298304515Spfg local status="@STATUSARG@" 299304515Spfg : ${status:=status} 300304515Spfg if [ -x /bin/systemctl -a -S /run/systemd/private ]; then 301304515Spfg RESTARTCMD="if /bin/systemctl --quiet is-active \$1.service; then 302304515Spfg /bin/systemctl restart \$1.service; 303304515Spfgfi" 304304515Spfg elif [ -x /usr/bin/systemctl -a -S /run/systemd/private ]; then 305304515Spfg RESTARTCMD="if /usr/bin/systemctl --quiet is-active \$1.service; then 306304515Spfg /usr/bin/systemctl restart \$1.service; 307304515Spfgfi" 308304515Spfg elif [ -x /sbin/rc-service -a \ 309304515Spfg -s /libexec/rc/init.d/softlevel -o -s /run/openrc/softlevel ] 310304515Spfg then 311304515Spfg RESTARTCMD="/sbin/rc-service -i \$1 -- -Ds restart" 312304515Spfg elif [ -x /usr/sbin/invoke-rc.d ]; then 313304515Spfg RCDIR=/etc/init.d 314304515Spfg RESTARTCMD="if /usr/sbin/invoke-rc.d --quiet \$1 status 1>/dev/null 2>&1; then 315304515Spfg /usr/sbin/invoke-rc.d \$1 restart; 316304515Spfgfi" 317304515Spfg elif [ -x /sbin/service ]; then 318304515Spfg # Old RedHat 319304515Spfg RCDIR=/etc/init.d 320304515Spfg RESTARTCMD="if /sbin/service \$1; then 321304515Spfg /sbin/service \$1 restart; 322304515Spfgfi" 323304515Spfg elif [ -x /usr/sbin/service ]; then 324304515Spfg # Could be FreeBSD 325304515Spfg RESTARTCMD="if /usr/sbin/service \$1 $status 1>/dev/null 2>&1; then 326304515Spfg /usr/sbin/service \$1 restart; 327304515Spfgfi" 328304515Spfg elif [ -x /bin/sv ]; then 329313980Spfg RESTARTCMD="/bin/sv status \$1 >/dev/null 2>&1 && /bin/sv try-restart \$1" 330304515Spfg elif [ -x /usr/bin/sv ]; then 331313980Spfg RESTARTCMD="/usr/bin/sv status \$1 >/dev/null 2>&1 && /usr/bin/sv try-restart \$1" 332304515Spfg elif [ -e /etc/arch-release -a -d /etc/rc.d ]; then 333304515Spfg RCDIR=/etc/rc.d 334304515Spfg RESTARTCMD="if [ -e /var/run/daemons/\$1 ]; then 335304515Spfg /etc/rc.d/\$1 restart; 336304515Spfgfi" 337304515Spfg elif [ -e /etc/slackware-version -a -d /etc/rc.d ]; then 338304515Spfg RESTARTCMD="if /etc/rc.d/rc.\$1 status 1>/dev/null 2>&1; then 339304515Spfg /etc/rc.d/rc.\$1 restart; 340304515Spfgfi" 341304515Spfg elif [ -e /etc/rc.d/rc.subr -a -d /etc/rc.d ]; then 342304515Spfg # OpenBSD 343304515Spfg RESTARTCMD="if /etc/rc.d/\$1 check 1>/dev/null 2>&1; then 344304515Spfg /etc/rc.d/\$1 restart; 345304515Spfgfi" 346304515Spfg else 347304515Spfg for x in /etc/init.d/rc.d /etc/rc.d /etc/init.d; do 348304515Spfg [ -d $x ] || continue 349304515Spfg RESTARTCMD="if $x/\$1 $status 1>/dev/null 2>&1; then 350304515Spfg $x/\$1 restart; 351304515Spfgfi" 352304515Spfg break 353304515Spfg done 354304515Spfg fi 355304515Spfg 356304515Spfg if [ -z "$RESTARTCMD" ]; then 357304515Spfg if [ "$NOINIT_WARNED" != true ]; then 358304515Spfg warn "could not detect a useable init system" 359304515Spfg _NOINIT_WARNED=true 360304515Spfg fi 361304515Spfg return 1 362304515Spfg fi 363304515Spfg _NOINIT_WARNED= 364304515Spfg return 0 365304515Spfg} 366304515Spfg 367304515Spfgecho_resolv() 368304515Spfg{ 369304515Spfg local line= OIFS="$IFS" 370304515Spfg 371304515Spfg [ -n "$1" -a -f "$IFACEDIR/$1" ] || return 1 372304515Spfg echo "# resolv.conf from $1" 373304515Spfg # Our variable maker works of the fact each resolv.conf per interface 374304515Spfg # is separated by blank lines. 375304515Spfg # So we remove them when echoing them. 376304515Spfg while read -r line; do 377304515Spfg IFS="$OIFS" 378304515Spfg if [ -n "$line" ]; then 379304515Spfg # We need to set IFS here to preserve any whitespace 380304515Spfg IFS='' 381304515Spfg printf "%s\n" "$line" 382304515Spfg fi 383304515Spfg done < "$IFACEDIR/$1" 384304515Spfg IFS="$OIFS" 385304515Spfg} 386304515Spfg 387219732Sumelist_resolv() 388219732Sume{ 389219732Sume [ -d "$IFACEDIR" ] || return 0 390219732Sume 391282434Sgjb local report=false list= retval=0 cmd="$1" excl= 392219732Sume shift 393219732Sume 394282434Sgjb case "$IF_EXCLUSIVE" in 395282434Sgjb [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) 396282434Sgjb if [ -d "$EXCLUSIVEDIR" ]; then 397282434Sgjb cd "$EXCLUSIVEDIR" 398282434Sgjb for i in *; do 399282434Sgjb if [ -f "$i" ]; then 400282434Sgjb list="${i#* }" 401282434Sgjb break 402282434Sgjb fi 403282434Sgjb done 404282434Sgjb fi 405282434Sgjb excl=true 406313980Spfg cd "$IFACEDIR" 407313980Spfg for i in $inclusive_interfaces; do 408313980Spfg if [ -f "$i" -a "$list" = "$i" ]; then 409313980Spfg list= 410313980Spfg excl=false 411313980Spfg break 412313980Spfg fi 413313980Spfg done 414282434Sgjb ;; 415282434Sgjb *) 416282434Sgjb excl=false 417282434Sgjb ;; 418282434Sgjb esac 419282434Sgjb 420219732Sume # If we have an interface ordering list, then use that. 421219732Sume # It works by just using pathname expansion in the interface directory. 422219732Sume if [ -n "$1" ]; then 423282434Sgjb list="$*" 424219732Sume $force || report=true 425282434Sgjb elif ! $excl; then 426219732Sume cd "$IFACEDIR" 427219732Sume for i in $interface_order; do 428282434Sgjb [ -f "$i" ] && list="$list $i" 429282434Sgjb for ii in "$i":* "$i".*; do 430282434Sgjb [ -f "$ii" ] && list="$list $ii" 431282434Sgjb done 432219732Sume done 433219732Sume for i in $dynamic_order; do 434219732Sume if [ -e "$i" -a ! -e "$METRICDIR/"*" $i" ]; then 435219732Sume list="$list $i" 436219732Sume fi 437282434Sgjb for ii in "$i":* "$i".*; do 438282434Sgjb if [ -f "$ii" -a ! -e "$METRICDIR/"*" $ii" ]; then 439282434Sgjb list="$list $ii" 440282434Sgjb fi 441282434Sgjb done 442219732Sume done 443219732Sume if [ -d "$METRICDIR" ]; then 444219732Sume cd "$METRICDIR" 445219732Sume for i in *; do 446282434Sgjb [ -f "$i" ] && list="$list ${i#* }" 447219732Sume done 448219732Sume fi 449219732Sume list="$list *" 450219732Sume fi 451219732Sume 452219732Sume cd "$IFACEDIR" 453282434Sgjb retval=1 454219732Sume for i in $(uniqify $list); do 455219732Sume # Only list interfaces which we really have 456282434Sgjb if ! [ -f "$i" ]; then 457219732Sume if $report; then 458219732Sume echo "No resolv.conf for interface $i" >&2 459282434Sgjb retval=2 460219732Sume fi 461219732Sume continue 462219732Sume fi 463219732Sume 464219732Sume if [ "$cmd" = i -o "$cmd" = "-i" ]; then 465282434Sgjb printf %s "$i " 466219732Sume else 467313980Spfg echo_resolv "$i" && echo 468219732Sume fi 469282434Sgjb [ $? = 0 -a "$retval" = 1 ] && retval=0 470219732Sume done 471219732Sume [ "$cmd" = i -o "$cmd" = "-i" ] && echo 472219732Sume return $retval 473219732Sume} 474219732Sume 475282434Sgjblist_remove() { 476282434Sgjb local list= e= l= result= found= retval=0 477282434Sgjb 478282434Sgjb [ -z "$2" ] && return 0 479282434Sgjb eval list=\"\$$1\" 480282434Sgjb shift 481282434Sgjb 482282434Sgjb set -f 483282434Sgjb for e; do 484282434Sgjb found=false 485282434Sgjb for l in $list; do 486282434Sgjb case "$e" in 487282434Sgjb $l) found=true;; 488282434Sgjb esac 489282434Sgjb $found && break 490282434Sgjb done 491282434Sgjb if $found; then 492282434Sgjb retval=$(($retval + 1)) 493282434Sgjb else 494282434Sgjb result="$result $e" 495282434Sgjb fi 496282434Sgjb done 497282434Sgjb set +f 498282434Sgjb echo "${result# *}" 499282434Sgjb return $retval 500282434Sgjb} 501282434Sgjb 502282434Sgjbecho_prepend() 503282434Sgjb{ 504282434Sgjb echo "# Generated by resolvconf" 505282434Sgjb if [ -n "$search_domains" ]; then 506282434Sgjb echo "search $search_domains" 507282434Sgjb fi 508282434Sgjb for n in $name_servers; do 509282434Sgjb echo "nameserver $n" 510282434Sgjb done 511282434Sgjb echo 512282434Sgjb} 513282434Sgjb 514282434Sgjbecho_append() 515282434Sgjb{ 516282434Sgjb echo "# Generated by resolvconf" 517282434Sgjb if [ -n "$search_domains_append" ]; then 518282434Sgjb echo "search $search_domains_append" 519282434Sgjb fi 520282434Sgjb for n in $name_servers_append; do 521282434Sgjb echo "nameserver $n" 522282434Sgjb done 523282434Sgjb echo 524282434Sgjb} 525282434Sgjb 526282434Sgjbreplace() 527282434Sgjb{ 528282434Sgjb local r= k= f= v= val= sub= 529282434Sgjb 530282434Sgjb while read -r keyword value; do 531282434Sgjb for r in $replace; do 532282434Sgjb k="${r%%/*}" 533282434Sgjb r="${r#*/}" 534282434Sgjb f="${r%%/*}" 535282434Sgjb r="${r#*/}" 536282434Sgjb v="${r%%/*}" 537282434Sgjb case "$keyword" in 538282434Sgjb $k) 539282434Sgjb case "$value" in 540282434Sgjb $f) value="$v";; 541282434Sgjb esac 542282434Sgjb ;; 543282434Sgjb esac 544282434Sgjb done 545282434Sgjb val= 546282434Sgjb for sub in $value; do 547282434Sgjb for r in $replace_sub; do 548282434Sgjb k="${r%%/*}" 549282434Sgjb r="${r#*/}" 550282434Sgjb f="${r%%/*}" 551282434Sgjb r="${r#*/}" 552282434Sgjb v="${r%%/*}" 553282434Sgjb case "$keyword" in 554282434Sgjb $k) 555282434Sgjb case "$sub" in 556282434Sgjb $f) sub="$v";; 557282434Sgjb esac 558282434Sgjb ;; 559282434Sgjb esac 560282434Sgjb done 561282434Sgjb val="$val${val:+ }$sub" 562282434Sgjb done 563282434Sgjb printf "%s %s\n" "$keyword" "$val" 564282434Sgjb done 565282434Sgjb} 566282434Sgjb 567219732Sumemake_vars() 568219732Sume{ 569282434Sgjb local newdomains= d= dn= newns= ns= 570219732Sume 571282434Sgjb # Clear variables 572282434Sgjb DOMAIN= 573282434Sgjb DOMAINS= 574282434Sgjb SEARCH= 575282434Sgjb NAMESERVERS= 576282434Sgjb LOCALNAMESERVERS= 577282434Sgjb 578282434Sgjb if [ -n "$name_servers" -o -n "$search_domains" ]; then 579282434Sgjb eval "$(echo_prepend | parse_resolv)" 580282434Sgjb fi 581282434Sgjb if [ -z "$VFLAG" ]; then 582282434Sgjb IF_EXCLUSIVE=1 583282434Sgjb list_resolv -i "$@" >/dev/null || IF_EXCLUSIVE=0 584282434Sgjb eval "$(list_resolv -l "$@" | replace | parse_resolv)" 585282434Sgjb fi 586282434Sgjb if [ -n "$name_servers_append" -o -n "$search_domains_append" ]; then 587282434Sgjb eval "$(echo_append | parse_resolv)" 588282434Sgjb fi 589282434Sgjb 590219732Sume # Ensure that we only list each domain once 591219732Sume for d in $DOMAINS; do 592219732Sume dn="${d%%:*}" 593282434Sgjb list_remove domain_blacklist "$dn" >/dev/null || continue 594219732Sume case " $newdomains" in 595219732Sume *" ${dn}:"*) continue;; 596219732Sume esac 597219732Sume newns= 598219732Sume for nd in $DOMAINS; do 599219732Sume if [ "$dn" = "${nd%%:*}" ]; then 600219732Sume ns="${nd#*:}" 601219732Sume while [ -n "$ns" ]; do 602219732Sume case ",$newns," in 603219732Sume *,${ns%%,*},*) ;; 604282434Sgjb *) list_remove name_server_blacklist \ 605282434Sgjb "${ns%%,*}" >/dev/null \ 606282434Sgjb && newns="$newns${newns:+,}${ns%%,*}";; 607219732Sume esac 608219732Sume [ "$ns" = "${ns#*,}" ] && break 609219732Sume ns="${ns#*,}" 610219732Sume done 611219732Sume fi 612219732Sume done 613282434Sgjb if [ -n "$newns" ]; then 614282434Sgjb newdomains="$newdomains${newdomains:+ }$dn:$newns" 615282434Sgjb fi 616219732Sume done 617282434Sgjb DOMAIN="$(list_remove domain_blacklist $DOMAIN)" 618282434Sgjb SEARCH="$(uniqify $SEARCH)" 619282434Sgjb SEARCH="$(list_remove domain_blacklist $SEARCH)" 620282434Sgjb NAMESERVERS="$(uniqify $NAMESERVERS)" 621282434Sgjb NAMESERVERS="$(list_remove name_server_blacklist $NAMESERVERS)" 622282434Sgjb LOCALNAMESERVERS="$(uniqify $LOCALNAMESERVERS)" 623282434Sgjb LOCALNAMESERVERS="$(list_remove name_server_blacklist $LOCALNAMESERVERS)" 624282434Sgjb echo "DOMAIN='$DOMAIN'" 625282434Sgjb echo "SEARCH='$SEARCH'" 626282434Sgjb echo "NAMESERVERS='$NAMESERVERS'" 627282434Sgjb echo "LOCALNAMESERVERS='$LOCALNAMESERVERS'" 628219732Sume echo "DOMAINS='$newdomains'" 629219732Sume} 630219732Sume 631219732Sumeforce=false 632282434SgjbVFLAG= 633304515Spfgwhile getopts a:Dd:fhIilm:pRruvVx OPT; do 634219732Sume case "$OPT" in 635219732Sume f) force=true;; 636219732Sume h) usage;; 637219732Sume m) IF_METRIC="$OPTARG";; 638219732Sume p) IF_PRIVATE=1;; 639282434Sgjb V) 640282434Sgjb VFLAG=1 641282434Sgjb if [ "$local_nameservers" = \ 642282434Sgjb "127.* 0.0.0.0 255.255.255.255 ::1" ] 643282434Sgjb then 644282434Sgjb local_nameservers= 645282434Sgjb fi 646282434Sgjb ;; 647282434Sgjb x) IF_EXCLUSIVE=1;; 648219732Sume '?') ;; 649219732Sume *) cmd="$OPT"; iface="$OPTARG";; 650219732Sume esac 651219732Sumedone 652219732Sumeshift $(($OPTIND - 1)) 653282434Sgjbargs="$iface${iface:+ }$*" 654219732Sume 655219732Sume# -I inits the state dir 656219732Sumeif [ "$cmd" = I ]; then 657219732Sume if [ -d "$VARDIR" ]; then 658219732Sume rm -rf "$VARDIR"/* 659219732Sume fi 660219732Sume exit $? 661219732Sumefi 662219732Sume 663282434Sgjb# -D ensures that the listed config file base dirs exist 664282434Sgjbif [ "$cmd" = D ]; then 665282434Sgjb config_mkdirs "$@" 666282434Sgjb exit $? 667282434Sgjbfi 668282434Sgjb 669219732Sume# -l lists our resolv files, optionally for a specific interface 670219732Sumeif [ "$cmd" = l -o "$cmd" = i ]; then 671219732Sume list_resolv "$cmd" "$args" 672219732Sume exit $? 673219732Sumefi 674219732Sume 675304515Spfg# Restart a service or echo the command to restart a service 676304515Spfgif [ "$cmd" = r -o "$cmd" = R ]; then 677304515Spfg detect_init || exit 1 678304515Spfg if [ "$cmd" = r ]; then 679304515Spfg set -- $args 680304515Spfg eval $RESTARTCMD 681304515Spfg else 682304515Spfg echo "$RESTARTCMD" 683304515Spfg fi 684304515Spfg exit $? 685304515Spfgfi 686304515Spfg 687219732Sume# Not normally needed, but subscribers should be able to run independently 688282434Sgjbif [ "$cmd" = v -o -n "$VFLAG" ]; then 689219732Sume make_vars "$iface" 690219732Sume exit $? 691219732Sumefi 692219732Sume 693219732Sume# Test that we have valid options 694219732Sumeif [ "$cmd" = a -o "$cmd" = d ]; then 695219732Sume if [ -z "$iface" ]; then 696219732Sume usage "Interface not specified" 697219732Sume fi 698219732Sumeelif [ "$cmd" != u ]; then 699219732Sume [ -n "$cmd" -a "$cmd" != h ] && usage "Unknown option $cmd" 700219732Sume usage 701219732Sumefi 702282434Sgjb 703219732Sumeif [ "$cmd" = a ]; then 704219732Sume for x in '/' \\ ' ' '*'; do 705219732Sume case "$iface" in 706219732Sume *[$x]*) error_exit "$x not allowed in interface name";; 707219732Sume esac 708219732Sume done 709219732Sume for x in '.' '-' '~'; do 710219732Sume case "$iface" in 711219732Sume [$x]*) error_exit \ 712219732Sume "$x not allowed at start of interface name";; 713219732Sume esac 714219732Sume done 715219732Sume [ "$cmd" = a -a -t 0 ] && error_exit "No file given via stdin" 716219732Sumefi 717219732Sume 718282434Sgjbif [ ! -d "$VARDIR" ]; then 719282434Sgjb if [ -L "$VARDIR" ]; then 720282434Sgjb dir="$(readlink "$VARDIR")" 721282434Sgjb # link maybe relative 722282434Sgjb cd "${VARDIR%/*}" 723282434Sgjb if ! mkdir -m 0755 -p "$dir"; then 724282434Sgjb error_exit "Failed to create needed" \ 725282434Sgjb "directory $dir" 726219732Sume fi 727282434Sgjb else 728282434Sgjb if ! mkdir -m 0755 -p "$VARDIR"; then 729282434Sgjb error_exit "Failed to create needed" \ 730282434Sgjb "directory $VARDIR" 731282434Sgjb fi 732219732Sume fi 733282434Sgjbfi 734282434Sgjb 735282434Sgjbif [ ! -d "$IFACEDIR" ]; then 736219732Sume mkdir -m 0755 -p "$IFACEDIR" || \ 737219732Sume error_exit "Failed to create needed directory $IFACEDIR" 738219732Sume if [ "$cmd" = d ]; then 739282434Sgjb # Provide the same error messages as below 740282434Sgjb if ! ${force}; then 741282434Sgjb cd "$IFACEDIR" 742282434Sgjb for i in $args; do 743282434Sgjb warn "No resolv.conf for interface $i" 744282434Sgjb done 745282434Sgjb fi 746282434Sgjb ${force} 747282434Sgjb exit $? 748219732Sume fi 749219732Sumefi 750219732Sume 751282434Sgjb# An interface was added, changed, deleted or a general update was called. 752282434Sgjb# Due to exclusivity we need to ensure that this is an atomic operation. 753282434Sgjb# Our subscribers *may* need this as well if the init system is sub par. 754282434Sgjb# As such we spinlock at this point as best we can. 755282434Sgjb# We don't use flock(1) because it's not widely available and normally resides 756282434Sgjb# in /usr which we do our very best to operate without. 757282434Sgjb[ -w "$VARDIR" ] || error_exit "Cannot write to $LOCKDIR" 758282434Sgjb: ${lock_timeout:=10} 759282434Sgjbwhile true; do 760282434Sgjb if mkdir "$LOCKDIR" 2>/dev/null; then 761282434Sgjb trap 'rm -rf "$LOCKDIR";' EXIT 762282434Sgjb trap 'rm -rf "$LOCKDIR"; exit 1' INT QUIT ABRT SEGV ALRM TERM 763282434Sgjb echo $$ >"$LOCKDIR/pid" 764282434Sgjb break 765282434Sgjb fi 766282434Sgjb pid=$(cat "$LOCKDIR/pid") 767282434Sgjb if ! kill -0 "$pid"; then 768282434Sgjb warn "clearing stale lock pid $pid" 769282434Sgjb rm -rf "$LOCKDIR" 770282434Sgjb continue 771282434Sgjb fi 772282434Sgjb lock_timeout=$(($lock_timeout - 1)) 773282434Sgjb if [ "$lock_timeout" -le 0 ]; then 774282434Sgjb error_exit "timed out waiting for lock from pid $pid" 775282434Sgjb fi 776282434Sgjb sleep 1 777282434Sgjbdone 778282434Sgjb 779282434Sgjbcase "$cmd" in 780282434Sgjba) 781219732Sume # Read resolv.conf from stdin 782225524Shrs resolv="$(cat)" 783282434Sgjb changed=false 784282434Sgjb changedfile=false 785219732Sume # If what we are given matches what we have, then do nothing 786219732Sume if [ -e "$IFACEDIR/$iface" ]; then 787282434Sgjb if [ "$(echo "$resolv")" != \ 788219732Sume "$(cat "$IFACEDIR/$iface")" ] 789219732Sume then 790282434Sgjb changed=true 791282434Sgjb changedfile=true 792219732Sume fi 793282434Sgjb else 794282434Sgjb changed=true 795282434Sgjb changedfile=true 796219732Sume fi 797282434Sgjb 798282434Sgjb # Set metric and private before creating the interface resolv.conf file 799282434Sgjb # to ensure that it will have the correct flags 800219732Sume [ ! -d "$METRICDIR" ] && mkdir "$METRICDIR" 801282434Sgjb oldmetric="$METRICDIR/"*" $iface" 802282434Sgjb newmetric= 803219732Sume if [ -n "$IF_METRIC" ]; then 804219732Sume # Pad metric to 6 characters, so 5 is less than 10 805219732Sume while [ ${#IF_METRIC} -le 6 ]; do 806219732Sume IF_METRIC="0$IF_METRIC" 807219732Sume done 808282434Sgjb newmetric="$METRICDIR/$IF_METRIC $iface" 809219732Sume fi 810282434Sgjb rm -f "$METRICDIR/"*" $iface" 811282434Sgjb [ "$oldmetric" != "$newmetric" -a \ 812282434Sgjb "$oldmetric" != "$METRICDIR/* $iface" ] && 813282434Sgjb changed=true 814282434Sgjb [ -n "$newmetric" ] && echo " " >"$newmetric" 815282434Sgjb 816219732Sume case "$IF_PRIVATE" in 817219732Sume [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) 818219732Sume if [ ! -d "$PRIVATEDIR" ]; then 819219732Sume [ -e "$PRIVATEDIR" ] && rm "$PRIVATEDIR" 820219732Sume mkdir "$PRIVATEDIR" 821219732Sume fi 822282434Sgjb [ -e "$PRIVATEDIR/$iface" ] || changed=true 823219732Sume [ -d "$PRIVATEDIR" ] && echo " " >"$PRIVATEDIR/$iface" 824219732Sume ;; 825219732Sume *) 826219732Sume if [ -e "$PRIVATEDIR/$iface" ]; then 827219732Sume rm -f "$PRIVATEDIR/$iface" 828282434Sgjb changed=true 829219732Sume fi 830219732Sume ;; 831219732Sume esac 832219732Sume 833282434Sgjb oldexcl= 834282434Sgjb for x in "$EXCLUSIVEDIR/"*" $iface"; do 835282434Sgjb if [ -f "$x" ]; then 836282434Sgjb oldexcl="$x" 837282434Sgjb break 838282434Sgjb fi 839282434Sgjb done 840282434Sgjb case "$IF_EXCLUSIVE" in 841282434Sgjb [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) 842282434Sgjb if [ ! -d "$EXCLUSIVEDIR" ]; then 843282434Sgjb [ -e "$EXCLUSIVEDIR" ] && rm "$EXCLUSIVEDIR" 844282434Sgjb mkdir "$EXCLUSIVEDIR" 845282434Sgjb fi 846282434Sgjb cd "$EXCLUSIVEDIR" 847282434Sgjb for x in *; do 848282434Sgjb [ -f "$x" ] && break 849282434Sgjb done 850282434Sgjb if [ "${x#* }" != "$iface" ]; then 851282434Sgjb if [ "$x" = "${x% *}" ]; then 852282434Sgjb x=10000000 853282434Sgjb else 854282434Sgjb x="${x% *}" 855282434Sgjb fi 856282434Sgjb if [ "$x" = "0000000" ]; then 857282434Sgjb warn "exclusive underflow" 858282434Sgjb else 859282434Sgjb x=$(($x - 1)) 860282434Sgjb fi 861282434Sgjb if [ -d "$EXCLUSIVEDIR" ]; then 862282434Sgjb echo " " >"$EXCLUSIVEDIR/$x $iface" 863282434Sgjb fi 864282434Sgjb changed=true 865282434Sgjb fi 866282434Sgjb ;; 867282434Sgjb *) 868282434Sgjb if [ -f "$oldexcl" ]; then 869282434Sgjb rm -f "$oldexcl" 870282434Sgjb changed=true 871282434Sgjb fi 872282434Sgjb ;; 873282434Sgjb esac 874282434Sgjb 875282434Sgjb if $changedfile; then 876282434Sgjb printf "%s\n" "$resolv" >"$IFACEDIR/$iface" || exit $? 877282434Sgjb elif ! $changed; then 878282434Sgjb exit 0 879282434Sgjb fi 880282434Sgjb unset changed changedfile oldmetric newmetric x oldexcl 881282434Sgjb ;; 882282434Sgjb 883282434Sgjbd) 884282434Sgjb # Delete any existing information about the interface 885282434Sgjb cd "$IFACEDIR" 886282434Sgjb changed=false 887282434Sgjb for i in $args; do 888282434Sgjb if [ -e "$i" ]; then 889282434Sgjb changed=true 890282434Sgjb elif ! ${force}; then 891282434Sgjb warn "No resolv.conf for interface $i" 892282434Sgjb fi 893282434Sgjb rm -f "$i" "$METRICDIR/"*" $i" \ 894282434Sgjb "$PRIVATEDIR/$i" \ 895282434Sgjb "$EXCLUSIVEDIR/"*" $i" || exit $? 896282434Sgjb done 897282434Sgjb if ! ${changed}; then 898282434Sgjb # Set the return code based on the forced flag 899282434Sgjb ${force} 900282434Sgjb exit $? 901282434Sgjb fi 902282434Sgjb unset changed i 903282434Sgjb ;; 904282434Sgjbesac 905282434Sgjb 906282434Sgjbcase "${resolvconf:-YES}" in 907282434Sgjb[Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) ;; 908282434Sgjb*) exit 0;; 909282434Sgjbesac 910282434Sgjb 911304515Spfg# Try and detect a suitable init system for our scripts 912304515Spfgdetect_init 913304515Spfgexport RESTARTCMD RCDIR _NOINIT_WARNED 914304515Spfg 915219732Sumeeval "$(make_vars)" 916219732Sumeexport RESOLVCONF DOMAINS SEARCH NAMESERVERS LOCALNAMESERVERS 917219732Sume: ${list_resolv:=list_resolv -l} 918219732Sumeretval=0 919296190Spfg 920296190Spfg# Run scripts in the same directory resolvconf is run from 921304515Spfg# in case any scripts accidentally dump files in the wrong place. 922296190Spfgcd "$_PWD" 923219732Sumefor script in "$LIBEXECDIR"/*; do 924219732Sume if [ -f "$script" ]; then 925282434Sgjb eval script_enabled="\$${script##*/}" 926282434Sgjb case "${script_enabled:-YES}" in 927282434Sgjb [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) ;; 928282434Sgjb *) continue;; 929282434Sgjb esac 930219732Sume if [ -x "$script" ]; then 931219732Sume "$script" "$cmd" "$iface" 932219732Sume else 933282434Sgjb (set -- "$cmd" "$iface"; . "$script") 934219732Sume fi 935219732Sume retval=$(($retval + $?)) 936219732Sume fi 937219732Sumedone 938219732Sumeexit $retval 939