#!/bin/sh # Filename: set-permanent-dns-resolvers.sh # Location: /etc/ansible/files # Author: bgstack15@gmail.com # Startdate: 2018-07-27 08:28:24 # Title: Set permanent Dns Resolvers # Purpose: To provide a single command that inserts dns resolver information into the permanent network config # Package: former-gists # History: # 2018-08-03 improve -d1 info about resolv.conf # Usage: # ./set-permanent-dns-resolvers.sh 1.2.3.4 5.6.7.8 -r --iface # Reference: ftemplate.sh 2018-06-21m; framework.sh 2017-11-11m # Improve: fiversion="2018-06-21m" SPDR_version="2018-08-03a" usage() { ${PAGER:-/usr/bin/less -F} >&2 <] [-i eth0] [-c conffile] [<1.2.3.4> [<1.2.3.5> ...]] Set the dns resolvers in both the interface file and resolv.conf. version ${SPDR_version} -d debug Show debugging info, including parsed variables. -u usage Show this usage block. -V version Show script version number. -c conf Read in this config file. -i interface Use this interface. --iface sets SPDR_DO_I --noiface unsets SPDR_DO_I -s silent sets SPDR_SILENT=1 --nosilent sets SPDR_SILENT=0 --ifile sets SPDR_IFILE --rfile sets SPDR_RFILE -r resolv sets SPDR_DO_R --nor unsets SPDR_DO_R 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 6 Invalid interface definition 7 Invalid resolv.conf file Environment variables: SPDR_DNS1 What resolvers to use. SPDR_DNS2 If one of these is set to "gone" the script will remove that value from the file. SPDR_DNS3 SPDR_DO_I If truthy, update SPDR_IFILE. Default is 1. SPDR_DO_R If truthy, update SPDR_RFILE. Default is 1. SPDR_INTERFACE The ethernet card to configure. If undefined, it will find the first ethernet card with a "DNS1" definition. If that fails, it will select the first ethernet card. SPDR_IFILE Exact interface file to manipulate. Default is blank, and it will be derived by the SPDR_INTERFACE value. SPDR_RFILE resolv.conf file. Default is /etc/resolv.conf SPDR_SILENT If truthy, suppress "changed" notification Debug levels: 1 changes being made 2 checks being performed 3 finding the interface to use ENDUSAGE } # DEFINE FUNCTIONS find_valid_interface() { # call: find_valid_interface # output: interface name on stdout. debuglev 9 && ferror "find_valid_interface $@" local output="" # Find first ethernet card with a DNS1 definition. output="$( grep -lE '^\s*DNS1\s*=' /etc/sysconfig/network-scripts/ifcfg* 2>/dev/null | sort | grep -vE '^(lo|loopback)$' | head -n1 )" if test -n "${output}" ; then echo "${output}" else # If that fails, select the first ethernet card found. output="$( find /etc/sysconfig/network-scripts/ifcfg* 2>/dev/null | sort | grep -vE -e '-(lo|loopback)$' )" if test -n "${output}" ; then echo "${output}" ; else # no DNS1 definitions and no ethernet card found. Fail out. ferror "${scriptfile}: 6. No valid interface definition found! Please check /etc/sysconfig/network-scripts. Aborted." exit 6 fi fi debuglev 1 && ferror "Using interface $( basename "${output}" | sed -r -e 's/ifcfg-//;' )" } apply_interface() { # call: apply_interface "DNS1" "${SPDR_DNS1}" "${SPDR_IFILE}" # performs action and does not directly care about output debuglev 9 && ferror "apply_interface $@" local dnsnum="${1}" local dnsr="${2}" local ifile="${3}" local dnscurrent="" # if defined if test -n "${dnsr}" ; then if echo "${dnsr}" | grep -qE -e "gone|absent|empty" ; then # remove line if it exists if grep -qE -e "^\s*${dnsnum}\s*=" "${ifile}" ; then # remove line debuglev 1 && ferror "remove ${ifile} ${dnsnum}" sed -i -r -n -e "/^\s*${dnsnum}\s*=/! p" "${ifile}" mark_changed else # no change needed debuglev 2 && ferror "unchanged ${ifile} ${dnsnum} undefined" fi else # set value because it is defined if grep -qE -e "^\s*${dnsnum}\s*=" "${ifile}" ; then # see if the dnscurrent dnscurrent="$( grep -E "${dnsnum}=" "${ifile}" | awk -F'=' '{print $2}' )" if ! test "${dnscurrent}" = "${dnsr}" ; then # replace it debuglev 1 && ferror "change ${ifile} ${dnsnum} from ${dnscurrent} to ${dnsr}" sed -i -r -e "s/(^\s*${dnsnum}\s*=).*/\1${dnsr}/;" "${ifile}" mark_changed else debuglev 2 && ferror "unchanged ${ifile} ${dnsnum}=${dnsr}" fi else # add it debuglev 1 && ferror "add ${ifile} ${dnsnum}=${dnsr}" echo "${dnsnum}=${dnsr}" >> "${ifile}" mark_changed fi fi fi } mark_changed() { echo "1" > "${tmpfile1}" } apply_resolv_conf() { # call: apply_resolv_conf "1.2.3.4" "${DNS2}" "${DNS3}" "${SPDR_RFILE}" # performs action and does not really care about output debuglev 9 && ferror "apply_resolv_conf $@" local dns1="${1}" local dns2="${2}" local dns3="${3}" local rfile="${4}" # populate dns variables from file for comparison local rfile_contents="$( cat "${rfile}" 2>/dev/null )" local rfile_dns1="$( echo "${rfile_contents}" | sed -n '/nameserver/p' | awk -v "num=1" 'NR==num{print $2;}' )" local rfile_dns2="$( echo "${rfile_contents}" | sed -n '/nameserver/p' | awk -v "num=2" 'NR==num{print $2;}' )" local rfile_dns3="$( echo "${rfile_contents}" | sed -n '/nameserver/p' | awk -v "num=3" 'NR==num{print $2;}' )" # compare and update each one if needed x=0 SPDR_rfile_done=0 while test "${SPDR_rfile_done}" = 0 ; do x=$(( x + 1 )) eval this_dns_goal="\${SPDR_DNS${x}}" eval this_dns_already="\${rfile_dns${x}}" # if defined if test -n "${this_dns_goal}" ; then # if requested to be absent, then please remove it if echo "${this_dns_goal}" | grep -qE -e "gone|absent|empty" ; then # remove line if it exists awk -v "dnsgone=${x}" 'BEGIN{a=0} /nameserver/{a=a+1; if(a==dnsgone)next;} {print;}' "${rfile}" > "${tmpfile2}" replace_file_if_modified "${rfile}" "${tmpfile2}" && { debuglev 1 && ferror "remove nameserver ${x} of ${this_dns_already}" ; } || { debuglev 2 && ferror "unchanged nameserver ${x} empty" ; } elif test "${this_dns_goal}" != "${this_dns_already}" ; then # not the same so please update awk -v "dnsnum=${x}" -v "goal=${this_dns_goal}" 'BEGIN{a=0} /nameserver/{a=a+1;if (a==dnsnum)$2=goal;} {print;}' "${rfile}" > "${tmpfile2}" replace_file_if_modified "${rfile}" "${tmpfile2}" && debuglev 1 && ferror "change nameserver ${x} from ${this_dns_already} to ${this_dns_goal}" # if it needs to add the above logic did not cover it, so check and add it here if ! grep -qE "nameserver\\s+${this_dns_goal}" "${rfile}" ; then # it is not present so please add it debuglev 1 && ferror "add ${rfile} nameserver ${x} ${this_dns_goal}" echo "nameserver ${this_dns_goal}" >> "${rfile}" fi mark_changed else # no change needed debuglev 2 && ferror "unchanged ${rfile} nameserver ${x}: ${this_dns_already}" fi fi test ${x} -ge 3 && SPDR_rfile_done=1 done } replace_file_if_modified() { # call: replace_file_if_modified "/etc/resolv.conf" "${tmpfile2}" # output: performs action and does not care about output # returns: 0 if a change occurred # goal is to replace first file if the second file is different than it debuglev 9 && ferror "replace_file_if_modified $@" local origfile="${1}" local tmpfile="${2}" if ! diff -q "${origfile}" "${tmpfile}" 1>/dev/null 2>&1 ; then cat "${tmpfile}" > "${origfile}" mark_changed ; # since we are just doing a simple boolean check at the end it is fine to spam this file return 0 fi return 1 } react_changed() { # call: react_changed # output: "changed" to stdout if there was any changes according to the tmpfile. # usage: in the trap before exiting this script # react to changed test -n "$( cat "${tmpfile1}" 2>/dev/null )" && ! fistruthy "${SPDR_SILENT}" && echo "changed" } # DEFINE TRAPS clean_SPDR() { # use at end of entire script if you need to clean up tmpfiles # rm -f "${tmpfile1}" "${tmpfile2}" 2>/dev/null # Delayed cleanup if test -z "${FETCH_NO_CLEAN}" ; then nohup /bin/bash </dev/null 2>&1 & sleep "${SPDR_CLEANUP_SEC:-300}" ; /bin/rm -r "${SPDR_TMPDIR:-NOTHINGTODELETE}" 1>/dev/null 2>&1 ; EOF fi } CTRLC() { # use with: trap "CTRLC" 2 # useful for controlling the ctrl+c keystroke : } CTRLZ() { # use with: 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}" __debug_set_by_param=1;; "u" | "usage" | "help" | "h" ) usage; exit 1;; "V" | "fcheck" | "version" ) ferror "${scriptfile} version ${SPDR_version}"; exit 1;; #"i" | "infile" | "inputfile" ) getval; infile1=${tempval};; "c" | "conf" | "conffile" | "config" ) getval; conffile="${tempval}";; "i" | "interface" ) getval; SPDR_INTERFACE="${tempval}";; "iface" ) SPDR_DO_I=1;; "noiface" | "no-iface" | "noi" | "ni" ) SPDR_DO_I=0;; "s" | "silent" | "q" | "quiet" ) SPDR_SILENT=1;; "nosilent" | "no-silent" | "verbose" | "v" ) SPDR_SILENT=0;; "ifile" ) getval; SPDR_IFILE="${tempval}";; "rfile" ) getval; SPDR_RFILE="${tempval}";; "resolv" | "resolve" ) SPDR_DO_R=1;; "noresolv" | "noresolve" | "no-resolv" | "no-resolve" | "nr" | "nor" ) SPDR_DO_R=0;; esac debuglev 10 && { test ${hasval} -eq 1 && ferror "flag: ${flag} = ${tempval}" || ferror "flag: ${flag}"; } } # DETERMINE LOCATION OF FRAMEWORK f_needed=20171111 while read flocation ; do if test -e ${flocation} ; then __thisfver="$( sh ${flocation} --fcheck 2>/dev/null )" ; if test ${__thisfver} -ge ${f_needed} ; then frameworkscript="${flocation}" ; break; else printf "Obsolete: %s %s\n" "${flocation}" "${__this_fver}" 1>&2 ; fi ; 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/default/set-permanent-dns-resolvers" #define_if_new defuser_conffile ~/.spdrrc # no need for a default user conf file test -z "${SPDR_TMPDIR}" && SPDR_TMPDIR="$( mktemp -d )" define_if_new SPDR_DO_I 1 define_if_new SPDR_DO_R 1 define_if_new SPDR_RFILE "/etc/resolv.conf" tmpfile1="$( TMPDIR="${SPDR_TMPDIR}" mktemp )" # if contents exist, then something changed tmpfile2="$( TMPDIR="${SPDR_TMPDIR}" mktemp )" # contents for temporary rfile # REACT TO OPERATING SYSTEM TYPE case $( uname -s ) in Linux) : ;; FreeBSD) : ;; *) echo "${scriptfile}: 3. Indeterminate OS: $( uname -s )" 1>&2 && exit 3;; esac # SET CUSTOM SCRIPT AND VALUES #setval 1 sendsh sendopts<&2 } # 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 # MAIN LOOP #{ # Should we do the interface configuration? if fistruthy "${SPDR_DO_I}" ; then # skip the check if SPDR_IFILE is defined already. if test -z "${SPDR_IFILE}" ; then # Get a valid interface if test -z "${SPDR_INTERFACE}" ; then debuglev 3 && ferror "Will scan for valid interface." SPDR_INTERFACE="$( find_valid_interface )" elif test ! -e "/etc/sysconfig/network-scripts/ifcfg-${SPDR_INTERFACE}" ; then # no requested interface, or the requested one is not valid debuglev 3 && ferror "${scriptfile}: invalid interface \"${SPDR_INTERFACE}\", so will scan for valid one." SPDR_INTERFACE="$( find_valid_interface )" fi # Trim interface name down to just "eth0" echo "${SPDR_INTERFACE}" | grep -qE -e 'ifcfg' && SPDR_INTERFACE="$( basename "${SPDR_INTERFACE}" 2>/dev/null | sed -r -e 's/ifcfg-//;' )" # Confirm interface is valid if test ! -e "/etc/sysconfig/network-scripts/ifcfg-${SPDR_INTERFACE}" ; then ferror "${scriptfile}: 6. Cannot find interface ${SPDR_INTERFACE}. Aborted." exit 6 fi SPDR_IFILE="/etc/sysconfig/network-scripts/ifcfg-${SPDR_INTERFACE}" fi # Now with the valid interface file at SPDR_IFILE, please update the DNS1 and DNS2 and DNS3 settings. x=0 SPDR_done=0 cat /dev/null > "${tmpfile1}" # set changed value to 0 while test "${SPDR_done}" = 0 ; do x=$(( x + 1 )) eval this_dnsr="\${SPDR_DNS${x}}" apply_interface "DNS${x}" "${this_dnsr}" "${SPDR_IFILE}" test $x -ge 3 && SPDR_done=1 done fi # Should we do the resolv.conf configuration? if fistruthy "${SPDR_DO_R}" ; then # confirm file exists # create it if it does not. if test ! -e "${SPDR_RFILE}" ; then touch "${SPDR_RFILE}" mark_changed fi # confirm file is writable if test ! -w "${SPDR_RFILE}" ; then ferror "${scriptfile}: 7. Cannot modify resolv \"${SPDR_RFILE}\". Aborted." exit 7 fi # peform changes apply_resolv_conf "${SPDR_DNS1}" "${SPDR_DNS2}" "${SPDR_DNS3}" "${SPDR_RFILE}" fi #} | tee -a ${logfile} # EMAIL LOGFILE #${sendsh} ${sendopts} "${server} ${scriptfile} out" ${logfile} ${interestedparties} # exit safely exit 0