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