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