update update dns-new
authorTero Marttila <terom@paivola.fi>
Tue, 17 Dec 2013 00:04:00 +0200
branchdns-new
changeset 82 26a307558602
parent 81 9a23fca9167a
child 83 5a83f2abc0dd
update update
bin/update
lib/update
lib/update.args
lib/update.hg
lib/update.log
lib/update.logging
lib/update.operations
lib/update.updates
lib/update.utils
--- a/bin/update	Mon Dec 16 21:53:41 2013 +0200
+++ b/bin/update	Tue Dec 17 00:04:00 2013 +0200
@@ -3,280 +3,117 @@
 
 set -ue
 
-# resolve $0 -> bin/update
-self=$0
-while [ -L $self ]; do
-    tgt=$(readlink $self)
-
-    if [ "${tgt:0:1}" == "/" ]; then
-        self=$tgt
-    else
-        self=$(dirname $self)/$tgt
-    fi
-done
-
-# root dir
-ROOT=$(dirname $(dirname $self))
+if [ $0 == './update' ]; then
+    SRV=$(pwd)
+    OPT=./opt
+else
+    SRV=${SRV:-/srv/dns}
+    OPT=${SRV:-/srv/dns/opt}
+    cd $SRV
+fi
 
-BIN=$ROOT/bin
-LIB=$ROOT/lib
-VAR=$ROOT/var
-
-## Data paths
-# absolute path to data files; can be changed using -d
-ROOT=$(pwd)
+# charset for files under etc/
+CHARSET='utf-8'
 
-DATA=settings
-ZONES=$VAR/zones
-SERIALS=$VAR/serials
+# External bins
+NAMED_CHECKZONE=/usr/sbin/named-checkzone
 
-DHCP=$VAR/dhcp
-DHCP_DATA=$DATA/dhcp
-
-# global DHCP conf to test
 DHCPD=/usr/sbin/dhcpd
 DHCPD_CONF=/etc/dhcp/dhcpd.conf
 DHCPD_INIT=/etc/init.d/isc-dhcp-server
 
-# hg repo to commit
-REPO=$DATA
-
-## Settings used in lib
-# Hide files under repo in commit diff output..
-REPO_HIDE='*.serial'
-
-# data input charsets; arguments to ./bin/... python scripts
-HOSTS_FILE_ARGS='--input-charset utf-8'
-DHCP_FILE_ARGS='--input-charset utf-8'
-
-# External bins
-NAMED_CHECKZONE=/usr/sbin/named-checkzone
-
 HG=/usr/bin/hg
 HG_ARGS=(--config trusted.users=root)
 
 RNDC=/usr/sbin/rndc
-
-# Path to rndc key, must be readable to run..
 RNDC_KEY=/etc/bind/rndc.key
 
-## Library includes
-# Command-line argument handling
-source $LIB/update.args
-
-# Logging
-source $LIB/update.logging
-
-# Utility functions
-source $LIB/update.utils
-
-# Dependency-based updates
-source $LIB/update.updates
-
-# Operations; the functions called from run()
-source $LIB/update.operations
+# Library includes
+source lib/update
 
 ## Flags
 # set by do_reload_zone if zone data has actually been reloaded
 RELOAD_ZONES=
 
 ## Site settings, used as arguments to scripts
-# MX record to generate in hosts --forward-zone
-FORWARD_MX=mx0
-
-# IP network to generate reverse records for in --reverse-zone
-REVERSE_ZONE=194.197.235
-
 # Origin domain to generate reverse records for in --reverse-zone
 REVERSE_DOMAIN=paivola.fi
 
-# Views used
-VIEWS=(internal external)
-
-# Base domain zone for domains
-DOMAIN_BASE=paivola
-
-# List of actual domains used; will be linked to $DOMAIN_BASE
-DOMAINS=(paivola.fi paivola.net paivola.org paivola.info paivola.mobi xn--pivl-load8j.fi)
-
-# Names of dhcp conf file names
-DHCP_CONFS=( $(list_files $DHCP_DATA *.conf) )
-
-## Operate!
-# these functions are all defined in lib/update.operations
-
-# Update $ZONES/$DHCP host-files from $DATA
+## Do things
 function run_hosts {
-    ## Hosts
     # test
     log "Testing hosts..."
-        #                   data                            args...
-        check_hosts         $DATA/paivola.txt               --check-exempt ufc
-
-    # update
-    log "Generating host zones..."
-        #                   hosts                           data                args...
-        update_hosts        $ZONES/hosts/paivola:internal   $DATA/paivola.txt   --forward-zone --forward-txt
-        update_hosts        $ZONES/hosts/paivola:external   $DATA/paivola.txt   --forward-zone
-        update_hosts        $ZONES/hosts/194.197.235        $DATA/paivola.txt   --reverse-zone $REVERSE_ZONE --reverse-domain $REVERSE_DOMAIN
+    for hosts in $(list_files etc/hosts); do
+        log_warn "TODO: check_hosts $hosts"
+    done
 
-        
-        update_hosts        $ZONES/hosts/10                 $DATA/pvl.txt       --reverse-zone 10 --reverse-domain pvl -q
-        update_hosts        $ZONES/hosts/10.0               $DATA/test.pvl.txt 	--reverse-zone 10.0 --reverse-domain test.pvl -q
-        update_hosts        $ZONES/hosts/fdc4:4cef:395a     $DATA/test.pvl.txt 	--reverse-zone fdc4:4cef:395a --reverse-domain test.pvl -q
-        update_hosts        $ZONES/hosts/192.168            $DATA/pvl.txt       --reverse-zone 192.168 --reverse-domain pvl -q
+    log "Updating host zones..."
+    for hosts in $(list_files etc/hosts); do
+        update_hosts_forward    var/zones/hosts/$hosts      etc/hosts/$hosts
+    done
 
-        # XXX: unsupported --forward-zone with pvl.txt
-        # update_hosts    $ZONES/hosts/pvl                    $DATA/pvl.txt      --forward-zone
-        copy_hosts          $ZONES/hosts/pvl                $DATA/pvl.txt
-        copy_hosts          $ZONES/hosts/test.pvl           $DATA/test.pvl.txt
+    log "Updating DHCP hosts..."
+    for hosts in $(list_files etc/hosts); do
+        update_hosts_dhcp       var/dhcp/$hosts.conf        etc/hosts/$hosts
+    done
 }
 
-# Update $ZONES files
 function run_zones {
-    ## Includes
     log "Copying zone includes..."
-        #                   view            zone                    base
-        copy_zone           includes        paivola:internal        paivola.zone.internal
-        copy_zone           includes        paivola:external        paivola.zone.external
-        copy_zone           includes        paivola.auto            paivola.zone.auto
-        copy_zone           includes        paivola.services        paivola.zone.services
-        copy_zone           includes        paivola.aux             paivola.zone.aux
-
-    ## Serials
-    log "Updating serials..."
+    for zone in $(list_files etc/zones/includes); do
+        copy                var/zones/includes/$zone        etc/zones/includes/$zone
+    done
 
-        #                   zone            deps...
-        #   includes...
-        update_serial       pvl             $ZONES/hosts/pvl            $DATA/pvl.zone
-        update_serial       test.pvl        $ZONES/hosts/test.pvl       $DATA/test.pvl.zone
-        update_serial       10              $ZONES/hosts/10             $DATA/10.zone
-        update_serial       10.0            $ZONES/hosts/10.0           $DATA/10.0.zone
-        update_serial       fdc4:4cef:395a  $ZONES/hosts/fdc4:4cef:395a $DATA/fdc4:4cef:395a.zone
-        update_serial       192.168         $ZONES/hosts/192.168        $DATA/192.168.zone
-
-        update_serial       paivola         $ZONES/hosts/paivola:*      $DATA/paivola.zone          \
-            $ZONES/includes/paivola:*       \
-            $ZONES/includes/paivola.*
-
-        update_serial       194.197.235     $ZONES/hosts/194.197.235    $DATA/194.197.235.zone          
+    log "Updating zone serials..."
+    for zone in $(list_files etc/zones); do
+        update_serial       var/serials/$zone               etc/zones/$zone 
+    done
 
-    ## Zones
     log "Updating zones..."
-        #                   view        zone            base
-        update_zone         internal    pvl
-        update_zone         internal    test.pvl
+    for zone in $(list_files etc/zones); do
+        update_zone         var/zones/$zone                 etc/zones/$zone         var/serials/$zone
+    done
 
-        update_zone         internal    10
-        update_zone         internal    10.0
-        update_zone         internal    fdc4:4cef:395a
-        update_zone         internal    192.168
-
-        update_zone         common      194.197.235
-        link_zone           internal    194.197.235
-        link_zone           external    194.197.235
-
-    ## Test
     log "Testing zones..."
-        #                   view        zone            origin
-        check_zone          internal    10              10.in-addr.arpa
-        check_zone          internal    10.0            0.10.in-addr.arpa
-        check_zone          internal    fdc4:4cef:395a	a.5.9.3.f.e.c.4.4.c.d.f.ip6.arpa
-	
-        check_zone          internal    192.168         192.168.in-addr.arpa
-        check_zone          common      194.197.235     235.197.194.in-addr.arpa
-
-    ## Domains...
-    log "Linking domains..."
-        for view in "${VIEWS[@]}"; do
-            for zone in "${DOMAINS[@]}"; do
-                # choose input .zone to use
-                base=$(choose_zone $zone $DOMAIN_BASE)
-                
-                if [ $base != $DOMAIN_BASE ]; then
-                    # serial
-                    # XXX: not all zones use all these includes?
-                    update_serial   $base       $DATA/$base.zone    \
-                        $ZONES/hosts/paivola:*                      \
-                        $ZONES/includes/paivola:*                   \
-                        $ZONES/includes/paivola.*
-                fi
-
-                # link
-                update_zone     $view       $zone           $base
-
-                # test
-                check_zone      $view       $zone           $zone
-            done
-        done
-}
-
-# Update $DHCP files from $DATA/dhcp
-function run_dhcp {
-    log_debug "DHCP_CONFS: ${DHCP_CONFS[*]}"
-
-    log "Copying DHCP configs..."
-        for conf in "${DHCP_CONFS[@]}"; do
-            # XXX: ei toimi, koska conf:it riippuu toisistaan include:ien takia
-            # check_dhcp_conf     $conf
-
-            #                   conf               base
-            copy_dhcp_conf      $conf
-        done
-
-    log "Testing dhcp..."
-        # checks the whole dhcpd.conf, with all includes..
-        check_dhcp
-}
-
-# Runs DHCP checks, once DNS hosts have been updated
-function run_dhcp_check {
-    log "Testing dhcp hosts..."
-        for conf in "${DHCP_CONFS[@]}"; do
-            check_dhcp_hosts    $DHCP/$conf.conf
-        done
+    for zone in $(list_files etc/zones); do
+        # check_zone          var/zones/$zone     $zone
+        log_warn "TODO: check_zone $zone"
+    done
 }
 
 function run_deploy {
-    ## Reload zones
     log "Reload zones..."
         reload_zones
 
-    ## DHCP
-    run_dhcp_check
-
     log "Reload dhcp..."
         reload_dhcp
 
-    ## Commit
-    log "Commit data..."
-        commit_data
+    log "Commit etc..."
+        commit_data     etc
 }
 
 ## Main entry point
 function main {
-    # test tty
-    [ -t 1 ] && IS_TTY=y
-    
     parse_args "$@"
 
     ## Input dirs
-    [ -d $ROOT/$DATA ] || die "Missing data: $ROOT/$DATA"
+    for dir in etc etc/dhcp etc/zones; do
+        [ -d $dir ] || die "Missing directory: $dir"
+    done
     
     ## Output dirs
-    for dir in $VAR $DHCP $ZONES $SERIALS; do
+    for dir in var var/dhcp var/zones var/serials; do
         ensure_dir  $dir
     done
     
     # sub-$ZONES
-    for dir in "common" "hosts" "includes" "${VIEWS[@]}"; do
-        ensure_dir  $ZONES/$dir
+    for dir in var/zones/hosts var/zones/includes; do
+        ensure_dir  $dir
     done
 
     ## Go
     run_hosts
     run_zones
-    run_dhcp
     run_deploy
 }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/update	Tue Dec 17 00:04:00 2013 +0200
@@ -0,0 +1,7 @@
+#!/bin/bash
+#
+## Library includes
+for lib in lib/update.*; do
+    source $lib
+done
+
--- a/lib/update.args	Mon Dec 16 21:53:41 2013 +0200
+++ b/lib/update.args	Tue Dec 17 00:04:00 2013 +0200
@@ -1,7 +1,7 @@
 #!/bin/bash
 # vim: set ft=sh :
 #
-# Command-line option handling
+# Command-line options
 
 # use color output?
 IS_TTY=
@@ -70,6 +70,9 @@
 
 ## Parse any command-line arguments, setting the global options vars.
 function parse_args {
+    # test tty
+    [ -t 1 ] && IS_TTY=y
+ 
     OPTIND=1
 
     while getopts 'hd:qvDVpFSsnCcm:Rr' opt "$@"; do
@@ -79,7 +82,7 @@
                 exit 0
             ;;
 
-            d)  ROOT="$OPTARG" ;;
+            d)  SRV="$OPTARG" ;;
 
             q)  
                 LOG= 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/update.hg	Tue Dec 17 00:04:00 2013 +0200
@@ -0,0 +1,51 @@
+#!/bin/bash
+#
+# HG wrappers
+
+## Run `hg ...` within $REPO.
+function hg {
+    cmd $HG "${HG_ARGS[@]}" "$@"
+}
+
+## Does the repo have local modifications?
+function hg_modified {
+    hg id | grep -q '+'
+}
+
+## Output possible -u flag for commit.
+function hg_user {
+    if [ ${SUDO_USER:-} ]; then
+        echo '-u' "$SUDO_USER"
+
+    elif [ $HOME ] && [ -e $HOME/.hgrc ]; then
+        debug "using .hgrc user"
+        echo ''
+
+    else
+        echo '-u' "$USER"
+    fi
+}
+
+## Show changes in repo
+#   hg_diff     [path ...]
+function hg_diff {
+    hg diff "$@"
+}
+
+## Commit changes in repo, with given message:
+#
+#   hg_commit   $msg
+#
+# Automatically determines possible -u to use when running with sudo.
+function hg_commit {
+    local msg=$1; shift
+    local user_opt=$(hg_user)
+    local msg_opt=
+
+    [ $msg ] && msg_opt=('-m' "$msg")
+    
+    debug "$user_opt: $msg"
+    hg commit ${user_opt[@]} ${msg_opt[@]}
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/update.log	Tue Dec 17 00:04:00 2013 +0200
@@ -0,0 +1,99 @@
+#!/bin/bash
+# vim: set ft=sh :
+#
+# Logging output
+
+# Output message to stderr.
+function log_msg {
+    echo "$*" >&2
+}
+
+# Output message to stderr, optionally with given color, if TTY.
+function log_color {
+    local code=$1; shift
+
+    if [ $IS_TTY ]; then
+        echo $'\e['${code}'m'"$*"$'\e[00m' >&2
+    else
+        echo "$*" >&2
+    fi
+}
+
+## Log at various log-levels
+# plain
+function log {
+    [ $LOG          ] && log_msg            "$*"            || true
+}
+
+function log_error {
+    [ $LOG_ERROR    ] && log_color '31'     "$*"            || true
+}
+
+function log_warn {
+    [ $LOG_WARN     ] && log_color '33'     "$*"            || true
+}
+
+function log_force {
+    [ $LOG_FORCE    ] && log_color '2;33'   "  $*"          || true
+}
+
+function log_update {
+    [ $LOG_UPDATE   ] && log_color '36'     "  $*"          || true
+}
+
+function log_noop {
+    [ $LOG_NOOP     ] && log_color '2;34'   "  $*"          || true
+}
+
+function log_skip {
+    [ $LOG_SKIP     ] && log_color '1;34'   "  $*"          || true
+}
+
+function log_debug {
+    [ $LOG_DEBUG    ] && log_color '32'     "    $*"        || true
+}
+
+function log_cmd {
+    [ $LOG_CMD      ] && log_color '35'     "        \$ $*" || true
+}
+
+# Output stacktrace, broken.
+function log_stack {
+    local level=1
+
+    while info=$(caller $level); do
+        echo $info | read line sub file
+
+        log_msg "$file:$lineno $sub()"
+
+        level=$(($level + 1))
+    done
+}
+
+# Output calling function's name.
+function func_caller {
+    caller 1 | cut -d ' ' -f 2
+}
+
+### High-level logging output
+# Log with func_caller at log_debug
+function debug {
+    printf -v prefix "%s" $(func_caller)
+
+    log_debug "$prefix: $*"
+}
+
+# Log with func_caller at log_error and exit, intended for internal errors...
+function fail {
+    log_error "$(func_caller): $*"
+
+    exit 2
+}
+
+# Log at log_error and exit
+function die {
+    log_error "$*"
+    exit 1
+}
+
+
--- a/lib/update.logging	Mon Dec 16 21:53:41 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,100 +0,0 @@
-#!/bin/bash
-# vim: set ft=sh :
-#
-# Logging output
-
-# Output message to stderr.
-function log_msg {
-    echo "$*" >&2
-}
-
-# Output message to stderr, optionally with given color, if TTY.
-function log_color {
-    local code=$1; shift
-
-    if [ $IS_TTY ]; then
-        echo $'\e['${code}'m'"$*"$'\e[00m' >&2
-    else
-        echo "$*" >&2
-    fi
-}
-
-## Log at various log-levels
-
-function log_error {
-    [ $LOG_ERROR ] && log_color '31' "$*"
-}
-
-function log_warn {
-    [ $LOG_WARN ] && log_color '33' "$*" || true
-}
-
-# plain
-function log {
-    [ $LOG ] && log_msg "$*" || true
-}
-
-function log_force {
-    [ $LOG_FORCE ] && log_color '2;33' "  $*" || true
-}
-
-function log_update {
-    [ $LOG_UPDATE ] && log_color '36' "  $*" || true
-}
-
-function log_noop {
-    [ $LOG_NOOP ] && log_color '2;34' "  $*" || true
-}
-
-function log_skip {
-    [ $LOG_SKIP ] && log_color '1;34' "  $*" || true
-}
-
-function log_debug {
-    [ $LOG_DEBUG ] && log_color 32 "    $*" || true
-}
-
-function log_cmd {
-    [ $LOG_CMD ] && log_color 35 "        \$ $*" || true
-}
-
-# Output stacktrace, broken.
-function log_stack {
-    local level=1
-
-    while info=$(caller $level); do
-        echo $info | read line sub file
-
-        log_msg "$file:$lineno $sub()"
-
-        level=$(($level + 1))
-    done
-}
-
-# Output calling function's name.
-function func_caller {
-    caller 1 | cut -d ' ' -f 2
-}
-
-### High-level logging output
-# Log with func_caller at log_debug
-function debug {
-    printf -v prefix "%s" $(func_caller)
-
-    log_debug "$prefix: $*"
-}
-
-# Log with func_caller at log_error and exit, intended for internal errors...
-function fail {
-    log_error "$(func_caller): $*"
-
-    exit 2
-}
-
-# Log at log_error and exit
-function die {
-    log_error "$*"
-    exit 1
-}
-
-
--- a/lib/update.operations	Mon Dec 16 21:53:41 2013 +0200
+++ b/lib/update.operations	Tue Dec 17 00:04:00 2013 +0200
@@ -3,7 +3,7 @@
 #
 # Operations on zonefiles/hosts/whatever
 
-function link_generic {
+function link {
     local out=$1
     local tgt=$2
 
@@ -17,7 +17,7 @@
     fi
 }
 
-function copy_generic {
+function copy {
     local out=$1
     local src=$2
 
@@ -25,7 +25,7 @@
         log_update "Copying $out <- $src..."
 
         do_update $out \
-            cat $ROOT/$src
+            cat $src
     else
         log_skip "Copying $out <- $src: not changed"
     fi
@@ -33,9 +33,9 @@
 
 ## Run check-command on given file, outputting results:
 #
-#   check_generic   $src    $cmd $args...
+#   check    $src    $cmd $args...
 #
-function check_generic {
+function check {
     local src=$1; shift
     local cmd=$1; shift
 
@@ -65,25 +65,38 @@
     local out=$zone
     local src=$base
 
-    copy_generic $out $src
+    copy $out $src
 }
 
-## Generate hosts from input zone data using $BIN/process-zone:
-#
-#   update_hosts    $ZONES/$zone    $DATA/$base
+## Generate forward zone from hosts hosts using pvl.hosts-dns:
 #
-# Writes process-zone'd data to $zone, deps on $base.
-function update_hosts {
-    local zone=$1; shift
-    local base=$1; shift
+#   update_hosts out/hosts/$hosts in/hosts/$hosts
+function update_hosts_forward {
+    local out=$1
+    local src=$2
 
-    if check_update $zone $base; then
-        log_update "Generating hosts $zone <- $base..."
+    if check_update $out $src; then
+        log_update "Generating hosts zone $out <- $src..."
+    
+        do_update $out $OPT/bin/pvl.hosts-dns $src \
+            --hosts-charset=$CHARSET
+    
+    else
+        log_skip "Generating hosts $out <- $src: not changed"
+    fi
+}
 
-        do_update $zone \
-            $BIN/process-zone $HOSTS_FILE_ARGS $ROOT/$base "$@"
+function update_hosts_dhcp {
+    local out=$1
+    local src=$2
+
+    if check_update $out $src; then
+        log_update "Generating DHCP hosts $out <- $src..."
+
+        do_update $out $OPT/bin/pvl.hosts-dhcp $src \
+            --hosts-charset=$CHARSET
     else
-        log_skip "Generating hosts $zone <- $base: not changed"
+        log_skip "Generating DHCP hosts $out <- $src: not changed"
     fi
 }
 
@@ -96,29 +109,26 @@
     local serial=$1
 
     # read
-    local old=$(test -e $ROOT/$serial && cat $ROOT/$serial || echo '')
+    local old=$(test -e $serial && cat $serial || echo '')
 
-
-    cmd $BIN/update-serial $ROOT/$serial
+    cmd $OPT/bin/pvl.dns-serial $serial
     
     # read
-    local new=$(cat $ROOT/$serial)
+    local new=$(cat $serial)
         
     debug "  $old -> $new"
 }
 
 
-## Generate new serial for zone using $BIN/update-serial, if the zone data has changed:
+## Generate new serial for zone using pvl.dns-serial, if the zone data has changed:
 #
 #   update_serial   $zone   $deps...
 #
 # Supports SERIAL_FORCE/NOOP.
 # Updates $SERIALS/$zone.serial.
 function update_serial {
-    local zone=$1; shift
+    local serial=$1; shift
     
-    local serial=$SERIALS/$zone.serial
-
     # test
     if [ $SERIAL_FORCE ]; then
         log_force "Updating $serial: forced"
@@ -148,7 +158,7 @@
     local out=$SERIALS/$zone.serial
     local tgt=$SERIALS/$base.serial
 
-    link_generic $out $tgt
+    link $out $tgt
 }
 
 ## Update zone file verbatim from source:
@@ -164,7 +174,7 @@
     local out=$ZONES/$view/$zone
     local src=$DATA/$base
 
-    copy_generic $out $src
+    copy $out $src
 }
 
 ## Return the first zone that exists under $DATA/$name.zone
@@ -183,29 +193,19 @@
     die "Unable to find zone in $DATA/*.zone: $@"
 }
 
-## Expand zone file from source using $BIN/expand-zone:
+## Expand zone file from source using pvl.dns-zone:
 #
 #   update_zone $view   $zone   [$base]
-#
-# Updates $ZONES/$view/$zone from $DATA/$base.{zone,serial} using $BIN/expand-zone.
 function update_zone {
-    local view=$1
-    local zone=$2
-    local base=${3:-$zone}
-
-    # zones <- data+serial file
-    local out=$ZONES/$view/$zone
-    local src=$DATA/$base.zone
-    local serial=$SERIALS/$base.serial
+    local out=$1
+    local src=$2
+    local serial=$3
 
     if check_update $out $src $serial; then
         log_update "Generating $out <- $src..." 
 
-        do_update $out \
-            $BIN/expand-zone $ROOT/$src \
-                --serial $ROOT/$serial              \
-                --expand zones=$(abspath $ZONES)    \
-                --expand view=$view
+        do_update $out $OPT/bin/pvl.dns-zone $src \
+                --serial $(cat $serial)
     else
         log_skip "Generating $out <- $src: not changed" 
     fi
@@ -226,7 +226,7 @@
     local out=$ZONES/$view/$zone
     local tgt=$(choose_link $out $ZONES/$view/$base $ZONES/common/$base)
 
-    link_generic $out $tgt
+    link $out $tgt
 }
 
 ## Link dhcp file directly from data to $DHCP
@@ -237,7 +237,7 @@
     local out=$DHCP/$conf.conf
     local tgt=$(choose_link $out $DHCP/$base.conf $DHCP_DATA/$base.conf)
 
-    link_generic $out $tgt
+    link $out $tgt
 }
 
 ## Copy dhcp conf from data to $DHCP
@@ -248,56 +248,29 @@
     local out=$DHCP/$conf.conf
     local src=$DHCP_DATA/$base.conf
 
-    copy_generic $out $src
+    copy $out $src
 }
 
-## Test hosts zone for validity:
+## Test hosts zone for validity using pvl.hosts-check:
 #
-#   check_hosts     $DATA/$hosts    --check-exempt ...
-#
-# Fails if the check fails.
+#   check_hosts     .../hosts
 function check_hosts {
     local hosts=$1; shift 1
-
-    local cmd=($BIN/process-zone $HOSTS_FILE_ARGS $ROOT/$hosts --check-hosts "$@")
-
-    if "${cmd[@]}" -q; then
-        log_skip "Check $hosts: OK"
-    else
-        log_error "  Check $hosts: Failed"
-
-        indent "    " "${cmd[@]}"
-
-        exit 1
-    fi
+    
+    # TODO
+    check $hosts \
+        $OPT/bin/pvl.hosts-check $hosts
 }
 
 ## Test zone file for validity using named-checkzone:
 #
-#   check_zone      $view       $zone       $origin
-#
-# Uses the zonefile at $ZONES/$view/$zone, loading it with given initial $ORIGIN.
-# Fails if the check fails.
+#   check_zone      ..../$zone $origin
 function check_zone {
-    local view=$1
-    local zone=$2
-    local origin=$3
-
-    local src=$ZONES/$view/$zone
+    local zone=$1
+    local origin=$2
 
-    local cmd=($NAMED_CHECKZONE $origin $ROOT/$src)
-
-    # test
-    # XXX: checkzone is very specific about the order of arguments, -q must be first
-    if cmd_test $NAMED_CHECKZONE -q $origin $ROOT/$src; then
-        log_skip "Check $src ($origin): OK"
-    else
-        log_error "  Check $src ($origin): Failed:"
-
-        indent "    " "${cmd[@]}"
-        
-        exit 1
-    fi
+    # checkzone is very specific about the order of arguments, -q must be first
+    check $zone $NAMED_CHECKZONE $origin $zone
 }
 
 ## Test DHCP configuration for validity using dhcpd -t:
@@ -314,7 +287,7 @@
         return 0
     fi
 
-    check_generic $conf \
+    check $conf \
         $DHCPD -cf $conf -t
 }
 
@@ -328,26 +301,8 @@
     check_dhcp $DHCP_DATA/$conf.conf
 }
 
-## Test DHCP hosts source configuration for invalid fixed-address stanzas:
-#
-#   check_dhcp_hosts    $hosts_conf
-#
-function check_dhcp_hosts {
-    local hosts=$1
-   
-    # XXX: still too unclear
-    local src=$hosts #$DHCP_DATA/$hosts.conf
-
-    # set in do_reload_zones below
-    if [ $RELOAD_ZONES ]; then
-        check_generic $src \
-            $BIN/check-dhcp-hosts $DHCP_FILE_ARGS $ROOT/$src
-    else
-        log_noop "Check $src: skipped; did not reload DNS zones"
-    fi
-}
-
-# Run rndc reload
+### Deploy
+## Run rndc reload
 function do_reload_zones {
     # run
     indent "        rndc: " \
@@ -432,7 +387,8 @@
     fi
 }
 
-## Perform `hg commit` for $DATA
+### Commit
+## Perform `hg commit`
 function do_commit {
     local msg=$1
 
@@ -442,16 +398,15 @@
 }
 
 
-## Commit changes in $DATA to version control:
+## Commit changes to version control:
 #
 #   commit_data
 #
-# Invokes `hg commit` in the $REPO, first showing the diff.
+# Invokes `hg commit`, first showing the diff.
 function commit_data {
-    local repo=$REPO
     local commit_msg="$COMMIT_MSG"
 
-    local msg="Commit changes in $repo"
+    local msg="Commit changes"
 
     # operate?
     if [ $COMMIT_FORCE ]; then
@@ -473,4 +428,3 @@
         do_commit "$commit_msg"
     fi
 }
-
--- a/lib/update.updates	Mon Dec 16 21:53:41 2013 +0200
+++ b/lib/update.updates	Tue Dec 17 00:04:00 2013 +0200
@@ -36,10 +36,10 @@
         [ $update ] && continue
 
         # check
-        if [ ! -e $ROOT/$dep ]; then
+        if [ ! -e $dep ]; then
             fail "$out: Missing source: $dep"
 
-        elif [ $ROOT/$out -ot $ROOT/$dep ]; then
+        elif [ $out -ot $dep ]; then
             debug "  update: $dep"
             update=y
         else
@@ -64,14 +64,14 @@
     local tmp=$out.new
 
     debug "$out"
-    cmd "$@" > $ROOT/$tmp
+    cmd "$@" > $tmp
 
     # compare
-    if [ -e $ROOT/$out ] && [ $UPDATE_DIFF ]; then
+    if [ -e $out ] && [ $UPDATE_DIFF ]; then
         debug "  changes:"
 
         # terse
-        indent "        " diff --unified=1 $ROOT/$out $ROOT/$tmp || true
+        indent "        " diff --unified=1 $out $tmp || true
     fi
     
     # deploy
@@ -79,12 +79,12 @@
         # cleanup
         debug "  no-op"
 
-        cmd rm $ROOT/$tmp
+        cmd rm $tmp
     else
         # commit
         debug "  deploy"
 
-        cmd mv $ROOT/$tmp $ROOT/$out
+        cmd mv $tmp $out
     fi
 }
 
@@ -99,7 +99,7 @@
     local tgt=-
 
     for tgt in "$@"; do
-        [ $tgt != $out ] && [ -e $ROOT/$tgt ] && break
+        [ $tgt != $out ] && [ -e $tgt ] && break
     done
     
     echo $tgt
@@ -116,9 +116,9 @@
     local lnk=$1
     local tgt=$2
 
-    [ ! -e $ROOT/$tgt ] && fail "$tgt: target does not exist"
+    [ ! -e $tgt ] && fail "$tgt: target does not exist"
     
-    [ ! -e $ROOT/$lnk ] || [ $(readlink $ROOT/$lnk) != $ROOT/$tgt ]
+    [ ! -e $lnk ] || [ $(readlink $lnk) != $tgt ]
 }
 
 ## Update symlink to point to target:
@@ -129,6 +129,6 @@
     local lnk=$1
     local tgt=$2
 
-    cmd ln -sf $ROOT/$tgt $ROOT/$lnk
+    cmd ln -sf $tgt $lnk
 }
 
--- a/lib/update.utils	Mon Dec 16 21:53:41 2013 +0200
+++ b/lib/update.utils	Tue Dec 17 00:04:00 2013 +0200
@@ -1,9 +1,7 @@
 #!/bin/bash
-# vim: set ft=sh :
 #
 # Utility functions
 
-
 ### Command execution
 ## Execute command, possibly logging its execution.
 #
@@ -16,7 +14,6 @@
     "$@" || die "Failed"
 }
 
-### Command execution
 ## Execute command as a test, logging its execution at log_cmd
 #
 #   cmd_test $cmd... && ... || ...
@@ -32,12 +29,9 @@
 #   indent  "    " $cmd...
 #
 # Output is kept on stdout, exit status is that of the given command.
-# Also logs the executed command at log_cmd level..
 function indent () {
     local indent=$1; shift
 
-    log_cmd "$@"
-
     "$@" | sed "s/^/$indent/"
 
     return ${PIPESTATUS[0]}
@@ -45,24 +39,25 @@
 
 
 ### FS utils
-# Create dir in $ROOT if not exists.
+# Create dir if not exists.
 function ensure_dir {
     local dir=$1
 
-    if [ ! -d $ROOT/$dir ]; then
+    if [ ! -d $dir ]; then
         log_warn "Creating output dir: $dir"
-        cmd mkdir $ROOT/$dir
+        cmd mkdir $dir
     fi
 }
 
-## Output absolute path from $ROOT:
+## Output absolute path
 #
 #   abspath $path
 #
+# XXX: improve...?
 function abspath () {
     local path=$1
 
-    echo "$ROOT/$path"
+    echo "$SRV/$path"
 }
 
 ## List names of files in dir:
@@ -71,10 +66,13 @@
 #
 function list_files {
     local dir=$1
-    local glob=$2
+    local glob=${2:-*}
     local name=
 
     for file in $dir/$glob; do
+        # only files
+        [ -f $file ] || continue
+
         # strip prefix
         name=${file#$dir/}
         name=${name%$glob}
@@ -83,50 +81,11 @@
     done
 }
 
-### HG wrappers
-# Run `hg ...` within $REPO.
-function hg {
-    local repo=$REPO
-
-    cmd $HG -R $ROOT/$repo "${HG_ARGS[@]}" "$@"
-}
-
-# Does the repo have local modifications?
-function hg_modified {
-    hg id | grep -q '+'
+## List names of files in dirs..
+function list_dirs {
+    for dir in "$@"; do
+        for file in $dir/*; do
+            echo -n "${file#$dir/}"
+        done
+    done
 }
-
-# Output possible -u flag for commit.
-function hg_user {
-    if [ ${SUDO_USER:-} ]; then
-        echo '-u' "$SUDO_USER"
-
-    elif [ $HOME ] && [ -e $HOME/.hgrc ]; then
-        debug "using .hgrc user"
-        echo ''
-
-    else
-        echo '-u' "$USER"
-    fi
-}
-
-# Show changes in repo
-function hg_diff {
-    # just stat hidden files, but show the rest
-    hg diff --stat -I "$REPO/$REPO_HIDE"
-    hg diff -X "$REPO/$REPO_HIDE"
-}
-
-## Commit changes in repo, with given message:
-#
-#   hg_commit   $msg
-#
-function hg_commit {
-    local msg=$1
-    local user_opt=$(hg_user)
-    
-    debug "$user_opt: $msg"
-    hg commit $user_opt -m "$msg"
-}
-
-