Initial commit from resolvconf-gentoo-1.4
authorRoy Marples <roy@marples.name>
Thu, 8 Nov 2007 14:29:27 +0000 (14:29 +0000)
committerRoy Marples <roy@marples.name>
Thu, 8 Nov 2007 14:29:27 +0000 (14:29 +0000)
Makefile [new file with mode: 0644]
bin/resolvconf [new file with mode: 0755]
man/resolvconf.8 [new file with mode: 0644]
update.d/libc [new file with mode: 0755]

diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..e0b24d7
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,19 @@
+INSTALL = install 
+ETCDIR = $(ROOT)/etc/resolvconf
+MANDIR = $(ROOT)/usr/share/man/man8
+BINDIR = $(ROOT)/sbin
+VARDIR = $(ROOT)/var/run
+
+default:
+
+install:
+       $(INSTALL) -d $(ETCDIR)/resolv.conf.d
+       $(INSTALL) -d $(ETCDIR)/update.d
+       $(INSTALL) -d $(ETCDIR)/update-libc.d
+       $(INSTALL) -d $(MANDIR)
+       $(INSTALL) -d $(BINDIR)
+       $(INSTALL) -d $(VARDIR)/resolvconf
+       $(INSTALL) bin/resolvconf $(BINDIR)
+       $(INSTALL) update.d/libc $(ETCDIR)/update.d
+       $(INSTALL) -m 644 man/resolvconf.8 $(MANDIR)
+       ln -snf ../../var/run/resolvconf $(ETCDIR)/run
diff --git a/bin/resolvconf b/bin/resolvconf
new file mode 100755 (executable)
index 0000000..e3cad26
--- /dev/null
@@ -0,0 +1,235 @@
+#!/bin/sh
+# Copyright 2006 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+
+# Written by Roy Marples (uberlord@gentoo.org)
+# Heavily based on Debian resolvconf by Thomas Hood
+
+argv0="$0"
+
+VARDIR=/etc/resolvconf/run
+IFACEDIR="${VARDIR}/interfaces"
+
+error_exit() {
+       echo "$*" >&2
+       exit 1
+}
+
+usage() {
+       cat <<-EOF
+       Usage: ${argv0##*/} [options]
+
+       Inform the system about any DNS updates.
+
+       Options:
+         -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
+         -u               Run updates from our current DNS information
+         -l [\$PATTERN]    Show DNS information, optionally from interfaces
+                          that match the specified pattern
+         -i [\$PATTERN]    Show interfaces that have supplied DNS information
+                   optionally from interfaces that match the specified
+                   pattern
+         -v [\$PATTERN]    echo NEWDOMAIN, NEWSEARCH and NEWNS variables to
+                          the console
+         -h               Show this help cruft
+       EOF
+       [ -z "$@" ] && exit 0
+       echo
+       error_exit "$*"
+}
+
+echo_resolv() {
+       [ -n "$1" -a -e "${IFACEDIR}/$1" ] || return 1
+       echo "# resolv.conf for interface $1"
+       grep "." "${IFACEDIR}/$1"
+       echo
+}
+
+uniqify() {
+    local result=
+    while [ -n "$1" ] ; do
+               case " ${result} " in
+                       *" $1 "*) ;;
+                       *) result="${result} $1" ;;
+               esac
+               shift
+       done
+    echo "${result# *}"
+}
+
+if [ -n "$1" ] ; then
+       CMD="$1"
+       shift
+fi
+if [ -n "$1" ] ; then
+       IFACE="$1"
+       shift
+fi
+
+# -l is a Gentoo option that lists our resolv files
+# optionally for a specific interface
+if [ "x${CMD}" = "x-l" -o "x${CMD}" = "x-i" ] ; then
+       [ ! -d "${IFACEDIR}" ] && exit 0
+       
+       # 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 /etc/resolvconf/interface-order ] ; then
+               LIST="$(cat /etc/resolvconf/interface-order)"
+       fi
+
+       # If we don't have a list then prefer lo, tunnels, ppp
+       # and then anything else.
+       if [ -z "${LIST}" ] ; then
+               LIST="lo lo[0-9]* tap[0-9]* tun[0-9]* vpn vpn[0-9]* ppp[0-9]* ippp[0-9]* *"
+       fi
+
+       cd "${IFACEDIR}"
+       for IFACE in $(uniqify ${LIST}) ; do
+               # Only list interfaces which we really have
+               [ -e "${IFACE}" ] || continue
+               
+               if [ "x${CMD}" = "x-i" ] ; then
+                       printf "${IFACE} "
+               else
+                       echo_resolv "${IFACE}"
+               fi
+       done
+       [ "x${CMD}" = "x-i" ] && echo
+       exit 0
+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
+
+       # Prefer DOMAIN nameservers over SEARCH nameservers
+       # if we are supplied both.
+       NEWDOMAIN="$(uniqify ${NEWDOMAIN})"
+       NEWSEARCH="$(uniqify ${NEWSEARCH})"
+       NEWNS="$(uniqify ${NEWNS})"
+       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")"
+                               break
+                       fi
+               done
+       done
+
+       echo "NEWDOMAIN='${NEWDOMAIN}'"
+       echo "NEWSEARCH='${NEWSEARCH}'"
+       echo "NEWNS='${NEWNS}'"
+       exit 0
+fi
+
+# Only root can update resolv.conf
+if ! touch "${VARDIR}"/.test ; then
+       error_exit "You must be root to run ${argv0##*/}"
+fi
+rm "${VARDIR}"/.test
+
+# Test that we have valid options
+if [ "x${CMD}" = "x-a" -o "x${CMD}" = "x-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}"
+       usage
+fi
+if [ "x${CMD}" = "x-a" ] ; then
+       q='\'
+       for x in '/' '\' ' ' '*' ; do
+               case "${IFACE}" in
+                       *[${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" ;;
+               esac
+       done
+       [ "x${CMD}" = "x-a" -a -t 0 ] && error_exit "No file given via stdin"
+       IFACERESOLV="${IFACEDIR}/${IFACE}"
+fi
+
+# Ensure that libdir exists
+if [ ! -d "${IFACEDIR}" ] ; then
+       if [ ! -d "${VARDIR}" ] ; then
+               if [ -L "${VARDIR}" ] ; then
+                       DIR="$(readlink -f "${VARDIR}")"
+                       [ -z "${DIR}" ] && DIR="$(readlink "${VARDIR}")"
+               fi
+               # Change to /etc as link maybe relative
+               cd "${VARDIR%/*}"
+               mkdir -m 0755 -p "${DIR}" \
+                       || error_exit "Failed to create needed directory ${DIR}"
+       fi
+       mkdir -m 0755 -p "${IFACEDIR}" || \
+               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
+               cd "${IFACEDIR}"
+               for iface in ${IFACE} ; do
+                       rm -f "${iface}"
+               done
+       fi
+fi
+
+if [ "x${CMD}" = "x-a" ] ; then
+       # Create our resolv.conf file
+       cat >"${IFACEDIR}"/"${IFACE}"
+fi
+
+for x in /etc/resolvconf/update.d/* ; do
+       [ -e "${x}" ] && "${x}" "${CMD}" "${IFACE}"
+done
+
+# vim: set ts=4 :
diff --git a/man/resolvconf.8 b/man/resolvconf.8
new file mode 100644 (file)
index 0000000..6d51a4c
--- /dev/null
@@ -0,0 +1,173 @@
+.\" $Id$
+.\"
+.TH RESOLVCONF 8 "1 Aug 2007" "resolvconf-1.4"
+.SH NAME
+resolvconf \- manage nameserver information
+.SH SYNOPSIS
+cat \fIFILE\fR |
+.B resolvconf
+\fB\-a\fR \fIINTERFACE\fR
+.PP
+.B resolvconf
+\fB\-d\fR \fIINTERFACE\fR
+.PP
+.B resolvconf
+\fB\-u\fR
+.PP
+.B resolvconf
+\fB\-l\fR \fIPATTERN\fR
+.PP
+.B resolvconf
+\fB\-i\fR \fIPATTERN\fR
+.PP
+.B resolvconf
+\fB\-v\fR \fIPATTERN\fr
+.SH DESCRIPTION
+Overwrite (\fB\-a\fR) or delete (\fB\-d\fR) the nameserver information
+record for network interface \fIINTERFACE\fR
+and run the update scripts in \fI/etc/resolvconf/update.d/\fR.
+.PP
+With \fB\-u\fR, just run the update scripts.
+.PP
+With \fB\-l\fR, list the resolv files for each interface, optionally
+with patterns to match interface names.
+.PP
+With \fB\-i\fR, list the interfaces we have resolv files for, optionally
+with patterns to match interface names.
+.PP
+With \fB\-v\fR, we echo variables NEWDOMAIN, NEWSEARCH and NEWNS to the
+console which can be used to make it easer writing scripts which configure
+DNS resolvers.
+.SH SERVERS
+Normally
+.B resolvconf
+is run only by hook scripts attached to network interface configurers
+such as
+.BR pppd (8) 
+(for ppp interfaces),
+to DHCP clients such as
+.BR dhclient (8),
+to
+.BR /etc/init.d/net.eth0 
+and
+.BR openvpn ,
+and
+to DNS caches such as
+.BR dnsmasq (8)
+(for the loopback interface).
+However, the administrator can also run
+.B resolvconf
+from the command line to add or delete auxiliary nameserver information.
+.SH CLIENTS
+Nameserver information provided to
+.B resolvconf
+is stored for use by subscribers to \fBresolvconf\fR's notification service.
+Subscribers that need to know when nameserver information has changed
+should install a script in \fI/etc/resolvconf/update.d/\fR
+(... or in \fI/etc/resolvconf/update-libc.d/\fR: see below).
+For example, DNS caches such as
+.BR dnsmasq (8)
+and
+.BR pdnsd (8)
+subscribe to the notification service so that they know
+whither to forward queries.
+.PP
+The most important piece of
+software that subscribes to the notification service is the set of functions
+that make up the GNU C Library
+.BR resolver (3).
+When nameserver information is updated the script
+\fI/etc/resolvconf/update.d/libc\fR writes a new resolver configuration
+file to \fI/etc/resolvconf/run/resolv.conf\fR and then runs the scripts in
+/etc/resolvconf/update-libc.d/.
+To make the resolver use the dynamically generated resolver configuration
+file the administrator should ensure that \fI/etc/resolv.conf\fR is a symbolic
+link to \fIresolvconf/run/resolv.conf\fR.
+This link is never modified by \fB/sbin/resolvconf\fR.
+If you find that \fI/etc/resolv.conf\fR is not being updated,
+check to see that the link is intact.
+.PP
+The GNU C Library resolver library isn't the only resolver library available.
+However, any resolver library that reads /etc/resolv.conf
+(and most of them do, in order to be compatible with the GNU C Library resolver)
+should work with
+.BR resolvconf .
+.PP
+Subscribers that need to know only when the resolver configuration file
+has changed should install a script in \fI/etc/resolvconf/update-libc.d/\fR
+rather than in \fI/etc/resolvconf/update.d/\fR.
+(This is important for synchronization purposes:
+scripts in \fIupdate-libc.d/\fR are run after resolv.conf has been updated;
+the same is not necessarily true of scripts in update.d/.)
+.SH OPTIONS
+.TP
+\fB\-a\fR \fIINTERFACE\fR
+Add or overwrite the record for network interface \fIINTERFACE\fR.
+When this option is used the information must be provided to
+.B resolvconf
+on its standard input in the format of the
+.BR resolv.conf (5)
+file.
+Each line in the file must be terminated by a newline.
+.TP
+\fB\-d\fR \fIINTERFACE\fR
+Delete the record for network interface \fIINTERFACE\fR.
+.PP
+The \fIINTERFACE\fR name may not contain spaces, slashes, asterisks or
+initial dots, hyphens or tildes.
+.PP
+Following the addition or deletion of the record, resolvconf runs
+the update scripts as described in the CLIENTS section.
+.TP
+\fB\-u\fR
+Just run the update scripts.
+.TP
+\fB\-l\fR \fIPATTERN\fR
+List the resolv.conf files for the interfaces that match the pattern,
+otherwise all the interfaces.
+.TP
+\fB\-i\fR \fIPATTERN\fR
+List the interfaces that match the pattern otherwise all the interfaces.
+.TP
+\fB\-v\fR \fIPATTERN\fR
+Echo variables NEWDOMAIN, NEWSEARCH and NEWNS to the console.
+.SH FILES
+.TP
+.I /etc/resolvconf/run
+This is either a directory where nameserver information can be stored
+or a symbolic link to such a directory.
+Clients should not make any assumptions about the canonical location
+of this directory or the hierarchy that is constructed under it.
+.TP
+.I /etc/resolvconf/interface-order
+Determines the order in which nameserver information records are processed
+by resolvconf -l.
+.TP
+.I /etc/resolvconf/resolv.conf.d/base
+File containing basic resolver information.
+The lines in this file are included in the resolver configuration file
+even when no interfaces are configured.
+.TP
+.I /etc/resolvconf/resolv.conf.d/head
+File to be prepended to the dynamically generated resolver configuration file.
+Normally this is just a comment line.
+.TP
+.I /etc/resolvconf/reslov.conf.d/tail
+File to be appended to the dynamically generated resolver configuration file.
+To append nothing, make this an empty file.
+.SH BUGS
+Currently
+.B resolvconf
+does not check the sanity of the information provided to it.
+.SH AUTHOR
+Written by Roy Marples <uberlord@gentoo.org>.
+.br
+Heavily based on Debians resolvconf by Thomas Hood <jdthood_AT_yahoo.co.uk>
+.SH COPYRIGHT
+Copyright \(co 2006-2007 Gentoo Foundation
+.br
+This is free software; see the source for copying conditions.  There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+.SH "SEE ALSO"
+.BR resolv.conf (5),
+.BR resolver (3).
diff --git a/update.d/libc b/update.d/libc
new file mode 100755 (executable)
index 0000000..022d38a
--- /dev/null
@@ -0,0 +1,135 @@
+#!/bin/sh
+# Copyright 2006 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+
+# libc resolv.conf updater
+
+# Written by Roy Marples (uberlord@gentoo.org)
+# Heavily based on Debian resolvconf by Thomas Hood
+
+[ "$(readlink /etc/resolv.conf 2>/dev/null)" \
+       != "resolvconf/run/resolv.conf" ] && exit 0
+
+RESOLVCONF="$(resolvconf -l)"
+BASE="/etc/resolvconf/resolv.conf.d/base"
+
+uniqify() {
+    local result=
+    while [ -n "$1" ] ; do
+               case " ${result} " in
+                       *" $1 "*) ;;
+                       *) result="${result} $1" ;;
+               esac
+               shift
+       done
+    echo "${result# *}"
+}
+
+OUR_NS=
+if [ -e "${BASE}" ] ; then
+       OUR_NS="$(sed -n -e 's/^[[:space:]]*nameserver[[:space:]]*//p' "${BASE}")"
+fi
+OUR_NS="$(uniqify \
+       ${OUR_NS} \
+       $(echo "${RESOLVCONF}" \
+       | sed -n -e 's/^[[:space:]]*nameserver[[:space:]]*//p') \
+)"
+
+# libc only allows for 3 nameservers
+# truncate after 127 as well
+i=0
+NS=
+LOCALH=false
+for N in ${OUR_NS} ; do
+       i=$((${i} + 1))
+       NS="${NS} ${N}"
+       [ "${i}" = "3" ] && break
+       case "${N}" in
+               127.*) LOCALH=true; break ;;
+       esac
+done
+
+# This is nasty!
+# If we have a local nameserver then assume they are intelligent enough
+# to be forwarding domain requests to the correct nameserver and not search
+# ones. This means we prefer search then domain, otherwise, we use them in
+# the order given to us.
+OUR_SEARCH=
+if ${LOCALH} ; then
+       if [ -e "${BASE}" ] ; then
+               OUR_SEARCH="$(sed -n -e 's/^[[:space:]]*search[[:space:]]*//p' "${BASE}")"
+       fi
+       OUR_SEARCH="${OUR_SEARCH} $(echo "${RESOLVCONF}" \
+               | sed -n 's/^[[:space:]]*search[[:space:]]*//p')"
+       if [ -e "${BASE}" ] ; then
+               OUR_SEARCH="${OUR_SEARCH} $(sed -n -e 's/^[[:space:]]*domain[[:space:]]*//p' "${BASE}")"
+       fi
+       OUR_SEARCH="${OUR_SEARCH} $( echo "${RESOLVCONF}" \
+               | sed -n -e 's/^[[:space:]]*domain[[:space:]]*//p')"
+else
+       if [ -e "${BASE}" ] ; then
+               OUR_SEARCH="$(sed -n -e 's/^[[:space:]]*search[[:space:]]*//p' \
+                       -e 's/^[[:space:]]*domain[[:space:]]*//p' "${BASE}")"
+       fi
+       OUR_SEARCH="${OUR_SEARCH} $(echo "${RESOLVCONF}" \
+               | sed -n -e 's/^[[:space:]]*search[[:space:]]*//p' \
+                       -e 's/^[[:space:]]*domain[[:space:]]*//p')"
+fi
+
+# libc only allows for 6 search domains 
+i=0
+SEARCH=
+for S in $(uniqify ${OUR_SEARCH}) ; do
+       i=$((${i} + 1))
+       SEARCH="${SEARCH} ${S}"
+       [ "${i}" = "6" ] && break
+done
+[ -n "${SEARCH}" ] && SEARCH="search${SEARCH}"
+
+# Hold our new resolv.conf in a variable to save on temporary files
+NEWCONF="# Generated by resolvconf\n"
+[ -e /etc/resolvconf/resolv.conf.d/head ] \
+       && NEWCONF="${NEWCONF}$(cat /etc/resolvconf/resolv.conf.d/head)\n"
+[ -n "${SEARCH}" ] && NEWCONF="${NEWCONF}${SEARCH}\n"
+for N in ${NS} ; do
+       NEWCONF="${NEWCONF}nameserver ${N}\n"
+done
+
+# Now dump everything else from our resolvs
+if [ -e "${BASE}" ] ; then
+       NEWCONF="${NEWCONF}$(sed -e '/^[[:space:]]*$/d' \
+               -e '/^[[:space:]]*nameserver[[:space:]]*.*/d' \
+               -e '/^[[:space:]]*search[[:space:]]*.*/d' \
+               -e '/^[[:space:]]*domain[[:space:]]*.*/d' \
+               "${BASE}")" 
+fi
+
+# We don't know we're using GNU sed, so we do it like this
+NEWCONF="${NEWCONF}$(echo "${RESOLVCONF}" | sed -e '/^[[:space:]]*$/d' \
+       -e '/^[[:space:]]*#/d' \
+       -e '/^[[:space:]]*nameserver[[:space:]]*.*/d' \
+       -e '/^[[:space:]]*search[[:space:]]*.*/d' \
+       -e '/^[[:space:]]*domain[[:space:]]*.*/d' \
+       )"
+[ -e /etc/resolvconf/resolv.conf.d/tail ] \
+       && NEWCONF="${NEWCONF}$(cat /etc/resolvconf/resolv.conf.d/tail)"
+
+# Check if the file has actually changed or not
+if [ -e /etc/resolv.conf ] ; then
+       [ "$(cat /etc/resolv.conf)" = "$(printf "${NEWCONF}")" ] && exit 0
+fi
+
+# Create our resolv.conf now
+printf "${NEWCONF}" > /etc/resolvconf/run/resolv.conf
+
+# Restart nscd if it's running
+if [ -x /etc/init.d/nscd ] ; then
+       /etc/init.d/nscd --nodeps --quiet conditionalrestart
+fi
+
+# Notify users of the resolver
+for x in /etc/resolvconf/update-libc.d/* ; do
+       [ -e "${x}" ] && "${x}" "$@"
+done
+
+# vim: ts=4 :