1219732Sume#!/bin/sh 2282746Sgjb# Copyright (c) 2007-2015 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" 28219732SumeSYSCONFDIR=@SYSCONFDIR@ 29219732SumeLIBEXECDIR=@LIBEXECDIR@ 30219732SumeVARDIR=@VARDIR@ 31282746Sgjb 32282746Sgjb# Disregard dhcpcd setting 33282746Sgjbunset interface_order state_dir 34282746Sgjb 35282746Sgjb# If you change this, change the test in VFLAG and libc.in as well 36282746Sgjblocal_nameservers="127.* 0.0.0.0 255.255.255.255 ::1" 37282746Sgjb 38282746Sgjbdynamic_order="tap[0-9]* tun[0-9]* vpn vpn[0-9]* ppp[0-9]* ippp[0-9]*" 39282746Sgjbinterface_order="lo lo[0-9]*" 40282746Sgjbname_server_blacklist="0.0.0.0" 41282746Sgjb 42219732Sume# Support original resolvconf configuration layout 43219732Sume# as well as the openresolv config file 44219732Sumeif [ -f "$SYSCONFDIR"/resolvconf.conf ]; then 45219732Sume . "$SYSCONFDIR"/resolvconf.conf 46219732Sume [ -n "$state_dir" ] && VARDIR="$state_dir" 47219732Sumeelif [ -d "$SYSCONFDIR/resolvconf" ]; then 48219732Sume SYSCONFDIR="$SYSCONFDIR/resolvconf" 49219732Sume if [ -f "$SYSCONFDIR"/interface-order ]; then 50219732Sume interface_order="$(cat "$SYSCONFDIR"/interface-order)" 51219732Sume fi 52219732Sumefi 53282746SgjbTMPDIR="$VARDIR/tmp" 54219732SumeIFACEDIR="$VARDIR/interfaces" 55219732SumeMETRICDIR="$VARDIR/metrics" 56219732SumePRIVATEDIR="$VARDIR/private" 57282746SgjbEXCLUSIVEDIR="$VARDIR/exclusive" 58282746SgjbLOCKDIR="$VARDIR/lock" 59219732Sume 60282746Sgjbwarn() 61282746Sgjb{ 62282746Sgjb echo "$*" >&2 63282746Sgjb} 64219732Sume 65219732Sumeerror_exit() 66219732Sume{ 67219732Sume echo "$*" >&2 68219732Sume exit 1 69219732Sume} 70219732Sume 71219732Sumeusage() 72219732Sume{ 73219732Sume cat <<-EOF 74219732Sume Usage: ${RESOLVCONF##*/} [options] 75219732Sume 76219732Sume Inform the system about any DNS updates. 77219732Sume 78219732Sume Options: 79219732Sume -a \$INTERFACE Add DNS information to the specified interface 80219732Sume (DNS supplied via stdin in resolv.conf format) 81219732Sume -m metric Give the added DNS information a metric 82219732Sume -p Mark the interface as private 83282746Sgjb -x Mark the interface as exclusive 84219732Sume -d \$INTERFACE Delete DNS information from the specified interface 85219732Sume -f Ignore non existant interfaces 86219732Sume -I Init the state dir 87219732Sume -u Run updates from our current DNS information 88219732Sume -l [\$PATTERN] Show DNS information, optionally from interfaces 89219732Sume that match the specified pattern 90219732Sume -i [\$PATTERN] Show interfaces that have supplied DNS information 91219732Sume optionally from interfaces that match the specified 92219732Sume pattern 93219732Sume -v [\$PATTERN] echo NEWDOMAIN, NEWSEARCH and NEWNS variables to 94219732Sume the console 95219732Sume -h Show this help cruft 96219732Sume EOF 97219732Sume [ -z "$1" ] && exit 0 98219732Sume echo 99219732Sume error_exit "$*" 100219732Sume} 101219732Sume 102219732Sumeecho_resolv() 103219732Sume{ 104282746Sgjb local line= OIFS="$IFS" 105282746Sgjb 106282746Sgjb [ -n "$1" -a -f "$IFACEDIR/$1" ] || return 1 107219732Sume echo "# resolv.conf from $1" 108219732Sume # Our variable maker works of the fact each resolv.conf per interface 109219732Sume # is separated by blank lines. 110219732Sume # So we remove them when echoing them. 111282746Sgjb while read -r line; do 112282746Sgjb IFS="$OIFS" 113282746Sgjb if [ -n "$line" ]; then 114282746Sgjb # We need to set IFS here to preserve any whitespace 115282746Sgjb IFS='' 116282746Sgjb printf "%s\n" "$line" 117282746Sgjb fi 118219732Sume done < "$IFACEDIR/$1" 119219732Sume echo 120282746Sgjb IFS="$OIFS" 121219732Sume} 122219732Sume 123219732Sume# Parse resolv.conf's and make variables 124219732Sume# for domain name servers, search name servers and global nameservers 125219732Sumeparse_resolv() 126219732Sume{ 127219732Sume local line= ns= ds= search= d= n= newns= 128282746Sgjb local new=true iface= private=false p= domain= l= islocal= 129219732Sume 130219732Sume newns= 131219732Sume 132282746Sgjb while read -r line; do 133219732Sume case "$line" in 134219732Sume "# resolv.conf from "*) 135219732Sume if ${new}; then 136219732Sume iface="${line#\# resolv.conf from *}" 137219732Sume new=false 138219732Sume if [ -e "$PRIVATEDIR/$iface" ]; then 139219732Sume private=true 140219732Sume else 141219732Sume # Allow expansion 142219732Sume cd "$IFACEDIR" 143219732Sume private=false 144219732Sume for p in $private_interfaces; do 145282746Sgjb case "$iface" in 146282746Sgjb "$p"|"$p":*) private=true; break;; 147282746Sgjb esac 148219732Sume done 149219732Sume fi 150219732Sume fi 151219732Sume ;; 152219732Sume "nameserver "*) 153282746Sgjb islocal=false 154282746Sgjb for l in $local_nameservers; do 155282746Sgjb case "${line#* }" in 156282746Sgjb $l) 157282746Sgjb islocal=true 158282746Sgjb echo "LOCALNAMESERVERS=\"\$LOCALNAMESERVERS ${line#* }\"" 159282746Sgjb break 160282746Sgjb ;; 161282746Sgjb esac 162282746Sgjb done 163282746Sgjb $islocal || ns="$ns${line#* } " 164219732Sume ;; 165282746Sgjb "domain "*) 166282746Sgjb if [ -z "$domain" ]; then 167282746Sgjb domain="${line#* }" 168282746Sgjb echo "DOMAIN=\"$domain\"" 169282746Sgjb fi 170219732Sume search="${line#* }" 171219732Sume ;; 172282746Sgjb "search "*) 173282746Sgjb search="${line#* }" 174282746Sgjb ;; 175219732Sume *) 176219732Sume [ -n "$line" ] && continue 177219732Sume if [ -n "$ns" -a -n "$search" ]; then 178219732Sume newns= 179219732Sume for n in $ns; do 180219732Sume newns="$newns${newns:+,}$n" 181219732Sume done 182219732Sume ds= 183219732Sume for d in $search; do 184219732Sume ds="$ds${ds:+ }$d:$newns" 185219732Sume done 186219732Sume echo "DOMAINS=\"\$DOMAINS $ds\"" 187219732Sume fi 188219732Sume echo "SEARCH=\"\$SEARCH $search\"" 189219732Sume if ! $private; then 190219732Sume echo "NAMESERVERS=\"\$NAMESERVERS $ns\"" 191219732Sume fi 192219732Sume ns= 193219732Sume search= 194219732Sume new=true 195219732Sume ;; 196219732Sume esac 197219732Sume done 198219732Sume} 199219732Sume 200219732Sumeuniqify() 201219732Sume{ 202219732Sume local result= 203219732Sume while [ -n "$1" ]; do 204219732Sume case " $result " in 205219732Sume *" $1 "*);; 206219732Sume *) result="$result $1";; 207219732Sume esac 208219732Sume shift 209219732Sume done 210219732Sume echo "${result# *}" 211219732Sume} 212219732Sume 213282746Sgjbdirname() 214282746Sgjb{ 215282746Sgjb local dir= OIFS="$IFS" 216282746Sgjb local IFS=/ 217282746Sgjb set -- $@ 218282746Sgjb IFS="$OIFS" 219282746Sgjb if [ -n "$1" ]; then 220282746Sgjb printf %s . 221282746Sgjb else 222282746Sgjb shift 223282746Sgjb fi 224282746Sgjb while [ -n "$2" ]; do 225282746Sgjb printf "/%s" "$1" 226282746Sgjb shift 227282746Sgjb done 228282746Sgjb printf "\n" 229282746Sgjb} 230282746Sgjb 231282746Sgjbconfig_mkdirs() 232282746Sgjb{ 233282746Sgjb local e=0 f d 234282746Sgjb for f; do 235282746Sgjb [ -n "$f" ] || continue 236282746Sgjb d="$(dirname "$f")" 237282746Sgjb if [ ! -d "$d" ]; then 238282746Sgjb if type install >/dev/null 2>&1; then 239282746Sgjb install -d "$d" || e=$? 240282746Sgjb else 241282746Sgjb mkdir "$d" || e=$? 242282746Sgjb fi 243282746Sgjb fi 244282746Sgjb done 245282746Sgjb return $e 246282746Sgjb} 247282746Sgjb 248219732Sumelist_resolv() 249219732Sume{ 250219732Sume [ -d "$IFACEDIR" ] || return 0 251219732Sume 252282746Sgjb local report=false list= retval=0 cmd="$1" excl= 253219732Sume shift 254219732Sume 255282746Sgjb case "$IF_EXCLUSIVE" in 256282746Sgjb [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) 257282746Sgjb if [ -d "$EXCLUSIVEDIR" ]; then 258282746Sgjb cd "$EXCLUSIVEDIR" 259282746Sgjb for i in *; do 260282746Sgjb if [ -f "$i" ]; then 261282746Sgjb list="${i#* }" 262282746Sgjb break 263282746Sgjb fi 264282746Sgjb done 265282746Sgjb fi 266282746Sgjb excl=true 267282746Sgjb ;; 268282746Sgjb *) 269282746Sgjb excl=false 270282746Sgjb ;; 271282746Sgjb esac 272282746Sgjb 273219732Sume # If we have an interface ordering list, then use that. 274219732Sume # It works by just using pathname expansion in the interface directory. 275219732Sume if [ -n "$1" ]; then 276282746Sgjb list="$*" 277219732Sume $force || report=true 278282746Sgjb elif ! $excl; then 279219732Sume cd "$IFACEDIR" 280219732Sume for i in $interface_order; do 281282746Sgjb [ -f "$i" ] && list="$list $i" 282282746Sgjb for ii in "$i":* "$i".*; do 283282746Sgjb [ -f "$ii" ] && list="$list $ii" 284282746Sgjb done 285219732Sume done 286219732Sume for i in $dynamic_order; do 287219732Sume if [ -e "$i" -a ! -e "$METRICDIR/"*" $i" ]; then 288219732Sume list="$list $i" 289219732Sume fi 290282746Sgjb for ii in "$i":* "$i".*; do 291282746Sgjb if [ -f "$ii" -a ! -e "$METRICDIR/"*" $ii" ]; then 292282746Sgjb list="$list $ii" 293282746Sgjb fi 294282746Sgjb done 295219732Sume done 296219732Sume if [ -d "$METRICDIR" ]; then 297219732Sume cd "$METRICDIR" 298219732Sume for i in *; do 299282746Sgjb [ -f "$i" ] && list="$list ${i#* }" 300219732Sume done 301219732Sume fi 302219732Sume list="$list *" 303219732Sume fi 304219732Sume 305219732Sume cd "$IFACEDIR" 306282746Sgjb retval=1 307219732Sume for i in $(uniqify $list); do 308219732Sume # Only list interfaces which we really have 309282746Sgjb if ! [ -f "$i" ]; then 310219732Sume if $report; then 311219732Sume echo "No resolv.conf for interface $i" >&2 312282746Sgjb retval=2 313219732Sume fi 314219732Sume continue 315219732Sume fi 316219732Sume 317219732Sume if [ "$cmd" = i -o "$cmd" = "-i" ]; then 318282746Sgjb printf %s "$i " 319219732Sume else 320219732Sume echo_resolv "$i" 321219732Sume fi 322282746Sgjb [ $? = 0 -a "$retval" = 1 ] && retval=0 323219732Sume done 324219732Sume [ "$cmd" = i -o "$cmd" = "-i" ] && echo 325219732Sume return $retval 326219732Sume} 327219732Sume 328282746Sgjblist_remove() { 329282746Sgjb local list= e= l= result= found= retval=0 330282746Sgjb 331282746Sgjb [ -z "$2" ] && return 0 332282746Sgjb eval list=\"\$$1\" 333282746Sgjb shift 334282746Sgjb 335282746Sgjb set -f 336282746Sgjb for e; do 337282746Sgjb found=false 338282746Sgjb for l in $list; do 339282746Sgjb case "$e" in 340282746Sgjb $l) found=true;; 341282746Sgjb esac 342282746Sgjb $found && break 343282746Sgjb done 344282746Sgjb if $found; then 345282746Sgjb retval=$(($retval + 1)) 346282746Sgjb else 347282746Sgjb result="$result $e" 348282746Sgjb fi 349282746Sgjb done 350282746Sgjb set +f 351282746Sgjb echo "${result# *}" 352282746Sgjb return $retval 353282746Sgjb} 354282746Sgjb 355282746Sgjbecho_prepend() 356282746Sgjb{ 357282746Sgjb echo "# Generated by resolvconf" 358282746Sgjb if [ -n "$search_domains" ]; then 359282746Sgjb echo "search $search_domains" 360282746Sgjb fi 361282746Sgjb for n in $name_servers; do 362282746Sgjb echo "nameserver $n" 363282746Sgjb done 364282746Sgjb echo 365282746Sgjb} 366282746Sgjb 367282746Sgjbecho_append() 368282746Sgjb{ 369282746Sgjb echo "# Generated by resolvconf" 370282746Sgjb if [ -n "$search_domains_append" ]; then 371282746Sgjb echo "search $search_domains_append" 372282746Sgjb fi 373282746Sgjb for n in $name_servers_append; do 374282746Sgjb echo "nameserver $n" 375282746Sgjb done 376282746Sgjb echo 377282746Sgjb} 378282746Sgjb 379282746Sgjbreplace() 380282746Sgjb{ 381282746Sgjb local r= k= f= v= val= sub= 382282746Sgjb 383282746Sgjb while read -r keyword value; do 384282746Sgjb for r in $replace; do 385282746Sgjb k="${r%%/*}" 386282746Sgjb r="${r#*/}" 387282746Sgjb f="${r%%/*}" 388282746Sgjb r="${r#*/}" 389282746Sgjb v="${r%%/*}" 390282746Sgjb case "$keyword" in 391282746Sgjb $k) 392282746Sgjb case "$value" in 393282746Sgjb $f) value="$v";; 394282746Sgjb esac 395282746Sgjb ;; 396282746Sgjb esac 397282746Sgjb done 398282746Sgjb val= 399282746Sgjb for sub in $value; do 400282746Sgjb for r in $replace_sub; do 401282746Sgjb k="${r%%/*}" 402282746Sgjb r="${r#*/}" 403282746Sgjb f="${r%%/*}" 404282746Sgjb r="${r#*/}" 405282746Sgjb v="${r%%/*}" 406282746Sgjb case "$keyword" in 407282746Sgjb $k) 408282746Sgjb case "$sub" in 409282746Sgjb $f) sub="$v";; 410282746Sgjb esac 411282746Sgjb ;; 412282746Sgjb esac 413282746Sgjb done 414282746Sgjb val="$val${val:+ }$sub" 415282746Sgjb done 416282746Sgjb printf "%s %s\n" "$keyword" "$val" 417282746Sgjb done 418282746Sgjb} 419282746Sgjb 420219732Sumemake_vars() 421219732Sume{ 422282746Sgjb local newdomains= d= dn= newns= ns= 423219732Sume 424282746Sgjb # Clear variables 425282746Sgjb DOMAIN= 426282746Sgjb DOMAINS= 427282746Sgjb SEARCH= 428282746Sgjb NAMESERVERS= 429282746Sgjb LOCALNAMESERVERS= 430282746Sgjb 431282746Sgjb if [ -n "$name_servers" -o -n "$search_domains" ]; then 432282746Sgjb eval "$(echo_prepend | parse_resolv)" 433282746Sgjb fi 434282746Sgjb if [ -z "$VFLAG" ]; then 435282746Sgjb IF_EXCLUSIVE=1 436282746Sgjb list_resolv -i "$@" >/dev/null || IF_EXCLUSIVE=0 437282746Sgjb eval "$(list_resolv -l "$@" | replace | parse_resolv)" 438282746Sgjb fi 439282746Sgjb if [ -n "$name_servers_append" -o -n "$search_domains_append" ]; then 440282746Sgjb eval "$(echo_append | parse_resolv)" 441282746Sgjb fi 442282746Sgjb 443219732Sume # Ensure that we only list each domain once 444219732Sume for d in $DOMAINS; do 445219732Sume dn="${d%%:*}" 446282746Sgjb list_remove domain_blacklist "$dn" >/dev/null || continue 447219732Sume case " $newdomains" in 448219732Sume *" ${dn}:"*) continue;; 449219732Sume esac 450219732Sume newns= 451219732Sume for nd in $DOMAINS; do 452219732Sume if [ "$dn" = "${nd%%:*}" ]; then 453219732Sume ns="${nd#*:}" 454219732Sume while [ -n "$ns" ]; do 455219732Sume case ",$newns," in 456219732Sume *,${ns%%,*},*) ;; 457282746Sgjb *) list_remove name_server_blacklist \ 458282746Sgjb "${ns%%,*}" >/dev/null \ 459282746Sgjb && newns="$newns${newns:+,}${ns%%,*}";; 460219732Sume esac 461219732Sume [ "$ns" = "${ns#*,}" ] && break 462219732Sume ns="${ns#*,}" 463219732Sume done 464219732Sume fi 465219732Sume done 466282746Sgjb if [ -n "$newns" ]; then 467282746Sgjb newdomains="$newdomains${newdomains:+ }$dn:$newns" 468282746Sgjb fi 469219732Sume done 470282746Sgjb DOMAIN="$(list_remove domain_blacklist $DOMAIN)" 471282746Sgjb SEARCH="$(uniqify $SEARCH)" 472282746Sgjb SEARCH="$(list_remove domain_blacklist $SEARCH)" 473282746Sgjb NAMESERVERS="$(uniqify $NAMESERVERS)" 474282746Sgjb NAMESERVERS="$(list_remove name_server_blacklist $NAMESERVERS)" 475282746Sgjb LOCALNAMESERVERS="$(uniqify $LOCALNAMESERVERS)" 476282746Sgjb LOCALNAMESERVERS="$(list_remove name_server_blacklist $LOCALNAMESERVERS)" 477282746Sgjb echo "DOMAIN='$DOMAIN'" 478282746Sgjb echo "SEARCH='$SEARCH'" 479282746Sgjb echo "NAMESERVERS='$NAMESERVERS'" 480282746Sgjb echo "LOCALNAMESERVERS='$LOCALNAMESERVERS'" 481219732Sume echo "DOMAINS='$newdomains'" 482219732Sume} 483219732Sume 484219732Sumeforce=false 485282746SgjbVFLAG= 486282746Sgjbwhile getopts a:Dd:fhIilm:puvVx OPT; do 487219732Sume case "$OPT" in 488219732Sume f) force=true;; 489219732Sume h) usage;; 490219732Sume m) IF_METRIC="$OPTARG";; 491219732Sume p) IF_PRIVATE=1;; 492282746Sgjb V) 493282746Sgjb VFLAG=1 494282746Sgjb if [ "$local_nameservers" = \ 495282746Sgjb "127.* 0.0.0.0 255.255.255.255 ::1" ] 496282746Sgjb then 497282746Sgjb local_nameservers= 498282746Sgjb fi 499282746Sgjb ;; 500282746Sgjb x) IF_EXCLUSIVE=1;; 501219732Sume '?') ;; 502219732Sume *) cmd="$OPT"; iface="$OPTARG";; 503219732Sume esac 504219732Sumedone 505219732Sumeshift $(($OPTIND - 1)) 506282746Sgjbargs="$iface${iface:+ }$*" 507219732Sume 508219732Sume# -I inits the state dir 509219732Sumeif [ "$cmd" = I ]; then 510219732Sume if [ -d "$VARDIR" ]; then 511219732Sume rm -rf "$VARDIR"/* 512219732Sume fi 513219732Sume exit $? 514219732Sumefi 515219732Sume 516282746Sgjb# -D ensures that the listed config file base dirs exist 517282746Sgjbif [ "$cmd" = D ]; then 518282746Sgjb config_mkdirs "$@" 519282746Sgjb exit $? 520282746Sgjbfi 521282746Sgjb 522219732Sume# -l lists our resolv files, optionally for a specific interface 523219732Sumeif [ "$cmd" = l -o "$cmd" = i ]; then 524219732Sume list_resolv "$cmd" "$args" 525219732Sume exit $? 526219732Sumefi 527219732Sume 528219732Sume# Not normally needed, but subscribers should be able to run independently 529282746Sgjbif [ "$cmd" = v -o -n "$VFLAG" ]; then 530219732Sume make_vars "$iface" 531219732Sume exit $? 532219732Sumefi 533219732Sume 534219732Sume# Test that we have valid options 535219732Sumeif [ "$cmd" = a -o "$cmd" = d ]; then 536219732Sume if [ -z "$iface" ]; then 537219732Sume usage "Interface not specified" 538219732Sume fi 539219732Sumeelif [ "$cmd" != u ]; then 540219732Sume [ -n "$cmd" -a "$cmd" != h ] && usage "Unknown option $cmd" 541219732Sume usage 542219732Sumefi 543282746Sgjb 544219732Sumeif [ "$cmd" = a ]; then 545219732Sume for x in '/' \\ ' ' '*'; do 546219732Sume case "$iface" in 547219732Sume *[$x]*) error_exit "$x not allowed in interface name";; 548219732Sume esac 549219732Sume done 550219732Sume for x in '.' '-' '~'; do 551219732Sume case "$iface" in 552219732Sume [$x]*) error_exit \ 553219732Sume "$x not allowed at start of interface name";; 554219732Sume esac 555219732Sume done 556219732Sume [ "$cmd" = a -a -t 0 ] && error_exit "No file given via stdin" 557219732Sumefi 558219732Sume 559282746Sgjbif [ ! -d "$VARDIR" ]; then 560282746Sgjb if [ -L "$VARDIR" ]; then 561282746Sgjb dir="$(readlink "$VARDIR")" 562282746Sgjb # link maybe relative 563282746Sgjb cd "${VARDIR%/*}" 564282746Sgjb if ! mkdir -m 0755 -p "$dir"; then 565282746Sgjb error_exit "Failed to create needed" \ 566282746Sgjb "directory $dir" 567219732Sume fi 568282746Sgjb else 569282746Sgjb if ! mkdir -m 0755 -p "$VARDIR"; then 570282746Sgjb error_exit "Failed to create needed" \ 571282746Sgjb "directory $VARDIR" 572282746Sgjb fi 573219732Sume fi 574282746Sgjbfi 575282746Sgjb 576282746Sgjbif [ ! -d "$IFACEDIR" ]; then 577219732Sume mkdir -m 0755 -p "$IFACEDIR" || \ 578219732Sume error_exit "Failed to create needed directory $IFACEDIR" 579219732Sume if [ "$cmd" = d ]; then 580282746Sgjb # Provide the same error messages as below 581282746Sgjb if ! ${force}; then 582282746Sgjb cd "$IFACEDIR" 583282746Sgjb for i in $args; do 584282746Sgjb warn "No resolv.conf for interface $i" 585282746Sgjb done 586282746Sgjb fi 587282746Sgjb ${force} 588282746Sgjb exit $? 589219732Sume fi 590219732Sumefi 591219732Sume 592282746Sgjb# An interface was added, changed, deleted or a general update was called. 593282746Sgjb# Due to exclusivity we need to ensure that this is an atomic operation. 594282746Sgjb# Our subscribers *may* need this as well if the init system is sub par. 595282746Sgjb# As such we spinlock at this point as best we can. 596282746Sgjb# We don't use flock(1) because it's not widely available and normally resides 597282746Sgjb# in /usr which we do our very best to operate without. 598282746Sgjb[ -w "$VARDIR" ] || error_exit "Cannot write to $LOCKDIR" 599282746Sgjb: ${lock_timeout:=10} 600282746Sgjbwhile true; do 601282746Sgjb if mkdir "$LOCKDIR" 2>/dev/null; then 602282746Sgjb trap 'rm -rf "$LOCKDIR";' EXIT 603282746Sgjb trap 'rm -rf "$LOCKDIR"; exit 1' INT QUIT ABRT SEGV ALRM TERM 604282746Sgjb echo $$ >"$LOCKDIR/pid" 605282746Sgjb break 606282746Sgjb fi 607282746Sgjb pid=$(cat "$LOCKDIR/pid") 608282746Sgjb if ! kill -0 "$pid"; then 609282746Sgjb warn "clearing stale lock pid $pid" 610282746Sgjb rm -rf "$LOCKDIR" 611282746Sgjb continue 612282746Sgjb fi 613282746Sgjb lock_timeout=$(($lock_timeout - 1)) 614282746Sgjb if [ "$lock_timeout" -le 0 ]; then 615282746Sgjb error_exit "timed out waiting for lock from pid $pid" 616282746Sgjb fi 617282746Sgjb sleep 1 618282746Sgjbdone 619282746Sgjb 620282746Sgjbcase "$cmd" in 621282746Sgjba) 622219732Sume # Read resolv.conf from stdin 623225524Shrs resolv="$(cat)" 624282746Sgjb changed=false 625282746Sgjb changedfile=false 626219732Sume # If what we are given matches what we have, then do nothing 627219732Sume if [ -e "$IFACEDIR/$iface" ]; then 628282746Sgjb if [ "$(echo "$resolv")" != \ 629219732Sume "$(cat "$IFACEDIR/$iface")" ] 630219732Sume then 631282746Sgjb changed=true 632282746Sgjb changedfile=true 633219732Sume fi 634282746Sgjb else 635282746Sgjb changed=true 636282746Sgjb changedfile=true 637219732Sume fi 638282746Sgjb 639282746Sgjb # Set metric and private before creating the interface resolv.conf file 640282746Sgjb # to ensure that it will have the correct flags 641219732Sume [ ! -d "$METRICDIR" ] && mkdir "$METRICDIR" 642282746Sgjb oldmetric="$METRICDIR/"*" $iface" 643282746Sgjb newmetric= 644219732Sume if [ -n "$IF_METRIC" ]; then 645219732Sume # Pad metric to 6 characters, so 5 is less than 10 646219732Sume while [ ${#IF_METRIC} -le 6 ]; do 647219732Sume IF_METRIC="0$IF_METRIC" 648219732Sume done 649282746Sgjb newmetric="$METRICDIR/$IF_METRIC $iface" 650219732Sume fi 651282746Sgjb rm -f "$METRICDIR/"*" $iface" 652282746Sgjb [ "$oldmetric" != "$newmetric" -a \ 653282746Sgjb "$oldmetric" != "$METRICDIR/* $iface" ] && 654282746Sgjb changed=true 655282746Sgjb [ -n "$newmetric" ] && echo " " >"$newmetric" 656282746Sgjb 657219732Sume case "$IF_PRIVATE" in 658219732Sume [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) 659219732Sume if [ ! -d "$PRIVATEDIR" ]; then 660219732Sume [ -e "$PRIVATEDIR" ] && rm "$PRIVATEDIR" 661219732Sume mkdir "$PRIVATEDIR" 662219732Sume fi 663282746Sgjb [ -e "$PRIVATEDIR/$iface" ] || changed=true 664219732Sume [ -d "$PRIVATEDIR" ] && echo " " >"$PRIVATEDIR/$iface" 665219732Sume ;; 666219732Sume *) 667219732Sume if [ -e "$PRIVATEDIR/$iface" ]; then 668219732Sume rm -f "$PRIVATEDIR/$iface" 669282746Sgjb changed=true 670219732Sume fi 671219732Sume ;; 672219732Sume esac 673219732Sume 674282746Sgjb oldexcl= 675282746Sgjb for x in "$EXCLUSIVEDIR/"*" $iface"; do 676282746Sgjb if [ -f "$x" ]; then 677282746Sgjb oldexcl="$x" 678282746Sgjb break 679282746Sgjb fi 680282746Sgjb done 681282746Sgjb case "$IF_EXCLUSIVE" in 682282746Sgjb [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) 683282746Sgjb if [ ! -d "$EXCLUSIVEDIR" ]; then 684282746Sgjb [ -e "$EXCLUSIVEDIR" ] && rm "$EXCLUSIVEDIR" 685282746Sgjb mkdir "$EXCLUSIVEDIR" 686282746Sgjb fi 687282746Sgjb cd "$EXCLUSIVEDIR" 688282746Sgjb for x in *; do 689282746Sgjb [ -f "$x" ] && break 690282746Sgjb done 691282746Sgjb if [ "${x#* }" != "$iface" ]; then 692282746Sgjb if [ "$x" = "${x% *}" ]; then 693282746Sgjb x=10000000 694282746Sgjb else 695282746Sgjb x="${x% *}" 696282746Sgjb fi 697282746Sgjb if [ "$x" = "0000000" ]; then 698282746Sgjb warn "exclusive underflow" 699282746Sgjb else 700282746Sgjb x=$(($x - 1)) 701282746Sgjb fi 702282746Sgjb if [ -d "$EXCLUSIVEDIR" ]; then 703282746Sgjb echo " " >"$EXCLUSIVEDIR/$x $iface" 704282746Sgjb fi 705282746Sgjb changed=true 706282746Sgjb fi 707282746Sgjb ;; 708282746Sgjb *) 709282746Sgjb if [ -f "$oldexcl" ]; then 710282746Sgjb rm -f "$oldexcl" 711282746Sgjb changed=true 712282746Sgjb fi 713282746Sgjb ;; 714282746Sgjb esac 715282746Sgjb 716282746Sgjb if $changedfile; then 717282746Sgjb printf "%s\n" "$resolv" >"$IFACEDIR/$iface" || exit $? 718282746Sgjb elif ! $changed; then 719282746Sgjb exit 0 720282746Sgjb fi 721282746Sgjb unset changed changedfile oldmetric newmetric x oldexcl 722282746Sgjb ;; 723282746Sgjb 724282746Sgjbd) 725282746Sgjb # Delete any existing information about the interface 726282746Sgjb cd "$IFACEDIR" 727282746Sgjb changed=false 728282746Sgjb for i in $args; do 729282746Sgjb if [ -e "$i" ]; then 730282746Sgjb changed=true 731282746Sgjb elif ! ${force}; then 732282746Sgjb warn "No resolv.conf for interface $i" 733282746Sgjb fi 734282746Sgjb rm -f "$i" "$METRICDIR/"*" $i" \ 735282746Sgjb "$PRIVATEDIR/$i" \ 736282746Sgjb "$EXCLUSIVEDIR/"*" $i" || exit $? 737282746Sgjb done 738282746Sgjb if ! ${changed}; then 739282746Sgjb # Set the return code based on the forced flag 740282746Sgjb ${force} 741282746Sgjb exit $? 742282746Sgjb fi 743282746Sgjb unset changed i 744282746Sgjb ;; 745282746Sgjbesac 746282746Sgjb 747282746Sgjbcase "${resolvconf:-YES}" in 748282746Sgjb[Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) ;; 749282746Sgjb*) exit 0;; 750282746Sgjbesac 751282746Sgjb 752219732Sumeeval "$(make_vars)" 753219732Sumeexport RESOLVCONF DOMAINS SEARCH NAMESERVERS LOCALNAMESERVERS 754219732Sume: ${list_resolv:=list_resolv -l} 755219732Sumeretval=0 756219732Sumefor script in "$LIBEXECDIR"/*; do 757219732Sume if [ -f "$script" ]; then 758282746Sgjb eval script_enabled="\$${script##*/}" 759282746Sgjb case "${script_enabled:-YES}" in 760282746Sgjb [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) ;; 761282746Sgjb *) continue;; 762282746Sgjb esac 763219732Sume if [ -x "$script" ]; then 764219732Sume "$script" "$cmd" "$iface" 765219732Sume else 766282746Sgjb (set -- "$cmd" "$iface"; . "$script") 767219732Sume fi 768219732Sume retval=$(($retval + $?)) 769219732Sume fi 770219732Sumedone 771219732Sumeexit $retval 772