1 1.1 roy #!/bin/sh 2 1.11 roy # Copyright (c) 2007-2025 Roy Marples 3 1.1 roy # All rights reserved 4 1.1 roy 5 1.1 roy # Redistribution and use in source and binary forms, with or without 6 1.1 roy # modification, are permitted provided that the following conditions 7 1.1 roy # are met: 8 1.1 roy # * Redistributions of source code must retain the above copyright 9 1.1 roy # notice, this list of conditions and the following disclaimer. 10 1.1 roy # * Redistributions in binary form must reproduce the above 11 1.1 roy # copyright notice, this list of conditions and the following 12 1.1 roy # disclaimer in the documentation and/or other materials provided 13 1.1 roy # with the distribution. 14 1.1 roy # 15 1.1 roy # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16 1.1 roy # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17 1.1 roy # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 18 1.1 roy # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 19 1.1 roy # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 1.1 roy # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 21 1.1 roy # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22 1.1 roy # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23 1.1 roy # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 1.1 roy # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 1.1 roy # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 1.1 roy 27 1.1 roy RESOLVCONF="$0" 28 1.19 roy OPENRESOLV_VERSION="3.17.4" 29 1.1 roy SYSCONFDIR=@SYSCONFDIR@ 30 1.1 roy LIBEXECDIR=@LIBEXECDIR@ 31 1.1 roy VARDIR=@VARDIR@ 32 1.2 kre RCDIR=@RCDIR@ 33 1.2 kre RESTARTCMD=@RESTARTCMD@ 34 1.2 kre 35 1.2 kre if [ "$1" = "--version" ]; then 36 1.2 kre echo "openresolv $OPENRESOLV_VERSION" 37 1.11 roy echo "Copyright (c) 2007-2025 Roy Marples" 38 1.2 kre exit 0 39 1.2 kre fi 40 1.2 kre 41 1.2 kre # Disregard dhcpcd setting 42 1.2 kre unset interface_order state_dir 43 1.2 kre 44 1.2 kre # If you change this, change the test in VFLAG and libc.in as well 45 1.2 kre local_nameservers="127.* 0.0.0.0 255.255.255.255 ::1" 46 1.2 kre 47 1.7 roy dynamic_order="tap[0-9]* tun[0-9]* vpn vpn[0-9]* wg[0-9]* ppp[0-9]* ippp[0-9]*" 48 1.2 kre interface_order="lo lo[0-9]*" 49 1.2 kre name_server_blacklist="0.0.0.0" 50 1.2 kre 51 1.11 roy # Poor mans cat 52 1.11 roy # /usr might not be available 53 1.11 roy cat() 54 1.11 roy { 55 1.11 roy OIFS="$IFS" 56 1.11 roy IFS='' 57 1.11 roy if [ -n "$1" ]; then 58 1.11 roy while read -r line; do 59 1.11 roy printf "%s\n" "$line" 60 1.11 roy done < "$1" 61 1.11 roy else 62 1.11 roy while read -r line; do 63 1.11 roy printf "%s\n" "$line" 64 1.11 roy done 65 1.11 roy fi 66 1.11 roy retval=$? 67 1.11 roy IFS="$OIFS" 68 1.11 roy return $retval 69 1.11 roy } 70 1.11 roy 71 1.11 roy 72 1.1 roy # Support original resolvconf configuration layout 73 1.1 roy # as well as the openresolv config file 74 1.1 roy if [ -f "$SYSCONFDIR"/resolvconf.conf ]; then 75 1.1 roy . "$SYSCONFDIR"/resolvconf.conf 76 1.1 roy [ -n "$state_dir" ] && VARDIR="$state_dir" 77 1.1 roy elif [ -d "$SYSCONFDIR/resolvconf" ]; then 78 1.1 roy SYSCONFDIR="$SYSCONFDIR/resolvconf" 79 1.1 roy if [ -f "$SYSCONFDIR"/interface-order ]; then 80 1.1 roy interface_order="$(cat "$SYSCONFDIR"/interface-order)" 81 1.1 roy fi 82 1.1 roy fi 83 1.7 roy 84 1.12 roy KEYDIR="$VARDIR/keys" 85 1.1 roy METRICDIR="$VARDIR/metrics" 86 1.1 roy PRIVATEDIR="$VARDIR/private" 87 1.12 roy NOSEARCHDIR="$VARDIR/nosearch" 88 1.2 kre EXCLUSIVEDIR="$VARDIR/exclusive" 89 1.9 roy DEPRECATEDDIR="$VARDIR/deprecated" 90 1.2 kre LOCKDIR="$VARDIR/lock" 91 1.2 kre _PWD="$PWD" 92 1.1 roy 93 1.12 roy # Compat 94 1.12 roy if [ ! -d "$KEYDIR" ] && [ -d "$VARDIR/interfaces" ]; then 95 1.12 roy KEYDIR="$VARDIR/interfaces" 96 1.12 roy fi 97 1.12 roy : ${allow_keys:="$allow_interfaces"} 98 1.12 roy : ${deny_keys:="$deny_interfaces"} 99 1.12 roy : ${key_order:="$interface_order"} 100 1.12 roy : ${inclusive_keys:="$inclusive_interfaces"} 101 1.12 roy : ${exclusive_keys:="$exclusive_interfaces"} 102 1.12 roy : ${private_keys:="$private_interfaces"} 103 1.12 roy : ${public_keys:="$public_interfaces"} 104 1.12 roy 105 1.2 kre warn() 106 1.2 kre { 107 1.19 roy echo "${RESOLVCONF##*/}: $*" >&2 108 1.2 kre } 109 1.1 roy 110 1.1 roy error_exit() 111 1.1 roy { 112 1.19 roy warn "$*" 113 1.1 roy exit 1 114 1.1 roy } 115 1.1 roy 116 1.1 roy usage() 117 1.1 roy { 118 1.1 roy cat <<-EOF 119 1.2 kre Usage: ${RESOLVCONF##*/} [options] command [argument] 120 1.1 roy 121 1.1 roy Inform the system about any DNS updates. 122 1.1 roy 123 1.2 kre Commands: 124 1.12 roy -a \$KEY Add DNS information to the specified key 125 1.1 roy (DNS supplied via stdin in resolv.conf format) 126 1.12 roy -C \$PATTERN Deprecate DNS information for matched key 127 1.12 roy -c \$PATTERN Configure DNS information for matched key 128 1.12 roy -d \$PATTERN Delete DNS information from the matched key 129 1.2 kre -h Show this help cruft 130 1.12 roy -i [\$PATTERN] Show keys that have supplied DNS information 131 1.12 roy optionally from keys that match the specified 132 1.1 roy pattern 133 1.12 roy -l [\$PATTERN] Show DNS information, optionally from keys 134 1.2 kre that match the specified pattern 135 1.11 roy -L [\$PATTERN] Same as -l, but adjusted by our config 136 1.2 kre 137 1.2 kre -u Run updates from our current DNS information 138 1.2 kre --version Echo the ${RESOLVCONF##*/} version 139 1.2 kre 140 1.2 kre Options: 141 1.12 roy -f Ignore non existent keys 142 1.2 kre -m metric Give the added DNS information a metric 143 1.12 roy -p Mark the resolv.conf as private 144 1.12 roy -x Mark the resolv.conf as exclusive 145 1.2 kre 146 1.2 kre Subscriber and System Init Commands: 147 1.2 kre -I Init the state dir 148 1.2 kre -r \$SERVICE Restart the system service 149 1.2 kre (restarting a non-existent or non-running service 150 1.2 kre should have no output and return 0) 151 1.2 kre -R Show the system service restart command 152 1.1 roy -v [\$PATTERN] echo NEWDOMAIN, NEWSEARCH and NEWNS variables to 153 1.1 roy the console 154 1.2 kre -V [\$PATTERN] Same as -v, but only uses configuration in 155 1.2 kre $SYSCONFDIR/resolvconf.conf 156 1.1 roy EOF 157 1.1 roy [ -z "$1" ] && exit 0 158 1.1 roy echo 159 1.12 roy error_exit "$@" 160 1.1 roy } 161 1.1 roy 162 1.12 roy public_key() { 163 1.12 roy key="$1" 164 1.12 roy 165 1.12 roy # Allow expansion 166 1.12 roy cd "$KEYDIR" 167 1.12 roy 168 1.12 roy # Public keys override private ones. 169 1.12 roy for p in $public_keys; do 170 1.12 roy case "$key" in 171 1.12 roy "$p"|"$p":*) return 0;; 172 1.5 roy esac 173 1.2 kre done 174 1.12 roy 175 1.12 roy return 1 176 1.2 kre } 177 1.2 kre 178 1.12 roy private_key() 179 1.1 roy { 180 1.12 roy key="$1" 181 1.12 roy 182 1.12 roy if public_key "$key"; then 183 1.12 roy return 1 184 1.12 roy fi 185 1.12 roy 186 1.12 roy if [ -e "$PRIVATEDIR/$key" ]; then 187 1.12 roy return 0 188 1.12 roy fi 189 1.2 kre 190 1.12 roy for p in $private_keys; do 191 1.12 roy case "$key" in 192 1.12 roy "$p"|"$p":*) return 0;; 193 1.2 kre esac 194 1.2 kre done 195 1.2 kre 196 1.12 roy # Not a private key 197 1.12 roy return 1 198 1.12 roy } 199 1.12 roy 200 1.12 roy nosearch_key() 201 1.12 roy { 202 1.12 roy key="$1" 203 1.12 roy 204 1.12 roy if public_key "$key"; then 205 1.12 roy return 1 206 1.12 roy fi 207 1.12 roy 208 1.12 roy if [ -e "$NOSEARCHDIR/$key" ]; then 209 1.2 kre return 0 210 1.2 kre fi 211 1.10 roy 212 1.12 roy for p in $nosearch_keys; do 213 1.12 roy case "$key" in 214 1.2 kre "$p"|"$p":*) return 0;; 215 1.2 kre esac 216 1.2 kre done 217 1.2 kre 218 1.12 roy # Not a non searchable key 219 1.12 roy return 1 220 1.12 roy } 221 1.12 roy 222 1.12 roy exclusive_key() 223 1.12 roy { 224 1.12 roy key="$1" 225 1.12 roy 226 1.12 roy for x in "$EXCLUSIVEDIR/"*" $key"; do 227 1.12 roy if [ -f "$x" ]; then 228 1.12 roy return 0 229 1.12 roy fi 230 1.12 roy done 231 1.12 roy 232 1.12 roy # Not an exclusive key 233 1.2 kre return 1 234 1.1 roy } 235 1.1 roy 236 1.16 roy # Quote input so it can be safely used for variable assignment via eval 237 1.16 roy quote() 238 1.16 roy { 239 1.16 roy if [ -z "$1" ]; then 240 1.16 roy R="''" 241 1.16 roy else 242 1.16 roy R= 243 1.16 roy for W; do 244 1.16 roy while [ -n "$W" ]; do 245 1.16 roy case "$W" in 246 1.16 roy \'*) R="$R\\'"; W=${W#?};; 247 1.16 roy ?\'*) R="$R\\${W%%\'*}"; W="${W#?}";; 248 1.16 roy *\'*) R="$R'${W%%\'*}'"; W="'${W#*\'}";; 249 1.16 roy ?) R="$R\\$W"; W=;; 250 1.16 roy *) R="$R'$W'"; W=;; 251 1.16 roy esac 252 1.16 roy done 253 1.16 roy done 254 1.16 roy fi 255 1.16 roy 256 1.16 roy printf '%s\n' "$R" 257 1.16 roy return 0 258 1.16 roy } 259 1.16 roy 260 1.1 roy # Parse resolv.conf's and make variables 261 1.1 roy # for domain name servers, search name servers and global nameservers 262 1.17 roy # Important! Each printf here should use the above quote function 263 1.17 roy # to ensure that user input is quoted for eval. 264 1.1 roy parse_resolv() 265 1.1 roy { 266 1.5 roy domain= 267 1.5 roy new=true 268 1.2 kre newns= 269 1.5 roy ns= 270 1.5 roy private=false 271 1.12 roy nosearch=false 272 1.5 roy search= 273 1.1 roy 274 1.2 kre while read -r line; do 275 1.17 roy value="${line#* }" 276 1.1 roy case "$line" in 277 1.1 roy "# resolv.conf from "*) 278 1.1 roy if ${new}; then 279 1.12 roy key="${line#\# resolv.conf from *}" 280 1.1 roy new=false 281 1.12 roy if nosearch_key "$key"; then 282 1.12 roy private=true 283 1.12 roy nosearch=true 284 1.12 roy elif private_key "$key"; then 285 1.1 roy private=true 286 1.12 roy nosearch=false 287 1.2 kre else 288 1.2 kre private=false 289 1.12 roy nosearch=false 290 1.2 kre fi 291 1.2 kre fi 292 1.2 kre ;; 293 1.2 kre "nameserver "*) 294 1.2 kre islocal=false 295 1.2 kre for l in $local_nameservers; do 296 1.12 roy case "$value" in 297 1.2 kre $l) 298 1.2 kre islocal=true 299 1.2 kre break 300 1.1 roy ;; 301 1.1 roy esac 302 1.2 kre done 303 1.5 roy if $islocal; then 304 1.17 roy printf 'LOCALNAMESERVERS="$LOCALNAMESERVERS "%s\n' "$(quote "$value")" 305 1.5 roy else 306 1.12 roy ns="$ns${ns:+ }$value" 307 1.5 roy fi 308 1.2 kre ;; 309 1.2 kre "domain "*) 310 1.12 roy search="$value" 311 1.2 kre if [ -z "$domain" ]; then 312 1.2 kre domain="$search" 313 1.12 roy if ! $nosearch; then 314 1.17 roy printf 'DOMAIN=%s\n' "$(quote "$domain")" 315 1.12 roy fi 316 1.1 roy fi 317 1.1 roy ;; 318 1.2 kre "search "*) 319 1.12 roy search="$value" 320 1.1 roy ;; 321 1.1 roy *) 322 1.1 roy [ -n "$line" ] && continue 323 1.2 kre if [ -n "$ns" ] && [ -n "$search" ]; then 324 1.1 roy newns= 325 1.1 roy for n in $ns; do 326 1.1 roy newns="$newns${newns:+,}$n" 327 1.1 roy done 328 1.1 roy ds= 329 1.1 roy for d in $search; do 330 1.1 roy ds="$ds${ds:+ }$d:$newns" 331 1.1 roy done 332 1.17 roy printf 'DOMAINS="$DOMAINS "%s\n' "$(quote "$ds")" 333 1.1 roy fi 334 1.12 roy if ! $nosearch; then 335 1.17 roy printf 'SEARCH="$SEARCH "%s\n' "$(quote "$search")" 336 1.12 roy fi 337 1.1 roy if ! $private; then 338 1.17 roy printf 'NAMESERVERS="$NAMESERVERS "%s\n' "$(quote "$ns")" 339 1.1 roy fi 340 1.1 roy ns= 341 1.1 roy search= 342 1.1 roy new=true 343 1.1 roy ;; 344 1.1 roy esac 345 1.1 roy done 346 1.1 roy } 347 1.1 roy 348 1.1 roy uniqify() 349 1.1 roy { 350 1.5 roy result= 351 1.1 roy while [ -n "$1" ]; do 352 1.1 roy case " $result " in 353 1.1 roy *" $1 "*);; 354 1.1 roy *) result="$result $1";; 355 1.1 roy esac 356 1.1 roy shift 357 1.1 roy done 358 1.1 roy echo "${result# *}" 359 1.1 roy } 360 1.1 roy 361 1.2 kre dirname() 362 1.2 kre { 363 1.5 roy OIFS="$IFS" 364 1.5 roy IFS=/ 365 1.15 roy set -- $@ 366 1.2 kre IFS="$OIFS" 367 1.2 kre if [ -n "$1" ]; then 368 1.2 kre printf %s . 369 1.2 kre else 370 1.2 kre shift 371 1.2 kre fi 372 1.2 kre while [ -n "$2" ]; do 373 1.2 kre printf "/%s" "$1" 374 1.2 kre shift 375 1.2 kre done 376 1.2 kre printf "\n" 377 1.2 kre } 378 1.2 kre 379 1.2 kre config_mkdirs() 380 1.2 kre { 381 1.2 kre for f; do 382 1.2 kre [ -n "$f" ] || continue 383 1.2 kre d="$(dirname "$f")" 384 1.2 kre if [ ! -d "$d" ]; then 385 1.9 roy mkdir -p "$d" || return $? 386 1.2 kre fi 387 1.2 kre done 388 1.9 roy return 0 389 1.2 kre } 390 1.2 kre 391 1.2 kre # With the advent of alternative init systems, it's possible to have 392 1.2 kre # more than one installed. So we need to try and guess what one we're 393 1.10 roy # using unless overridden by configure. 394 1.2 kre # Note that restarting a service is a last resort - the subscribers 395 1.10 roy # should make a reasonable attempt to reconfigure the service via some 396 1.2 kre # method, normally SIGHUP. 397 1.2 kre detect_init() 398 1.2 kre { 399 1.2 kre [ -n "$RESTARTCMD" ] && return 0 400 1.2 kre 401 1.2 kre # Detect the running init system. 402 1.2 kre # As systemd and OpenRC can be installed on top of legacy init 403 1.2 kre # systems we try to detect them first. 404 1.5 roy status="@STATUSARG@" 405 1.2 kre : ${status:=status} 406 1.2 kre if [ -x /bin/systemctl ] && [ -S /run/systemd/private ]; then 407 1.2 kre RESTARTCMD=' 408 1.2 kre if /bin/systemctl --quiet is-active $1.service 409 1.2 kre then 410 1.2 kre /bin/systemctl restart $1.service 411 1.2 kre fi' 412 1.2 kre elif [ -x /usr/bin/systemctl ] && [ -S /run/systemd/private ]; then 413 1.2 kre RESTARTCMD=' 414 1.2 kre if /usr/bin/systemctl --quiet is-active $1.service 415 1.2 kre then 416 1.2 kre /usr/bin/systemctl restart $1.service 417 1.2 kre fi' 418 1.5 roy elif [ -x /sbin/rc-service ] && 419 1.5 roy { [ -s /libexec/rc/init.d/softlevel ] || 420 1.5 roy [ -s /run/openrc/softlevel ]; } 421 1.2 kre then 422 1.2 kre RESTARTCMD='/sbin/rc-service -i $1 -- -Ds restart' 423 1.2 kre elif [ -x /usr/sbin/invoke-rc.d ]; then 424 1.2 kre RCDIR=/etc/init.d 425 1.2 kre RESTARTCMD=' 426 1.2 kre if /usr/sbin/invoke-rc.d --quiet $1 status >/dev/null 2>&1 427 1.2 kre then 428 1.2 kre /usr/sbin/invoke-rc.d $1 restart 429 1.2 kre fi' 430 1.10 roy elif [ -x /usr/bin/s6-rc ] && [ -x /usr/bin/s6-svc ]; then 431 1.10 roy RESTARTCMD=' 432 1.11 roy if s6-rc -a list 2>/dev/null | grep -qFx $1-srv 433 1.10 roy then 434 1.10 roy s6-svc -r /run/service/$1-srv 435 1.10 roy fi' 436 1.2 kre elif [ -x /sbin/service ]; then 437 1.2 kre # Old RedHat 438 1.2 kre RCDIR=/etc/init.d 439 1.2 kre RESTARTCMD=' 440 1.2 kre if /sbin/service $1; then 441 1.2 kre /sbin/service $1 restart 442 1.2 kre fi' 443 1.2 kre elif [ -x /usr/sbin/service ]; then 444 1.2 kre # Could be FreeBSD 445 1.4 kre RESTARTCMD=" 446 1.4 kre if /usr/sbin/service \$1 $status >/dev/null 2>&1 447 1.2 kre then 448 1.4 kre /usr/sbin/service \$1 restart 449 1.4 kre fi" 450 1.2 kre elif [ -x /bin/sv ]; then 451 1.2 kre RESTARTCMD='/bin/sv status $1 >/dev/null 2>&1 && 452 1.2 kre /bin/sv try-restart $1' 453 1.2 kre elif [ -x /usr/bin/sv ]; then 454 1.2 kre RESTARTCMD='/usr/bin/sv status $1 >/dev/null 2>&1 && 455 1.2 kre /usr/bin/sv try-restart $1' 456 1.2 kre elif [ -e /etc/arch-release ] && [ -d /etc/rc.d ]; then 457 1.2 kre RCDIR=/etc/rc.d 458 1.2 kre RESTARTCMD=' 459 1.2 kre if [ -e /var/run/daemons/$1 ] 460 1.2 kre then 461 1.2 kre /etc/rc.d/$1 restart 462 1.2 kre fi' 463 1.2 kre elif [ -e /etc/slackware-version ] && [ -d /etc/rc.d ]; then 464 1.2 kre RESTARTCMD=' 465 1.2 kre if /etc/rc.d/rc.$1 status >/dev/null 2>&1 466 1.2 kre then 467 1.2 kre /etc/rc.d/rc.$1 restart 468 1.2 kre fi' 469 1.2 kre elif [ -e /etc/rc.d/rc.subr ] && [ -d /etc/rc.d ]; then 470 1.2 kre # OpenBSD 471 1.2 kre RESTARTCMD=' 472 1.2 kre if /etc/rc.d/$1 check >/dev/null 2>&1 473 1.2 kre then 474 1.2 kre /etc/rc.d/$1 restart 475 1.2 kre fi' 476 1.10 roy elif [ -d /etc/dinit.d ] && command -v dinitctl >/dev/null 2>&1; then 477 1.10 roy RESTARTCMD='dinitctl --quiet restart --ignore-unstarted $1' 478 1.2 kre else 479 1.2 kre for x in /etc/init.d/rc.d /etc/rc.d /etc/init.d; do 480 1.2 kre [ -d $x ] || continue 481 1.2 kre RESTARTCMD=" 482 1.2 kre if $x/\$1 $status >/dev/null 2>&1 483 1.2 kre then 484 1.2 kre $x/\$1 restart 485 1.2 kre fi" 486 1.2 kre break 487 1.2 kre done 488 1.2 kre fi 489 1.2 kre 490 1.2 kre if [ -z "$RESTARTCMD" ]; then 491 1.2 kre if [ "$_NOINIT_WARNED" != true ]; then 492 1.2 kre warn "could not detect a useable init system" 493 1.2 kre _NOINIT_WARNED=true 494 1.2 kre fi 495 1.2 kre return 1 496 1.2 kre fi 497 1.2 kre _NOINIT_WARNED= 498 1.2 kre return 0 499 1.2 kre } 500 1.2 kre 501 1.2 kre echo_resolv() 502 1.2 kre { 503 1.5 roy OIFS="$IFS" 504 1.2 kre 505 1.12 roy [ -n "$1" ] && [ -f "$KEYDIR/$1" ] || return 1 506 1.2 kre echo "# resolv.conf from $1" 507 1.12 roy # Our variable maker works of the fact each resolv.conf per key 508 1.2 kre # is separated by blank lines. 509 1.2 kre # So we remove them when echoing them. 510 1.2 kre while read -r line; do 511 1.2 kre IFS="$OIFS" 512 1.2 kre if [ -n "$line" ]; then 513 1.2 kre # We need to set IFS here to preserve any whitespace 514 1.2 kre IFS='' 515 1.2 kre printf "%s\n" "$line" 516 1.2 kre fi 517 1.12 roy done < "$KEYDIR/$1" 518 1.2 kre IFS="$OIFS" 519 1.2 kre } 520 1.2 kre 521 1.12 roy deprecated_key() 522 1.9 roy { 523 1.9 roy [ -d "$DEPRECATEDDIR" ] || return 1 524 1.9 roy 525 1.9 roy cd "$DEPRECATEDDIR" 526 1.9 roy for da; do 527 1.9 roy for daf in *; do 528 1.9 roy [ -f "$daf" ] || continue 529 1.9 roy case "$da" in 530 1.9 roy $daf) return 0;; 531 1.9 roy esac 532 1.9 roy done 533 1.9 roy done 534 1.9 roy return 1 535 1.9 roy } 536 1.9 roy 537 1.11 roy match() 538 1.11 roy { 539 1.11 roy match="$1" 540 1.11 roy file="$2" 541 1.11 roy retval=1 542 1.11 roy count=0 543 1.11 roy 544 1.11 roy while read -r keyword value; do 545 1.11 roy new_match= 546 1.11 roy for om in $match; do 547 1.11 roy m="$om" 548 1.11 roy keep= 549 1.11 roy while [ -n "$m" ]; do 550 1.11 roy k="${m%%/*}" 551 1.11 roy r="${m#*/}" 552 1.11 roy f="${r%%/*}" 553 1.11 roy r="${r#*/}" 554 1.11 roy # If the length of m is the same as k/f then 555 1.11 roy # we know that we are done 556 1.11 roy if [ ${#m} = $((${#k} + 1 + ${#f})) ]; then 557 1.11 roy r= 558 1.11 roy fi 559 1.11 roy m="$r" 560 1.11 roy matched=false 561 1.11 roy case "$keyword" in 562 1.11 roy $k) 563 1.11 roy case "$value" in 564 1.11 roy $f) 565 1.11 roy matched=true 566 1.11 roy ;; 567 1.11 roy esac 568 1.11 roy ;; 569 1.11 roy esac 570 1.11 roy if ! $matched; then 571 1.11 roy keep="$keep${keep:+/}$k/$f" 572 1.11 roy fi 573 1.11 roy done 574 1.11 roy if [ -n "$om" ] && [ -z "$keep" ]; then 575 1.11 roy retval=0 576 1.11 roy break 2 577 1.11 roy fi 578 1.11 roy new_match="${new_match}${new_match:+ }${keep}" 579 1.11 roy done 580 1.11 roy match="${new_match}" 581 1.11 roy done < "$file" 582 1.11 roy return $retval 583 1.11 roy } 584 1.11 roy 585 1.12 roy list_keys() { 586 1.12 roy list_cmd="$1" 587 1.12 roy shift 588 1.11 roy 589 1.12 roy [ -d "$KEYDIR" ] || return 0 590 1.12 roy cd "$KEYDIR" 591 1.10 roy 592 1.12 roy [ -n "$1" ] || set -- "*" 593 1.5 roy list= 594 1.5 roy retval=0 595 1.12 roy if [ "$list_cmd" = -i ] || [ "$list_cmd" = -l ]; then 596 1.12 roy for i in $@; do 597 1.12 roy if [ ! -f "$i" ]; then 598 1.15 roy if ! $force && [ "$i" != "*" ]; then 599 1.12 roy echo "No resolv.conf for key $i" >&2 600 1.12 roy fi 601 1.12 roy retval=2 602 1.12 roy continue 603 1.12 roy fi 604 1.12 roy list="$list $i" 605 1.12 roy done 606 1.12 roy [ -z "$list" ] || uniqify $list 607 1.12 roy return $retval 608 1.12 roy fi 609 1.1 roy 610 1.12 roy if [ "$list_cmd" != -I ] && [ "$list_cmd" != -L ]; then 611 1.12 roy echo "list_keys: unknown command $list_cmd" >&2 612 1.12 roy return 1 613 1.12 roy fi 614 1.12 roy 615 1.12 roy if [ -d "$EXCLUSIVEDIR" ]; then 616 1.12 roy cd "$EXCLUSIVEDIR" 617 1.12 roy for i in $EXCLUSIVEDIR/*; do 618 1.12 roy if [ -f "$i" ]; then 619 1.12 roy cd "$KEYDIR" 620 1.12 roy for ii in $inclusive_keys; do 621 1.13 roy if [ -f "$ii" ] && [ "${i#* }" = "$ii" ]; then 622 1.12 roy continue 2 623 1.12 roy fi 624 1.12 roy done 625 1.12 roy list="${i#* }" 626 1.12 roy break 627 1.12 roy fi 628 1.12 roy done 629 1.12 roy cd "$KEYDIR" 630 1.12 roy if [ -n "$list" ]; then 631 1.12 roy for i in $@; do 632 1.12 roy # list will be one item due to the above 633 1.12 roy if [ -f "$i" ] && [ "$i" = "$list" ]; then 634 1.12 roy echo "$i" 635 1.12 roy return 0 636 1.2 kre fi 637 1.2 kre done 638 1.12 roy return 0 639 1.2 kre fi 640 1.12 roy fi 641 1.12 roy 642 1.12 roy for i in $key_order; do 643 1.12 roy for ii in "$i" "$i":* "$i".*; do 644 1.12 roy [ -f "$ii" ] && list="$list $ii" 645 1.12 roy done 646 1.12 roy done 647 1.12 roy 648 1.12 roy for i in $dynamic_order; do 649 1.12 roy for ii in "$i" "$i":* "$i".*; do 650 1.12 roy if [ -f "$ii" ] && ! [ -e "$METRICDIR/"*" $ii" ] 651 1.12 roy then 652 1.12 roy list="$list $ii" 653 1.2 kre fi 654 1.2 kre done 655 1.12 roy done 656 1.12 roy 657 1.12 roy # Interfaces have an implicit metric of 0 if not specified. 658 1.12 roy for i in *; do 659 1.12 roy if [ -f "$i" ] && ! [ -e "$METRICDIR/"*" $i" ]; then 660 1.12 roy list="$list $i" 661 1.12 roy fi 662 1.12 roy done 663 1.2 kre 664 1.12 roy if [ -d "$METRICDIR" ]; then 665 1.12 roy cd "$METRICDIR" 666 1.12 roy for i in *; do 667 1.12 roy [ -f "$i" ] && list="$list ${i#* }" 668 1.1 roy done 669 1.12 roy cd "$KEYDIR" 670 1.12 roy fi 671 1.9 roy 672 1.12 roy # Move deprecated keys to the back 673 1.12 roy active= 674 1.12 roy deprecated= 675 1.12 roy for i in $list; do 676 1.12 roy if deprecated_key "$i"; then 677 1.12 roy deprecated="$deprecated $i" 678 1.12 roy else 679 1.12 roy active="$active $i" 680 1.12 roy fi 681 1.12 roy done 682 1.12 roy list="$active $deprecated" 683 1.12 roy 684 1.12 roy retval=0 685 1.12 roy if [ "$1" != "*" ]; then 686 1.12 roy cd "$KEYDIR" 687 1.12 roy matched= 688 1.12 roy for i in $@; do 689 1.12 roy if ! [ -f "$i" ]; then 690 1.12 roy if ! $force; then 691 1.12 roy echo "No resolv.conf for key $i" >&2 692 1.12 roy fi 693 1.12 roy retval=2 694 1.12 roy continue 695 1.1 roy fi 696 1.12 roy for ii in $list; do 697 1.12 roy if [ "$i" = "$ii" ]; then 698 1.12 roy matched="$matched${matched:+ }$i" 699 1.12 roy break 700 1.2 kre fi 701 1.2 kre done 702 1.1 roy done 703 1.12 roy if [ -z "$matched" ]; then 704 1.12 roy return $retval 705 1.12 roy fi 706 1.12 roy list="$matched" 707 1.12 roy fi 708 1.9 roy 709 1.12 roy allowed= 710 1.12 roy for i in $(uniqify $list); do 711 1.12 roy if [ -n "$allow_keys" ]; then 712 1.12 roy x=false 713 1.12 roy for ii in $allow_keys; do 714 1.12 roy if [ "$i" = "$ii" ]; then 715 1.12 roy x=true 716 1.12 roy break 717 1.12 roy fi 718 1.12 roy done 719 1.12 roy $x || continue 720 1.12 roy fi 721 1.12 roy for ii in $deny_keys; do 722 1.12 roy if [ "$i" = "$ii" ]; then 723 1.12 roy continue 2 724 1.5 roy fi 725 1.5 roy done 726 1.9 roy 727 1.12 roy if [ -n "$exclude" ] && match "$exclude" "$i"; then 728 1.12 roy continue 729 1.1 roy fi 730 1.12 roy allowed="$allowed${allowed:+ }$i" 731 1.12 roy done 732 1.9 roy 733 1.12 roy cd "$KEYDIR" 734 1.12 roy for i in $exclusive_keys; do 735 1.12 roy for ii in $allowed; do 736 1.12 roy if [ "$i" = "$ii" ]; then 737 1.12 roy echo "$i" 738 1.12 roy return 739 1.9 roy fi 740 1.9 roy done 741 1.12 roy done 742 1.12 roy [ -z "$allowed" ] || echo "$allowed" 743 1.12 roy } 744 1.12 roy 745 1.12 roy list_resolv() 746 1.12 roy { 747 1.12 roy keys="$(list_keys "$@")" 748 1.12 roy retval=$? 749 1.12 roy if [ "$retval" != 0 ]; then 750 1.12 roy return $retval 751 1.1 roy fi 752 1.12 roy for i in $keys; do 753 1.12 roy echo_resolv "$i" && echo 754 1.12 roy done 755 1.12 roy } 756 1.1 roy 757 1.12 roy list_private() 758 1.12 roy { 759 1.12 roy KEYS= 760 1.12 roy cd "$KEYDIR" 761 1.12 roy if [ -z "$1" ]; then 762 1.12 roy set -- "*" 763 1.12 roy fi 764 1.12 roy for i in $@; do 765 1.12 roy if private_key "$i"; then 766 1.12 roy KEYS="${KEYS}${KEYS:+ }$i" 767 1.12 roy fi 768 1.12 roy done 769 1.12 roy if [ -n "$KEYS" ]; then 770 1.12 roy echo "$KEYS" 771 1.10 roy fi 772 1.12 roy } 773 1.7 roy 774 1.12 roy list_nosearch() 775 1.12 roy { 776 1.11 roy 777 1.12 roy KEYS= 778 1.12 roy cd "$KEYDIR" 779 1.12 roy if [ -z "$1" ]; then 780 1.12 roy set -- "*" 781 1.12 roy fi 782 1.12 roy for i in $@; do 783 1.12 roy if nosearch_key "$i"; then 784 1.12 roy KEYS="${KEYS}${KEYS:+ }$i" 785 1.7 roy fi 786 1.12 roy done 787 1.12 roy if [ -n "$KEYS" ]; then 788 1.12 roy echo "$KEYS" 789 1.12 roy fi 790 1.12 roy } 791 1.10 roy 792 1.12 roy list_exclusive() 793 1.12 roy { 794 1.12 roy KEYS= 795 1.12 roy cd "$KEYDIR" 796 1.12 roy if [ -z "$1" ]; then 797 1.12 roy set -- "*" 798 1.12 roy fi 799 1.12 roy for i in $@; do 800 1.12 roy if exclusive_key "$i"; then 801 1.12 roy KEYS="${KEYS}${KEYS:+ }$i" 802 1.2 kre fi 803 1.2 kre done 804 1.12 roy if [ -n "$KEYS" ]; then 805 1.12 roy echo "$KEYS" 806 1.12 roy fi 807 1.2 kre } 808 1.2 kre 809 1.5 roy list_remove() 810 1.5 roy { 811 1.2 kre [ -z "$2" ] && return 0 812 1.2 kre eval list=\"\$$1\" 813 1.2 kre shift 814 1.5 roy result= 815 1.5 roy retval=0 816 1.2 kre 817 1.2 kre set -f 818 1.2 kre for e; do 819 1.2 kre found=false 820 1.2 kre for l in $list; do 821 1.2 kre case "$e" in 822 1.2 kre $l) found=true;; 823 1.2 kre esac 824 1.2 kre $found && break 825 1.2 kre done 826 1.2 kre if $found; then 827 1.2 kre retval=$(($retval + 1)) 828 1.1 roy else 829 1.2 kre result="$result $e" 830 1.1 roy fi 831 1.1 roy done 832 1.2 kre set +f 833 1.2 kre echo "${result# *}" 834 1.1 roy return $retval 835 1.1 roy } 836 1.1 roy 837 1.2 kre echo_prepend() 838 1.2 kre { 839 1.2 kre echo "# Generated by resolvconf" 840 1.2 kre if [ -n "$search_domains" ]; then 841 1.2 kre echo "search $search_domains" 842 1.2 kre fi 843 1.2 kre for n in $name_servers; do 844 1.2 kre echo "nameserver $n" 845 1.2 kre done 846 1.2 kre echo 847 1.2 kre } 848 1.2 kre 849 1.2 kre echo_append() 850 1.2 kre { 851 1.2 kre echo "# Generated by resolvconf" 852 1.2 kre if [ -n "$search_domains_append" ]; then 853 1.2 kre echo "search $search_domains_append" 854 1.2 kre fi 855 1.2 kre for n in $name_servers_append; do 856 1.2 kre echo "nameserver $n" 857 1.2 kre done 858 1.2 kre echo 859 1.2 kre } 860 1.2 kre 861 1.12 roy tolower() { 862 1.12 roy # There is no good way of doing this portably in shell :( 863 1.12 roy # Luckily we are only doing this for domain names which we 864 1.12 roy # know have to be ASCII. 865 1.12 roy # Non ASCII domains *should* be translated to ASCII *before* 866 1.12 roy # we get to this stage. 867 1.12 roy # We could use echo "$@" | tr '[:upper:]' '[:lower:]' but 868 1.12 roy # tr is in /usr/bin and may not be available when data is fed 869 1.12 roy # to resolvconf. 870 1.12 roy # So it's the cost of a pipe + fork vs this slow loop 871 1.12 roy # 872 1.12 roy for word; do 873 1.12 roy # Check if we have any upper to avoid looping per char 874 1.12 roy case "$word" in 875 1.12 roy *[A-Z]*) ;; 876 1.12 roy *) printf "%s " "$word"; continue;; 877 1.12 roy esac 878 1.12 roy 879 1.12 roy while [ -n "$word" ]; do 880 1.12 roy # Remove everything except the first character 881 1.12 roy afterchar="${word#?}" 882 1.12 roy # Remove the afterchar to get the first character 883 1.12 roy char="${word%%$afterchar}" 884 1.12 roy # Assign afterchar back to word for looping 885 1.12 roy word="$afterchar" 886 1.12 roy 887 1.12 roy # Now enforce lowercase a-z 888 1.12 roy case "$char" in 889 1.12 roy A) char=a;; 890 1.12 roy B) char=b;; 891 1.12 roy C) char=c;; 892 1.12 roy D) char=d;; 893 1.12 roy E) char=e;; 894 1.12 roy F) char=f;; 895 1.12 roy G) char=g;; 896 1.12 roy H) char=h;; 897 1.12 roy I) char=i;; 898 1.12 roy J) char=j;; 899 1.12 roy K) char=k;; 900 1.12 roy L) char=l;; 901 1.12 roy M) char=m;; 902 1.12 roy N) char=n;; 903 1.12 roy O) char=o;; 904 1.12 roy P) char=p;; 905 1.12 roy Q) char=q;; 906 1.12 roy R) char=r;; 907 1.12 roy S) char=s;; 908 1.12 roy T) char=t;; 909 1.12 roy U) char=u;; 910 1.12 roy V) char=v;; 911 1.12 roy W) char=w;; 912 1.12 roy X) char=x;; 913 1.12 roy Y) char=y;; 914 1.12 roy Z) char=z;; 915 1.12 roy esac 916 1.12 roy printf %s "$char" 917 1.12 roy done 918 1.12 roy printf " " 919 1.12 roy done 920 1.12 roy printf "\n" 921 1.12 roy } 922 1.12 roy 923 1.12 roy # Strip any trailing dot from each name as a FQDN does not belong 924 1.12 roy # in resolv.conf(5). 925 1.12 roy # While DNS is not case sensitive, our labels for building the zones 926 1.12 roy # are, so ensure it's lower case. 927 1.12 roy process_domain() 928 1.12 roy { 929 1.12 roy for word in $(tolower "$@"); do 930 1.12 roy printf "%s " "${word%.}" 931 1.12 roy done 932 1.12 roy printf "\n" 933 1.12 roy } 934 1.12 roy 935 1.12 roy process_resolv() 936 1.2 kre { 937 1.2 kre while read -r keyword value; do 938 1.2 kre for r in $replace; do 939 1.2 kre k="${r%%/*}" 940 1.2 kre r="${r#*/}" 941 1.2 kre f="${r%%/*}" 942 1.2 kre r="${r#*/}" 943 1.2 kre v="${r%%/*}" 944 1.2 kre case "$keyword" in 945 1.2 kre $k) 946 1.2 kre case "$value" in 947 1.2 kre $f) value="$v";; 948 1.2 kre esac 949 1.2 kre ;; 950 1.2 kre esac 951 1.2 kre done 952 1.2 kre val= 953 1.2 kre for sub in $value; do 954 1.2 kre for r in $replace_sub; do 955 1.2 kre k="${r%%/*}" 956 1.2 kre r="${r#*/}" 957 1.2 kre f="${r%%/*}" 958 1.2 kre r="${r#*/}" 959 1.2 kre v="${r%%/*}" 960 1.2 kre case "$keyword" in 961 1.2 kre $k) 962 1.2 kre case "$sub" in 963 1.2 kre $f) sub="$v";; 964 1.2 kre esac 965 1.2 kre ;; 966 1.2 kre esac 967 1.2 kre done 968 1.2 kre val="$val${val:+ }$sub" 969 1.2 kre done 970 1.12 roy case "$keyword" in 971 1.12 roy \#) 972 1.12 roy case "$val" in 973 1.12 roy "resolv.conf from "*) ;; 974 1.12 roy *) continue;; 975 1.12 roy esac 976 1.12 roy ;; 977 1.12 roy \#*) continue;; 978 1.12 roy esac 979 1.12 roy case "$keyword" in 980 1.12 roy domain|search) val="$(process_domain $val)";; 981 1.12 roy esac 982 1.2 kre printf "%s %s\n" "$keyword" "$val" 983 1.2 kre done 984 1.2 kre } 985 1.2 kre 986 1.1 roy make_vars() 987 1.1 roy { 988 1.2 kre # Clear variables 989 1.2 kre DOMAIN= 990 1.2 kre DOMAINS= 991 1.2 kre SEARCH= 992 1.2 kre NAMESERVERS= 993 1.2 kre LOCALNAMESERVERS= 994 1.7 roy 995 1.2 kre if [ -n "${name_servers}${search_domains}" ]; then 996 1.2 kre eval "$(echo_prepend | parse_resolv)" 997 1.2 kre fi 998 1.2 kre if [ -z "$VFLAG" ]; then 999 1.12 roy eval "$(list_resolv -L "$@" | process_resolv | parse_resolv)" 1000 1.2 kre fi 1001 1.3 ryoon if [ -n "${name_servers_append}${search_domains_append}" ]; then 1002 1.2 kre eval "$(echo_append | parse_resolv)" 1003 1.2 kre fi 1004 1.1 roy 1005 1.1 roy # Ensure that we only list each domain once 1006 1.5 roy newdomains= 1007 1.1 roy for d in $DOMAINS; do 1008 1.1 roy dn="${d%%:*}" 1009 1.2 kre list_remove domain_blacklist "$dn" >/dev/null || continue 1010 1.1 roy case " $newdomains" in 1011 1.1 roy *" ${dn}:"*) continue;; 1012 1.1 roy esac 1013 1.1 roy newns= 1014 1.1 roy for nd in $DOMAINS; do 1015 1.1 roy if [ "$dn" = "${nd%%:*}" ]; then 1016 1.1 roy ns="${nd#*:}" 1017 1.1 roy while [ -n "$ns" ]; do 1018 1.1 roy case ",$newns," in 1019 1.1 roy *,${ns%%,*},*) ;; 1020 1.2 kre *) list_remove name_server_blacklist \ 1021 1.2 kre "${ns%%,*}" >/dev/null \ 1022 1.2 kre && newns="$newns${newns:+,}${ns%%,*}";; 1023 1.1 roy esac 1024 1.1 roy [ "$ns" = "${ns#*,}" ] && break 1025 1.1 roy ns="${ns#*,}" 1026 1.1 roy done 1027 1.1 roy fi 1028 1.1 roy done 1029 1.2 kre if [ -n "$newns" ]; then 1030 1.2 kre newdomains="$newdomains${newdomains:+ }$dn:$newns" 1031 1.2 kre fi 1032 1.1 roy done 1033 1.16 roy 1034 1.2 kre DOMAIN="$(list_remove domain_blacklist $DOMAIN)" 1035 1.2 kre SEARCH="$(uniqify $SEARCH)" 1036 1.2 kre SEARCH="$(list_remove domain_blacklist $SEARCH)" 1037 1.2 kre NAMESERVERS="$(uniqify $NAMESERVERS)" 1038 1.2 kre NAMESERVERS="$(list_remove name_server_blacklist $NAMESERVERS)" 1039 1.2 kre LOCALNAMESERVERS="$(uniqify $LOCALNAMESERVERS)" 1040 1.2 kre LOCALNAMESERVERS="$(list_remove name_server_blacklist $LOCALNAMESERVERS)" 1041 1.16 roy 1042 1.16 roy # Ensure output is quoted for eval 1043 1.17 roy printf 'DOMAIN=%s\n' "$(quote "$DOMAIN")" 1044 1.17 roy printf 'SEARCH=%s\n' "$(quote "$SEARCH")" 1045 1.17 roy printf 'NAMESERVERS=%s\n' "$(quote "$NAMESERVERS")" 1046 1.17 roy printf 'LOCALNAMESERVERS=%s\n' "$(quote "$LOCALNAMESERVERS")" 1047 1.17 roy printf 'DOMAINS=%s\n' "$(quote "$newdomains")" 1048 1.1 roy } 1049 1.1 roy 1050 1.1 roy force=false 1051 1.12 roy LFLAG= 1052 1.2 kre VFLAG= 1053 1.11 roy while getopts a:C:c:Dd:fhIiLlm:pRruvVx OPT; do 1054 1.1 roy case "$OPT" in 1055 1.1 roy f) force=true;; 1056 1.1 roy h) usage;; 1057 1.1 roy m) IF_METRIC="$OPTARG";; 1058 1.12 roy p) 1059 1.12 roy if [ "$IF_PRIVATE" = 1 ]; then 1060 1.12 roy IF_NOSEARCH=1 1061 1.12 roy else 1062 1.12 roy IF_PRIVATE=1 1063 1.12 roy fi 1064 1.12 roy ;; 1065 1.2 kre V) 1066 1.2 kre VFLAG=1 1067 1.2 kre if [ "$local_nameservers" = \ 1068 1.2 kre "127.* 0.0.0.0 255.255.255.255 ::1" ] 1069 1.2 kre then 1070 1.2 kre local_nameservers= 1071 1.2 kre fi 1072 1.2 kre ;; 1073 1.2 kre x) IF_EXCLUSIVE=1;; 1074 1.9 roy '?') exit 1;; 1075 1.12 roy *) 1076 1.12 roy [ "$OPT" != L ] || LFLAG=1 1077 1.12 roy cmd="$OPT"; key="$OPTARG";; 1078 1.1 roy esac 1079 1.1 roy done 1080 1.1 roy shift $(($OPTIND - 1)) 1081 1.12 roy if [ -n "$key" ]; then 1082 1.12 roy set -- "$key" "$@" 1083 1.12 roy fi 1084 1.12 roy 1085 1.12 roy if [ -z "$cmd" ]; then 1086 1.19 roy if [ "$IF_PRIVATE" = 1 ]; then 1087 1.12 roy cmd=p 1088 1.12 roy elif [ "$IF_EXCLUSIVE" = 1 ]; then 1089 1.12 roy cmd=x 1090 1.12 roy fi 1091 1.12 roy fi 1092 1.1 roy 1093 1.2 kre # -D ensures that the listed config file base dirs exist 1094 1.2 kre if [ "$cmd" = D ]; then 1095 1.2 kre config_mkdirs "$@" 1096 1.2 kre exit $? 1097 1.2 kre fi 1098 1.2 kre 1099 1.12 roy # -i lists which keys have a resolv file 1100 1.12 roy if [ "$cmd" = i ]; then 1101 1.12 roy # If the -L modifier is given, the list is post-processed 1102 1.12 roy if [ "$LFLAG" = 1 ]; then 1103 1.12 roy cmd="L" 1104 1.12 roy fi 1105 1.12 roy list_keys "-$cmd" "$@" 1106 1.12 roy exit $? 1107 1.12 roy fi 1108 1.12 roy 1109 1.12 roy # -l lists our resolv files, optionally for a specific key 1110 1.12 roy if [ "$cmd" = l ]; then 1111 1.12 roy list_resolv "-$cmd" "$@" 1112 1.1 roy exit $? 1113 1.1 roy fi 1114 1.11 roy # -L is the same as -l, but post-processed from our config 1115 1.11 roy if [ "$cmd" = L ]; then 1116 1.12 roy list_resolv "-$cmd" "$@" | process_resolv 1117 1.12 roy exit $? 1118 1.12 roy fi 1119 1.12 roy 1120 1.12 roy if [ "$cmd" = p ]; then 1121 1.12 roy if [ "$IF_NOSEARCH" = 1 ]; then 1122 1.12 roy list_nosearch "$@" 1123 1.12 roy else 1124 1.12 roy list_private "$@" 1125 1.12 roy fi 1126 1.12 roy exit $? 1127 1.12 roy fi 1128 1.12 roy 1129 1.12 roy if [ "$cmd" = x ]; then 1130 1.12 roy list_exclusive "$@" 1131 1.12 roy exit $? 1132 1.11 roy fi 1133 1.1 roy 1134 1.2 kre # Restart a service or echo the command to restart a service 1135 1.2 kre if [ "$cmd" = r ] || [ "$cmd" = R ]; then 1136 1.2 kre detect_init || exit 1 1137 1.2 kre if [ "$cmd" = r ]; then 1138 1.4 kre eval "$RESTARTCMD" 1139 1.2 kre else 1140 1.5 roy echo "$RESTARTCMD" | 1141 1.5 roy sed -e '/^$/d' -e 's/^ //g' 1142 1.2 kre fi 1143 1.2 kre exit $? 1144 1.2 kre fi 1145 1.2 kre 1146 1.1 roy # Not normally needed, but subscribers should be able to run independently 1147 1.2 kre if [ "$cmd" = v ] || [ -n "$VFLAG" ]; then 1148 1.12 roy make_vars "$@" 1149 1.1 roy exit $? 1150 1.1 roy fi 1151 1.1 roy 1152 1.1 roy # Test that we have valid options 1153 1.9 roy case "$cmd" in 1154 1.9 roy a|d|C|c) 1155 1.12 roy if [ -z "$key" ]; then 1156 1.12 roy error_exit "Key not specified" 1157 1.9 roy fi 1158 1.9 roy ;; 1159 1.15 roy I|u) ;; 1160 1.9 roy *) 1161 1.9 roy if [ -n "$cmd" ] && [ "$cmd" != h ]; then 1162 1.9 roy error_exit "Unknown option $cmd" 1163 1.1 roy fi 1164 1.1 roy usage 1165 1.9 roy ;; 1166 1.9 roy esac 1167 1.2 kre 1168 1.1 roy if [ "$cmd" = a ]; then 1169 1.19 roy for x in '/' '\' ' ' '*'; do 1170 1.19 roy case "$key" in 1171 1.19 roy "$x"|"$x"*|*"$x"|*"$x"*) error_exit "$x not allowed in key name";; 1172 1.1 roy esac 1173 1.1 roy done 1174 1.1 roy for x in '.' '-' '~'; do 1175 1.19 roy case "$key" in 1176 1.19 roy "$x"*) error_exit \ 1177 1.12 roy "$x not allowed at start of key name";; 1178 1.1 roy esac 1179 1.1 roy done 1180 1.2 kre [ "$cmd" = a ] && [ -t 0 ] && error_exit "No file given via stdin" 1181 1.1 roy fi 1182 1.1 roy 1183 1.2 kre if [ ! -d "$VARDIR" ]; then 1184 1.2 kre if [ -L "$VARDIR" ]; then 1185 1.2 kre dir="$(readlink "$VARDIR")" 1186 1.2 kre # link maybe relative 1187 1.2 kre cd "${VARDIR%/*}" 1188 1.2 kre if ! mkdir -m 0755 -p "$dir"; then 1189 1.2 kre error_exit "Failed to create needed" \ 1190 1.2 kre "directory $dir" 1191 1.2 kre fi 1192 1.2 kre else 1193 1.2 kre if ! mkdir -m 0755 -p "$VARDIR"; then 1194 1.2 kre error_exit "Failed to create needed" \ 1195 1.2 kre "directory $VARDIR" 1196 1.1 roy fi 1197 1.1 roy fi 1198 1.2 kre fi 1199 1.2 kre 1200 1.12 roy if [ ! -d "$KEYDIR" ]; then 1201 1.12 roy mkdir -m 0755 -p "$KEYDIR" || \ 1202 1.12 roy error_exit "Failed to create needed directory $KEYDIR" 1203 1.1 roy if [ "$cmd" = d ]; then 1204 1.2 kre # Provide the same error messages as below 1205 1.2 kre if ! ${force}; then 1206 1.12 roy cd "$KEYDIR" 1207 1.12 roy for i in $@; do 1208 1.12 roy warn "No resolv.conf for key $i" 1209 1.2 kre done 1210 1.2 kre fi 1211 1.2 kre ${force} 1212 1.2 kre exit $? 1213 1.1 roy fi 1214 1.1 roy fi 1215 1.1 roy 1216 1.12 roy # A key was added, changed, deleted or a general update was called. 1217 1.2 kre # Due to exclusivity we need to ensure that this is an atomic operation. 1218 1.2 kre # Our subscribers *may* need this as well if the init system is sub par. 1219 1.2 kre # As such we spinlock at this point as best we can. 1220 1.2 kre # We don't use flock(1) because it's not widely available and normally resides 1221 1.2 kre # in /usr which we do our very best to operate without. 1222 1.2 kre [ -w "$VARDIR" ] || error_exit "Cannot write to $LOCKDIR" 1223 1.2 kre : ${lock_timeout:=10} 1224 1.9 roy : ${clear_nopids:=5} 1225 1.9 roy have_pid=false 1226 1.9 roy had_pid=false 1227 1.2 kre while true; do 1228 1.2 kre if mkdir "$LOCKDIR" 2>/dev/null; then 1229 1.2 kre trap 'rm -rf "$LOCKDIR";' EXIT 1230 1.2 kre trap 'rm -rf "$LOCKDIR"; exit 1' INT QUIT ABRT SEGV ALRM TERM 1231 1.2 kre echo $$ >"$LOCKDIR/pid" 1232 1.2 kre break 1233 1.2 kre fi 1234 1.9 roy pid=$(cat "$LOCKDIR/pid" 2>/dev/null) 1235 1.9 roy if [ "$pid" -gt 0 ] 2>/dev/null; then 1236 1.9 roy have_pid=true 1237 1.9 roy had_pid=true 1238 1.9 roy else 1239 1.9 roy have_pid=false 1240 1.9 roy clear_nopids=$(($clear_nopids - 1)) 1241 1.9 roy if [ "$clear_nopids" -le 0 ]; then 1242 1.9 roy warn "not seen a pid, clearing lock directory" 1243 1.9 roy rm -rf "$LOCKDIR" 1244 1.9 roy else 1245 1.9 roy lock_timeout=$(($lock_timeout - 1)) 1246 1.9 roy sleep 1 1247 1.9 roy fi 1248 1.9 roy continue 1249 1.9 roy fi 1250 1.9 roy if $have_pid && ! kill -0 "$pid"; then 1251 1.2 kre warn "clearing stale lock pid $pid" 1252 1.2 kre rm -rf "$LOCKDIR" 1253 1.2 kre continue 1254 1.2 kre fi 1255 1.2 kre lock_timeout=$(($lock_timeout - 1)) 1256 1.2 kre if [ "$lock_timeout" -le 0 ]; then 1257 1.9 roy if $have_pid; then 1258 1.9 roy error_exit "timed out waiting for lock from pid $pid" 1259 1.9 roy else 1260 1.9 roy if $had_pid; then 1261 1.9 roy error_exit "timed out waiting for lock" \ 1262 1.9 roy "from some pids" 1263 1.9 roy else 1264 1.9 roy error_exit "timed out waiting for lock" 1265 1.9 roy fi 1266 1.9 roy fi 1267 1.2 kre fi 1268 1.2 kre sleep 1 1269 1.2 kre done 1270 1.9 roy unset have_pid had_pid clear_nopids 1271 1.2 kre 1272 1.2 kre case "$cmd" in 1273 1.2 kre a) 1274 1.1 roy # Read resolv.conf from stdin 1275 1.2 kre resolv="$(cat)" 1276 1.2 kre changed=false 1277 1.2 kre changedfile=false 1278 1.1 roy # If what we are given matches what we have, then do nothing 1279 1.12 roy if [ -e "$KEYDIR/$key" ]; then 1280 1.2 kre if [ "$(echo "$resolv")" != \ 1281 1.12 roy "$(cat "$KEYDIR/$key")" ] 1282 1.1 roy then 1283 1.2 kre changed=true 1284 1.2 kre changedfile=true 1285 1.1 roy fi 1286 1.2 kre else 1287 1.2 kre changed=true 1288 1.2 kre changedfile=true 1289 1.1 roy fi 1290 1.2 kre 1291 1.12 roy # Set metric and private before creating the resolv.conf file 1292 1.2 kre # to ensure that it will have the correct flags 1293 1.1 roy [ ! -d "$METRICDIR" ] && mkdir "$METRICDIR" 1294 1.12 roy oldmetric="$METRICDIR/"*" $key" 1295 1.2 kre newmetric= 1296 1.1 roy if [ -n "$IF_METRIC" ]; then 1297 1.1 roy # Pad metric to 6 characters, so 5 is less than 10 1298 1.1 roy while [ ${#IF_METRIC} -le 6 ]; do 1299 1.1 roy IF_METRIC="0$IF_METRIC" 1300 1.1 roy done 1301 1.12 roy newmetric="$METRICDIR/$IF_METRIC $key" 1302 1.1 roy fi 1303 1.12 roy rm -f "$METRICDIR/"*" $key" 1304 1.2 kre [ "$oldmetric" != "$newmetric" ] && 1305 1.12 roy [ "$oldmetric" != "$METRICDIR/* $key" ] && 1306 1.2 kre changed=true 1307 1.2 kre [ -n "$newmetric" ] && echo " " >"$newmetric" 1308 1.2 kre 1309 1.1 roy case "$IF_PRIVATE" in 1310 1.1 roy [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) 1311 1.1 roy if [ ! -d "$PRIVATEDIR" ]; then 1312 1.1 roy [ -e "$PRIVATEDIR" ] && rm "$PRIVATEDIR" 1313 1.1 roy mkdir "$PRIVATEDIR" 1314 1.1 roy fi 1315 1.12 roy [ -e "$PRIVATEDIR/$key" ] || changed=true 1316 1.12 roy [ -d "$PRIVATEDIR" ] && echo " " >"$PRIVATEDIR/$key" 1317 1.1 roy ;; 1318 1.1 roy *) 1319 1.12 roy if [ -e "$PRIVATEDIR/$key" ]; then 1320 1.12 roy rm -f "$PRIVATEDIR/$key" 1321 1.2 kre changed=true 1322 1.1 roy fi 1323 1.1 roy ;; 1324 1.1 roy esac 1325 1.2 kre 1326 1.12 roy case "$IF_NOSEARCH" in 1327 1.12 roy [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) 1328 1.12 roy if [ ! -d "$NOSEARCHDIR" ]; then 1329 1.12 roy [ -e "$NOSEARCHDIR" ] && rm "$NOSEARCHDIR" 1330 1.12 roy mkdir "$NOSEARCHDIR" 1331 1.12 roy fi 1332 1.12 roy [ -e "$NOSEARCHDIR/$key" ] || changed=true 1333 1.12 roy [ -d "$NOSEARCHDIR" ] && echo " " >"$NOSEARCHDIR/$key" 1334 1.12 roy ;; 1335 1.12 roy *) 1336 1.12 roy if [ -e "$NOSEARCHDIR/$key" ]; then 1337 1.12 roy rm -f "$NOSEARCHDIR/$key" 1338 1.12 roy changed=true 1339 1.12 roy fi 1340 1.12 roy ;; 1341 1.12 roy esac 1342 1.12 roy set +x 1343 1.12 roy 1344 1.2 kre oldexcl= 1345 1.12 roy for x in "$EXCLUSIVEDIR/"*" $key"; do 1346 1.2 kre if [ -f "$x" ]; then 1347 1.2 kre oldexcl="$x" 1348 1.2 kre break 1349 1.2 kre fi 1350 1.2 kre done 1351 1.2 kre case "$IF_EXCLUSIVE" in 1352 1.2 kre [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) 1353 1.2 kre if [ ! -d "$EXCLUSIVEDIR" ]; then 1354 1.2 kre [ -e "$EXCLUSIVEDIR" ] && rm "$EXCLUSIVEDIR" 1355 1.2 kre mkdir "$EXCLUSIVEDIR" 1356 1.2 kre fi 1357 1.2 kre cd "$EXCLUSIVEDIR" 1358 1.2 kre for x in *; do 1359 1.2 kre [ -f "$x" ] && break 1360 1.2 kre done 1361 1.12 roy if [ "${x#* }" != "$key" ]; then 1362 1.2 kre if [ "$x" = "${x% *}" ]; then 1363 1.2 kre x=10000000 1364 1.2 kre else 1365 1.2 kre x="${x% *}" 1366 1.2 kre fi 1367 1.2 kre if [ "$x" = "0000000" ]; then 1368 1.2 kre warn "exclusive underflow" 1369 1.2 kre else 1370 1.2 kre x=$(($x - 1)) 1371 1.2 kre fi 1372 1.2 kre if [ -d "$EXCLUSIVEDIR" ]; then 1373 1.12 roy echo " " >"$EXCLUSIVEDIR/$x $key" 1374 1.2 kre fi 1375 1.2 kre changed=true 1376 1.2 kre fi 1377 1.2 kre ;; 1378 1.2 kre *) 1379 1.2 kre if [ -f "$oldexcl" ]; then 1380 1.2 kre rm -f "$oldexcl" 1381 1.2 kre changed=true 1382 1.2 kre fi 1383 1.2 kre ;; 1384 1.2 kre esac 1385 1.2 kre 1386 1.2 kre if $changedfile; then 1387 1.12 roy printf "%s\n" "$resolv" >"$KEYDIR/$key" || exit $? 1388 1.15 roy elif ! $changed && [ ! -e "$VARDIR"/error ]; then 1389 1.2 kre exit 0 1390 1.2 kre fi 1391 1.2 kre unset changed changedfile oldmetric newmetric x oldexcl 1392 1.2 kre ;; 1393 1.2 kre 1394 1.2 kre d) 1395 1.12 roy # Delete any existing information about the key 1396 1.12 roy cd "$KEYDIR" 1397 1.2 kre changed=false 1398 1.12 roy for i in $@; do 1399 1.2 kre if [ -e "$i" ]; then 1400 1.2 kre changed=true 1401 1.2 kre elif ! ${force}; then 1402 1.12 roy warn "No resolv.conf for key $i" 1403 1.2 kre fi 1404 1.2 kre rm -f "$i" "$METRICDIR/"*" $i" \ 1405 1.2 kre "$PRIVATEDIR/$i" \ 1406 1.2 kre "$EXCLUSIVEDIR/"*" $i" || exit $? 1407 1.2 kre done 1408 1.9 roy 1409 1.15 roy if ! $changed && [ ! -e "$VARDIR"/error ]; then 1410 1.2 kre # Set the return code based on the forced flag 1411 1.9 roy $force 1412 1.2 kre exit $? 1413 1.2 kre fi 1414 1.2 kre unset changed i 1415 1.2 kre ;; 1416 1.9 roy 1417 1.9 roy C) 1418 1.12 roy # Mark key as deprecated 1419 1.9 roy [ ! -d "$DEPRECATEDDIR" ] && mkdir "$DEPRECATEDDIR" 1420 1.9 roy cd "$DEPRECATEDDIR" 1421 1.9 roy changed=false 1422 1.12 roy for i in $@; do 1423 1.9 roy if [ ! -e "$i" ]; then 1424 1.9 roy changed=true 1425 1.9 roy echo " " >"$i" || exit $? 1426 1.9 roy fi 1427 1.9 roy done 1428 1.15 roy if ! $changed && [ ! -e "$VARDIR"/error ]; then 1429 1.15 roy exit 0 1430 1.15 roy fi 1431 1.9 roy unset changed i 1432 1.9 roy ;; 1433 1.9 roy 1434 1.9 roy c) 1435 1.12 roy # Mark key as active 1436 1.9 roy if [ -d "$DEPRECATEDDIR" ]; then 1437 1.9 roy cd "$DEPRECATEDDIR" 1438 1.9 roy changed=false 1439 1.12 roy for i in $@; do 1440 1.9 roy if [ -e "$i" ]; then 1441 1.9 roy changed=true 1442 1.9 roy rm "$i" || exit $? 1443 1.9 roy fi 1444 1.9 roy done 1445 1.15 roy if ! $changed && [ ! -e "$VARDIR"/error ]; then 1446 1.15 roy exit 0 1447 1.15 roy fi 1448 1.9 roy unset changed i 1449 1.9 roy fi 1450 1.9 roy ;; 1451 1.15 roy I) 1452 1.15 roy # Init the state dir, keeping our lock and key directories only 1453 1.15 roy for i in "$VARDIR"/*; do 1454 1.15 roy case "$i" in 1455 1.15 roy "$LOCKDIR") ;; 1456 1.15 roy "$KEYDIR") rm -rf "$KEYDIR"/*;; 1457 1.15 roy *) rm -rf "$i";; 1458 1.15 roy esac 1459 1.15 roy done 1460 1.15 roy ;; 1461 1.2 kre esac 1462 1.2 kre 1463 1.2 kre case "${resolvconf:-YES}" in 1464 1.2 kre [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) ;; 1465 1.2 kre *) exit 0;; 1466 1.2 kre esac 1467 1.2 kre 1468 1.2 kre # Try and detect a suitable init system for our scripts 1469 1.2 kre detect_init 1470 1.2 kre export RESTARTCMD RCDIR _NOINIT_WARNED 1471 1.1 roy 1472 1.1 roy eval "$(make_vars)" 1473 1.2 kre export RESOLVCONF DOMAINS SEARCH NAMESERVERS LOCALNAMESERVERS 1474 1.11 roy : ${list_resolv:=list_resolv -L} 1475 1.1 roy retval=0 1476 1.2 kre 1477 1.2 kre # Run scripts in the same directory resolvconf is run from 1478 1.2 kre # in case any scripts accidentally dump files in the wrong place. 1479 1.2 kre cd "$_PWD" 1480 1.1 roy for script in "$LIBEXECDIR"/*; do 1481 1.2 kre if [ -f "$script" ]; then 1482 1.12 roy script_var="${script##*/}" 1483 1.12 roy while [ "${script_var%%-*}" != "$script_var" ]; do 1484 1.12 roy script_var="${script_var%%-*}_${script_var#*-}" 1485 1.12 roy done 1486 1.12 roy eval script_enabled="\$$script_var" 1487 1.2 kre case "${script_enabled:-YES}" in 1488 1.2 kre [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) ;; 1489 1.2 kre *) continue;; 1490 1.2 kre esac 1491 1.2 kre if [ -x "$script" ]; then 1492 1.12 roy "$script" "$cmd" "$key" 1493 1.2 kre else 1494 1.12 roy (set -- "$cmd" "$key"; . "$script") 1495 1.2 kre fi 1496 1.2 kre retval=$(($retval + $?)) 1497 1.2 kre fi 1498 1.1 roy done 1499 1.15 roy if [ "$retval" = 0 ]; then 1500 1.15 roy rm -f "$VARDIR"/error 1501 1.15 roy else 1502 1.15 roy echo "$retval" >"$VARDIR"/error 1503 1.15 roy fi 1504 1.1 roy exit $retval 1505