Release openresolv-1.7.1
[openresolv] / resolvconf.in
index 06f460fa7eaf7cf5d9712828dcec91aa5051718c..1512c6fd2c7bc49d5ca3fe9e7e3724950e7c844f 100755 (executable)
@@ -1,5 +1,5 @@
 #!/bin/sh
-# Copyright 2007 Roy Marples
+# Copyright 2007-2008 Roy Marples
 # All rights reserved
 
 # Redistribution and use in source and binary forms, with or without
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-argv0="$0"
-
-PREFIX=
-RESOLVCONF="${PREFIX}"/etc/resolvconf
-UPDATED="${RESOLVCONF}"/update.d
-VARDIR="${RESOLVCONF}"/run
+ARGV0="$0"
+SYSCONFDIR=@SYSCONFDIR@
+VARDIR=@VARBASE@/run/resolvconf
 IFACEDIR="${VARDIR}/interfaces"
 
-error_exit() {
+error_exit()
+{
        echo "$*" >&2
        exit 1
 }
 
-usage() {
+usage()
+{
        cat <<-EOF
-       Usage: ${argv0##*/} [options]
+       Usage: ${ARGV0##*/} [options]
 
        Inform the system about any DNS updates.
 
@@ -47,6 +46,7 @@ usage() {
          -a \$INTERFACE    Add DNS information to the specified interface
                           (DNS supplied via stdin in resolv.conf format)
          -d \$INTERFACE    Delete DNS information from the specified interface
+         -f               Ignore non existant interfaces
          -u               Run updates from our current DNS information
          -l [\$PATTERN]    Show DNS information, optionally from interfaces
                           that match the specified pattern
@@ -63,38 +63,96 @@ usage() {
        error_exit "$*"
 }
 
-echo_resolv() {
+echo_resolv()
+{
+       local LINE=
        [ -n "$1" -a -e "${IFACEDIR}/$1" ] || return 1
-       echo "# resolv.conf for interface $1"
-       cat "${IFACEDIR}/$1"
+       echo "# resolv.conf from $1"
+       # Our variable maker works of the fact each resolv.conf per interface
+       # is separated by blank lines.
+       # So we remove them when echoing them.
+       while read LINE; do
+               [ -n "${LINE}" ] && echo "${LINE}"
+       done < "${IFACEDIR}/$1"
        echo
 }
 
-uniqify() {
+# Parse resolv.conf's and make variables
+# for domain name servers, search name servers and global nameservers
+parse_resolv()
+{
+       local LINE= NS= DOMAIN= SEARCH= N= NEWDOMAIN= NEWSEARCH= NEWNS=
+
+       echo "NEWDOMAIN="
+       echo "NEWSEARCH="
+       echo "NEWNS="
+
+       while read LINE; do
+               case "${LINE}" in
+               "nameserver "*)
+                       case "${LINE#* }" in
+                       127.*) continue;;
+                       esac
+                       NS="${NS}${LINE#* } "
+                       ;;
+               "domain "*)
+                       [ -z "${SEARCH}" ] && DOMAIN="${LINE#* }"
+                       ;;
+               "search "*)
+                       SEARCH="${LINE#* }"
+                       DOMAIN=
+                       ;;
+               *)
+                       if [ -z "${LINE}" ]; then
+                               for N in ${NS}; do
+                                       if [ -n "${DOMAIN}" ]; then
+                                               echo "NEWDOMAIN=\"\${NEWDOMAIN} ${DOMAIN},${N}\""
+                                       elif [ -n "${SEARCH}" ]; then
+                                               for S in ${SEARCH}; do
+                                                       echo "NEWSEARCH=\"\${NEWSEARCH} ${S},${N}\""
+                                               done
+                                       else
+                                               echo "NEWNS=\"\${NEWNS} ${N}\""
+                                       fi
+                               done
+                               NS=
+                               DOMAIN=
+                               SEARCH=
+                       fi
+                       ;;
+               esac
+       done
+}
+
+uniqify()
+{
     local result=
     while [ -n "$1" ]; do
                case " ${result} " in
-                       *" $1 "*);;
-                       *) result="${result} $1";;
+               *" $1 "*);;
+               *) result="${result} $1";;
                esac
                shift
        done
     echo "${result# *}"
 }
 
-if [ -n "$1" ]; then
-       CMD="$1"
-       shift
-fi
+FORCE=false
+while getopts a:d:fhils:uv OPT; do
+       case "${OPT}" in
+       f) FORCE=true;;
+       h) usage;;
+       s) CMD=s; SERVICE="${OPTARG}";;
+       '?') exit 1;;
+       *) CMD="${OPT}"; IFACE="${OPTARG}";;
+       esac
+done
+shift $((${OPTIND} - 1))
+ARGS="${IFACE}${IFACE:+ }$@"
 
 # We do our service restarting here so that our subscribers don't have to know
 # about the OS's init system.
-if [ "x${CMD}" = "x-s" ]; then
-       if [ -n "$1" ]; then
-               SERVICE="$1"
-               shift
-       fi
-       [ -z "${SERVICE}" ] && usage "Service not specified"
+if [ "${CMD}" = "s" ]; then
        if [ -n "$1" ]; then
                ACTION="$1"
                shift
@@ -102,23 +160,23 @@ if [ "x${CMD}" = "x-s" ]; then
        [ -z "${ACTION}" ] && usage "Action not specified"
 
        # If restarting check if service is running or not if we can
-       if [ "x${ACTION}" = "xrestart" ]; then
+       if [ "${ACTION}" = "restart" ]; then
                if [ -s /var/run/"${SERVICE}".pid ]; then
                        kill -0 $(cat /var/run/"${SERVICE}".pid) 2>/dev/null
                elif [ -s /var/run/"${SERVICE}"/"${SERVICE}".pid ]; then
-                       kill -0 $(cat /var/run/"${SERVICE}".pid) 2>/dev/null
+                       kill -0 $(cat /var/run/"${SERVICE}"/"${SERVICE}".pid) 2>/dev/null
                elif [ -s /var/run/"${SERVICE}"/pid ]; then
                        kill -0 $(cat /var/run/"${SERVICE}"/pid) 2>/dev/null
                else
                        false
                fi
                # Service not running, so don't restart
-               [ $? != 0 ] && exit 1
+               [ $? != 0 ] && exit 0
        fi      
        if [ -x /sbin/service ]; then
                service "${SERVICE}" "${ACTION}" "$@" 
        elif [ -x /etc/init.d/"${SERVICE}" -a -x /sbin/runscript ]; then
-               if [ "x${ACTION}" = "xrestart" ]; then
+               if [ "${ACTION}" = "restart" ]; then
                        /etc/init.d/"${SERVICE}" --quiet --nodeps conditionalrestart "$@"
                else
                        /etc/init.d/"${SERVICE}" --quiet --nodeps "${ACTION}" "$@"
@@ -135,21 +193,18 @@ if [ "x${CMD}" = "x-s" ]; then
        exit $?
 fi
 
-if [ -n "$1" ]; then
-       IFACE="$1"
-       shift
-fi
-
 # -l lists our resolv files, optionally for a specific interface
-if [ "x${CMD}" = "x-l" -o "x${CMD}" = "x-i" ]; then
+if [ "${CMD}" = "l" -o "${CMD}" = "i" ]; then
        [ -d "${IFACEDIR}" ] || exit 0
-       
+
+       REPORT=false
        # If we have an interface ordering list, then use that.
        # It works by just using pathname expansion in the interface directory.
-       if [ -n "${IFACE}" ]; then
-               LIST="${IFACE} $@"
-       elif [ -r "${RESOLVCONF}"/interface-order ]; then
-               LIST="$(cat "${RESOLVCONF}"/interface-order)"
+       if [ -n "${ARGS}" ]; then
+               LIST="${ARGS}"
+               ${FORCE} || REPORT=true
+       elif [ -r "${SYSCONFDIR}"/interface-order ]; then
+               LIST="$(cat "${SYSCONFDIR}"/interface-order)"
        fi
 
        # If we don't have a list then prefer lo, tunnels, ppp
@@ -158,108 +213,86 @@ if [ "x${CMD}" = "x-l" -o "x${CMD}" = "x-i" ]; then
                LIST="lo lo[0-9]* tap[0-9]* tun[0-9]* vpn vpn[0-9]* ppp[0-9]* ippp[0-9]* *"
        fi
 
+       RETVAL=0
        cd "${IFACEDIR}"
        for IFACE in $(uniqify ${LIST}); do
                # Only list interfaces which we really have
-               [ -e "${IFACE}" ] || continue
+               if ! [ -e "${IFACE}" ]; then
+                       if ${REPORT}; then
+                               echo "No resolv.conf for interface ${IFACE}" >&2
+                               RETVAL=$((${RETVAL} + 1))
+                       fi
+                       continue
+               fi
                
-               if [ "x${CMD}" = "x-i" ]; then
+               if [ "${CMD}" = "i" ]; then
                        printf "${IFACE} "
                else
                        echo_resolv "${IFACE}"
                fi
        done
-       [ "x${CMD}" = "x-i" ] && echo
-       exit 0
+       [ "${CMD}" = "i" ] && echo
+       exit ${RETVAL} 
 fi
 
-if [ "x${CMD}" = "x-v" ]; then
-       NS=
-       DOMAIN=
-       SEARCH=
-       NEWSEARCH=
-       NEWNS=
-       NEWDOMAIN=
-       LINES="$("${argv0}" -l "${IFACE}" | sed -e "s/'/'\\\\''/g" -e "s/^/'/g" -e "s/$/'/g")"
-       eval set -- ${LINES}
-       for LINE in "$@"; do
-               case "${LINE}" in
-                       "nameserver "*)
-                               case "${LINE#* }" in
-                                       127.*) continue;;
-                               esac
-                               NS="${NS}${LINE#* } "
-                               ;;
-                       "domain "*)
-                               [ -z "${SEARCH}" ] && DOMAIN="${LINE#* }"
-                               ;;
-                       "search "*)
-                               SEARCH="${LINE#* }"
-                               DOMAIN=
-                               ;;
-                       *)
-                               if [ -z "${LINE}" ]; then
-                                       for N in ${NS}; do
-                                               if [ -n "${DOMAIN}" ]; then
-                                                       NEWDOMAIN="${NEWDOMAIN} ${DOMAIN},${N}"
-                                               elif [ -n "${SEARCH}" ]; then
-                                                       for S in ${SEARCH}; do
-                                                               NEWSEARCH="${NEWSEARCH} ${S},${N}"
-                                                       done
-                                               else
-                                                       NEWNS="${NEWNS} ${N}"
-                                               fi
-                                       done
-                                       NS=
-                                       DOMAIN=
-                                       SEARCH=
-                               fi
-                               ;;
-               esac
-       done
+if [ "${CMD}" = "v" ]; then
+       eval "$("${ARGV0}" -l "${IFACE}" | parse_resolv)"
 
        # Prefer DOMAIN nameservers over SEARCH nameservers
-       # if we are supplied both.
+       # if we are supplied both, but put them in the SEARCH list
        NEWDOMAIN="$(uniqify ${NEWDOMAIN})"
        NEWSEARCH="$(uniqify ${NEWSEARCH})"
        NEWNS="$(uniqify ${NEWNS})"
+       _NEWDOMAIN=
+       _NEWSEARCH=
        for S in ${NEWSEARCH}; do
                for DN in ${NEWDOMAIN}; do
                        if [ "${S%,*}" = "${DN%,*}" ]; then
-                               NEWSEARCH="$(echo "${NEWSEARCH}" | sed -e "s/${S}/${DN}/g")"
-                               NEWDOMAIN="$(echo "${NEWDOMAIN}" | sed -e "s/${DN}//g")"
+                               S="${DN}"
+                               break
+                       fi
+               done
+               _NEWSEARCH="${_NEWSEARCH}${_NEWSEARCH:+ }${S}"
+       done
+       for DN in ${NEWDOMAIN}; do
+               for S in ${_NEWSEARCH}; do
+                       if [ "${S%,*}" = "${DN%,*}" ]; then
+                               DN=
                                break
                        fi
                done
+               if [ -n "${DN}" ]; then
+                       _NEWDOMAIN="${_NEWDOMAIN}${_NEWDOMAIN:+ }${DN}"
+               fi
        done
 
-       echo "NEWDOMAIN='${NEWDOMAIN}'"
-       echo "NEWSEARCH='${NEWSEARCH}'"
+       echo "NEWDOMAIN='${_NEWDOMAIN}'"
+       echo "NEWSEARCH='${_NEWSEARCH}'"
        echo "NEWNS='${NEWNS}'"
        exit 0
 fi
 
 # Test that we have valid options
-if [ "x${CMD}" = "x-a" -o "x${CMD}" = "x-d" ]; then
+if [ "${CMD}" = "a" -o "${CMD}" = "d" ]; then
        if [ -z "${IFACE}" ]; then
                usage "Interface not specified"
        fi
-elif [ "x${CMD}" != "x-u" ]; then
-       [ -n "x${CMD}" -a "x${CMD}" != "x-h" ] && usage "Unknown option ${CMD}"
+elif [ "${CMD}" != "u" ]; then
+       [ -n "${CMD}" -a "${CMD}" != "h" ] && usage "Unknown option ${CMD}"
        usage
 fi
-if [ "x${CMD}" = "x-a" ]; then
+if [ "${CMD}" = "a" ]; then
        for x in '/' \\ ' ' '*'; do
                case "${IFACE}" in
-                       *[${x}]*) error_exit "${x} not allowed in interface name";;
+               *[${x}]*) error_exit "${x} not allowed in interface name";;
                esac
        done
        for x in '.' '-' '~'; do
                case "${IFACE}" in
-                       [${x}]*) error_exit "${x} not allowed at start of interface name";;
+               [${x}]*) error_exit "${x} not allowed at start of interface name";;
                esac
        done
-       [ "x${CMD}" = "x-a" -a -t 0 ] && error_exit "No file given via stdin"
+       [ "${CMD}" = "a" -a -t 0 ] && error_exit "No file given via stdin"
        IFACERESOLV="${IFACEDIR}/${IFACE}"
 fi
 
@@ -283,27 +316,38 @@ if [ ! -d "${IFACEDIR}" ]; then
                error_exit "Failed to create needed directory ${IFACEDIR}"
 else
        # Delete any existing information about the interface
-       if [ "x${CMD}" = "x-a" -o "x${CMD}" = "x-d" ]; then
+       if [ "${CMD}" = "d" ]; then
                cd "${IFACEDIR}"
-               for iface in ${IFACE}; do
-                       rm -f "${iface}" || exit $?
+               for ARG in ${ARGS}; do
+                       if [ "${CMD}" = "d" -a ! -e "${ARG}" ]; then
+                               ${FORCE} && continue
+                               error_exit "No resolv.conf for interface ${ARG}"
+                       fi
+                       rm -f "${ARG}" || exit $?
                done
        fi
 fi
 
-if [ "x${CMD}" = "x-a" ]; then
-       # Create our resolv.conf file
-       cat >"${IFACEDIR}"/"${IFACE}" || exit $?
+if [ "${CMD}" = "a" ]; then
+       # Read resolv.conf from stdin
+       RESOLV="$(cat)\n"
+       # If what we are given matches what we have, then do nothing
+       if [ -e "${IFACEDIR}/${IFACE}" ]; then
+               if [ "$(printf "${RESOLV}")" = "$(cat "${IFACEDIR}/${IFACE}")" ]
+               then
+                       exit 0
+               fi
+               rm "${IFACEDIR}/${IFACE}"
+       fi
+       printf "${RESOLV}" >"${IFACEDIR}/${IFACE}" || exit $?
 fi
 
-retval=0
-for x in "${UPDATED}"/*; do
-       if [ -e "${x}" ]; then
-               "${x}" "${CMD}" "${IFACE}"
-               retval=$((${retval} + $?))
+RETVAL=0
+for SCRIPT in "${SYSCONFDIR}"/update.d/*; do
+       if [ -e "${SCRIPT}" ]; then
+               "${SCRIPT}" "${CMD}" "${IFACE}"
+               RETVAL=$((${RETVAL} + $?))
        fi
 done
 
-exit ${retval}
-
-# vim: set ts=4 :
+exit ${RETVAL}