resolvconf.in revision 1.19 1 1.1 roy #!/bin/sh
2 1.11 roy # Copyright (c) 2007-2025 Roy Marples
3 1.1 roy # All rights reserved
4 1.1 roy
5 1.1 roy # Redistribution and use in source and binary forms, with or without
6 1.1 roy # modification, are permitted provided that the following conditions
7 1.1 roy # are met:
8 1.1 roy # * Redistributions of source code must retain the above copyright
9 1.1 roy # notice, this list of conditions and the following disclaimer.
10 1.1 roy # * Redistributions in binary form must reproduce the above
11 1.1 roy # copyright notice, this list of conditions and the following
12 1.1 roy # disclaimer in the documentation and/or other materials provided
13 1.1 roy # with the distribution.
14 1.1 roy #
15 1.1 roy # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 1.1 roy # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 1.1 roy # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18 1.1 roy # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
19 1.1 roy # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 1.1 roy # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
21 1.1 roy # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 1.1 roy # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 1.1 roy # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 1.1 roy # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 1.1 roy # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 1.1 roy
27 1.1 roy RESOLVCONF="$0"
28 1.19 roy OPENRESOLV_VERSION="3.17.4"
29 1.1 roy SYSCONFDIR=@SYSCONFDIR@
30 1.1 roy LIBEXECDIR=@LIBEXECDIR@
31 1.1 roy VARDIR=@VARDIR@
32 1.2 kre RCDIR=@RCDIR@
33 1.2 kre RESTARTCMD=@RESTARTCMD@
34 1.2 kre
35 1.2 kre if [ "$1" = "--version" ]; then
36 1.2 kre echo "openresolv $OPENRESOLV_VERSION"
37 1.11 roy echo "Copyright (c) 2007-2025 Roy Marples"
38 1.2 kre exit 0
39 1.2 kre fi
40 1.2 kre
41 1.2 kre # Disregard dhcpcd setting
42 1.2 kre unset interface_order state_dir
43 1.2 kre
44 1.2 kre # If you change this, change the test in VFLAG and libc.in as well
45 1.2 kre local_nameservers="127.* 0.0.0.0 255.255.255.255 ::1"
46 1.2 kre
47 1.7 roy dynamic_order="tap[0-9]* tun[0-9]* vpn vpn[0-9]* wg[0-9]* ppp[0-9]* ippp[0-9]*"
48 1.2 kre interface_order="lo lo[0-9]*"
49 1.2 kre name_server_blacklist="0.0.0.0"
50 1.2 kre
51 1.11 roy # Poor mans cat
52 1.11 roy # /usr might not be available
53 1.11 roy cat()
54 1.11 roy {
55 1.11 roy OIFS="$IFS"
56 1.11 roy IFS=''
57 1.11 roy if [ -n "$1" ]; then
58 1.11 roy while read -r line; do
59 1.11 roy printf "%s\n" "$line"
60 1.11 roy done < "$1"
61 1.11 roy else
62 1.11 roy while read -r line; do
63 1.11 roy printf "%s\n" "$line"
64 1.11 roy done
65 1.11 roy fi
66 1.11 roy retval=$?
67 1.11 roy IFS="$OIFS"
68 1.11 roy return $retval
69 1.11 roy }
70 1.11 roy
71 1.11 roy
72 1.1 roy # Support original resolvconf configuration layout
73 1.1 roy # as well as the openresolv config file
74 1.1 roy if [ -f "$SYSCONFDIR"/resolvconf.conf ]; then
75 1.1 roy . "$SYSCONFDIR"/resolvconf.conf
76 1.1 roy [ -n "$state_dir" ] && VARDIR="$state_dir"
77 1.1 roy elif [ -d "$SYSCONFDIR/resolvconf" ]; then
78 1.1 roy SYSCONFDIR="$SYSCONFDIR/resolvconf"
79 1.1 roy if [ -f "$SYSCONFDIR"/interface-order ]; then
80 1.1 roy interface_order="$(cat "$SYSCONFDIR"/interface-order)"
81 1.1 roy fi
82 1.1 roy fi
83 1.7 roy
84 1.12 roy KEYDIR="$VARDIR/keys"
85 1.1 roy METRICDIR="$VARDIR/metrics"
86 1.1 roy PRIVATEDIR="$VARDIR/private"
87 1.12 roy NOSEARCHDIR="$VARDIR/nosearch"
88 1.2 kre EXCLUSIVEDIR="$VARDIR/exclusive"
89 1.9 roy DEPRECATEDDIR="$VARDIR/deprecated"
90 1.2 kre LOCKDIR="$VARDIR/lock"
91 1.2 kre _PWD="$PWD"
92 1.1 roy
93 1.12 roy # Compat
94 1.12 roy if [ ! -d "$KEYDIR" ] && [ -d "$VARDIR/interfaces" ]; then
95 1.12 roy KEYDIR="$VARDIR/interfaces"
96 1.12 roy fi
97 1.12 roy : ${allow_keys:="$allow_interfaces"}
98 1.12 roy : ${deny_keys:="$deny_interfaces"}
99 1.12 roy : ${key_order:="$interface_order"}
100 1.12 roy : ${inclusive_keys:="$inclusive_interfaces"}
101 1.12 roy : ${exclusive_keys:="$exclusive_interfaces"}
102 1.12 roy : ${private_keys:="$private_interfaces"}
103 1.12 roy : ${public_keys:="$public_interfaces"}
104 1.12 roy
105 1.2 kre warn()
106 1.2 kre {
107 1.19 roy echo "${RESOLVCONF##*/}: $*" >&2
108 1.2 kre }
109 1.1 roy
110 1.1 roy error_exit()
111 1.1 roy {
112 1.19 roy warn "$*"
113 1.1 roy exit 1
114 1.1 roy }
115 1.1 roy
116 1.1 roy usage()
117 1.1 roy {
118 1.1 roy cat <<-EOF
119 1.2 kre Usage: ${RESOLVCONF##*/} [options] command [argument]
120 1.1 roy
121 1.1 roy Inform the system about any DNS updates.
122 1.1 roy
123 1.2 kre Commands:
124 1.12 roy -a \$KEY Add DNS information to the specified key
125 1.1 roy (DNS supplied via stdin in resolv.conf format)
126 1.12 roy -C \$PATTERN Deprecate DNS information for matched key
127 1.12 roy -c \$PATTERN Configure DNS information for matched key
128 1.12 roy -d \$PATTERN Delete DNS information from the matched key
129 1.2 kre -h Show this help cruft
130 1.12 roy -i [\$PATTERN] Show keys that have supplied DNS information
131 1.12 roy optionally from keys that match the specified
132 1.1 roy pattern
133 1.12 roy -l [\$PATTERN] Show DNS information, optionally from keys
134 1.2 kre that match the specified pattern
135 1.11 roy -L [\$PATTERN] Same as -l, but adjusted by our config
136 1.2 kre
137 1.2 kre -u Run updates from our current DNS information
138 1.2 kre --version Echo the ${RESOLVCONF##*/} version
139 1.2 kre
140 1.2 kre Options:
141 1.12 roy -f Ignore non existent keys
142 1.2 kre -m metric Give the added DNS information a metric
143 1.12 roy -p Mark the resolv.conf as private
144 1.12 roy -x Mark the resolv.conf as exclusive
145 1.2 kre
146 1.2 kre Subscriber and System Init Commands:
147 1.2 kre -I Init the state dir
148 1.2 kre -r \$SERVICE Restart the system service
149 1.2 kre (restarting a non-existent or non-running service
150 1.2 kre should have no output and return 0)
151 1.2 kre -R Show the system service restart command
152 1.1 roy -v [\$PATTERN] echo NEWDOMAIN, NEWSEARCH and NEWNS variables to
153 1.1 roy the console
154 1.2 kre -V [\$PATTERN] Same as -v, but only uses configuration in
155 1.2 kre $SYSCONFDIR/resolvconf.conf
156 1.1 roy EOF
157 1.1 roy [ -z "$1" ] && exit 0
158 1.1 roy echo
159 1.12 roy error_exit "$@"
160 1.1 roy }
161 1.1 roy
162 1.12 roy public_key() {
163 1.12 roy key="$1"
164 1.12 roy
165 1.12 roy # Allow expansion
166 1.12 roy cd "$KEYDIR"
167 1.12 roy
168 1.12 roy # Public keys override private ones.
169 1.12 roy for p in $public_keys; do
170 1.12 roy case "$key" in
171 1.12 roy "$p"|"$p":*) return 0;;
172 1.5 roy esac
173 1.2 kre done
174 1.12 roy
175 1.12 roy return 1
176 1.2 kre }
177 1.2 kre
178 1.12 roy private_key()
179 1.1 roy {
180 1.12 roy key="$1"
181 1.12 roy
182 1.12 roy if public_key "$key"; then
183 1.12 roy return 1
184 1.12 roy fi
185 1.12 roy
186 1.12 roy if [ -e "$PRIVATEDIR/$key" ]; then
187 1.12 roy return 0
188 1.12 roy fi
189 1.2 kre
190 1.12 roy for p in $private_keys; do
191 1.12 roy case "$key" in
192 1.12 roy "$p"|"$p":*) return 0;;
193 1.2 kre esac
194 1.2 kre done
195 1.2 kre
196 1.12 roy # Not a private key
197 1.12 roy return 1
198 1.12 roy }
199 1.12 roy
200 1.12 roy nosearch_key()
201 1.12 roy {
202 1.12 roy key="$1"
203 1.12 roy
204 1.12 roy if public_key "$key"; then
205 1.12 roy return 1
206 1.12 roy fi
207 1.12 roy
208 1.12 roy if [ -e "$NOSEARCHDIR/$key" ]; then
209 1.2 kre return 0
210 1.2 kre fi
211 1.10 roy
212 1.12 roy for p in $nosearch_keys; do
213 1.12 roy case "$key" in
214 1.2 kre "$p"|"$p":*) return 0;;
215 1.2 kre esac
216 1.2 kre done
217 1.2 kre
218 1.12 roy # Not a non searchable key
219 1.12 roy return 1
220 1.12 roy }
221 1.12 roy
222 1.12 roy exclusive_key()
223 1.12 roy {
224 1.12 roy key="$1"
225 1.12 roy
226 1.12 roy for x in "$EXCLUSIVEDIR/"*" $key"; do
227 1.12 roy if [ -f "$x" ]; then
228 1.12 roy return 0
229 1.12 roy fi
230 1.12 roy done
231 1.12 roy
232 1.12 roy # Not an exclusive key
233 1.2 kre return 1
234 1.1 roy }
235 1.1 roy
236 1.16 roy # Quote input so it can be safely used for variable assignment via eval
237 1.16 roy quote()
238 1.16 roy {
239 1.16 roy if [ -z "$1" ]; then
240 1.16 roy R="''"
241 1.16 roy else
242 1.16 roy R=
243 1.16 roy for W; do
244 1.16 roy while [ -n "$W" ]; do
245 1.16 roy case "$W" in
246 1.16 roy \'*) R="$R\\'"; W=${W#?};;
247 1.16 roy ?\'*) R="$R\\${W%%\'*}"; W="${W#?}";;
248 1.16 roy *\'*) R="$R'${W%%\'*}'"; W="'${W#*\'}";;
249 1.16 roy ?) R="$R\\$W"; W=;;
250 1.16 roy *) R="$R'$W'"; W=;;
251 1.16 roy esac
252 1.16 roy done
253 1.16 roy done
254 1.16 roy fi
255 1.16 roy
256 1.16 roy printf '%s\n' "$R"
257 1.16 roy return 0
258 1.16 roy }
259 1.16 roy
260 1.1 roy # Parse resolv.conf's and make variables
261 1.1 roy # for domain name servers, search name servers and global nameservers
262 1.17 roy # Important! Each printf here should use the above quote function
263 1.17 roy # to ensure that user input is quoted for eval.
264 1.1 roy parse_resolv()
265 1.1 roy {
266 1.5 roy domain=
267 1.5 roy new=true
268 1.2 kre newns=
269 1.5 roy ns=
270 1.5 roy private=false
271 1.12 roy nosearch=false
272 1.5 roy search=
273 1.1 roy
274 1.2 kre while read -r line; do
275 1.17 roy value="${line#* }"
276 1.1 roy case "$line" in
277 1.1 roy "# resolv.conf from "*)
278 1.1 roy if ${new}; then
279 1.12 roy key="${line#\# resolv.conf from *}"
280 1.1 roy new=false
281 1.12 roy if nosearch_key "$key"; then
282 1.12 roy private=true
283 1.12 roy nosearch=true
284 1.12 roy elif private_key "$key"; then
285 1.1 roy private=true
286 1.12 roy nosearch=false
287 1.2 kre else
288 1.2 kre private=false
289 1.12 roy nosearch=false
290 1.2 kre fi
291 1.2 kre fi
292 1.2 kre ;;
293 1.2 kre "nameserver "*)
294 1.2 kre islocal=false
295 1.2 kre for l in $local_nameservers; do
296 1.12 roy case "$value" in
297 1.2 kre $l)
298 1.2 kre islocal=true
299 1.2 kre break
300 1.1 roy ;;
301 1.1 roy esac
302 1.2 kre done
303 1.5 roy if $islocal; then
304 1.17 roy printf 'LOCALNAMESERVERS="$LOCALNAMESERVERS "%s\n' "$(quote "$value")"
305 1.5 roy else
306 1.12 roy ns="$ns${ns:+ }$value"
307 1.5 roy fi
308 1.2 kre ;;
309 1.2 kre "domain "*)
310 1.12 roy search="$value"
311 1.2 kre if [ -z "$domain" ]; then
312 1.2 kre domain="$search"
313 1.12 roy if ! $nosearch; then
314 1.17 roy printf 'DOMAIN=%s\n' "$(quote "$domain")"
315 1.12 roy fi
316 1.1 roy fi
317 1.1 roy ;;
318 1.2 kre "search "*)
319 1.12 roy search="$value"
320 1.1 roy ;;
321 1.1 roy *)
322 1.1 roy [ -n "$line" ] && continue
323 1.2 kre if [ -n "$ns" ] && [ -n "$search" ]; then
324 1.1 roy newns=
325 1.1 roy for n in $ns; do
326 1.1 roy newns="$newns${newns:+,}$n"
327 1.1 roy done
328 1.1 roy ds=
329 1.1 roy for d in $search; do
330 1.1 roy ds="$ds${ds:+ }$d:$newns"
331 1.1 roy done
332 1.17 roy printf 'DOMAINS="$DOMAINS "%s\n' "$(quote "$ds")"
333 1.1 roy fi
334 1.12 roy if ! $nosearch; then
335 1.17 roy printf 'SEARCH="$SEARCH "%s\n' "$(quote "$search")"
336 1.12 roy fi
337 1.1 roy if ! $private; then
338 1.17 roy printf 'NAMESERVERS="$NAMESERVERS "%s\n' "$(quote "$ns")"
339 1.1 roy fi
340 1.1 roy ns=
341 1.1 roy search=
342 1.1 roy new=true
343 1.1 roy ;;
344 1.1 roy esac
345 1.1 roy done
346 1.1 roy }
347 1.1 roy
348 1.1 roy uniqify()
349 1.1 roy {
350 1.5 roy result=
351 1.1 roy while [ -n "$1" ]; do
352 1.1 roy case " $result " in
353 1.1 roy *" $1 "*);;
354 1.1 roy *) result="$result $1";;
355 1.1 roy esac
356 1.1 roy shift
357 1.1 roy done
358 1.1 roy echo "${result# *}"
359 1.1 roy }
360 1.1 roy
361 1.2 kre dirname()
362 1.2 kre {
363 1.5 roy OIFS="$IFS"
364 1.5 roy IFS=/
365 1.15 roy set -- $@
366 1.2 kre IFS="$OIFS"
367 1.2 kre if [ -n "$1" ]; then
368 1.2 kre printf %s .
369 1.2 kre else
370 1.2 kre shift
371 1.2 kre fi
372 1.2 kre while [ -n "$2" ]; do
373 1.2 kre printf "/%s" "$1"
374 1.2 kre shift
375 1.2 kre done
376 1.2 kre printf "\n"
377 1.2 kre }
378 1.2 kre
379 1.2 kre config_mkdirs()
380 1.2 kre {
381 1.2 kre for f; do
382 1.2 kre [ -n "$f" ] || continue
383 1.2 kre d="$(dirname "$f")"
384 1.2 kre if [ ! -d "$d" ]; then
385 1.9 roy mkdir -p "$d" || return $?
386 1.2 kre fi
387 1.2 kre done
388 1.9 roy return 0
389 1.2 kre }
390 1.2 kre
391 1.2 kre # With the advent of alternative init systems, it's possible to have
392 1.2 kre # more than one installed. So we need to try and guess what one we're
393 1.10 roy # using unless overridden by configure.
394 1.2 kre # Note that restarting a service is a last resort - the subscribers
395 1.10 roy # should make a reasonable attempt to reconfigure the service via some
396 1.2 kre # method, normally SIGHUP.
397 1.2 kre detect_init()
398 1.2 kre {
399 1.2 kre [ -n "$RESTARTCMD" ] && return 0
400 1.2 kre
401 1.2 kre # Detect the running init system.
402 1.2 kre # As systemd and OpenRC can be installed on top of legacy init
403 1.2 kre # systems we try to detect them first.
404 1.5 roy status="@STATUSARG@"
405 1.2 kre : ${status:=status}
406 1.2 kre if [ -x /bin/systemctl ] && [ -S /run/systemd/private ]; then
407 1.2 kre RESTARTCMD='
408 1.2 kre if /bin/systemctl --quiet is-active $1.service
409 1.2 kre then
410 1.2 kre /bin/systemctl restart $1.service
411 1.2 kre fi'
412 1.2 kre elif [ -x /usr/bin/systemctl ] && [ -S /run/systemd/private ]; then
413 1.2 kre RESTARTCMD='
414 1.2 kre if /usr/bin/systemctl --quiet is-active $1.service
415 1.2 kre then
416 1.2 kre /usr/bin/systemctl restart $1.service
417 1.2 kre fi'
418 1.5 roy elif [ -x /sbin/rc-service ] &&
419 1.5 roy { [ -s /libexec/rc/init.d/softlevel ] ||
420 1.5 roy [ -s /run/openrc/softlevel ]; }
421 1.2 kre then
422 1.2 kre RESTARTCMD='/sbin/rc-service -i $1 -- -Ds restart'
423 1.2 kre elif [ -x /usr/sbin/invoke-rc.d ]; then
424 1.2 kre RCDIR=/etc/init.d
425 1.2 kre RESTARTCMD='
426 1.2 kre if /usr/sbin/invoke-rc.d --quiet $1 status >/dev/null 2>&1
427 1.2 kre then
428 1.2 kre /usr/sbin/invoke-rc.d $1 restart
429 1.2 kre fi'
430 1.10 roy elif [ -x /usr/bin/s6-rc ] && [ -x /usr/bin/s6-svc ]; then
431 1.10 roy RESTARTCMD='
432 1.11 roy if s6-rc -a list 2>/dev/null | grep -qFx $1-srv
433 1.10 roy then
434 1.10 roy s6-svc -r /run/service/$1-srv
435 1.10 roy fi'
436 1.2 kre elif [ -x /sbin/service ]; then
437 1.2 kre # Old RedHat
438 1.2 kre RCDIR=/etc/init.d
439 1.2 kre RESTARTCMD='
440 1.2 kre if /sbin/service $1; then
441 1.2 kre /sbin/service $1 restart
442 1.2 kre fi'
443 1.2 kre elif [ -x /usr/sbin/service ]; then
444 1.2 kre # Could be FreeBSD
445 1.4 kre RESTARTCMD="
446 1.4 kre if /usr/sbin/service \$1 $status >/dev/null 2>&1
447 1.2 kre then
448 1.4 kre /usr/sbin/service \$1 restart
449 1.4 kre fi"
450 1.2 kre elif [ -x /bin/sv ]; then
451 1.2 kre RESTARTCMD='/bin/sv status $1 >/dev/null 2>&1 &&
452 1.2 kre /bin/sv try-restart $1'
453 1.2 kre elif [ -x /usr/bin/sv ]; then
454 1.2 kre RESTARTCMD='/usr/bin/sv status $1 >/dev/null 2>&1 &&
455 1.2 kre /usr/bin/sv try-restart $1'
456 1.2 kre elif [ -e /etc/arch-release ] && [ -d /etc/rc.d ]; then
457 1.2 kre RCDIR=/etc/rc.d
458 1.2 kre RESTARTCMD='
459 1.2 kre if [ -e /var/run/daemons/$1 ]
460 1.2 kre then
461 1.2 kre /etc/rc.d/$1 restart
462 1.2 kre fi'
463 1.2 kre elif [ -e /etc/slackware-version ] && [ -d /etc/rc.d ]; then
464 1.2 kre RESTARTCMD='
465 1.2 kre if /etc/rc.d/rc.$1 status >/dev/null 2>&1
466 1.2 kre then
467 1.2 kre /etc/rc.d/rc.$1 restart
468 1.2 kre fi'
469 1.2 kre elif [ -e /etc/rc.d/rc.subr ] && [ -d /etc/rc.d ]; then
470 1.2 kre # OpenBSD
471 1.2 kre RESTARTCMD='
472 1.2 kre if /etc/rc.d/$1 check >/dev/null 2>&1
473 1.2 kre then
474 1.2 kre /etc/rc.d/$1 restart
475 1.2 kre fi'
476 1.10 roy elif [ -d /etc/dinit.d ] && command -v dinitctl >/dev/null 2>&1; then
477 1.10 roy RESTARTCMD='dinitctl --quiet restart --ignore-unstarted $1'
478 1.2 kre else
479 1.2 kre for x in /etc/init.d/rc.d /etc/rc.d /etc/init.d; do
480 1.2 kre [ -d $x ] || continue
481 1.2 kre RESTARTCMD="
482 1.2 kre if $x/\$1 $status >/dev/null 2>&1
483 1.2 kre then
484 1.2 kre $x/\$1 restart
485 1.2 kre fi"
486 1.2 kre break
487 1.2 kre done
488 1.2 kre fi
489 1.2 kre
490 1.2 kre if [ -z "$RESTARTCMD" ]; then
491 1.2 kre if [ "$_NOINIT_WARNED" != true ]; then
492 1.2 kre warn "could not detect a useable init system"
493 1.2 kre _NOINIT_WARNED=true
494 1.2 kre fi
495 1.2 kre return 1
496 1.2 kre fi
497 1.2 kre _NOINIT_WARNED=
498 1.2 kre return 0
499 1.2 kre }
500 1.2 kre
501 1.2 kre echo_resolv()
502 1.2 kre {
503 1.5 roy OIFS="$IFS"
504 1.2 kre
505 1.12 roy [ -n "$1" ] && [ -f "$KEYDIR/$1" ] || return 1
506 1.2 kre echo "# resolv.conf from $1"
507 1.12 roy # Our variable maker works of the fact each resolv.conf per key
508 1.2 kre # is separated by blank lines.
509 1.2 kre # So we remove them when echoing them.
510 1.2 kre while read -r line; do
511 1.2 kre IFS="$OIFS"
512 1.2 kre if [ -n "$line" ]; then
513 1.2 kre # We need to set IFS here to preserve any whitespace
514 1.2 kre IFS=''
515 1.2 kre printf "%s\n" "$line"
516 1.2 kre fi
517 1.12 roy done < "$KEYDIR/$1"
518 1.2 kre IFS="$OIFS"
519 1.2 kre }
520 1.2 kre
521 1.12 roy deprecated_key()
522 1.9 roy {
523 1.9 roy [ -d "$DEPRECATEDDIR" ] || return 1
524 1.9 roy
525 1.9 roy cd "$DEPRECATEDDIR"
526 1.9 roy for da; do
527 1.9 roy for daf in *; do
528 1.9 roy [ -f "$daf" ] || continue
529 1.9 roy case "$da" in
530 1.9 roy $daf) return 0;;
531 1.9 roy esac
532 1.9 roy done
533 1.9 roy done
534 1.9 roy return 1
535 1.9 roy }
536 1.9 roy
537 1.11 roy match()
538 1.11 roy {
539 1.11 roy match="$1"
540 1.11 roy file="$2"
541 1.11 roy retval=1
542 1.11 roy count=0
543 1.11 roy
544 1.11 roy while read -r keyword value; do
545 1.11 roy new_match=
546 1.11 roy for om in $match; do
547 1.11 roy m="$om"
548 1.11 roy keep=
549 1.11 roy while [ -n "$m" ]; do
550 1.11 roy k="${m%%/*}"
551 1.11 roy r="${m#*/}"
552 1.11 roy f="${r%%/*}"
553 1.11 roy r="${r#*/}"
554 1.11 roy # If the length of m is the same as k/f then
555 1.11 roy # we know that we are done
556 1.11 roy if [ ${#m} = $((${#k} + 1 + ${#f})) ]; then
557 1.11 roy r=
558 1.11 roy fi
559 1.11 roy m="$r"
560 1.11 roy matched=false
561 1.11 roy case "$keyword" in
562 1.11 roy $k)
563 1.11 roy case "$value" in
564 1.11 roy $f)
565 1.11 roy matched=true
566 1.11 roy ;;
567 1.11 roy esac
568 1.11 roy ;;
569 1.11 roy esac
570 1.11 roy if ! $matched; then
571 1.11 roy keep="$keep${keep:+/}$k/$f"
572 1.11 roy fi
573 1.11 roy done
574 1.11 roy if [ -n "$om" ] && [ -z "$keep" ]; then
575 1.11 roy retval=0
576 1.11 roy break 2
577 1.11 roy fi
578 1.11 roy new_match="${new_match}${new_match:+ }${keep}"
579 1.11 roy done
580 1.11 roy match="${new_match}"
581 1.11 roy done < "$file"
582 1.11 roy return $retval
583 1.11 roy }
584 1.11 roy
585 1.12 roy list_keys() {
586 1.12 roy list_cmd="$1"
587 1.12 roy shift
588 1.11 roy
589 1.12 roy [ -d "$KEYDIR" ] || return 0
590 1.12 roy cd "$KEYDIR"
591 1.10 roy
592 1.12 roy [ -n "$1" ] || set -- "*"
593 1.5 roy list=
594 1.5 roy retval=0
595 1.12 roy if [ "$list_cmd" = -i ] || [ "$list_cmd" = -l ]; then
596 1.12 roy for i in $@; do
597 1.12 roy if [ ! -f "$i" ]; then
598 1.15 roy if ! $force && [ "$i" != "*" ]; then
599 1.12 roy echo "No resolv.conf for key $i" >&2
600 1.12 roy fi
601 1.12 roy retval=2
602 1.12 roy continue
603 1.12 roy fi
604 1.12 roy list="$list $i"
605 1.12 roy done
606 1.12 roy [ -z "$list" ] || uniqify $list
607 1.12 roy return $retval
608 1.12 roy fi
609 1.1 roy
610 1.12 roy if [ "$list_cmd" != -I ] && [ "$list_cmd" != -L ]; then
611 1.12 roy echo "list_keys: unknown command $list_cmd" >&2
612 1.12 roy return 1
613 1.12 roy fi
614 1.12 roy
615 1.12 roy if [ -d "$EXCLUSIVEDIR" ]; then
616 1.12 roy cd "$EXCLUSIVEDIR"
617 1.12 roy for i in $EXCLUSIVEDIR/*; do
618 1.12 roy if [ -f "$i" ]; then
619 1.12 roy cd "$KEYDIR"
620 1.12 roy for ii in $inclusive_keys; do
621 1.13 roy if [ -f "$ii" ] && [ "${i#* }" = "$ii" ]; then
622 1.12 roy continue 2
623 1.12 roy fi
624 1.12 roy done
625 1.12 roy list="${i#* }"
626 1.12 roy break
627 1.12 roy fi
628 1.12 roy done
629 1.12 roy cd "$KEYDIR"
630 1.12 roy if [ -n "$list" ]; then
631 1.12 roy for i in $@; do
632 1.12 roy # list will be one item due to the above
633 1.12 roy if [ -f "$i" ] && [ "$i" = "$list" ]; then
634 1.12 roy echo "$i"
635 1.12 roy return 0
636 1.2 kre fi
637 1.2 kre done
638 1.12 roy return 0
639 1.2 kre fi
640 1.12 roy fi
641 1.12 roy
642 1.12 roy for i in $key_order; do
643 1.12 roy for ii in "$i" "$i":* "$i".*; do
644 1.12 roy [ -f "$ii" ] && list="$list $ii"
645 1.12 roy done
646 1.12 roy done
647 1.12 roy
648 1.12 roy for i in $dynamic_order; do
649 1.12 roy for ii in "$i" "$i":* "$i".*; do
650 1.12 roy if [ -f "$ii" ] && ! [ -e "$METRICDIR/"*" $ii" ]
651 1.12 roy then
652 1.12 roy list="$list $ii"
653 1.2 kre fi
654 1.2 kre done
655 1.12 roy done
656 1.12 roy
657 1.12 roy # Interfaces have an implicit metric of 0 if not specified.
658 1.12 roy for i in *; do
659 1.12 roy if [ -f "$i" ] && ! [ -e "$METRICDIR/"*" $i" ]; then
660 1.12 roy list="$list $i"
661 1.12 roy fi
662 1.12 roy done
663 1.2 kre
664 1.12 roy if [ -d "$METRICDIR" ]; then
665 1.12 roy cd "$METRICDIR"
666 1.12 roy for i in *; do
667 1.12 roy [ -f "$i" ] && list="$list ${i#* }"
668 1.1 roy done
669 1.12 roy cd "$KEYDIR"
670 1.12 roy fi
671 1.9 roy
672 1.12 roy # Move deprecated keys to the back
673 1.12 roy active=
674 1.12 roy deprecated=
675 1.12 roy for i in $list; do
676 1.12 roy if deprecated_key "$i"; then
677 1.12 roy deprecated="$deprecated $i"
678 1.12 roy else
679 1.12 roy active="$active $i"
680 1.12 roy fi
681 1.12 roy done
682 1.12 roy list="$active $deprecated"
683 1.12 roy
684 1.12 roy retval=0
685 1.12 roy if [ "$1" != "*" ]; then
686 1.12 roy cd "$KEYDIR"
687 1.12 roy matched=
688 1.12 roy for i in $@; do
689 1.12 roy if ! [ -f "$i" ]; then
690 1.12 roy if ! $force; then
691 1.12 roy echo "No resolv.conf for key $i" >&2
692 1.12 roy fi
693 1.12 roy retval=2
694 1.12 roy continue
695 1.1 roy fi
696 1.12 roy for ii in $list; do
697 1.12 roy if [ "$i" = "$ii" ]; then
698 1.12 roy matched="$matched${matched:+ }$i"
699 1.12 roy break
700 1.2 kre fi
701 1.2 kre done
702 1.1 roy done
703 1.12 roy if [ -z "$matched" ]; then
704 1.12 roy return $retval
705 1.12 roy fi
706 1.12 roy list="$matched"
707 1.12 roy fi
708 1.9 roy
709 1.12 roy allowed=
710 1.12 roy for i in $(uniqify $list); do
711 1.12 roy if [ -n "$allow_keys" ]; then
712 1.12 roy x=false
713 1.12 roy for ii in $allow_keys; do
714 1.12 roy if [ "$i" = "$ii" ]; then
715 1.12 roy x=true
716 1.12 roy break
717 1.12 roy fi
718 1.12 roy done
719 1.12 roy $x || continue
720 1.12 roy fi
721 1.12 roy for ii in $deny_keys; do
722 1.12 roy if [ "$i" = "$ii" ]; then
723 1.12 roy continue 2
724 1.5 roy fi
725 1.5 roy done
726 1.9 roy
727 1.12 roy if [ -n "$exclude" ] && match "$exclude" "$i"; then
728 1.12 roy continue
729 1.1 roy fi
730 1.12 roy allowed="$allowed${allowed:+ }$i"
731 1.12 roy done
732 1.9 roy
733 1.12 roy cd "$KEYDIR"
734 1.12 roy for i in $exclusive_keys; do
735 1.12 roy for ii in $allowed; do
736 1.12 roy if [ "$i" = "$ii" ]; then
737 1.12 roy echo "$i"
738 1.12 roy return
739 1.9 roy fi
740 1.9 roy done
741 1.12 roy done
742 1.12 roy [ -z "$allowed" ] || echo "$allowed"
743 1.12 roy }
744 1.12 roy
745 1.12 roy list_resolv()
746 1.12 roy {
747 1.12 roy keys="$(list_keys "$@")"
748 1.12 roy retval=$?
749 1.12 roy if [ "$retval" != 0 ]; then
750 1.12 roy return $retval
751 1.1 roy fi
752 1.12 roy for i in $keys; do
753 1.12 roy echo_resolv "$i" && echo
754 1.12 roy done
755 1.12 roy }
756 1.1 roy
757 1.12 roy list_private()
758 1.12 roy {
759 1.12 roy KEYS=
760 1.12 roy cd "$KEYDIR"
761 1.12 roy if [ -z "$1" ]; then
762 1.12 roy set -- "*"
763 1.12 roy fi
764 1.12 roy for i in $@; do
765 1.12 roy if private_key "$i"; then
766 1.12 roy KEYS="${KEYS}${KEYS:+ }$i"
767 1.12 roy fi
768 1.12 roy done
769 1.12 roy if [ -n "$KEYS" ]; then
770 1.12 roy echo "$KEYS"
771 1.10 roy fi
772 1.12 roy }
773 1.7 roy
774 1.12 roy list_nosearch()
775 1.12 roy {
776 1.11 roy
777 1.12 roy KEYS=
778 1.12 roy cd "$KEYDIR"
779 1.12 roy if [ -z "$1" ]; then
780 1.12 roy set -- "*"
781 1.12 roy fi
782 1.12 roy for i in $@; do
783 1.12 roy if nosearch_key "$i"; then
784 1.12 roy KEYS="${KEYS}${KEYS:+ }$i"
785 1.7 roy fi
786 1.12 roy done
787 1.12 roy if [ -n "$KEYS" ]; then
788 1.12 roy echo "$KEYS"
789 1.12 roy fi
790 1.12 roy }
791 1.10 roy
792 1.12 roy list_exclusive()
793 1.12 roy {
794 1.12 roy KEYS=
795 1.12 roy cd "$KEYDIR"
796 1.12 roy if [ -z "$1" ]; then
797 1.12 roy set -- "*"
798 1.12 roy fi
799 1.12 roy for i in $@; do
800 1.12 roy if exclusive_key "$i"; then
801 1.12 roy KEYS="${KEYS}${KEYS:+ }$i"
802 1.2 kre fi
803 1.2 kre done
804 1.12 roy if [ -n "$KEYS" ]; then
805 1.12 roy echo "$KEYS"
806 1.12 roy fi
807 1.2 kre }
808 1.2 kre
809 1.5 roy list_remove()
810 1.5 roy {
811 1.2 kre [ -z "$2" ] && return 0
812 1.2 kre eval list=\"\$$1\"
813 1.2 kre shift
814 1.5 roy result=
815 1.5 roy retval=0
816 1.2 kre
817 1.2 kre set -f
818 1.2 kre for e; do
819 1.2 kre found=false
820 1.2 kre for l in $list; do
821 1.2 kre case "$e" in
822 1.2 kre $l) found=true;;
823 1.2 kre esac
824 1.2 kre $found && break
825 1.2 kre done
826 1.2 kre if $found; then
827 1.2 kre retval=$(($retval + 1))
828 1.1 roy else
829 1.2 kre result="$result $e"
830 1.1 roy fi
831 1.1 roy done
832 1.2 kre set +f
833 1.2 kre echo "${result# *}"
834 1.1 roy return $retval
835 1.1 roy }
836 1.1 roy
837 1.2 kre echo_prepend()
838 1.2 kre {
839 1.2 kre echo "# Generated by resolvconf"
840 1.2 kre if [ -n "$search_domains" ]; then
841 1.2 kre echo "search $search_domains"
842 1.2 kre fi
843 1.2 kre for n in $name_servers; do
844 1.2 kre echo "nameserver $n"
845 1.2 kre done
846 1.2 kre echo
847 1.2 kre }
848 1.2 kre
849 1.2 kre echo_append()
850 1.2 kre {
851 1.2 kre echo "# Generated by resolvconf"
852 1.2 kre if [ -n "$search_domains_append" ]; then
853 1.2 kre echo "search $search_domains_append"
854 1.2 kre fi
855 1.2 kre for n in $name_servers_append; do
856 1.2 kre echo "nameserver $n"
857 1.2 kre done
858 1.2 kre echo
859 1.2 kre }
860 1.2 kre
861 1.12 roy tolower() {
862 1.12 roy # There is no good way of doing this portably in shell :(
863 1.12 roy # Luckily we are only doing this for domain names which we
864 1.12 roy # know have to be ASCII.
865 1.12 roy # Non ASCII domains *should* be translated to ASCII *before*
866 1.12 roy # we get to this stage.
867 1.12 roy # We could use echo "$@" | tr '[:upper:]' '[:lower:]' but
868 1.12 roy # tr is in /usr/bin and may not be available when data is fed
869 1.12 roy # to resolvconf.
870 1.12 roy # So it's the cost of a pipe + fork vs this slow loop
871 1.12 roy #
872 1.12 roy for word; do
873 1.12 roy # Check if we have any upper to avoid looping per char
874 1.12 roy case "$word" in
875 1.12 roy *[A-Z]*) ;;
876 1.12 roy *) printf "%s " "$word"; continue;;
877 1.12 roy esac
878 1.12 roy
879 1.12 roy while [ -n "$word" ]; do
880 1.12 roy # Remove everything except the first character
881 1.12 roy afterchar="${word#?}"
882 1.12 roy # Remove the afterchar to get the first character
883 1.12 roy char="${word%%$afterchar}"
884 1.12 roy # Assign afterchar back to word for looping
885 1.12 roy word="$afterchar"
886 1.12 roy
887 1.12 roy # Now enforce lowercase a-z
888 1.12 roy case "$char" in
889 1.12 roy A) char=a;;
890 1.12 roy B) char=b;;
891 1.12 roy C) char=c;;
892 1.12 roy D) char=d;;
893 1.12 roy E) char=e;;
894 1.12 roy F) char=f;;
895 1.12 roy G) char=g;;
896 1.12 roy H) char=h;;
897 1.12 roy I) char=i;;
898 1.12 roy J) char=j;;
899 1.12 roy K) char=k;;
900 1.12 roy L) char=l;;
901 1.12 roy M) char=m;;
902 1.12 roy N) char=n;;
903 1.12 roy O) char=o;;
904 1.12 roy P) char=p;;
905 1.12 roy Q) char=q;;
906 1.12 roy R) char=r;;
907 1.12 roy S) char=s;;
908 1.12 roy T) char=t;;
909 1.12 roy U) char=u;;
910 1.12 roy V) char=v;;
911 1.12 roy W) char=w;;
912 1.12 roy X) char=x;;
913 1.12 roy Y) char=y;;
914 1.12 roy Z) char=z;;
915 1.12 roy esac
916 1.12 roy printf %s "$char"
917 1.12 roy done
918 1.12 roy printf " "
919 1.12 roy done
920 1.12 roy printf "\n"
921 1.12 roy }
922 1.12 roy
923 1.12 roy # Strip any trailing dot from each name as a FQDN does not belong
924 1.12 roy # in resolv.conf(5).
925 1.12 roy # While DNS is not case sensitive, our labels for building the zones
926 1.12 roy # are, so ensure it's lower case.
927 1.12 roy process_domain()
928 1.12 roy {
929 1.12 roy for word in $(tolower "$@"); do
930 1.12 roy printf "%s " "${word%.}"
931 1.12 roy done
932 1.12 roy printf "\n"
933 1.12 roy }
934 1.12 roy
935 1.12 roy process_resolv()
936 1.2 kre {
937 1.2 kre while read -r keyword value; do
938 1.2 kre for r in $replace; do
939 1.2 kre k="${r%%/*}"
940 1.2 kre r="${r#*/}"
941 1.2 kre f="${r%%/*}"
942 1.2 kre r="${r#*/}"
943 1.2 kre v="${r%%/*}"
944 1.2 kre case "$keyword" in
945 1.2 kre $k)
946 1.2 kre case "$value" in
947 1.2 kre $f) value="$v";;
948 1.2 kre esac
949 1.2 kre ;;
950 1.2 kre esac
951 1.2 kre done
952 1.2 kre val=
953 1.2 kre for sub in $value; do
954 1.2 kre for r in $replace_sub; do
955 1.2 kre k="${r%%/*}"
956 1.2 kre r="${r#*/}"
957 1.2 kre f="${r%%/*}"
958 1.2 kre r="${r#*/}"
959 1.2 kre v="${r%%/*}"
960 1.2 kre case "$keyword" in
961 1.2 kre $k)
962 1.2 kre case "$sub" in
963 1.2 kre $f) sub="$v";;
964 1.2 kre esac
965 1.2 kre ;;
966 1.2 kre esac
967 1.2 kre done
968 1.2 kre val="$val${val:+ }$sub"
969 1.2 kre done
970 1.12 roy case "$keyword" in
971 1.12 roy \#)
972 1.12 roy case "$val" in
973 1.12 roy "resolv.conf from "*) ;;
974 1.12 roy *) continue;;
975 1.12 roy esac
976 1.12 roy ;;
977 1.12 roy \#*) continue;;
978 1.12 roy esac
979 1.12 roy case "$keyword" in
980 1.12 roy domain|search) val="$(process_domain $val)";;
981 1.12 roy esac
982 1.2 kre printf "%s %s\n" "$keyword" "$val"
983 1.2 kre done
984 1.2 kre }
985 1.2 kre
986 1.1 roy make_vars()
987 1.1 roy {
988 1.2 kre # Clear variables
989 1.2 kre DOMAIN=
990 1.2 kre DOMAINS=
991 1.2 kre SEARCH=
992 1.2 kre NAMESERVERS=
993 1.2 kre LOCALNAMESERVERS=
994 1.7 roy
995 1.2 kre if [ -n "${name_servers}${search_domains}" ]; then
996 1.2 kre eval "$(echo_prepend | parse_resolv)"
997 1.2 kre fi
998 1.2 kre if [ -z "$VFLAG" ]; then
999 1.12 roy eval "$(list_resolv -L "$@" | process_resolv | parse_resolv)"
1000 1.2 kre fi
1001 1.3 ryoon if [ -n "${name_servers_append}${search_domains_append}" ]; then
1002 1.2 kre eval "$(echo_append | parse_resolv)"
1003 1.2 kre fi
1004 1.1 roy
1005 1.1 roy # Ensure that we only list each domain once
1006 1.5 roy newdomains=
1007 1.1 roy for d in $DOMAINS; do
1008 1.1 roy dn="${d%%:*}"
1009 1.2 kre list_remove domain_blacklist "$dn" >/dev/null || continue
1010 1.1 roy case " $newdomains" in
1011 1.1 roy *" ${dn}:"*) continue;;
1012 1.1 roy esac
1013 1.1 roy newns=
1014 1.1 roy for nd in $DOMAINS; do
1015 1.1 roy if [ "$dn" = "${nd%%:*}" ]; then
1016 1.1 roy ns="${nd#*:}"
1017 1.1 roy while [ -n "$ns" ]; do
1018 1.1 roy case ",$newns," in
1019 1.1 roy *,${ns%%,*},*) ;;
1020 1.2 kre *) list_remove name_server_blacklist \
1021 1.2 kre "${ns%%,*}" >/dev/null \
1022 1.2 kre && newns="$newns${newns:+,}${ns%%,*}";;
1023 1.1 roy esac
1024 1.1 roy [ "$ns" = "${ns#*,}" ] && break
1025 1.1 roy ns="${ns#*,}"
1026 1.1 roy done
1027 1.1 roy fi
1028 1.1 roy done
1029 1.2 kre if [ -n "$newns" ]; then
1030 1.2 kre newdomains="$newdomains${newdomains:+ }$dn:$newns"
1031 1.2 kre fi
1032 1.1 roy done
1033 1.16 roy
1034 1.2 kre DOMAIN="$(list_remove domain_blacklist $DOMAIN)"
1035 1.2 kre SEARCH="$(uniqify $SEARCH)"
1036 1.2 kre SEARCH="$(list_remove domain_blacklist $SEARCH)"
1037 1.2 kre NAMESERVERS="$(uniqify $NAMESERVERS)"
1038 1.2 kre NAMESERVERS="$(list_remove name_server_blacklist $NAMESERVERS)"
1039 1.2 kre LOCALNAMESERVERS="$(uniqify $LOCALNAMESERVERS)"
1040 1.2 kre LOCALNAMESERVERS="$(list_remove name_server_blacklist $LOCALNAMESERVERS)"
1041 1.16 roy
1042 1.16 roy # Ensure output is quoted for eval
1043 1.17 roy printf 'DOMAIN=%s\n' "$(quote "$DOMAIN")"
1044 1.17 roy printf 'SEARCH=%s\n' "$(quote "$SEARCH")"
1045 1.17 roy printf 'NAMESERVERS=%s\n' "$(quote "$NAMESERVERS")"
1046 1.17 roy printf 'LOCALNAMESERVERS=%s\n' "$(quote "$LOCALNAMESERVERS")"
1047 1.17 roy printf 'DOMAINS=%s\n' "$(quote "$newdomains")"
1048 1.1 roy }
1049 1.1 roy
1050 1.1 roy force=false
1051 1.12 roy LFLAG=
1052 1.2 kre VFLAG=
1053 1.11 roy while getopts a:C:c:Dd:fhIiLlm:pRruvVx OPT; do
1054 1.1 roy case "$OPT" in
1055 1.1 roy f) force=true;;
1056 1.1 roy h) usage;;
1057 1.1 roy m) IF_METRIC="$OPTARG";;
1058 1.12 roy p)
1059 1.12 roy if [ "$IF_PRIVATE" = 1 ]; then
1060 1.12 roy IF_NOSEARCH=1
1061 1.12 roy else
1062 1.12 roy IF_PRIVATE=1
1063 1.12 roy fi
1064 1.12 roy ;;
1065 1.2 kre V)
1066 1.2 kre VFLAG=1
1067 1.2 kre if [ "$local_nameservers" = \
1068 1.2 kre "127.* 0.0.0.0 255.255.255.255 ::1" ]
1069 1.2 kre then
1070 1.2 kre local_nameservers=
1071 1.2 kre fi
1072 1.2 kre ;;
1073 1.2 kre x) IF_EXCLUSIVE=1;;
1074 1.9 roy '?') exit 1;;
1075 1.12 roy *)
1076 1.12 roy [ "$OPT" != L ] || LFLAG=1
1077 1.12 roy cmd="$OPT"; key="$OPTARG";;
1078 1.1 roy esac
1079 1.1 roy done
1080 1.1 roy shift $(($OPTIND - 1))
1081 1.12 roy if [ -n "$key" ]; then
1082 1.12 roy set -- "$key" "$@"
1083 1.12 roy fi
1084 1.12 roy
1085 1.12 roy if [ -z "$cmd" ]; then
1086 1.19 roy if [ "$IF_PRIVATE" = 1 ]; then
1087 1.12 roy cmd=p
1088 1.12 roy elif [ "$IF_EXCLUSIVE" = 1 ]; then
1089 1.12 roy cmd=x
1090 1.12 roy fi
1091 1.12 roy fi
1092 1.1 roy
1093 1.2 kre # -D ensures that the listed config file base dirs exist
1094 1.2 kre if [ "$cmd" = D ]; then
1095 1.2 kre config_mkdirs "$@"
1096 1.2 kre exit $?
1097 1.2 kre fi
1098 1.2 kre
1099 1.12 roy # -i lists which keys have a resolv file
1100 1.12 roy if [ "$cmd" = i ]; then
1101 1.12 roy # If the -L modifier is given, the list is post-processed
1102 1.12 roy if [ "$LFLAG" = 1 ]; then
1103 1.12 roy cmd="L"
1104 1.12 roy fi
1105 1.12 roy list_keys "-$cmd" "$@"
1106 1.12 roy exit $?
1107 1.12 roy fi
1108 1.12 roy
1109 1.12 roy # -l lists our resolv files, optionally for a specific key
1110 1.12 roy if [ "$cmd" = l ]; then
1111 1.12 roy list_resolv "-$cmd" "$@"
1112 1.1 roy exit $?
1113 1.1 roy fi
1114 1.11 roy # -L is the same as -l, but post-processed from our config
1115 1.11 roy if [ "$cmd" = L ]; then
1116 1.12 roy list_resolv "-$cmd" "$@" | process_resolv
1117 1.12 roy exit $?
1118 1.12 roy fi
1119 1.12 roy
1120 1.12 roy if [ "$cmd" = p ]; then
1121 1.12 roy if [ "$IF_NOSEARCH" = 1 ]; then
1122 1.12 roy list_nosearch "$@"
1123 1.12 roy else
1124 1.12 roy list_private "$@"
1125 1.12 roy fi
1126 1.12 roy exit $?
1127 1.12 roy fi
1128 1.12 roy
1129 1.12 roy if [ "$cmd" = x ]; then
1130 1.12 roy list_exclusive "$@"
1131 1.12 roy exit $?
1132 1.11 roy fi
1133 1.1 roy
1134 1.2 kre # Restart a service or echo the command to restart a service
1135 1.2 kre if [ "$cmd" = r ] || [ "$cmd" = R ]; then
1136 1.2 kre detect_init || exit 1
1137 1.2 kre if [ "$cmd" = r ]; then
1138 1.4 kre eval "$RESTARTCMD"
1139 1.2 kre else
1140 1.5 roy echo "$RESTARTCMD" |
1141 1.5 roy sed -e '/^$/d' -e 's/^ //g'
1142 1.2 kre fi
1143 1.2 kre exit $?
1144 1.2 kre fi
1145 1.2 kre
1146 1.1 roy # Not normally needed, but subscribers should be able to run independently
1147 1.2 kre if [ "$cmd" = v ] || [ -n "$VFLAG" ]; then
1148 1.12 roy make_vars "$@"
1149 1.1 roy exit $?
1150 1.1 roy fi
1151 1.1 roy
1152 1.1 roy # Test that we have valid options
1153 1.9 roy case "$cmd" in
1154 1.9 roy a|d|C|c)
1155 1.12 roy if [ -z "$key" ]; then
1156 1.12 roy error_exit "Key not specified"
1157 1.9 roy fi
1158 1.9 roy ;;
1159 1.15 roy I|u) ;;
1160 1.9 roy *)
1161 1.9 roy if [ -n "$cmd" ] && [ "$cmd" != h ]; then
1162 1.9 roy error_exit "Unknown option $cmd"
1163 1.1 roy fi
1164 1.1 roy usage
1165 1.9 roy ;;
1166 1.9 roy esac
1167 1.2 kre
1168 1.1 roy if [ "$cmd" = a ]; then
1169 1.19 roy for x in '/' '\' ' ' '*'; do
1170 1.19 roy case "$key" in
1171 1.19 roy "$x"|"$x"*|*"$x"|*"$x"*) error_exit "$x not allowed in key name";;
1172 1.1 roy esac
1173 1.1 roy done
1174 1.1 roy for x in '.' '-' '~'; do
1175 1.19 roy case "$key" in
1176 1.19 roy "$x"*) error_exit \
1177 1.12 roy "$x not allowed at start of key name";;
1178 1.1 roy esac
1179 1.1 roy done
1180 1.2 kre [ "$cmd" = a ] && [ -t 0 ] && error_exit "No file given via stdin"
1181 1.1 roy fi
1182 1.1 roy
1183 1.2 kre if [ ! -d "$VARDIR" ]; then
1184 1.2 kre if [ -L "$VARDIR" ]; then
1185 1.2 kre dir="$(readlink "$VARDIR")"
1186 1.2 kre # link maybe relative
1187 1.2 kre cd "${VARDIR%/*}"
1188 1.2 kre if ! mkdir -m 0755 -p "$dir"; then
1189 1.2 kre error_exit "Failed to create needed" \
1190 1.2 kre "directory $dir"
1191 1.2 kre fi
1192 1.2 kre else
1193 1.2 kre if ! mkdir -m 0755 -p "$VARDIR"; then
1194 1.2 kre error_exit "Failed to create needed" \
1195 1.2 kre "directory $VARDIR"
1196 1.1 roy fi
1197 1.1 roy fi
1198 1.2 kre fi
1199 1.2 kre
1200 1.12 roy if [ ! -d "$KEYDIR" ]; then
1201 1.12 roy mkdir -m 0755 -p "$KEYDIR" || \
1202 1.12 roy error_exit "Failed to create needed directory $KEYDIR"
1203 1.1 roy if [ "$cmd" = d ]; then
1204 1.2 kre # Provide the same error messages as below
1205 1.2 kre if ! ${force}; then
1206 1.12 roy cd "$KEYDIR"
1207 1.12 roy for i in $@; do
1208 1.12 roy warn "No resolv.conf for key $i"
1209 1.2 kre done
1210 1.2 kre fi
1211 1.2 kre ${force}
1212 1.2 kre exit $?
1213 1.1 roy fi
1214 1.1 roy fi
1215 1.1 roy
1216 1.12 roy # A key was added, changed, deleted or a general update was called.
1217 1.2 kre # Due to exclusivity we need to ensure that this is an atomic operation.
1218 1.2 kre # Our subscribers *may* need this as well if the init system is sub par.
1219 1.2 kre # As such we spinlock at this point as best we can.
1220 1.2 kre # We don't use flock(1) because it's not widely available and normally resides
1221 1.2 kre # in /usr which we do our very best to operate without.
1222 1.2 kre [ -w "$VARDIR" ] || error_exit "Cannot write to $LOCKDIR"
1223 1.2 kre : ${lock_timeout:=10}
1224 1.9 roy : ${clear_nopids:=5}
1225 1.9 roy have_pid=false
1226 1.9 roy had_pid=false
1227 1.2 kre while true; do
1228 1.2 kre if mkdir "$LOCKDIR" 2>/dev/null; then
1229 1.2 kre trap 'rm -rf "$LOCKDIR";' EXIT
1230 1.2 kre trap 'rm -rf "$LOCKDIR"; exit 1' INT QUIT ABRT SEGV ALRM TERM
1231 1.2 kre echo $$ >"$LOCKDIR/pid"
1232 1.2 kre break
1233 1.2 kre fi
1234 1.9 roy pid=$(cat "$LOCKDIR/pid" 2>/dev/null)
1235 1.9 roy if [ "$pid" -gt 0 ] 2>/dev/null; then
1236 1.9 roy have_pid=true
1237 1.9 roy had_pid=true
1238 1.9 roy else
1239 1.9 roy have_pid=false
1240 1.9 roy clear_nopids=$(($clear_nopids - 1))
1241 1.9 roy if [ "$clear_nopids" -le 0 ]; then
1242 1.9 roy warn "not seen a pid, clearing lock directory"
1243 1.9 roy rm -rf "$LOCKDIR"
1244 1.9 roy else
1245 1.9 roy lock_timeout=$(($lock_timeout - 1))
1246 1.9 roy sleep 1
1247 1.9 roy fi
1248 1.9 roy continue
1249 1.9 roy fi
1250 1.9 roy if $have_pid && ! kill -0 "$pid"; then
1251 1.2 kre warn "clearing stale lock pid $pid"
1252 1.2 kre rm -rf "$LOCKDIR"
1253 1.2 kre continue
1254 1.2 kre fi
1255 1.2 kre lock_timeout=$(($lock_timeout - 1))
1256 1.2 kre if [ "$lock_timeout" -le 0 ]; then
1257 1.9 roy if $have_pid; then
1258 1.9 roy error_exit "timed out waiting for lock from pid $pid"
1259 1.9 roy else
1260 1.9 roy if $had_pid; then
1261 1.9 roy error_exit "timed out waiting for lock" \
1262 1.9 roy "from some pids"
1263 1.9 roy else
1264 1.9 roy error_exit "timed out waiting for lock"
1265 1.9 roy fi
1266 1.9 roy fi
1267 1.2 kre fi
1268 1.2 kre sleep 1
1269 1.2 kre done
1270 1.9 roy unset have_pid had_pid clear_nopids
1271 1.2 kre
1272 1.2 kre case "$cmd" in
1273 1.2 kre a)
1274 1.1 roy # Read resolv.conf from stdin
1275 1.2 kre resolv="$(cat)"
1276 1.2 kre changed=false
1277 1.2 kre changedfile=false
1278 1.1 roy # If what we are given matches what we have, then do nothing
1279 1.12 roy if [ -e "$KEYDIR/$key" ]; then
1280 1.2 kre if [ "$(echo "$resolv")" != \
1281 1.12 roy "$(cat "$KEYDIR/$key")" ]
1282 1.1 roy then
1283 1.2 kre changed=true
1284 1.2 kre changedfile=true
1285 1.1 roy fi
1286 1.2 kre else
1287 1.2 kre changed=true
1288 1.2 kre changedfile=true
1289 1.1 roy fi
1290 1.2 kre
1291 1.12 roy # Set metric and private before creating the resolv.conf file
1292 1.2 kre # to ensure that it will have the correct flags
1293 1.1 roy [ ! -d "$METRICDIR" ] && mkdir "$METRICDIR"
1294 1.12 roy oldmetric="$METRICDIR/"*" $key"
1295 1.2 kre newmetric=
1296 1.1 roy if [ -n "$IF_METRIC" ]; then
1297 1.1 roy # Pad metric to 6 characters, so 5 is less than 10
1298 1.1 roy while [ ${#IF_METRIC} -le 6 ]; do
1299 1.1 roy IF_METRIC="0$IF_METRIC"
1300 1.1 roy done
1301 1.12 roy newmetric="$METRICDIR/$IF_METRIC $key"
1302 1.1 roy fi
1303 1.12 roy rm -f "$METRICDIR/"*" $key"
1304 1.2 kre [ "$oldmetric" != "$newmetric" ] &&
1305 1.12 roy [ "$oldmetric" != "$METRICDIR/* $key" ] &&
1306 1.2 kre changed=true
1307 1.2 kre [ -n "$newmetric" ] && echo " " >"$newmetric"
1308 1.2 kre
1309 1.1 roy case "$IF_PRIVATE" in
1310 1.1 roy [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1)
1311 1.1 roy if [ ! -d "$PRIVATEDIR" ]; then
1312 1.1 roy [ -e "$PRIVATEDIR" ] && rm "$PRIVATEDIR"
1313 1.1 roy mkdir "$PRIVATEDIR"
1314 1.1 roy fi
1315 1.12 roy [ -e "$PRIVATEDIR/$key" ] || changed=true
1316 1.12 roy [ -d "$PRIVATEDIR" ] && echo " " >"$PRIVATEDIR/$key"
1317 1.1 roy ;;
1318 1.1 roy *)
1319 1.12 roy if [ -e "$PRIVATEDIR/$key" ]; then
1320 1.12 roy rm -f "$PRIVATEDIR/$key"
1321 1.2 kre changed=true
1322 1.1 roy fi
1323 1.1 roy ;;
1324 1.1 roy esac
1325 1.2 kre
1326 1.12 roy case "$IF_NOSEARCH" in
1327 1.12 roy [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1)
1328 1.12 roy if [ ! -d "$NOSEARCHDIR" ]; then
1329 1.12 roy [ -e "$NOSEARCHDIR" ] && rm "$NOSEARCHDIR"
1330 1.12 roy mkdir "$NOSEARCHDIR"
1331 1.12 roy fi
1332 1.12 roy [ -e "$NOSEARCHDIR/$key" ] || changed=true
1333 1.12 roy [ -d "$NOSEARCHDIR" ] && echo " " >"$NOSEARCHDIR/$key"
1334 1.12 roy ;;
1335 1.12 roy *)
1336 1.12 roy if [ -e "$NOSEARCHDIR/$key" ]; then
1337 1.12 roy rm -f "$NOSEARCHDIR/$key"
1338 1.12 roy changed=true
1339 1.12 roy fi
1340 1.12 roy ;;
1341 1.12 roy esac
1342 1.12 roy set +x
1343 1.12 roy
1344 1.2 kre oldexcl=
1345 1.12 roy for x in "$EXCLUSIVEDIR/"*" $key"; do
1346 1.2 kre if [ -f "$x" ]; then
1347 1.2 kre oldexcl="$x"
1348 1.2 kre break
1349 1.2 kre fi
1350 1.2 kre done
1351 1.2 kre case "$IF_EXCLUSIVE" in
1352 1.2 kre [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1)
1353 1.2 kre if [ ! -d "$EXCLUSIVEDIR" ]; then
1354 1.2 kre [ -e "$EXCLUSIVEDIR" ] && rm "$EXCLUSIVEDIR"
1355 1.2 kre mkdir "$EXCLUSIVEDIR"
1356 1.2 kre fi
1357 1.2 kre cd "$EXCLUSIVEDIR"
1358 1.2 kre for x in *; do
1359 1.2 kre [ -f "$x" ] && break
1360 1.2 kre done
1361 1.12 roy if [ "${x#* }" != "$key" ]; then
1362 1.2 kre if [ "$x" = "${x% *}" ]; then
1363 1.2 kre x=10000000
1364 1.2 kre else
1365 1.2 kre x="${x% *}"
1366 1.2 kre fi
1367 1.2 kre if [ "$x" = "0000000" ]; then
1368 1.2 kre warn "exclusive underflow"
1369 1.2 kre else
1370 1.2 kre x=$(($x - 1))
1371 1.2 kre fi
1372 1.2 kre if [ -d "$EXCLUSIVEDIR" ]; then
1373 1.12 roy echo " " >"$EXCLUSIVEDIR/$x $key"
1374 1.2 kre fi
1375 1.2 kre changed=true
1376 1.2 kre fi
1377 1.2 kre ;;
1378 1.2 kre *)
1379 1.2 kre if [ -f "$oldexcl" ]; then
1380 1.2 kre rm -f "$oldexcl"
1381 1.2 kre changed=true
1382 1.2 kre fi
1383 1.2 kre ;;
1384 1.2 kre esac
1385 1.2 kre
1386 1.2 kre if $changedfile; then
1387 1.12 roy printf "%s\n" "$resolv" >"$KEYDIR/$key" || exit $?
1388 1.15 roy elif ! $changed && [ ! -e "$VARDIR"/error ]; then
1389 1.2 kre exit 0
1390 1.2 kre fi
1391 1.2 kre unset changed changedfile oldmetric newmetric x oldexcl
1392 1.2 kre ;;
1393 1.2 kre
1394 1.2 kre d)
1395 1.12 roy # Delete any existing information about the key
1396 1.12 roy cd "$KEYDIR"
1397 1.2 kre changed=false
1398 1.12 roy for i in $@; do
1399 1.2 kre if [ -e "$i" ]; then
1400 1.2 kre changed=true
1401 1.2 kre elif ! ${force}; then
1402 1.12 roy warn "No resolv.conf for key $i"
1403 1.2 kre fi
1404 1.2 kre rm -f "$i" "$METRICDIR/"*" $i" \
1405 1.2 kre "$PRIVATEDIR/$i" \
1406 1.2 kre "$EXCLUSIVEDIR/"*" $i" || exit $?
1407 1.2 kre done
1408 1.9 roy
1409 1.15 roy if ! $changed && [ ! -e "$VARDIR"/error ]; then
1410 1.2 kre # Set the return code based on the forced flag
1411 1.9 roy $force
1412 1.2 kre exit $?
1413 1.2 kre fi
1414 1.2 kre unset changed i
1415 1.2 kre ;;
1416 1.9 roy
1417 1.9 roy C)
1418 1.12 roy # Mark key as deprecated
1419 1.9 roy [ ! -d "$DEPRECATEDDIR" ] && mkdir "$DEPRECATEDDIR"
1420 1.9 roy cd "$DEPRECATEDDIR"
1421 1.9 roy changed=false
1422 1.12 roy for i in $@; do
1423 1.9 roy if [ ! -e "$i" ]; then
1424 1.9 roy changed=true
1425 1.9 roy echo " " >"$i" || exit $?
1426 1.9 roy fi
1427 1.9 roy done
1428 1.15 roy if ! $changed && [ ! -e "$VARDIR"/error ]; then
1429 1.15 roy exit 0
1430 1.15 roy fi
1431 1.9 roy unset changed i
1432 1.9 roy ;;
1433 1.9 roy
1434 1.9 roy c)
1435 1.12 roy # Mark key as active
1436 1.9 roy if [ -d "$DEPRECATEDDIR" ]; then
1437 1.9 roy cd "$DEPRECATEDDIR"
1438 1.9 roy changed=false
1439 1.12 roy for i in $@; do
1440 1.9 roy if [ -e "$i" ]; then
1441 1.9 roy changed=true
1442 1.9 roy rm "$i" || exit $?
1443 1.9 roy fi
1444 1.9 roy done
1445 1.15 roy if ! $changed && [ ! -e "$VARDIR"/error ]; then
1446 1.15 roy exit 0
1447 1.15 roy fi
1448 1.9 roy unset changed i
1449 1.9 roy fi
1450 1.9 roy ;;
1451 1.15 roy I)
1452 1.15 roy # Init the state dir, keeping our lock and key directories only
1453 1.15 roy for i in "$VARDIR"/*; do
1454 1.15 roy case "$i" in
1455 1.15 roy "$LOCKDIR") ;;
1456 1.15 roy "$KEYDIR") rm -rf "$KEYDIR"/*;;
1457 1.15 roy *) rm -rf "$i";;
1458 1.15 roy esac
1459 1.15 roy done
1460 1.15 roy ;;
1461 1.2 kre esac
1462 1.2 kre
1463 1.2 kre case "${resolvconf:-YES}" in
1464 1.2 kre [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) ;;
1465 1.2 kre *) exit 0;;
1466 1.2 kre esac
1467 1.2 kre
1468 1.2 kre # Try and detect a suitable init system for our scripts
1469 1.2 kre detect_init
1470 1.2 kre export RESTARTCMD RCDIR _NOINIT_WARNED
1471 1.1 roy
1472 1.1 roy eval "$(make_vars)"
1473 1.2 kre export RESOLVCONF DOMAINS SEARCH NAMESERVERS LOCALNAMESERVERS
1474 1.11 roy : ${list_resolv:=list_resolv -L}
1475 1.1 roy retval=0
1476 1.2 kre
1477 1.2 kre # Run scripts in the same directory resolvconf is run from
1478 1.2 kre # in case any scripts accidentally dump files in the wrong place.
1479 1.2 kre cd "$_PWD"
1480 1.1 roy for script in "$LIBEXECDIR"/*; do
1481 1.2 kre if [ -f "$script" ]; then
1482 1.12 roy script_var="${script##*/}"
1483 1.12 roy while [ "${script_var%%-*}" != "$script_var" ]; do
1484 1.12 roy script_var="${script_var%%-*}_${script_var#*-}"
1485 1.12 roy done
1486 1.12 roy eval script_enabled="\$$script_var"
1487 1.2 kre case "${script_enabled:-YES}" in
1488 1.2 kre [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) ;;
1489 1.2 kre *) continue;;
1490 1.2 kre esac
1491 1.2 kre if [ -x "$script" ]; then
1492 1.12 roy "$script" "$cmd" "$key"
1493 1.2 kre else
1494 1.12 roy (set -- "$cmd" "$key"; . "$script")
1495 1.2 kre fi
1496 1.2 kre retval=$(($retval + $?))
1497 1.2 kre fi
1498 1.1 roy done
1499 1.15 roy if [ "$retval" = 0 ]; then
1500 1.15 roy rm -f "$VARDIR"/error
1501 1.15 roy else
1502 1.15 roy echo "$retval" >"$VARDIR"/error
1503 1.15 roy fi
1504 1.1 roy exit $retval
1505