#!/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; }