terom@525: #!/bin/bash terom@525: # vim: set ft=sh : terom@525: terom@525: set -ue terom@525: terom@525: ROOT=$(pwd) terom@525: terom@530: BIN=bin terom@525: PROCESS_ZONE=$BIN/process-zone terom@525: EXPAND_ZONE=$BIN/expand-zone terom@530: UPDATE_SERIAL=$BIN/update-serial terom@525: terom@530: SETTINGS=settings terom@530: ZONES=zones terom@530: SERIALS=serials terom@525: terom@525: PROCESS_ARGS='--input-charset latin-1' terom@525: terom@525: FORWARD_MX=mail terom@525: REVERSE_ZONE=194.197.235 terom@525: REVERSE_DOMAIN=paivola.fi terom@525: terom@533: NAMED_CHECKZONE=/usr/sbin/named-checkzone terom@533: terom@530: ## options terom@530: IS_TTY= terom@531: terom@531: LOG=y terom@531: LOG_INFO= terom@531: LOG_DEBUG= terom@531: LOG_CMD= terom@531: terom@530: UPDATE_FORCE= terom@531: UPDATE_NOOP= terom@531: UPDATE_DIFF= terom@530: SERIAL_NOUPDATE= terom@530: terom@530: function help_args { terom@530: local prog=$1 terom@530: terom@530: cat <&2 terom@525: } terom@525: terom@530: function log_color { terom@530: local code=$1; shift terom@530: terom@530: if [ $IS_TTY ]; then terom@530: echo $'\e[0;'${code}'m'"$*"$'\e[00m' >&2 terom@530: else terom@530: echo "$*" >&2 terom@530: fi terom@530: } terom@530: terom@530: function log_error { terom@530: log_color 31 "$*" terom@530: } terom@530: terom@530: function log { terom@531: [ $LOG ] && log_msg "$*" || true terom@530: } terom@530: terom@530: function log_info { terom@531: [ $LOG_INFO ] && log_color 36 " $*" || true terom@530: } terom@530: terom@530: function log_debug { terom@531: [ $LOG_DEBUG ] && log_color 32 " $*" || true terom@530: } terom@530: terom@530: function log_cmd { terom@531: [ $LOG_CMD ] && log_color 35 " \$ $*" || true terom@531: } terom@531: terom@531: # XXX: broken terom@531: function log_stack { terom@531: local level=1 terom@531: terom@531: while info=$(caller $level); do terom@531: echo $info | read line sub file terom@531: terom@531: log_msg "$file:$lineno $sub()" terom@531: terom@531: level=$(($level + 1)) terom@531: done terom@531: } terom@531: terom@531: function fail { terom@531: func=$(caller 1 | cut -d ' ' -f 2) terom@531: terom@531: log_error "$func: $*" terom@531: terom@531: exit 2 terom@530: } terom@530: terom@525: function die { terom@530: log_error "$*" terom@525: exit 1 terom@525: } terom@525: terom@530: function cmd { terom@530: log_cmd "$@" terom@530: terom@530: "$@" || die "Failed" terom@530: } terom@530: terom@530: function run_cmd { terom@525: local msg=$1; shift terom@525: terom@530: log_info "$msg... " terom@525: terom@530: cmd "$@" terom@525: } terom@525: terom@530: function indent () { terom@531: local indent=$1; shift terom@531: terom@530: "$@" | ( terom@530: while read line; do terom@531: echo "$indent$line" terom@530: done terom@530: ) || exit $? terom@530: } terom@530: terom@530: ## test terom@525: [ -d $SETTINGS ] || die "Missing settings: $SETTINGS" terom@525: [ -d $SERIALS ] || die "Missing serials: $SERIALS" terom@525: [ -d $ZONES ] || die "Missing zones: $ZONES" terom@525: terom@530: ## functions terom@531: function check_update { terom@531: # target terom@531: local dst=$1; shift terom@531: terom@532: log_debug "check_update: $dst" terom@531: terom@531: # need update? terom@531: local update= terom@531: terom@531: if [ ! -e $dst ] || [ $UPDATE_FORCE ]; then terom@531: log_debug " update forced" terom@531: update=y terom@531: fi terom@531: terom@531: # check deps terom@531: for dep in "$@"; do terom@531: # don't bother checking if already figured out terom@531: [ $update ] && continue terom@531: terom@531: # check terom@531: terom@531: if [ $dst -ot $dep ]; then terom@531: log_debug " changed: $dep" terom@531: update=y terom@531: fi terom@531: done terom@531: terom@531: [ ! $update ] && log_debug " up-to-date" terom@531: terom@531: # return terom@531: [ $update ] terom@531: } terom@531: terom@531: function do_update { terom@531: local dst=$1; shift terom@530: local tmp=$dst.new terom@530: terom@532: log_debug "update: $dst" terom@531: cmd "$@" > $tmp terom@530: terom@531: # compare terom@531: if [ -e $dst ] && [ $UPDATE_DIFF ]; then terom@531: log_debug " changes:" terom@531: terom@531: # terse terom@531: indent " " diff --unified=1 $dst $tmp terom@531: fi terom@531: terom@531: if [ $UPDATE_NOOP ]; then terom@531: # cleanup terom@532: log_debug " no-op" terom@531: terom@531: cmd rm $tmp terom@531: else terom@531: # commit terom@532: log_debug " update" terom@531: terom@531: cmd mv $tmp $dst terom@531: fi terom@531: } terom@531: terom@531: function update { terom@531: local dst=$1; shift; terom@531: terom@531: local sep= terom@531: local dep=() terom@531: local cmd=() terom@531: terom@531: for arg in "$@"; do terom@531: if [ $arg == '--' ]; then terom@531: sep=y terom@530: fi terom@530: terom@531: if [ $sep ]; then terom@531: cmd=("${cmd[@]:-}" "$arg") terom@531: else terom@531: dep=("${dep[@]:-}" "$arg") terom@531: fi terom@531: done terom@531: terom@531: [ ! $sep ] && fail "Invalid args given: $@" terom@531: terom@531: check_update $dst "${dep[@]}" && do_update $dst "${cmd[@]}" || true terom@530: } terom@530: terom@530: ## bin wrappers terom@530: function update_serial { terom@531: local serial=$1; shift terom@531: local old=$(cat $serial) terom@531: terom@531: log_info "Updating serial: $serial" terom@531: terom@531: cmd $UPDATE_SERIAL $* $serial terom@531: terom@531: local new=$(cat $serial) terom@531: terom@531: log_debug " $old -> $new" terom@530: } terom@530: terom@525: function expand_zone { terom@525: local output=$1; shift terom@530: local src=$1; shift terom@525: terom@525: } terom@525: terom@525: function process_zone { terom@525: local output=$1; shift terom@530: local src=$1; shift terom@525: terom@531: check_update $output $src && update $output $PROCESS_ZONE $PROCESS_ARGS "$@" $src terom@525: } terom@525: terom@530: ## actions terom@525: function copy_zone_part { terom@525: local zone=$1 terom@525: local part=$2 terom@525: terom@525: local name=$zone.zone.$part terom@531: local src=$SETTINGS/$name terom@531: local dst=$ZONES/$name terom@525: terom@530: terom@531: if check_update $dst $src; then terom@531: log_info "Copying zone $zone.$part..." terom@531: terom@532: do_update $dst cat $src terom@531: else terom@531: log_info "Copying zone $zone.$part: not changed" terom@531: fi terom@525: } terom@525: terom@525: function update_zone { terom@525: local zone=$1 terom@531: terom@525: local name=$zone.zone terom@525: terom@531: local out=$ZONES/$name terom@531: local in=$SETTINGS/$zone.zone terom@531: local serial=$SERIALS/$zone.serial terom@530: terom@531: if check_update $out $in $serial; then terom@531: log_info "Generating $zone zone headers..." terom@531: terom@531: do_update $out \ terom@531: $EXPAND_ZONE $SETTINGS/$zone.zone \ terom@531: --serial $SERIALS/$zone.serial \ terom@531: --expand zones=$ROOT/$ZONES terom@531: else terom@531: log_info "Generating $zone zone headers: not changed" terom@531: fi terom@525: } terom@525: terom@525: function update_zone_view { terom@525: local zone=$1 terom@525: local view=$2 terom@525: terom@525: local name=$view/$zone.zone terom@525: terom@531: local out=$ZONES/$name terom@531: local in=$SETTINGS/$zone.zone terom@531: local serial=$SERIALS/$zone.serial terom@530: terom@531: if check_update $out $in $serial; then terom@531: log_info "Generating $zone:$view zone headers..." terom@531: terom@531: do_update $out \ terom@531: $EXPAND_ZONE $SETTINGS/$zone.zone \ terom@531: --serial $SERIALS/$zone.serial \ terom@531: --expand zones=$ROOT/$ZONES \ terom@531: --expand view=$view terom@531: else terom@531: log_info "Generating $zone:$view zone headers: not changed" terom@531: fi terom@531: } terom@531: terom@531: function update_hosts { terom@531: local dst=$1; shift terom@531: local src=$1; shift terom@531: terom@531: terom@531: if check_update $dst $src; then terom@531: log_info "Generating $dst..." terom@531: terom@531: do_update $dst $PROCESS_ZONE $PROCESS_ARGS $src "$@" terom@531: else terom@531: log_info "Generating $dst: not changed" terom@531: fi terom@525: } terom@525: terom@533: function check_zone { terom@533: local name=$1 terom@533: local file=$2 terom@533: terom@533: # test terom@533: if $NAMED_CHECKZONE -q $name $file; then terom@533: log_info "Check $file($name): OK" terom@533: else terom@533: log_error " Check $file($name): Failed:" terom@533: terom@533: indent " " $NAMED_CHECKZONE $name $file terom@533: fi terom@533: } terom@533: terom@525: function main { terom@530: # test tty terom@530: [ -t 1 ] && IS_TTY=y terom@525: terom@530: parse_args "$@" terom@530: terom@531: log "Updating serials..." terom@531: terom@530: if [ $SERIAL_NOUPDATE ]; then terom@533: log_info "Skipped" terom@530: else terom@531: update_serial $SERIALS/paivola.serial terom@531: update_serial $SERIALS/paivola-reverse.serial terom@530: fi terom@530: terom@530: log "Generating host zones..." terom@531: update_hosts $ZONES/external/paivola.zone.hosts $SETTINGS/paivola.txt --forward-zone terom@531: update_hosts $ZONES/internal/paivola.zone.hosts $SETTINGS/paivola.txt --forward-zone --forward-txt --forward-mx $FORWARD_MX terom@531: update_hosts $ZONES/paivola-reverse.zone.hosts $SETTINGS/paivola.txt --reverse-zone $REVERSE_ZONE --reverse-domain $REVERSE_DOMAIN terom@525: terom@530: log "Copying zone parts..." terom@530: copy_zone_part paivola auto terom@530: copy_zone_part paivola services terom@530: copy_zone_part paivola internal terom@530: copy_zone_part paivola external terom@525: terom@531: log "Updating zones headers..." terom@530: update_zone paivola-reverse terom@530: update_zone_view paivola internal terom@530: update_zone_view paivola external terom@533: terom@533: log "Testing zones..." terom@533: check_zone paivola.fi $ZONES/external/paivola.zone terom@533: check_zone paivola.fi $ZONES/external/paivola.zone terom@533: check_zone 235.197.194.in-addr.arpa $ZONES/paivola-reverse.zone terom@533: terom@525: } terom@525: terom@530: main "$@"