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