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