7484314a2369632f9a86429dea7f81fedaac50bb
[openresolv] / resolvconf
1 #!/bin/sh
2 # Copyright 2006 Gentoo Foundation
3 # Copyright 2007 Roy Marples
4 # Distributed under the terms of the GNU General Public License v2
5
6 # Written by Roy Marples (roy@marples.name)
7 # Based on Debian resolvconf by Thomas Hood
8
9 argv0="$0"
10
11 PREFIX=
12 RESOLVCONF="${PREFIX}"/etc/resolvconf
13 UPDATED="${RESOLVCONF}"/update.d
14 VARDIR="${RESOLVCONF}"/run
15 IFACEDIR="${VARDIR}/interfaces"
16
17 error_exit() {
18         echo "$*" >&2
19         exit 1
20 }
21
22 usage() {
23         cat <<-EOF
24         Usage: ${argv0##*/} [options]
25
26         Inform the system about any DNS updates.
27
28         Options:
29           -a \$INTERFACE    Add DNS information to the specified interface
30                            (DNS supplied via stdin in resolv.conf format)
31           -d \$INTERFACE    Delete DNS information from the specified interface
32           -u               Run updates from our current DNS information
33           -l [\$PATTERN]    Show DNS information, optionally from interfaces
34                            that match the specified pattern
35           -i [\$PATTERN]    Show interfaces that have supplied DNS information
36                    optionally from interfaces that match the specified
37                    pattern
38           -v [\$PATTERN]    echo NEWDOMAIN, NEWSEARCH and NEWNS variables to
39                            the console
40           -h               Show this help cruft
41         EOF
42         [ -z "$@" ] && exit 0
43         echo
44         error_exit "$*"
45 }
46
47 echo_resolv() {
48         [ -n "$1" -a -e "${IFACEDIR}/$1" ] || return 1
49         echo "# resolv.conf for interface $1"
50         grep "." "${IFACEDIR}/$1"
51         echo
52 }
53
54 uniqify() {
55     local result=
56     while [ -n "$1" ]; do
57                 case " ${result} " in
58                         *" $1 "*) ;;
59                         *) result="${result} $1" ;;
60                 esac
61                 shift
62         done
63     echo "${result# *}"
64 }
65
66 if [ -n "$1" ]; then
67         CMD="$1"
68         shift
69 fi
70 if [ -n "$1" ]; then
71         IFACE="$1"
72         shift
73 fi
74
75 # -l is a Gentoo option that lists our resolv files
76 # optionally for a specific interface
77 if [ "x${CMD}" = "x-l" -o "x${CMD}" = "x-i" ]; then
78         [ -d "${IFACEDIR}" ] || exit 0
79         
80         # If we have an interface ordering list, then use that.
81         # It works by just using pathname expansion in the interface directory.
82         if [ -n "${IFACE}" ]; then
83                 LIST="${IFACE} $@"
84         elif [ -r "${RESOLVCONF}"/interface-order ]; then
85                 LIST="$(cat "${RESOLVCONF}"/interface-order)"
86         fi
87
88         # If we don't have a list then prefer lo, tunnels, ppp
89         # and then anything else.
90         if [ -z "${LIST}" ]; then
91                 LIST="lo lo[0-9]* tap[0-9]* tun[0-9]* vpn vpn[0-9]* ppp[0-9]* ippp[0-9]* *"
92         fi
93
94         cd "${IFACEDIR}"
95         for IFACE in $(uniqify ${LIST}); do
96                 # Only list interfaces which we really have
97                 [ -e "${IFACE}" ] || continue
98                 
99                 if [ "x${CMD}" = "x-i" ]; then
100                         printf "${IFACE} "
101                 else
102                         echo_resolv "${IFACE}"
103                 fi
104         done
105         [ "x${CMD}" = "x-i" ] && echo
106         exit 0
107 fi
108
109 if [ "x${CMD}" = "x-v" ]; then
110         NS=
111         DOMAIN=
112         SEARCH=
113         NEWSEARCH=
114         NEWNS=
115         NEWDOMAIN=
116         LINES="$("${argv0}" -l "${IFACE}" | sed -e "s/'/'\\\\''/g" -e "s/^/'/g" -e "s/$/'/g")"
117         eval set -- ${LINES}
118         for LINE in "$@"; do
119                 case "${LINE}" in
120                         "nameserver "*)
121                                 case "${LINE#* }" in
122                                         127.*) continue ;;
123                                 esac
124                                 NS="${NS}${LINE#* } "
125                                 ;;
126                         "domain "*)
127                                 [ -z "${SEARCH}" ] && DOMAIN="${LINE#* }"
128                                 ;;
129                         "search "*)
130                                 SEARCH="${LINE#* }"
131                                 DOMAIN=
132                                 ;;
133                         *)
134                                 if [ -z "${LINE}" ]; then
135                                         for N in ${NS}; do
136                                                 if [ -n "${DOMAIN}" ]; then
137                                                         NEWDOMAIN="${NEWDOMAIN} ${DOMAIN},${N}"
138                                                 elif [ -n "${SEARCH}" ]; then
139                                                         for S in ${SEARCH}; do
140                                                                 NEWSEARCH="${NEWSEARCH} ${S},${N}"
141                                                         done
142                                                 else
143                                                         NEWNS="${NEWNS} ${N}"
144                                                 fi
145                                         done
146                                         NS=
147                                         DOMAIN=
148                                         SEARCH=
149                                 fi
150                                 ;;
151                 esac
152         done
153
154         # Prefer DOMAIN nameservers over SEARCH nameservers
155         # if we are supplied both.
156         NEWDOMAIN="$(uniqify ${NEWDOMAIN})"
157         NEWSEARCH="$(uniqify ${NEWSEARCH})"
158         NEWNS="$(uniqify ${NEWNS})"
159         for S in ${NEWSEARCH}; do
160                 for DN in ${NEWDOMAIN}; do
161                         if [ "${S%,*}" = "${DN%,*}" ]; then
162                                 NEWSEARCH="$(echo "${NEWSEARCH}" | sed -e "s/${S}/${DN}/g")"
163                                 NEWDOMAIN="$(echo "${NEWDOMAIN}" | sed -e "s/${DN}//g")"
164                                 break
165                         fi
166                 done
167         done
168
169         echo "NEWDOMAIN='${NEWDOMAIN}'"
170         echo "NEWSEARCH='${NEWSEARCH}'"
171         echo "NEWNS='${NEWNS}'"
172         exit 0
173 fi
174
175 # Test that we have valid options
176 if [ "x${CMD}" = "x-a" -o "x${CMD}" = "x-d" ]; then
177         if [ -z "${IFACE}" ]; then
178                 usage "Interface not specified"
179         fi
180 elif [ "x${CMD}" != "x-u" ]; then
181         [ -n "x${CMD}" -a "x${CMD}" != "x-h" ] && usage "Unknown option ${CMD}"
182         usage
183 fi
184 if [ "x${CMD}" = "x-a" ]; then
185         for x in '/' \\ ' ' '*'; do
186                 case "${IFACE}" in
187                         *[${x}]*) error_exit "${x} not allowed in interface name" ;;
188                 esac
189         done
190         for x in '.' '-' '~'; do
191                 case "${IFACE}" in
192                         [${x}]*) error_exit "${x} not allowed at start of interface name" ;;
193                 esac
194         done
195         [ "x${CMD}" = "x-a" -a -t 0 ] && error_exit "No file given via stdin"
196         IFACERESOLV="${IFACEDIR}/${IFACE}"
197 fi
198
199 # Ensure that libdir exists
200 if [ ! -d "${IFACEDIR}" ]; then
201         if [ ! -d "${VARDIR}" ]; then
202                 if [ -L "${VARDIR}" ]; then
203                         DIR="$(readlink "${VARDIR}")"
204                         # Change to /etc as link maybe relative
205                         cd "${VARDIR%/*}"
206                         if ! mkdir -m 0755 -p "${DIR}"; then
207                                 error_exit "Failed to create needed directory ${DIR}"
208                         fi
209                 else
210                         if ! mkdir -m 0755 -p "${VARDIR}"; then
211                                 error_exit "Failed to create needed directory ${VARDIR}"
212                         fi
213                 fi
214         fi
215         mkdir -m 0755 -p "${IFACEDIR}" || \
216                 error_exit "Failed to create needed directory ${IFACEDIR}"
217 else
218         # Delete any existing information about the interface
219         if [ "x${CMD}" = "x-a" -o "x${CMD}" = "x-d" ]; then
220                 cd "${IFACEDIR}"
221                 for iface in ${IFACE}; do
222                         rm -f "${iface}" || exit $?
223                 done
224         fi
225 fi
226
227 if [ "x${CMD}" = "x-a" ]; then
228         # Create our resolv.conf file
229         cat >"${IFACEDIR}"/"${IFACE}" || exit $?
230 fi
231
232 retval=0
233 for x in "${UPDATED}"/*; do
234         if [ -e "${x}" ]; then
235                 "${x}" "${CMD}" "${IFACE}"
236                 retval=$((${retval} + $?))
237         fi
238 done
239
240 exit ${retval}
241
242 # vim: set ts=4 :