1#!/bin/sh 2# Copyright 2007 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 27argv0="$0" 28 29PREFIX= 30RESOLVCONF="${PREFIX}"/etc/resolvconf 31UPDATED="${RESOLVCONF}"/update.d 32VARDIR="${RESOLVCONF}"/run 33IFACEDIR="${VARDIR}/interfaces" 34 35error_exit() { 36 echo "$*" >&2 37 exit 1 38} 39 40usage() { 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 66echo_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 73uniqify() { 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 85if [ -n "$1" ]; then 86 CMD="$1" 87 shift 88fi 89 90# We do our service restarting here so that our subscribers don't have to know 91# about the OS's init system. 92if [ "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) 108 elif [ -s /var/run/"${SERVICE}"/"${SERVICE}".pid ]; then 109 kill -0 $(cat /var/run/"${SERVICE}".pid) 110 elif [ -s /var/run/"${SERVICE}"/pid ]; then 111 kill -0 $(cat /var/run/"${SERVICE}"/pid) 112 else 113 false 114 fi 115 # Service not running, so don't restart 116 [ $? != 0 ] && exit 1 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 $? 136fi 137 138if [ -n "$1" ]; then 139 IFACE="$1" 140 shift 141fi 142 143# -l lists our resolv files, optionally for a specific interface 144if [ "x${CMD}" = "x-l" -o "x${CMD}" = "x-i" ]; then 145 [ -d "${IFACEDIR}" ] || exit 0 146 147 # If we have an interface ordering list, then use that. 148 # It works by just using pathname expansion in the interface directory. 149 if [ -n "${IFACE}" ]; then 150 LIST="${IFACE} $@" 151 elif [ -r "${RESOLVCONF}"/interface-order ]; then 152 LIST="$(cat "${RESOLVCONF}"/interface-order)" 153 fi 154 155 # If we don't have a list then prefer lo, tunnels, ppp 156 # and then anything else. 157 if [ -z "${LIST}" ]; then 158 LIST="lo lo[0-9]* tap[0-9]* tun[0-9]* vpn vpn[0-9]* ppp[0-9]* ippp[0-9]* *" 159 fi 160 161 cd "${IFACEDIR}" 162 for IFACE in $(uniqify ${LIST}); do 163 # Only list interfaces which we really have 164 [ -e "${IFACE}" ] || continue 165 166 if [ "x${CMD}" = "x-i" ]; then 167 printf "${IFACE} " 168 else 169 echo_resolv "${IFACE}" 170 fi 171 done 172 [ "x${CMD}" = "x-i" ] && echo 173 exit 0 174fi 175 176if [ "x${CMD}" = "x-v" ]; then 177 NS= 178 DOMAIN= 179 SEARCH= 180 NEWSEARCH= 181 NEWNS= 182 NEWDOMAIN= 183 LINES="$("${argv0}" -l "${IFACE}" | sed -e "s/'/'\\\\''/g" -e "s/^/'/g" -e "s/$/'/g")" 184 eval set -- ${LINES} 185 for LINE in "$@"; do 186 case "${LINE}" in 187 "nameserver "*) 188 case "${LINE#* }" in 189 127.*) continue;; 190 esac 191 NS="${NS}${LINE#* } " 192 ;; 193 "domain "*) 194 [ -z "${SEARCH}" ] && DOMAIN="${LINE#* }" 195 ;; 196 "search "*) 197 SEARCH="${LINE#* }" 198 DOMAIN= 199 ;; 200 *) 201 if [ -z "${LINE}" ]; then 202 for N in ${NS}; do 203 if [ -n "${DOMAIN}" ]; then 204 NEWDOMAIN="${NEWDOMAIN} ${DOMAIN},${N}" 205 elif [ -n "${SEARCH}" ]; then 206 for S in ${SEARCH}; do 207 NEWSEARCH="${NEWSEARCH} ${S},${N}" 208 done 209 else 210 NEWNS="${NEWNS} ${N}" 211 fi 212 done 213 NS= 214 DOMAIN= 215 SEARCH= 216 fi 217 ;; 218 esac 219 done 220 221 # Prefer DOMAIN nameservers over SEARCH nameservers 222 # if we are supplied both. 223 NEWDOMAIN="$(uniqify ${NEWDOMAIN})" 224 NEWSEARCH="$(uniqify ${NEWSEARCH})" 225 NEWNS="$(uniqify ${NEWNS})" 226 for S in ${NEWSEARCH}; do 227 for DN in ${NEWDOMAIN}; do 228 if [ "${S%,*}" = "${DN%,*}" ]; then 229 NEWSEARCH="$(echo "${NEWSEARCH}" | sed -e "s/${S}/${DN}/g")" 230 NEWDOMAIN="$(echo "${NEWDOMAIN}" | sed -e "s/${DN}//g")" 231 break 232 fi 233 done 234 done 235 236 echo "NEWDOMAIN='${NEWDOMAIN}'" 237 echo "NEWSEARCH='${NEWSEARCH}'" 238 echo "NEWNS='${NEWNS}'" 239 exit 0 240fi 241 242# Test that we have valid options 243if [ "x${CMD}" = "x-a" -o "x${CMD}" = "x-d" ]; then 244 if [ -z "${IFACE}" ]; then 245 usage "Interface not specified" 246 fi 247elif [ "x${CMD}" != "x-u" ]; then 248 [ -n "x${CMD}" -a "x${CMD}" != "x-h" ] && usage "Unknown option ${CMD}" 249 usage 250fi 251if [ "x${CMD}" = "x-a" ]; then 252 for x in '/' \\ ' ' '*'; do 253 case "${IFACE}" in 254 *[${x}]*) error_exit "${x} not allowed in interface name";; 255 esac 256 done 257 for x in '.' '-' '~'; do 258 case "${IFACE}" in 259 [${x}]*) error_exit "${x} not allowed at start of interface name";; 260 esac 261 done 262 [ "x${CMD}" = "x-a" -a -t 0 ] && error_exit "No file given via stdin" 263 IFACERESOLV="${IFACEDIR}/${IFACE}" 264fi 265 266# Ensure that libdir exists 267if [ ! -d "${IFACEDIR}" ]; then 268 if [ ! -d "${VARDIR}" ]; then 269 if [ -L "${VARDIR}" ]; then 270 DIR="$(readlink "${VARDIR}")" 271 # Change to /etc as link maybe relative 272 cd "${VARDIR%/*}" 273 if ! mkdir -m 0755 -p "${DIR}"; then 274 error_exit "Failed to create needed directory ${DIR}" 275 fi 276 else 277 if ! mkdir -m 0755 -p "${VARDIR}"; then 278 error_exit "Failed to create needed directory ${VARDIR}" 279 fi 280 fi 281 fi 282 mkdir -m 0755 -p "${IFACEDIR}" || \ 283 error_exit "Failed to create needed directory ${IFACEDIR}" 284else 285 # Delete any existing information about the interface 286 if [ "x${CMD}" = "x-a" -o "x${CMD}" = "x-d" ]; then 287 cd "${IFACEDIR}" 288 for iface in ${IFACE}; do 289 rm -f "${iface}" || exit $? 290 done 291 fi 292fi 293 294if [ "x${CMD}" = "x-a" ]; then 295 # Create our resolv.conf file 296 cat >"${IFACEDIR}"/"${IFACE}" || exit $? 297fi 298 299retval=0 300for x in "${UPDATED}"/*; do 301 if [ -e "${x}" ]; then 302 "${x}" "${CMD}" "${IFACE}" 303 retval=$((${retval} + $?)) 304 fi 305done 306 307exit ${retval} 308 309# vim: set ts=4 : 310