#!/bin/sh # Utility to regenerate an nsd database from parts # Copyright (c) 2013 Taylor R. Campbell # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY EXPRESS # OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN # NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, # OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, # EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # Example: # # You are the administrator of the multi-user host eland.example.com. # Your users, don, jon, and ron, have own the zones don.example.com, # jon.example.com, and ron.example.com. nsd is configured to use zones # in the files # # /etc/nsd/zones/don.zone # /etc/nsd/zones/jon.zone # /etc/nsd/zones/ron.zone # # but the users want to update their own zones without privileges. So # they own # # /home/don/myzone # /home/ron/dns/zone # /home/jon/public_html/jon.example.com # # and store their zone files there. You create symlinks # # /etc/nsd/zones/don.zone.link -> /home/don/myzone # /etc/nsd/zones/jon.zone.link -> /home/ron/dns/zone # /etc/nsd/zones/ron.zone.link -> /home/jon/public_html/jon.example.com # # and every time you want nsd to publish whatever don, jon, or ron have # put in their files, you run `nsd-update'. You might create a fifo # # mkfifo -m 0622 /var/run/nsd-update.fifo # # and use # # fifowatchd -p /var/run/nsd-updated.pid -d 3 /var/run/nsd-update.fifo \ # /bin/sh -c \ # '/usr/local/sbin/nsd-update 2>&1 \ # | logger -t nsd-update -p daemon.notice' # # to watch it, so that anyone can run # # echo >> /var/run/nsd-update.fifo # # to request that nsd's zones be updated. Any zone files with errors # will be ignored. set -Ceu # Set some parameters. : ${NSD_BINDIR:='@NSD_BINDIR@'} : ${NSD_CONF:='@NSD_CONF@'} # Make NSD_CONF an absolute pathname. case $NSD_CONF in /*) ;; */*) NSD_CONF="$(cd -- "${NSD_CONF%/*}" && pwd)/${NSD_CONF##*/}";; *) NSD_CONF="$(pwd)/${NSD_CONF}";; esac NSD_CHECKCONF="${NSD_BINDIR}/nsd-checkconf" NSD_CHECKZONE="${NSD_BINDIR}/nsd-checkzone" NSD_DB="$("$NSD_CHECKCONF" -o database -- "$NSD_CONF")" NSD_PIDFILE="$("$NSD_CHECKCONF" -o pidfile -- "$NSD_CONF")" NSD_ZONESDIR="$("$NSD_CHECKCONF" -o zonesdir -- "$NSD_CONF")" # XXX Gronk. Seems to be no nice way to get this in the config file. case $NSD_PIDFILE in */*) NSD_PIDDIR="${NSD_PIDFILE%/*}";; *) NSD_PIDDIR="";; esac : ${NSD_LOCKFILE:="${NSD_PIDDIR:+${NSD_PIDDIR}/}nsd.lock"} # Utilities. progname="${0##*/}" note () { printf >&2 '%s: ' "$progname" printf >&2 "$@" printf >&2 '\n' } warn () { note "warning: $@" } fail () { note "error: $@" exit 1 } rm_variables= rm_on_exit () { rm_variables="${rm_variables+${rm_variables} }$@" } clean () { for var in $rm_variables; do eval "pathname=\$${var}" if [ -n "$pathname" ]; then rm -f -- "$pathname" fi done } reset_clean () { rm_variables= trap clean EXIT HUP INT TERM } reset_clean # Parse arguments. if [ $# -ne 0 ]; then fail 'usage: %s' "$progname" fi # Enter the zones directory in case any pathnames are relative. if [ -n "$NSD_ZONESDIR" ]; then cd -- "$NSD_ZONESDIR" fi # Lock the zone database. Wait at most three seconds before failing. lockfd=3 exec 3>> "$NSD_LOCKFILE" flock --wait 3 "$lockfd" "$NSD_CHECKCONF" -o zones -- "$NSD_CONF" \ | ( reset_clean # subshell zonefile_tmp= rm_on_exit zonefile_tmp while read zone; do # Read the configuration file. zonefile="$("$NSD_CHECKCONF" -z "$zone" -o zonefile -- "$NSD_CONF")" # Sanity-check the link. zonefile_link="${zonefile}.link" if [ ! -h "$zonefile_link" ]; then # If there's no link, assume it is maintained some other # way, and don't complain. continue fi if [ ! -f "$zonefile_link" ]; then warn 'zone %s link is broken: %s -> %s' "$zone" "$zonefile_link" \ "$(readlink -- "$zonefile_link")" continue fi # Grab a snapshot of the new zone file. zonefile_tmp="${zonefile}.tmp" rm -f -- "$zonefile_tmp" cat < "$zonefile_link" > "$zonefile_tmp" if cmp -s -- "$zonefile" "$zonefile_tmp"; then # No changes; skip this. continue fi # All set. Check the zone. if "$NSD_CHECKZONE" "$zone" "$zonefile_tmp"; then # Zone file seems OK -- commit it. mv -f -- "$zonefile_tmp" "$zonefile" zonefile_tmp= else warn 'zone not changed because file is broken: %s' "$zone" # Leave the zone file snapshot around for post-mortem analysis. zonefile_tmp= fi done # Notify nsd. if ! pid="$(head -c 16 < "$NSD_PIDFILE")"; then warn 'can'\''t read pidfile; is nsd running?' elif ! kill -HUP "$pid"; then warn 'can'\''t send SIGHUP; is nsd running?' fi )