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