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