From 8f27989ba7524851b53140258390bb896674c9c4 Mon Sep 17 00:00:00 2001 From: "B. Stack" Date: Mon, 19 Aug 2024 12:30:40 -0400 Subject: update the utilites for current paths, rearrange whole package for 0.0.4-1 --- src/etc/bash_completion.d/updatezone | 13 + src/etc/ddtools/ipa.example.com.conf.example | 7 + src/etc/sysconfig/dhcpd-control | 5 + src/usr/bin/dhcpd-control | 478 +++++++++++++++++++++++++++ src/usr/bin/updatezone | 434 ++++++++++++++++++++++++ src/usr/share/doc/ddtools/README.txt | 61 ++++ src/usr/share/doc/ddtools/version.txt | 1 + 7 files changed, 999 insertions(+) create mode 100644 src/etc/bash_completion.d/updatezone create mode 100644 src/etc/ddtools/ipa.example.com.conf.example create mode 100644 src/etc/sysconfig/dhcpd-control create mode 100755 src/usr/bin/dhcpd-control create mode 100755 src/usr/bin/updatezone create mode 100644 src/usr/share/doc/ddtools/README.txt create mode 100644 src/usr/share/doc/ddtools/version.txt (limited to 'src') diff --git a/src/etc/bash_completion.d/updatezone b/src/etc/bash_completion.d/updatezone new file mode 100644 index 0000000..f55d1ce --- /dev/null +++ b/src/etc/bash_completion.d/updatezone @@ -0,0 +1,13 @@ +_updatezone_autocomplete() { + #devtty=/dev/pts/2 + local cur prev words cword; + _init_completion || return + local _tmpfile1="$( mktemp )" + grep -iE "UZ_ZONE_NAME=" /etc/ddtools/*.conf 2>/dev/null | sed -r -e 's/^UZ_.*=//;' >> "${_tmpfile1}" + #echo "----- ${cur} --- prev: ${prev} -- words: ${words} -- cword: ${cword} --" > "${devtty}" + grep -viE "${cur}" "${_tmpfile1}" + COMPREPLY=($( compgen -W "$( grep -viE "${prev}" "${_tmpfile1}" )" -- "$cur" )) + command rm -f "${_tmpfile1}" 1>/dev/null 2>&1 + return 0 +} +complete -F _updatezone_autocomplete updatezone diff --git a/src/etc/ddtools/ipa.example.com.conf.example b/src/etc/ddtools/ipa.example.com.conf.example new file mode 100644 index 0000000..060005a --- /dev/null +++ b/src/etc/ddtools/ipa.example.com.conf.example @@ -0,0 +1,7 @@ +UZ_ZONE_NAME=ipa.example.com +UZ_FORWARD_ZONE=ipa.example.com +UZ_FORWARD_FILE=/var/named/data/db.ipa.example.com +UZ_REVERSE_ZONE=1.168.192.in-addr.arpa +UZ_REVERSE_FILE=/var/named/data/db.192.168.1 +UZ_SLAVE_COUNT=1 +UZ_SLAVE_1=dns2 diff --git a/src/etc/sysconfig/dhcpd-control b/src/etc/sysconfig/dhcpd-control new file mode 100644 index 0000000..3073169 --- /dev/null +++ b/src/etc/sysconfig/dhcpd-control @@ -0,0 +1,5 @@ +DHCPD_CONTROL_COMBINED_FILE=/etc/dhcp/dhcpd.combined.conf +DHCPD_CONTROL_DHCPD_FILE=/etc/dhcp/dhcpd.conf +DHCPD_CONTROL_LEASES_FILE=/var/lib/dhcpd/dhcpd.leases +DHCPD_CONTROL_LEASES_TEMP_FILE=/var/lib/dhcpd/dhcpd.leases~ +DHCPD_CONTROL_SERVICE=dhcpd diff --git a/src/usr/bin/dhcpd-control b/src/usr/bin/dhcpd-control new file mode 100755 index 0000000..7ee4e0a --- /dev/null +++ b/src/usr/bin/dhcpd-control @@ -0,0 +1,478 @@ +#!/bin/sh +# Filename: dhcpd-control.sh +# Location: +# Author: bgstack15@gmail.com +# Startdate: 2017-05-28 18:18:46 +# Title: Script that Facilitates the Configuration of DHCPD Across a Server Pair +# Purpose: Provides a single command for would take a series of steps +# Package: ddtools +# History: +# 2024-08-19 fix framework path +# Usage: +# Reference: ftemplate.sh 2017-05-24a; framework.sh 2017-05-24a +# order of dhcpd servers to restart https://kb.isc.org/article/AA-01043/0/Recommendations-for-restarting-a-DHCP-failover-pair.html +# merge lines with sed http://www.linuxquestions.org/questions/programming-9/merge-lines-in-a-file-using-sed-191121/ +# Improve: +# provide mechanisms for non-systemd service control +# provide ways to specify what to see in the --list output +# list leases on both servers +# Dependencies: +# systemd +fiversion="2017-05-24a" +dhcpdcontrolversion="2024-08-19a" + +usage() { + less -F >&2 < | --list ] [ --force ] +version ${dhcpdcontrolversion} + -d debug Show debugging info, including parsed variables. + -u usage Show this usage block. + -V version Show script version number. + --flush Clears all current leases + --edit Edit the combined file-- the one shared by both servers. + --edit-local Edit the local file. + --edit-other Edit the other server dhcpd file. + --remove-mac Clears the leases for this MAC address. + --list List current leases. +Return values: +0 Normal +1 Help or version info displayed +2 Count or type of flaglessvals is incorrect +3 Incorrect OS type +4 Unable to find dependency +5 Not run as root or sudo +ENDUSAGE +} + +# DEFINE FUNCTIONS + +# DEFINE TRAPS + +clean_dhcpdcontrol() { + { + rm -f ${tmp_dhcpd_combined_file} ${tmp_dhcpd_local_file} ${tmp_dhcpd_other_file} ${tmp_mac_local_file} ${tmp_macless_local_file} ${tmp_leases_other_file} ${tmp_mac_other_file} ${tmp_macless_other_file} + } 1>/dev/null 2>&1 + #use at end of entire script if you need to clean up tmpfiles +} + +CTRLC() { + #trap "CTRLC" 2 + clean_dhcpdcontrol + #useful for controlling the ctrl+c keystroke +} + +CTRLZ() { + #trap "CTRLZ" 18 + [ ] #useful for controlling the ctrl+z keystroke +} + +parseFlag() { + flag="$1" + hasval=0 + case ${flag} in + # INSERT FLAGS HERE + "d" | "debug" | "DEBUG" | "dd" ) setdebug; ferror "debug level ${debug}";; + "u" | "usage" | "help" | "h" ) usage; exit 1;; + "V" | "fcheck" | "version" ) ferror "${scriptfile} version ${dhcpdcontrolversion}"; exit 1;; + #"i" | "infile" | "inputfile" ) getval;infile1=${tempval};; + "f" | "force" ) DHCPD_CONTROL_FORCE=1;; + "flush" ) action="flush";; + "edit-local" ) action="edit-local";; + "edit-other" ) action="edit-other";; + "edit" ) action="edit";; + "remove-mac" ) getval; DHCPD_CONTROL_MAC_TO_REMOVE="${tempval}"; action="remove-mac";; + "list" ) action="list";; + esac + + debuglev 10 && { test ${hasval} -eq 1 && ferror "flag: ${flag} = ${tempval}" || ferror "flag: ${flag}"; } +} + +# DETERMINE LOCATION OF FRAMEWORK +while read flocation; do if test -x ${flocation} && test "$( ${flocation} --fcheck )" -ge 20170524; then frameworkscript="${flocation}"; break; fi; done <&2 && exit 4 + +# INITIALIZE VARIABLES +# variables set in framework: +# today server thistty scriptdir scriptfile scripttrim +# is_cronjob stdin_piped stdout_piped stderr_piped sendsh sendopts +. ${frameworkscript} || echo "$0: framework did not run properly. Continuing..." 1>&2 +infile1= +outfile1= +logfile=${scriptdir}/${scripttrim}.${today}.out +action="" +define_if_new interestedparties "bgstack15@gmail.com" +# SIMPLECONF +#define_if_new default_conffile "/etc/sysconfig/dhcpd-control" +define_if_new default_conffile "/etc/sysconfig/dhcpd-control" +#define_if_new defuser_conffile ~/.config/dhcpdcontrol/dhcpdcontrol.conf +define_if_new EDITOR vi + +# REACT TO OPERATING SYSTEM TYPE +case $( uname -s ) in + Linux) [ ];; + FreeBSD) [ ];; + *) echo "${scriptfile}: 3. Indeterminate OS: $( uname -s )" 1>&2 && exit 3;; +esac + +## REACT TO ROOT STATUS +#case ${is_root} in +# 1) # proper root +# [ ] ;; +# sudo) # sudo to root +# [ ] ;; +# "") # not root at all +# #ferror "${scriptfile}: 5. Please run as root or sudo. Aborted." +# #exit 5 +# [ ] +# ;; +#esac + +# SET CUSTOM SCRIPT AND VALUES +#setval 1 sendsh sendopts<&2 + } + + update_conf_local=0 + update_leases_local=0 + restart_service_local=0 + update_conf_other=0 + update_leases_other=0 + restart_service_other=0 + debuglev 8 && ferror "BEGIN action ${action}" + case "${action}" in + + "flush") + debuglev 8 && ferror "BEGIN flush" + # Clear temorary leases file + debuglev 4 && ferror "Flushing all leases" + if test -z "${DHCPD_CONTROL_LEASES_TEMP_FILE}"; + then + ferror "Skipping leases temp file. Variable not defined: DHCPD_CONTROL_LEASES_TEMP_FILE." + else + if test -f "${DHCPD_CONTROL_LEASES_TEMP_FILE}"; + then + case "${DHCPD_CONTROL_LEASES_TEMP_FILE}" in + /var/lib/dhcp*) + systemctl stop "${DHCPD_CONTROL_SERVICE}" + rm -f "${DHCPD_CONTROL_LEASES_TEMP_FILE}" + update_leases_other=1; + restart_service_other=1; + restart_service_local=1; + ;; + *) + ferror "Will not delete unsafe leases temp file ${DHCPD_CONTROL_LEASES_TEMP_FILE}." + ;; + esac + fi + fi + # Clear leases file + if test -z "${DHCPD_CONTROL_LEASES_FILE}"; + then + ferror "Skipping leases file. Variable not defined: DHCPD_CONTROL_LEASES_FILE." + else + if test -f "${DHCPD_CONTROL_LEASES_FILE}"; + then + case "${DHCPD_CONTROL_LEASES_FILE}" in + /var/lib/dhcp*) + systemctl stop "${DHCPD_CONTROL_SERVICE}" + printf "" > "${DHCPD_CONTROL_LEASES_FILE}" + update_leases_other=1; + restart_service_other=1; + restart_service_local=1; + ;; + *) + ferror "Will not clear unsafe leases file ${DHCPD_CONTROL_LEASES_FILE}." + ;; + esac + fi + fi + ;; + + "edit") + debuglev 8 && ferror "BEGIN edit" + # prepare temp file + tmp_dhcpd_combined_file="$( mktemp -p /tmp dhcpd.combined.XXXXX )" + cp -p "${DHCPD_CONTROL_COMBINED_FILE}" "${tmp_dhcpd_combined_file}" + # edit file + $EDITOR "${tmp_dhcpd_combined_file}" + # if change occurred, prepare to replace + if ! cmp -s "${DHCPD_CONTROL_COMBINED_FILE}" "${tmp_dhcpd_combined_file}"; + then + debuglev 1 && ferror "Updating dhcpd combined file." + bup "${DHCPD_CONTROL_COMBINED_FILE}" + cp -p "${tmp_dhcpd_combined_file}" "${DHCPD_CONTROL_COMBINED_FILE}" + update_conf_other=1 + restart_service_local=1 + restart_service_other=1 + fi + ;; + + "edit-local") + debuglev 8 && ferror "BEGIN edit-local" + # prepare temp file + tmp_dhcpd_local_file="$( mktemp -p /tmp dhcpd.XXXXX )" + cp -p "${DHCPD_CONTROL_DHCPD_FILE}" "${tmp_dhcpd_local_file}" + $EDITOR "${tmp_dhcpd_local_file}" + # if change occurred, prepare to replace + if ! cmp -s "${DHCPD_CONTROL_DHCPD_FILE}" "${tmp_dhcpd_local_file}"; + then + debuglev 1 && ferror "Updating local dhcpd file." + bup "${DHCPD_CONTROL_DHCPD_FILE}" + cp -p "${tmp_dhcpd_local_file}" "${DHCPD_CONTROL_DHCPD_FILE}" + restart_service_local=1 + fi + ;; + + "edit-other") + debuglev 8 && ferror "BEGIN edit-other" + tmp_dhcpd_other_file="$( mktemp -p /tmp dhcpd.other.XXXXX )" + scp -p "${DHCPD_CONTROL_OTHER_SERVER}:${DHCPD_CONTROL_DHCPD_FILE}" "${tmp_dhcpd_other_file}" + cp -p "${tmp_dhcpd_other_file}" "${tmp_dhcpd_other_file}8" #arbitrary number # edit file + ${EDITOR} "${tmp_dhcpd_other_file}8" + if ! cmp -s "${tmp_dhcpd_other_file}" "${tmp_dhcpd_other_file}8"; + then + debuglev 1 && ferror "Updating other server dhcpd file." + ssh "${DHCPD_CONTROL_OTHER_SERVER}" bup "${DHCPD_CONTROL_DHCPD_FILE}"; + scp -p "${tmp_dhcpd_other_file}8" "${DHCPD_CONTROL_OTHER_SERVER}:${DHCPD_CONTROL_DHCPD_FILE}"; + restart_service_other=1 + fi + ;; + + "remove-mac") + debuglev 8 && ferror "BEGIN remove-mac" + # working on this + # sed -n -r -e '/^lease.*\{/,/^\}/{/^lease|hardware|\}/{p}}' /tmp/foo1 | sed -e ':a;/\}/!{N;s/\n/ /;ba};' # base form + # sed -n -r -e '/^lease.*\{/,/^\}/{p}' /tmp/foo1 | sed -e ':a;/\}/!{N;s/\n/ /;ba};' -e 's/\s\+/ /g;' # slightly trimmed + # sed -n -r -e '/\{/,/^\}/{p}' /tmp/foo1 | sed -e ':a;/\}/!{N;s/\n/ /;ba};' -e 's/\s\+/ /g;' | grep -iE "ec:9a:74:48:bc:c4" # find the one mac address + + # prepare temp files + tmp_mac_local_file="$( mktemp -p /tmp leases.mac.XXXXX )" + tmp_macless_local_file="$( mktemp -p /tmp leases.macless.XXXXX )" + tmp_leases_other_file="$( mktemp -p /tmp leases.other.XXXXX )" + tmp_mac_other_file="$( mktemp -p /tmp leases.mac.XXXXX )" + tmp_macless_other_file="$( mktemp -p /tmp leases.macless.XXXXX )" + if test -z "${DHCPD_CONTROL_MAC_TO_REMOVE}"; + then + ferror "${scripttrim}: 2. No MAC address provided. aborted." + exit 2 + fi + + scp -p "${DHCPD_CONTROL_OTHER_SERVER}:${DHCPD_CONTROL_LEASES_FILE}" "${tmp_leases_other_file}" + + # prepare list of local leases to clear + sed -n -r -e '/\{/,/^\}/{p}' "${DHCPD_CONTROL_LEASES_FILE}" | sed -e ':a;/\}/!{N;s/\n/ /;ba};' -e 's/\s\+/ /g;' | grep -iE "${DHCPD_CONTROL_MAC_TO_REMOVE}" > "${tmp_mac_local_file}" + # prepare list of other leases to clear + sed -n -r -e '/\{/,/^\}/{p}' "${tmp_leases_other_file}" | sed -e ':a;/\}/!{N;s/\n/ /;ba};' -e 's/\s\+/ /g;' | grep -iE "${DHCPD_CONTROL_MAC_TO_REMOVE}" > "${tmp_mac_other_file}" + + # remove leases from this dhcpd server + if test -n "$( cat "${tmp_mac_local_file}" )"; + then + ferror "Removing leases from this dhcpd server:" + cat "${tmp_mac_local_file}" 1>&2 + fi + sed -n -r -e '/\{/,/^\}/{p}' "${DHCPD_CONTROL_LEASES_FILE}" | sed -e ':a;/\}/!{N;s/\n/ /;ba};' -e 's/\s\+/ /g;' | grep -viE "${DHCPD_CONTROL_MAC_TO_REMOVE}" > "${tmp_macless_local_file}" + if ! cmp -s "${tmp_macless_local_file}" "${DHCPD_CONTROL_LEASES_FILE}" + then + systemctl stop "${DHCPD_CONTROL_SERVICE}" + cp -p "${tmp_macless_local_file}" "${DHCPD_CONTROL_LEASES_FILE}" + restart_service_local=1 + fi + + # remove leases from other dhcpd server + if test -n "$( cat "${tmp_mac_other_file}" )"; + then + ferror "Removing leases from other dhcpd server:" + cat "${tmp_mac_other_file}" 1>&2 + fi + sed -n -r -e '/\{/,/^\}/{p}' "${tmp_leases_other_file}" | sed -e ':a;/\}/!{N;s/\n/ /;ba};' -e 's/\s\+/ /g;' | grep -viE "${DHCPD_CONTROL_MAC_TO_REMOVE}" > "${tmp_macless_other_file}" + if ! cmp -s "${tmp_macless_other_file}" "${tmp_leases_other_file}"; + then + ssh "${DHCPD_CONTROL_OTHER_SERVER}" systemctl stop "${DHCPD_CONTROL_SERVICE}" + scp -p "${tmp_macless_other_file}" "${DHCPD_CONTROL_LEASES_FILE}" + restart_service_other=1 + fi + ;; + + "list") + debuglev 8 && ferror "BEGIN list" + lease_type="active" + #sed -n -r -e '/\{/,/^\}/{p}' /var/lib/dhcpd/dhcpd.leases | sed -e ':a;/\}/!{N;s/\n/ /;ba};' -e 's/\s\+/ /g;' | grep -iE "active" + #sed -n -r -e '/\{/,/^\}/{p}' /var/lib/dhcpd/dhcpd.leases | grep -iE "\{|\}|client-fqdn|hostname|hardware|starts|ends" | sed -e ':a;/\}/!{N;s/\n/ /;ba};' -e 's/\s\+/ /g;' | awk '!x[$14]++' | sort -k2 + #sed -n -r -e '/\{/,/^\}/{p}' /var/lib/dhcpd/dhcpd.leases | grep -iE "\{|\}|client-fqdn|hostname|hardware|starts|ends" | sed -e ':a;/\}/!{N;s/\n/ /;ba};' -e 's/\s\+/ /g;' -e 's | awk '!x[$14]++' | sort -k2 + #sed -n -r -e '/\{/,/^\}/{p}' /var/lib/dhcpd/dhcpd.leases | grep -iE "\{|\}|client-fqdn|hostname|hardware|starts|ends" | sed -e ':a;/\}/!{N;s/\n/ /;ba};' -e 's/\s\+/ /g;' -e 's/hardware ethernet/mac/;' | awk '!x[$13]++' | sort -k2 + sed -n -r -e '/\{/,/^\}/{p}' "${DHCPD_CONTROL_LEASES_FILE}" | grep -iE "\{|\}|client-fqdn|hostname|hardware|starts|ends" | sed -e ':a;/\}/!{N;s/\n/ /;ba};' -e 's/\s\+/ /g;' -e 's/hardware ethernet/mac/;' | grep -viE "failover peer" | awk '!x[$13]++' | sort -k2 + ;; + *) + ferror "Gotta say unh! Action ${action} ${fallopts} not yet implemented. Aborted." + exit 1 + ;; + esac + + # Prepare instructions for other server + debuglev 8 && ferror "BEGIN prepare instructions for other server" + local_instructions="" + other_instructions="" + instructions="" + fistruthy "${update_leases_other}" && \ + other_instructions="${other_instructions}systemctl stop ${DHCPD_CONTROL_SERVICE}\; rm -f ${DHCPD_CONTROL_LEASES_TEMP_FILE}\; echo \"\" \> ${DHCPD_CONTROL_LEASES_FILE}\; " + fistruthy "${update_conf_other}" && \ + local_instructions="${local_instructions}scp ${DHCPD_CONTROL_COMBINED_FILE} ${DHCPD_CONTROL_OTHER_SERVER}:${DHCPD_CONTROL_COMBINED_FILE}; " + fistruthy "${restart_service_local}" && \ + instructions="${instructions}systemctl restart ${DHCPD_CONTROL_SERVICE}.service" + fistruthy "${restart_service_other}" && \ + other_instructions="${other_instructions}systemctl restart ${DHCPD_CONTROL_SERVICE}" + + # Instruct other server to act + debuglev 8 && ferror "BEGIN instruct other server to act" + if test -n "${DHCPD_CONTROL_OTHER_SERVER}"; + then + debuglev 1 && { + test -n "${local_instructions}" && { + ferror "run local commands:" + ferror "${local_instructions}" + } + test -n "${other_instructions}" && { + ferror "run on other server ${DHCPD_CONTROL_OTHER_SERVER}:" + ferror "ssh ${DHCPD_CONTROL_OTHER_SERVER} ${other_instructions}" + } + } + test -n "${local_instructions}" && ${local_instructions} + test -n "${other_instructions}" && ssh ${DHCPD_CONTROL_OTHER_SERVER} eval ${other_instructions} + fi + + # Local actions regardless of other server + if test -n "${instructions}"; + then + debuglev 1 && { + ferror "run commands:" + ferror "${instructions}" + } + ${instructions} + fi + +#} | tee -a ${logfile} + +# EMAIL LOGFILE +#${sendsh} ${sendopts} "${server} ${scriptfile} out" ${logfile} ${interestedparties} + +## STOP THE READ CONFIG FILE +#exit 0 +#fi; done; } diff --git a/src/usr/bin/updatezone b/src/usr/bin/updatezone new file mode 100755 index 0000000..dc6efde --- /dev/null +++ b/src/usr/bin/updatezone @@ -0,0 +1,434 @@ +#!/bin/sh +# Filename: updatezone.sh +# Location: +# Author: bgstack15@gmail.com +# Startdate: 2017-05-26 07:02:47 +# Title: Script that Updates a DNS Zone +# Purpose: Provides a single command to update dns zones +# Package: updatezone +# History: +# 2017-10-15 Added --flush option +# 2024-08-19 fix typo in tmp_rev_file2 +# Usage: +# Primarily intended for updating forward and reverse zones for bind9. +# Reference: ftemplate.sh 2017-05-24a; framework.sh 2017-05-24a +# Improve: +# Dependencies: +# rndc +# ssh with password-less authentication to slave servers +# each zone file has only a single zone +fiversion="2017-05-24a" +updatezoneversion="2024-08-19a" + +usage() { + less -F >&2 <> "${zones_to_thaw_file}" + + # prepare temp file + cp -p "${zone_real_file}" "${zone_temp_file}" + fi + fi +} + +zone_action() { + # call: zone_action ${forwardzone} + debuglev 9 && ferror "zone_action $@" + local action="$1" + local zone="$2" + case "${action}" in + freeze|thaw) + rndc "${action}" "${zone}" 2>&1 | grep -viE "a zone reload and thaw|Check the logs to see" + ;; + *) + ferror "${scriptfile} minor error: ignoring unknown zone_action $@" + ;; + esac +} + +update_real_zone_if_updated() { + # call: update_real_zone_if_updated "${UZ_REVERSE_ZONE}" "${UZ_REVERSE_FILE}" "${tmp_rev_file}" + debuglev 9 && ferror "update_real_zone_if_updated $@" + local zone_name="$1" + local zone_real_file="$2" + local zone_temp_file="$3" + if test -n "${zone_temp_file}" && test -f "${zone_temp_file}"; + then + if ! cmp -s "${zone_real_file}" "${zone_temp_file}"; + then + # a change occurred, so increment the serial number and replace the original zone file + increment_serial_in_zone_file "${zone_temp_file}" + cat "${zone_temp_file}" > "${zone_real_file}" + + # plan to notify the dns slaves + echo "${zone_name}" >> "${zones_to_update_file}" + fi + fi + + # If the temp file does not exist, it was deleted because the real file was invalid for whatever reason. +} + +increment_serial_in_zone_file() { + # call: increment_serial_in_zone_file "${zone_temp_file}" + # dependencies: a single zone in the zone file, with the ";serial" comment after the number. + debuglev 9 && ferror "increment_serial_in_zone_file $@" + local infile="$1" + currentnum="$( grep -iE "[0-9]+\s*;\s*serial" "${infile}" | grep -oIE "[0-9]+" )" + nextnum=$(( currentnum + 1 )) + sed -i -r -e "s/${currentnum}(\s*;\s*serial)/${nextnum}\1/" "${infile}" +} + +# DEFINE TRAPS + +clean_updatezone() { + rm -rf ${tempdir} > /dev/null 2>&1 + [ ] #use at end of entire script if you need to clean up tmpfiles +} + +CTRLC() { + #trap "CTRLC" 2 + [ ] #useful for controlling the ctrl+c keystroke + exit 0 +} + +CTRLZ() { + #trap "CTRLZ" 18 + [ ] #useful for controlling the ctrl+z keystroke +} + +parseFlag() { + flag="$1" + hasval=0 + case ${flag} in + # INSERT FLAGS HERE + "d" | "debug" | "DEBUG" | "dd" ) setdebug; ferror "debug level ${debug}";; + "u" | "usage" | "help" | "h" ) usage; exit 1;; + "V" | "fcheck" | "version" ) ferror "${scriptfile} version ${updatezoneversion}"; exit 1;; + #"i" | "infile" | "inputfile" ) getval;infile1=${tempval};; + "c" | "conf" | "config" | "conffile" ) getval;conffile=${tempval};; + "flush" ) action=flush;; + esac + + debuglev 10 && { test ${hasval} -eq 1 && ferror "flag: ${flag} = ${tempval}" || ferror "flag: ${flag}"; } +} + +# DETERMINE LOCATION OF FRAMEWORK +while read flocation; do if test -x ${flocation} && test "$( ${flocation} --fcheck )" -ge 20170524; then frameworkscript="${flocation}"; break; fi; done <&2 && exit 4 + +# INITIALIZE VARIABLES +# variables set in framework: +# today server thistty scriptdir scriptfile scripttrim +# is_cronjob stdin_piped stdout_piped stderr_piped sendsh sendopts +. ${frameworkscript} || echo "$0: framework did not run properly. Continuing..." 1>&2 +infile1= +outfile1= +logfile=${scriptdir}/${scripttrim}.${today}.out +define_if_new interestedparties "bgstack15@gmail.com" +# SIMPLECONF +#define_if_new default_conffile "/etc/ddtools/updatezone.conf" +#define_if_new defuser_conffile ~/.config/ddtools/updatezone.conf +define_if_new EDITOR vi +define_if_new default_dir "/etc/ddtools" + +# REACT TO OPERATING SYSTEM TYPE +case $( uname -s ) in + Linux) [ ];; + FreeBSD) [ ];; + *) echo "${scriptfile}: 3. Indeterminate OS: $( uname -s )" 1>&2 && exit 3;; +esac + +## REACT TO ROOT STATUS +#case ${is_root} in +# 1) # proper root +# [ ] ;; +# sudo) # sudo to root +# [ ] ;; +# "") # not root at all +# #ferror "${scriptfile}: 5. Please run as root or sudo. Aborted." +# #exit 5 +# [ ] +# ;; +#esac + +# VALIDATE PARAMETERS +# objects before the dash are options, which get filled with the optvals +# to debug flags, use option DEBUG. Variables set in framework: fallopts +validateparams - "$@" + +# CONFIRM TOTAL NUMBER OF FLAGLESSVALS IS CORRECT +#if test ${thiscount} -lt 1; +#then +# #ferror "${scriptfile}: 2. Fewer than 2 flaglessvals. Aborted." +# #exit 2 +#fi + +# CONFIGURE VARIABLES AFTER PARAMETERS + +## LOAD CONFIG FROM SIMPLECONF +## This section follows a simple hierarchy of precedence, with first being used: +## 1. parameters and flags +## 2. environment +## 3. config file +## 4. default user config: ~/.config/script/script.conf +## 5. default config: /etc/script/script.conf +#if test -f "${conffile}"; +#then +# get_conf "${conffile}" +#else +# #if test "${conffile}" = "${default_conffile}" || test "${conffile}" = "${defuser_conffile}"; then :; else +# ferror "${scriptfile}: Ignoring conf file which is not found: ${conffile}." +# #fi +#fi +#test -f "${defuser_conffile}" && get_conf "${defuser_conffile}" +#test -f "${default_conffile}" && get_conf "${default_conffile}" + +## REACT TO BEING A CRONJOB +#if test ${is_cronjob} -eq 1; +#then +# [ ] +#else +# [ ] +#fi + +# SET TRAPS +trap "CTRLC" 2 +#trap "CTRLZ" 18 +trap "clean_updatezone" 0 + +## DEBUG SIMPLECONF +#debuglev 5 && { +# ferror "Using values" +# # used values: EX_(OPT1|OPT2|VERBOSE) +# set | grep -iE "^UZ_" 1>&2 +#} + +# MAKE TEMP LOCATIONS +tempdir=/tmp/updatezone/ +if ! mkdir -p "${tempdir}"; +then + ferror "${scriptfile}: 4. Unable to make temp directory ${tempdir}. Aborted." + exit 4 +fi + +# MAIN ACTIONS + +# EDIT +main_action() { + # call: main_action "${action}" "${conffile}" + local action="${1}" + get_conf "${2}" + # DEBUG SIMPLECONF + debuglev 5 && { + ferror "Using values" + # used values: EX_(OPT1|OPT2|VERBOSE) + set | grep -iE "^UZ_" 1>&2 + } + local tmp_for_file="$( mktemp -p "${tempdir}" forward.XXXX 2>/dev/null )" + local tmp_rev_file="$( mktemp -p "${tempdir}" reverse.XXXX 2>/dev/null )" + local zones_to_thaw_file="$( mktemp -p "${tempdir}" thaw.XXXX )" + local zones_to_update_file="$( mktemp -p "${tempdir}" update.XXXX )" + for word in "${tmp_for_file}" "${tmp_rev_file}"; + do + if test ! -f "${word}"; + then + ferror "${scriptfile}: 4. Unable to make temp file ${word}. Aborted." + exit 4 + fi + done + + # Freezing the zone ensures all records are in the primary files which we check. + local pause_to_show_error=0 + # Check forward zone file and freeze + check_zone_file forward "${UZ_FORWARD_ZONE}" "${UZ_FORWARD_FILE}" "${tmp_for_file}" + + # Check reverse zone file and freeze + check_zone_file reverse "${UZ_REVERSE_ZONE}" "${UZ_REVERSE_FILE}" "${tmp_rev_file}" + + # Slow down to show errors if any + fistruthy "${pause_to_show_error}" && sleep 1.3 + + case "${action}" in + + edit) + # EDIT FILES INTERACTIVELY + local these_temp_files="$( find "${tmp_for_file}" "${tmp_rev_file}" 2>/dev/null | xargs )" + test -n "${these_temp_files}" && $EDITOR ${these_temp_files} + ;; + + flush) + # FLUSH FILES AUTOMATICALLY + + # CALCULATE WHICH RECORDS TO FLUSH + # get dhcpd ttl. + #set -x + local tmp_flush_master_file="$( mktemp -p "${tempdir}" flush.master.XXXX 2>/dev/null )" + local tmp_flush_for_file="$( mktemp -p "${tempdir}" flush.for.XXXX 2>/dev/null )" + local tmp_flush_rev_file="$( mktemp -p "${tempdir}" flush.rev.XXXX 2>/dev/null )" + + local dhcpd_ttl="$( grep -hE "default-lease-time" $( { $( which dhcpd-control ) --debug 5 nop; } 2>&1 | grep -E "_FILE=" | grep -vE "LEASES_" | cut -d'=' -f2 | xargs ) | cut -d' ' -f2 | tr -d ';' )" + # this next statement only returns an integer, but the rounding should be the same as the dns ttl rounding + local dns_ttl="$( printf "${dhcpd_ttl}/2\n" | bc )" + + debuglev 2 && ferror "Flushing entries that have ttl ${dns_ttl} and have a TXT hash" + # fetch all dns A and TXT records that have the requested TTL + awk '$1 == "$TTL" {a=$2;} $1 != "$TTL" {if(a=='${dns_ttl}' && ($1=="TXT" || $2=="A")) print;}' "${tmp_for_file}" | \ + # restrict to the A records that have associated TXT records + awk 'NR>1{if ($1=="TXT") print prev,$0;} {prev=$0;}' | \ + # only keep ones whose TXT hash is the correct length + awk '$4=="TXT" && $5 ~ /"[a-fA-F0-9]{34}"/ {print;}' > "${tmp_flush_master_file}" + + # prepare items to flush, forward + # convert to each a single line in a file, for future grep -v + grep -oE "(([0-9]{1,3}\.){3}[0-9]{1,3}|"[a-fA-F0-9]{34}")" "${tmp_flush_master_file}" > "${tmp_flush_for_file}" + # prepare items to flush, reverse + awk '{print $1}' "${tmp_flush_master_file}" | sed -e 's/^/PTR\\s\*/;' > "${tmp_flush_rev_file}" + + # flush forward records + grep -v -f "${tmp_flush_for_file}" "${tmp_for_file}" > "${tmp_for_file}2"; mv -f "${tmp_for_file}2" "${tmp_for_file}" + # flush reverse records + grep -v -E -f "${tmp_flush_rev_file}" "${tmp_rev_file}" > "${tmp_rev_file}2"; mv -f "${tmp_rev_file}2" "${tmp_rev_file}" + ;; + + esac + + # Update the real zone if the temp file was updated + update_real_zone_if_updated "${UZ_FORWARD_ZONE}" "${UZ_FORWARD_FILE}" "${tmp_for_file}" + update_real_zone_if_updated "${UZ_REVERSE_ZONE}" "${UZ_REVERSE_FILE}" "${tmp_rev_file}" + # Thaw zones that need it + while read thiszone; + do + zone_action thaw "${thiszone}" + done < "${zones_to_thaw_file}" + + # Transfer zones that need it + # This section exists because my automatic zone transfers/updates do not work. + + # Build list of commands to run on each dns slave server + transfercommand="" + while read thiszone; + do + transfercommand="${transfercommand}rndc retransfer ${thiszone}; " + done < "${zones_to_update_file}" + + # Execute command on each slave server + if test -n "${transfercommand}"; + then + x=0 + while test ${x} -lt ${UZ_SLAVE_COUNT}; + do + x=$(( x + 1 )) + eval this_dns_slave=\"\${UZ_SLAVE_${x}}\" + debuglev 5 && ferror "ssh ${this_dns_slave} ${transfercommand}" + ssh ${this_dns_slave} ${transfercommand} + done + fi + +} #| tee -a ${logfile} + +####################################################### + +# DETERMINE ACTION +case "${action}" in + "flush") + debuglev 2 && ferror "Action is flush." + ;; + *) + debuglev 2 && ferror "Action is edit." + action=edit + ;; +esac + +# MAIN LOOP +if test -n "${conffile}"; +then + ( main_action "${action}" "${conffile}"; ) +else + # assume the $opt items are the zone names + y=0 + while test $y -lt $thiscount; + do + y=$(( y + 1 )) + eval "thiszonename=\${opt${y}}" + debuglev 1 && ferror "Will try to ${action} zone ${thiszonename}" + file_for_this_zone="$( grep -liE "UZ_ZONE_NAME=${thiszonename}" "${default_dir}/"*.conf 2>/dev/null )" + if test -n "${file_for_this_zone}" && test -f "${file_for_this_zone}"; + then + ( main_action "${action}" "${file_for_this_zone}"; ) + else + ferror "Skipping zone ${thiszonename} for which no file was found in ${default_dir}/" + fi + done +fi + +# EMAIL LOGFILE +#${sendsh} ${sendopts} "${server} ${scriptfile} out" ${logfile} ${interestedparties} + +## STOP THE READ CONFIG FILE +#exit 0 +#fi; done; } diff --git a/src/usr/share/doc/ddtools/README.txt b/src/usr/share/doc/ddtools/README.txt new file mode 100644 index 0000000..747eee1 --- /dev/null +++ b/src/usr/share/doc/ddtools/README.txt @@ -0,0 +1,61 @@ +File: usr/share/ddtools/docs/README.txt +Package: ddtools +Author: bgstack15 +Startdate: 2017-05-26 +Title: Readme file for ddtools +Purpose: All packages should come with a readme +Usage: Read it. +Reference: README.txt +Improve: +Document: Below this line + +### WELCOME +ddtools is a suite of shell scripts that help manage dns and dhcpd. +Updatezone provides an easy way to update dns zone files. Intended primarily for bind9 zone files, experimentation is encouraged. +Instead of running the series of commands manually: rndc freeze, vi zonefile, rndc thaw and so on, use updatezone. +dhcpd-control helps manage paired dhcpd servers. + +### CONFIGURATION +The conf files belong in /etc/ddtools/. See example in /usr/share/ddtools/examples/. + +### USING THIS TOOL + +$ updatezone ipa.smith122.com +Where this file exists: /etc/ddtools/ipa.smith122.com.conf + + UZ_ZONE_NAME=ipa.smith122.com + UZ_FORWARD_ZONE=ipa.smith122.com + UZ_FORWARD_FILE=/var/named/data/db.ipa.smith122.com + UZ_REVERSE_ZONE=1.168.192.in-addr.arpa + UZ_REVERSE_FILE=/var/named/data/db.192.168.1 + UZ_SLAVE_COUNT=1 + UZ_SLAVE_1=dns2 + +The updatezone tool searches for the value of UZ_ZONE_NAME to declare a match and use that configuration file. +The zone definitions are used in the freeze/thaw/retransfer commands. + +This tool will only request updates for zones that are updated. Also, you do not need to adjust the serial number at all. The script will detect changes and then increment the serial number for you. + +You can also specify multiple zones on the command line. +$ updatezone ipa.smith122.com ad.smith122.com + +You can also use the --flush flag to clear out the A and PTR records whose TTL matches the dhcp server TTL. It ties in nicely with the dhcpd-control --flush command. Remember that you need to give a zone name (or -c conffile) option as well. + +Example: +updatezone --flush ipa.smith122.com + +### NOTES + +### REFERENCE + +### CHANGELOG +2017-05-27 B Stack 0.0-2 +- Initial package construction + +2017-10-15 B Stack 0.0-3 +- Rearranged directory structure to match current standards +- Added bash autocompletion definition for updatezone +- Added --flush to updatezone to match dhcpd-control + +2024-08-19 B. Stack - 0.0.4-1 +- Rearranged directory structure to match current standards diff --git a/src/usr/share/doc/ddtools/version.txt b/src/usr/share/doc/ddtools/version.txt new file mode 100644 index 0000000..81340c7 --- /dev/null +++ b/src/usr/share/doc/ddtools/version.txt @@ -0,0 +1 @@ +0.0.4 -- cgit