1#!/bin/sh 2# Copyright (c) 2007-2015 Roy Marples 3# All rights reserved 4 5# Redistribution and use in source and binary forms, with or without 6# modification, are permitted provided that the following conditions 7# are met: 8# * Redistributions of source code must retain the above copyright 9# notice, this list of conditions and the following disclaimer. 10# * Redistributions in binary form must reproduce the above 11# copyright notice, this list of conditions and the following 12# disclaimer in the documentation and/or other materials provided 13# with the distribution. 14# 15# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 18# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 19# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 21# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 27RESOLVCONF="$0" 28SYSCONFDIR=@SYSCONFDIR@ 29LIBEXECDIR=@LIBEXECDIR@ 30VARDIR=@VARDIR@ 31 32# Disregard dhcpcd setting 33unset interface_order state_dir 34 35# If you change this, change the test in VFLAG and libc.in as well 36local_nameservers="127.* 0.0.0.0 255.255.255.255 ::1" 37 38dynamic_order="tap[0-9]* tun[0-9]* vpn vpn[0-9]* ppp[0-9]* ippp[0-9]*" 39interface_order="lo lo[0-9]*" 40name_server_blacklist="0.0.0.0" 41 42# Support original resolvconf configuration layout 43# as well as the openresolv config file 44if [ -f "$SYSCONFDIR"/resolvconf.conf ]; then 45 . "$SYSCONFDIR"/resolvconf.conf 46 [ -n "$state_dir" ] && VARDIR="$state_dir" 47elif [ -d "$SYSCONFDIR/resolvconf" ]; then 48 SYSCONFDIR="$SYSCONFDIR/resolvconf" 49 if [ -f "$SYSCONFDIR"/interface-order ]; then 50 interface_order="$(cat "$SYSCONFDIR"/interface-order)" 51 fi 52fi 53IFACEDIR="$VARDIR/interfaces" 54METRICDIR="$VARDIR/metrics" 55PRIVATEDIR="$VARDIR/private" 56EXCLUSIVEDIR="$VARDIR/exclusive" 57LOCKDIR="$VARDIR/lock" 58 59warn() 60{ 61 echo "$*" >&2 62} 63 64error_exit() 65{ 66 echo "$*" >&2 67 exit 1 68} 69 70usage() 71{ 72 cat <<-EOF 73 Usage: ${RESOLVCONF##*/} [options] 74 75 Inform the system about any DNS updates. 76 77 Options: 78 -a \$INTERFACE Add DNS information to the specified interface 79 (DNS supplied via stdin in resolv.conf format) 80 -m metric Give the added DNS information a metric 81 -p Mark the interface as private 82 -x Mark the interface as exclusive 83 -d \$INTERFACE Delete DNS information from the specified interface 84 -f Ignore non existant interfaces 85 -I Init the state dir 86 -u Run updates from our current DNS information 87 -l [\$PATTERN] Show DNS information, optionally from interfaces 88 that match the specified pattern 89 -i [\$PATTERN] Show interfaces that have supplied DNS information 90 optionally from interfaces that match the specified 91 pattern 92 -v [\$PATTERN] echo NEWDOMAIN, NEWSEARCH and NEWNS variables to 93 the console 94 -h Show this help cruft 95 EOF 96 [ -z "$1" ] && exit 0 97 echo 98 error_exit "$*" 99} 100 101echo_resolv() 102{ 103 local line= OIFS="$IFS" 104 105 [ -n "$1" -a -f "$IFACEDIR/$1" ] || return 1 106 echo "# resolv.conf from $1" 107 # Our variable maker works of the fact each resolv.conf per interface 108 # is separated by blank lines. 109 # So we remove them when echoing them. 110 while read -r line; do 111 IFS="$OIFS" 112 if [ -n "$line" ]; then 113 # We need to set IFS here to preserve any whitespace 114 IFS='' 115 printf "%s\n" "$line" 116 fi 117 done < "$IFACEDIR/$1" 118 echo 119 IFS="$OIFS" 120} 121 122# Parse resolv.conf's and make variables 123# for domain name servers, search name servers and global nameservers 124parse_resolv() 125{ 126 local line= ns= ds= search= d= n= newns= 127 local new=true iface= private=false p= domain= l= islocal= 128 129 newns= 130 131 while read -r line; do 132 case "$line" in 133 "# resolv.conf from "*) 134 if ${new}; then 135 iface="${line#\# resolv.conf from *}" 136 new=false 137 if [ -e "$PRIVATEDIR/$iface" ]; then 138 private=true 139 else 140 # Allow expansion 141 cd "$IFACEDIR" 142 private=false 143 for p in $private_interfaces; do 144 case "$iface" in 145 "$p"|"$p":*) private=true; break;; 146 esac 147 done 148 fi 149 fi 150 ;; 151 "nameserver "*) 152 islocal=false 153 for l in $local_nameservers; do 154 case "${line#* }" in 155 $l) 156 islocal=true 157 echo "LOCALNAMESERVERS=\"\$LOCALNAMESERVERS ${line#* }\"" 158 break 159 ;; 160 esac 161 done 162 $islocal || ns="$ns${line#* } " 163 ;; 164 "domain "*) 165 if [ -z "$domain" ]; then 166 domain="${line#* }" 167 echo "DOMAIN=\"$domain\"" 168 fi 169 search="${line#* }" 170 ;; 171 "search "*) 172 search="${line#* }" 173 ;; 174 *) 175 [ -n "$line" ] && continue 176 if [ -n "$ns" -a -n "$search" ]; then 177 newns= 178 for n in $ns; do 179 newns="$newns${newns:+,}$n" 180 done 181 ds= 182 for d in $search; do 183 ds="$ds${ds:+ }$d:$newns" 184 done 185 echo "DOMAINS=\"\$DOMAINS $ds\"" 186 fi 187 echo "SEARCH=\"\$SEARCH $search\"" 188 if ! $private; then 189 echo "NAMESERVERS=\"\$NAMESERVERS $ns\"" 190 fi 191 ns= 192 search= 193 new=true 194 ;; 195 esac 196 done 197} 198 199uniqify() 200{ 201 local result= 202 while [ -n "$1" ]; do 203 case " $result " in 204 *" $1 "*);; 205 *) result="$result $1";; 206 esac 207 shift 208 done 209 echo "${result# *}" 210} 211 212dirname() 213{ 214 local dir= OIFS="$IFS" 215 local IFS=/ 216 set -- $@ 217 IFS="$OIFS" 218 if [ -n "$1" ]; then 219 printf %s . 220 else 221 shift 222 fi 223 while [ -n "$2" ]; do 224 printf "/%s" "$1" 225 shift 226 done 227 printf "\n" 228} 229 230config_mkdirs() 231{ 232 local e=0 f d 233 for f; do 234 [ -n "$f" ] || continue 235 d="$(dirname "$f")" 236 if [ ! -d "$d" ]; then 237 if type install >/dev/null 2>&1; then 238 install -d "$d" || e=$? 239 else 240 mkdir "$d" || e=$? 241 fi 242 fi 243 done 244 return $e 245} 246 247list_resolv() 248{ 249 [ -d "$IFACEDIR" ] || return 0 250 251 local report=false list= retval=0 cmd="$1" excl= 252 shift 253 254 case "$IF_EXCLUSIVE" in 255 [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) 256 if [ -d "$EXCLUSIVEDIR" ]; then 257 cd "$EXCLUSIVEDIR" 258 for i in *; do 259 if [ -f "$i" ]; then 260 list="${i#* }" 261 break 262 fi 263 done 264 fi 265 excl=true 266 ;; 267 *) 268 excl=false 269 ;; 270 esac 271 272 # If we have an interface ordering list, then use that. 273 # It works by just using pathname expansion in the interface directory. 274 if [ -n "$1" ]; then 275 list="$*" 276 $force || report=true 277 elif ! $excl; then 278 cd "$IFACEDIR" 279 for i in $interface_order; do 280 [ -f "$i" ] && list="$list $i" 281 for ii in "$i":* "$i".*; do 282 [ -f "$ii" ] && list="$list $ii" 283 done 284 done 285 for i in $dynamic_order; do 286 if [ -e "$i" -a ! -e "$METRICDIR/"*" $i" ]; then 287 list="$list $i" 288 fi 289 for ii in "$i":* "$i".*; do 290 if [ -f "$ii" -a ! -e "$METRICDIR/"*" $ii" ]; then 291 list="$list $ii" 292 fi 293 done 294 done 295 if [ -d "$METRICDIR" ]; then 296 cd "$METRICDIR" 297 for i in *; do 298 [ -f "$i" ] && list="$list ${i#* }" 299 done 300 fi 301 list="$list *" 302 fi 303 304 cd "$IFACEDIR" 305 retval=1 306 for i in $(uniqify $list); do 307 # Only list interfaces which we really have 308 if ! [ -f "$i" ]; then 309 if $report; then 310 echo "No resolv.conf for interface $i" >&2 311 retval=2 312 fi 313 continue 314 fi 315 316 if [ "$cmd" = i -o "$cmd" = "-i" ]; then 317 printf %s "$i " 318 else 319 echo_resolv "$i" 320 fi 321 [ $? = 0 -a "$retval" = 1 ] && retval=0 322 done 323 [ "$cmd" = i -o "$cmd" = "-i" ] && echo 324 return $retval 325} 326 327list_remove() { 328 local list= e= l= result= found= retval=0 329 330 [ -z "$2" ] && return 0 331 eval list=\"\$$1\" 332 shift 333 334 set -f 335 for e; do 336 found=false 337 for l in $list; do 338 case "$e" in 339 $l) found=true;; 340 esac 341 $found && break 342 done 343 if $found; then 344 retval=$(($retval + 1)) 345 else 346 result="$result $e" 347 fi 348 done 349 set +f 350 echo "${result# *}" 351 return $retval 352} 353 354echo_prepend() 355{ 356 echo "# Generated by resolvconf" 357 if [ -n "$search_domains" ]; then 358 echo "search $search_domains" 359 fi 360 for n in $name_servers; do 361 echo "nameserver $n" 362 done 363 echo 364} 365 366echo_append() 367{ 368 echo "# Generated by resolvconf" 369 if [ -n "$search_domains_append" ]; then 370 echo "search $search_domains_append" 371 fi 372 for n in $name_servers_append; do 373 echo "nameserver $n" 374 done 375 echo 376} 377 378replace() 379{ 380 local r= k= f= v= val= sub= 381 382 while read -r keyword value; do 383 for r in $replace; do 384 k="${r%%/*}" 385 r="${r#*/}" 386 f="${r%%/*}" 387 r="${r#*/}" 388 v="${r%%/*}" 389 case "$keyword" in 390 $k) 391 case "$value" in 392 $f) value="$v";; 393 esac 394 ;; 395 esac 396 done 397 val= 398 for sub in $value; do 399 for r in $replace_sub; do 400 k="${r%%/*}" 401 r="${r#*/}" 402 f="${r%%/*}" 403 r="${r#*/}" 404 v="${r%%/*}" 405 case "$keyword" in 406 $k) 407 case "$sub" in 408 $f) sub="$v";; 409 esac 410 ;; 411 esac 412 done 413 val="$val${val:+ }$sub" 414 done 415 printf "%s %s\n" "$keyword" "$val" 416 done 417} 418 419make_vars() 420{ 421 local newdomains= d= dn= newns= ns= 422 423 # Clear variables 424 DOMAIN= 425 DOMAINS= 426 SEARCH= 427 NAMESERVERS= 428 LOCALNAMESERVERS= 429 430 if [ -n "$name_servers" -o -n "$search_domains" ]; then 431 eval "$(echo_prepend | parse_resolv)" 432 fi 433 if [ -z "$VFLAG" ]; then 434 IF_EXCLUSIVE=1 435 list_resolv -i "$@" >/dev/null || IF_EXCLUSIVE=0 436 eval "$(list_resolv -l "$@" | replace | parse_resolv)" 437 fi 438 if [ -n "$name_servers_append" -o -n "$search_domains_append" ]; then 439 eval "$(echo_append | parse_resolv)" 440 fi 441 442 # Ensure that we only list each domain once 443 for d in $DOMAINS; do 444 dn="${d%%:*}" 445 list_remove domain_blacklist "$dn" >/dev/null || continue 446 case " $newdomains" in 447 *" ${dn}:"*) continue;; 448 esac 449 newns= 450 for nd in $DOMAINS; do 451 if [ "$dn" = "${nd%%:*}" ]; then 452 ns="${nd#*:}" 453 while [ -n "$ns" ]; do 454 case ",$newns," in 455 *,${ns%%,*},*) ;; 456 *) list_remove name_server_blacklist \ 457 "${ns%%,*}" >/dev/null \ 458 && newns="$newns${newns:+,}${ns%%,*}";; 459 esac 460 [ "$ns" = "${ns#*,}" ] && break 461 ns="${ns#*,}" 462 done 463 fi 464 done 465 if [ -n "$newns" ]; then 466 newdomains="$newdomains${newdomains:+ }$dn:$newns" 467 fi 468 done 469 DOMAIN="$(list_remove domain_blacklist $DOMAIN)" 470 SEARCH="$(uniqify $SEARCH)" 471 SEARCH="$(list_remove domain_blacklist $SEARCH)" 472 NAMESERVERS="$(uniqify $NAMESERVERS)" 473 NAMESERVERS="$(list_remove name_server_blacklist $NAMESERVERS)" 474 LOCALNAMESERVERS="$(uniqify $LOCALNAMESERVERS)" 475 LOCALNAMESERVERS="$(list_remove name_server_blacklist $LOCALNAMESERVERS)" 476 echo "DOMAIN='$DOMAIN'" 477 echo "SEARCH='$SEARCH'" 478 echo "NAMESERVERS='$NAMESERVERS'" 479 echo "LOCALNAMESERVERS='$LOCALNAMESERVERS'" 480 echo "DOMAINS='$newdomains'" 481} 482 483force=false 484VFLAG= 485while getopts a:Dd:fhIilm:puvVx OPT; do 486 case "$OPT" in 487 f) force=true;; 488 h) usage;; 489 m) IF_METRIC="$OPTARG";; 490 p) IF_PRIVATE=1;; 491 V) 492 VFLAG=1 493 if [ "$local_nameservers" = \ 494 "127.* 0.0.0.0 255.255.255.255 ::1" ] 495 then 496 local_nameservers= 497 fi 498 ;; 499 x) IF_EXCLUSIVE=1;; 500 '?') ;; 501 *) cmd="$OPT"; iface="$OPTARG";; 502 esac 503done 504shift $(($OPTIND - 1)) 505args="$iface${iface:+ }$*" 506 507# -I inits the state dir 508if [ "$cmd" = I ]; then 509 if [ -d "$VARDIR" ]; then 510 rm -rf "$VARDIR"/* 511 fi 512 exit $? 513fi 514 515# -D ensures that the listed config file base dirs exist 516if [ "$cmd" = D ]; then 517 config_mkdirs "$@" 518 exit $? 519fi 520 521# -l lists our resolv files, optionally for a specific interface 522if [ "$cmd" = l -o "$cmd" = i ]; then 523 list_resolv "$cmd" "$args" 524 exit $? 525fi 526 527# Not normally needed, but subscribers should be able to run independently 528if [ "$cmd" = v -o -n "$VFLAG" ]; then 529 make_vars "$iface" 530 exit $? 531fi 532 533# Test that we have valid options 534if [ "$cmd" = a -o "$cmd" = d ]; then 535 if [ -z "$iface" ]; then 536 usage "Interface not specified" 537 fi 538elif [ "$cmd" != u ]; then 539 [ -n "$cmd" -a "$cmd" != h ] && usage "Unknown option $cmd" 540 usage 541fi 542 543if [ "$cmd" = a ]; then 544 for x in '/' \\ ' ' '*'; do 545 case "$iface" in 546 *[$x]*) error_exit "$x not allowed in interface name";; 547 esac 548 done 549 for x in '.' '-' '~'; do 550 case "$iface" in 551 [$x]*) error_exit \ 552 "$x not allowed at start of interface name";; 553 esac 554 done 555 [ "$cmd" = a -a -t 0 ] && error_exit "No file given via stdin" 556fi 557 558if [ ! -d "$VARDIR" ]; then 559 if [ -L "$VARDIR" ]; then 560 dir="$(readlink "$VARDIR")" 561 # link maybe relative 562 cd "${VARDIR%/*}" 563 if ! mkdir -m 0755 -p "$dir"; then 564 error_exit "Failed to create needed" \ 565 "directory $dir" 566 fi 567 else 568 if ! mkdir -m 0755 -p "$VARDIR"; then 569 error_exit "Failed to create needed" \ 570 "directory $VARDIR" 571 fi 572 fi 573fi 574 575if [ ! -d "$IFACEDIR" ]; then 576 mkdir -m 0755 -p "$IFACEDIR" || \ 577 error_exit "Failed to create needed directory $IFACEDIR" 578 if [ "$cmd" = d ]; then 579 # Provide the same error messages as below 580 if ! ${force}; then 581 cd "$IFACEDIR" 582 for i in $args; do 583 warn "No resolv.conf for interface $i" 584 done 585 fi 586 ${force} 587 exit $? 588 fi 589fi 590 591# An interface was added, changed, deleted or a general update was called. 592# Due to exclusivity we need to ensure that this is an atomic operation. 593# Our subscribers *may* need this as well if the init system is sub par. 594# As such we spinlock at this point as best we can. 595# We don't use flock(1) because it's not widely available and normally resides 596# in /usr which we do our very best to operate without. 597[ -w "$VARDIR" ] || error_exit "Cannot write to $LOCKDIR" 598: ${lock_timeout:=10} 599while true; do 600 if mkdir "$LOCKDIR" 2>/dev/null; then 601 trap 'rm -rf "$LOCKDIR";' EXIT 602 trap 'rm -rf "$LOCKDIR"; exit 1' INT QUIT ABRT SEGV ALRM TERM 603 echo $$ >"$LOCKDIR/pid" 604 break 605 fi 606 pid=$(cat "$LOCKDIR/pid") 607 if ! kill -0 "$pid"; then 608 warn "clearing stale lock pid $pid" 609 rm -rf "$LOCKDIR" 610 continue 611 fi 612 lock_timeout=$(($lock_timeout - 1)) 613 if [ "$lock_timeout" -le 0 ]; then 614 error_exit "timed out waiting for lock from pid $pid" 615 fi 616 sleep 1 617done 618 619case "$cmd" in 620a) 621 # Read resolv.conf from stdin 622 resolv="$(cat)" 623 changed=false 624 changedfile=false 625 # If what we are given matches what we have, then do nothing 626 if [ -e "$IFACEDIR/$iface" ]; then 627 if [ "$(echo "$resolv")" != \ 628 "$(cat "$IFACEDIR/$iface")" ] 629 then 630 changed=true 631 changedfile=true 632 fi 633 else 634 changed=true 635 changedfile=true 636 fi 637 638 # Set metric and private before creating the interface resolv.conf file 639 # to ensure that it will have the correct flags 640 [ ! -d "$METRICDIR" ] && mkdir "$METRICDIR" 641 oldmetric="$METRICDIR/"*" $iface" 642 newmetric= 643 if [ -n "$IF_METRIC" ]; then 644 # Pad metric to 6 characters, so 5 is less than 10 645 while [ ${#IF_METRIC} -le 6 ]; do 646 IF_METRIC="0$IF_METRIC" 647 done 648 newmetric="$METRICDIR/$IF_METRIC $iface" 649 fi 650 rm -f "$METRICDIR/"*" $iface" 651 [ "$oldmetric" != "$newmetric" -a \ 652 "$oldmetric" != "$METRICDIR/* $iface" ] && 653 changed=true 654 [ -n "$newmetric" ] && echo " " >"$newmetric" 655 656 case "$IF_PRIVATE" in 657 [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) 658 if [ ! -d "$PRIVATEDIR" ]; then 659 [ -e "$PRIVATEDIR" ] && rm "$PRIVATEDIR" 660 mkdir "$PRIVATEDIR" 661 fi 662 [ -e "$PRIVATEDIR/$iface" ] || changed=true 663 [ -d "$PRIVATEDIR" ] && echo " " >"$PRIVATEDIR/$iface" 664 ;; 665 *) 666 if [ -e "$PRIVATEDIR/$iface" ]; then 667 rm -f "$PRIVATEDIR/$iface" 668 changed=true 669 fi 670 ;; 671 esac 672 673 oldexcl= 674 for x in "$EXCLUSIVEDIR/"*" $iface"; do 675 if [ -f "$x" ]; then 676 oldexcl="$x" 677 break 678 fi 679 done 680 case "$IF_EXCLUSIVE" in 681 [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) 682 if [ ! -d "$EXCLUSIVEDIR" ]; then 683 [ -e "$EXCLUSIVEDIR" ] && rm "$EXCLUSIVEDIR" 684 mkdir "$EXCLUSIVEDIR" 685 fi 686 cd "$EXCLUSIVEDIR" 687 for x in *; do 688 [ -f "$x" ] && break 689 done 690 if [ "${x#* }" != "$iface" ]; then 691 if [ "$x" = "${x% *}" ]; then 692 x=10000000 693 else 694 x="${x% *}" 695 fi 696 if [ "$x" = "0000000" ]; then 697 warn "exclusive underflow" 698 else 699 x=$(($x - 1)) 700 fi 701 if [ -d "$EXCLUSIVEDIR" ]; then 702 echo " " >"$EXCLUSIVEDIR/$x $iface" 703 fi 704 changed=true 705 fi 706 ;; 707 *) 708 if [ -f "$oldexcl" ]; then 709 rm -f "$oldexcl" 710 changed=true 711 fi 712 ;; 713 esac 714 715 if $changedfile; then 716 printf "%s\n" "$resolv" >"$IFACEDIR/$iface" || exit $? 717 elif ! $changed; then 718 exit 0 719 fi 720 unset changed changedfile oldmetric newmetric x oldexcl 721 ;; 722 723d) 724 # Delete any existing information about the interface 725 cd "$IFACEDIR" 726 changed=false 727 for i in $args; do 728 if [ -e "$i" ]; then 729 changed=true 730 elif ! ${force}; then 731 warn "No resolv.conf for interface $i" 732 fi 733 rm -f "$i" "$METRICDIR/"*" $i" \ 734 "$PRIVATEDIR/$i" \ 735 "$EXCLUSIVEDIR/"*" $i" || exit $? 736 done 737 if ! ${changed}; then 738 # Set the return code based on the forced flag 739 ${force} 740 exit $? 741 fi 742 unset changed i 743 ;; 744esac 745 746case "${resolvconf:-YES}" in 747[Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) ;; 748*) exit 0;; 749esac 750 751eval "$(make_vars)" 752export RESOLVCONF DOMAINS SEARCH NAMESERVERS LOCALNAMESERVERS 753: ${list_resolv:=list_resolv -l} 754retval=0 755for script in "$LIBEXECDIR"/*; do 756 if [ -f "$script" ]; then 757 eval script_enabled="\$${script##*/}" 758 case "${script_enabled:-YES}" in 759 [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) ;; 760 *) continue;; 761 esac 762 if [ -x "$script" ]; then 763 "$script" "$cmd" "$iface" 764 else 765 (set -- "$cmd" "$iface"; . "$script") 766 fi 767 retval=$(($retval + $?)) 768 fi 769done 770exit $retval 771