Error when deleting or listing non existant resolv.confs
[openresolv] / resolvconf.in
1 #!/bin/sh
2 # Copyright 2007-2008 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 argv0="$0"
28
29 PREFIX=
30 RESOLVCONF="${PREFIX}"/etc/resolvconf
31 UPDATED="${RESOLVCONF}"/update.d
32 VARDIR="${RESOLVCONF}"/run
33 IFACEDIR="${VARDIR}/interfaces"
34
35 error_exit() {
36         echo "$*" >&2
37         exit 1
38 }
39
40 usage() {
41         cat <<-EOF
42         Usage: ${argv0##*/} [options]
43
44         Inform the system about any DNS updates.
45
46         Options:
47           -a \$INTERFACE    Add DNS information to the specified interface
48                            (DNS supplied via stdin in resolv.conf format)
49           -d \$INTERFACE    Delete DNS information from the specified interface
50           -u               Run updates from our current DNS information
51           -l [\$PATTERN]    Show DNS information, optionally from interfaces
52                            that match the specified pattern
53           -i [\$PATTERN]    Show interfaces that have supplied DNS information
54                    optionally from interfaces that match the specified
55                    pattern
56           -v [\$PATTERN]    echo NEWDOMAIN, NEWSEARCH and NEWNS variables to
57                            the console
58           -s \$SVC \$CMD     Do \$CMD for the system service \$SVC
59           -h               Show this help cruft
60         EOF
61         [ -z "$@" ] && exit 0
62         echo
63         error_exit "$*"
64 }
65
66 echo_resolv() {
67         [ -n "$1" -a -e "${IFACEDIR}/$1" ] || return 1
68         echo "# resolv.conf for interface $1"
69         cat "${IFACEDIR}/$1"
70         echo
71 }
72
73 uniqify() {
74     local result=
75     while [ -n "$1" ]; do
76                 case " ${result} " in
77                         *" $1 "*);;
78                         *) result="${result} $1";;
79                 esac
80                 shift
81         done
82     echo "${result# *}"
83 }
84
85 if [ -n "$1" ]; then
86         CMD="$1"
87         shift
88 fi
89
90 # We do our service restarting here so that our subscribers don't have to know
91 # about the OS's init system.
92 if [ "x${CMD}" = "x-s" ]; then
93         if [ -n "$1" ]; then
94                 SERVICE="$1"
95                 shift
96         fi
97         [ -z "${SERVICE}" ] && usage "Service not specified"
98         if [ -n "$1" ]; then
99                 ACTION="$1"
100                 shift
101         fi
102         [ -z "${ACTION}" ] && usage "Action not specified"
103
104         # If restarting check if service is running or not if we can
105         if [ "x${ACTION}" = "xrestart" ]; then
106                 if [ -s /var/run/"${SERVICE}".pid ]; then
107                         kill -0 $(cat /var/run/"${SERVICE}".pid) 2>/dev/null
108                 elif [ -s /var/run/"${SERVICE}"/"${SERVICE}".pid ]; then
109                         kill -0 $(cat /var/run/"${SERVICE}".pid) 2>/dev/null
110                 elif [ -s /var/run/"${SERVICE}"/pid ]; then
111                         kill -0 $(cat /var/run/"${SERVICE}"/pid) 2>/dev/null
112                 else
113                         false
114                 fi
115                 # Service not running, so don't restart
116                 [ $? != 0 ] && exit 0
117         fi      
118         if [ -x /sbin/service ]; then
119                 service "${SERVICE}" "${ACTION}" "$@" 
120         elif [ -x /etc/init.d/"${SERVICE}" -a -x /sbin/runscript ]; then
121                 if [ "x${ACTION}" = "xrestart" ]; then
122                         /etc/init.d/"${SERVICE}" --quiet --nodeps conditionalrestart "$@"
123                 else
124                         /etc/init.d/"${SERVICE}" --quiet --nodeps "${ACTION}" "$@"
125                 fi
126         elif [ -x /etc/init.d/"${SERVICE}" ]; then
127                 /etc/init.d/"${SERVICE}" "${ACTION}" "$@"
128         elif [ -x /etc/rc.d/"${SERVICE}" ]; then
129                 /etc/rc.d/"${SERVICE}" "${ACTION}" "$@" 
130         elif [ -x /etc/rc.d/rc."${SERVICE}" ]; then
131                 /etc/rc.d/rc."${SERVICE}" "${ACTION}" "$@"
132         else
133                 error_exit "Don't know how to interact with services on this platform"
134         fi
135         exit $?
136 fi
137
138 if [ -n "$1" ]; then
139         IFACE="$1"
140         shift
141 fi
142
143 # -l lists our resolv files, optionally for a specific interface
144 if [ "x${CMD}" = "x-l" -o "x${CMD}" = "x-i" ]; then
145         [ -d "${IFACEDIR}" ] || exit 0
146
147         report=false
148         
149         # If we have an interface ordering list, then use that.
150         # It works by just using pathname expansion in the interface directory.
151         if [ -n "${IFACE}" ]; then
152                 LIST="${IFACE} $@"
153                 report=true
154         elif [ -r "${RESOLVCONF}"/interface-order ]; then
155                 LIST="$(cat "${RESOLVCONF}"/interface-order)"
156         fi
157
158         # If we don't have a list then prefer lo, tunnels, ppp
159         # and then anything else.
160         if [ -z "${LIST}" ]; then
161                 LIST="lo lo[0-9]* tap[0-9]* tun[0-9]* vpn vpn[0-9]* ppp[0-9]* ippp[0-9]* *"
162         fi
163
164         retval=0
165         cd "${IFACEDIR}"
166         for IFACE in $(uniqify ${LIST}); do
167                 # Only list interfaces which we really have
168                 if ! [ -e "${IFACE}" ]; then
169                         if ${report}; then
170                                 echo "No resolv.conf for interface ${IFACE}" >&2
171                                 retval=$((${retval} + 1))
172                         fi
173                         continue
174                 fi
175                 
176                 if [ "x${CMD}" = "x-i" ]; then
177                         printf "${IFACE} "
178                 else
179                         echo_resolv "${IFACE}"
180                 fi
181         done
182         [ "x${CMD}" = "x-i" ] && echo
183         exit ${retval} 
184 fi
185
186 if [ "x${CMD}" = "x-v" ]; then
187         NS=
188         DOMAIN=
189         SEARCH=
190         NEWSEARCH=
191         NEWNS=
192         NEWDOMAIN=
193         LINES="$("${argv0}" -l "${IFACE}" |
194                 sed -e "s/'/'\\\\''/g" -e "s/^/'/g" -e "s/$/'/g")"
195         eval set -- ${LINES}
196         for LINE in "$@"; do
197                 case "${LINE}" in
198                         "nameserver "*)
199                                 case "${LINE#* }" in
200                                         127.*) continue;;
201                                 esac
202                                 NS="${NS}${LINE#* } "
203                                 ;;
204                         "domain "*)
205                                 [ -z "${SEARCH}" ] && DOMAIN="${LINE#* }"
206                                 ;;
207                         "search "*)
208                                 SEARCH="${LINE#* }"
209                                 DOMAIN=
210                                 ;;
211                         *)
212                                 if [ -z "${LINE}" ]; then
213                                         for N in ${NS}; do
214                                                 if [ -n "${DOMAIN}" ]; then
215                                                         NEWDOMAIN="${NEWDOMAIN} ${DOMAIN},${N}"
216                                                 elif [ -n "${SEARCH}" ]; then
217                                                         for S in ${SEARCH}; do
218                                                                 NEWSEARCH="${NEWSEARCH} ${S},${N}"
219                                                         done
220                                                 else
221                                                         NEWNS="${NEWNS} ${N}"
222                                                 fi
223                                         done
224                                         NS=
225                                         DOMAIN=
226                                         SEARCH=
227                                 fi
228                                 ;;
229                 esac
230         done
231
232         # Prefer DOMAIN nameservers over SEARCH nameservers
233         # if we are supplied both.
234         NEWDOMAIN="$(uniqify ${NEWDOMAIN})"
235         NEWSEARCH="$(uniqify ${NEWSEARCH})"
236         NEWNS="$(uniqify ${NEWNS})"
237         for S in ${NEWSEARCH}; do
238                 for DN in ${NEWDOMAIN}; do
239                         if [ "${S%,*}" = "${DN%,*}" ]; then
240                                 NEWSEARCH="$(echo "${NEWSEARCH}" |
241                                         sed -e "s/${S}/${DN}/g")"
242                                 NEWDOMAIN="$(echo "${NEWDOMAIN}" |
243                                         sed -e "s/${DN}//g")"
244                                 break
245                         fi
246                 done
247         done
248
249         echo "NEWDOMAIN='${NEWDOMAIN}'"
250         echo "NEWSEARCH='${NEWSEARCH}'"
251         echo "NEWNS='${NEWNS}'"
252         exit 0
253 fi
254
255 # Test that we have valid options
256 if [ "x${CMD}" = "x-a" -o "x${CMD}" = "x-d" ]; then
257         if [ -z "${IFACE}" ]; then
258                 usage "Interface not specified"
259         fi
260 elif [ "x${CMD}" != "x-u" ]; then
261         [ -n "x${CMD}" -a "x${CMD}" != "x-h" ] && usage "Unknown option ${CMD}"
262         usage
263 fi
264 if [ "x${CMD}" = "x-a" ]; then
265         for x in '/' \\ ' ' '*'; do
266                 case "${IFACE}" in
267                         *[${x}]*) error_exit "${x} not allowed in interface name";;
268                 esac
269         done
270         for x in '.' '-' '~'; do
271                 case "${IFACE}" in
272                         [${x}]*) error_exit "${x} not allowed at start of interface name";;
273                 esac
274         done
275         [ "x${CMD}" = "x-a" -a -t 0 ] && error_exit "No file given via stdin"
276         IFACERESOLV="${IFACEDIR}/${IFACE}"
277 fi
278
279 # Ensure that libdir exists
280 if [ ! -d "${IFACEDIR}" ]; then
281         if [ ! -d "${VARDIR}" ]; then
282                 if [ -L "${VARDIR}" ]; then
283                         DIR="$(readlink "${VARDIR}")"
284                         # Change to /etc as link maybe relative
285                         cd "${VARDIR%/*}"
286                         if ! mkdir -m 0755 -p "${DIR}"; then
287                                 error_exit "Failed to create needed directory ${DIR}"
288                         fi
289                 else
290                         if ! mkdir -m 0755 -p "${VARDIR}"; then
291                                 error_exit "Failed to create needed directory ${VARDIR}"
292                         fi
293                 fi
294         fi
295         mkdir -m 0755 -p "${IFACEDIR}" || \
296                 error_exit "Failed to create needed directory ${IFACEDIR}"
297 else
298         # Delete any existing information about the interface
299         if [ "x${CMD}" = "x-a" -o "x${CMD}" = "x-d" ]; then
300                 cd "${IFACEDIR}"
301                 for iface in ${IFACE}; do
302                         if [ "x${CMD}" = "x-d" -a ! -e "${iface}" ]; then
303                                 error_exit "No resolv.conf for interface ${iface}"
304                         fi
305                         rm -f "${iface}" || exit $?
306                 done
307         fi
308 fi
309
310 if [ "x${CMD}" = "x-a" ]; then
311         # Create our resolv.conf file
312         cat >"${IFACEDIR}"/"${IFACE}" || exit $?
313 fi
314
315 retval=0
316 for x in "${UPDATED}"/*; do
317         if [ -e "${x}" ]; then
318                 "${x}" "${CMD}" "${IFACE}"
319                 retval=$((${retval} + $?))
320         fi
321 done
322
323 exit ${retval}