#!/usr/bin/env bash # # Update /etc/default/postfwd (idempotent) and restart postfwd if needed. # # For every variable below: # - already set to the wanted value -> nothing to do # - set to a different value -> comment out the current setting # and add the new one directly below # - not set at all -> append the new setting # # Quotes around values are ignored when comparing (PORT="10040" == PORT=10040). # # At the end: # - file changed -> 'systemctl restart postfwd' # (ARGS, INET and PORT are start parameters of the # daemon - a reload does NOT apply them) # - file unchanged -> postfwd is left alone (no restart, no reload) # postfwd_default_file="/etc/default/postfwd" # - Wanted settings: "VAR|VALUE" (VALUE written exactly as it should appear) wanted_settings=( 'STARTUP|1' 'CONF|/etc/postfix/postfwd.cf' 'INET|127.0.0.1' 'PORT|10040' 'ARGS|"--summary=600 --cache=0"' ) # ------------- # --- Some functions # ------------- log_file="$(mktemp)" clean_up() { rm -f "$log_file" exit $1 } echononl() { echo -e -n "$*" } echo_ok() { echo -e "\033[60G[ \033[32mok\033[m ]" } echo_skipped() { echo -e "\033[60G[ \033[33m\033[1mskipped\033[m ]" } echo_failed() { echo -e "\033[60G[ \033[1;31mfailed\033[m ]" } error() { echo "" echo -e "\t[ \033[31m\033[1mError\033[m ]: $*" echo "" } fatal() { error "$*" echo -e "\t\033[31m\033[1mScript will be interrupted\033[m" echo "" clean_up 1 } # - set_postfwd_default # - # - returns 0 if the file was changed, 1 if nothing changed, 2 on error # - set_postfwd_default() { local _var="$1" local _val="$2" local _tmp_file local _rval echononl " ${_var}=${_val}" _tmp_file="$(mktemp)" awk -v var="$_var" -v val="$_val" ' function unq(s) { gsub(/^[ \t\r]+|[ \t\r]+$/, "", s) if (s ~ /^".*"$/ || s ~ /^\047.*\047$/) s = substr(s, 2, length(s) - 2) return s } function is_set(s) { return s ~ ("^[ \t]*" var "=") } function is_wanted(s) { sub("^[ \t]*" var "=", "", s) return unq(s) == unq(val) } # - Read the whole file first: a line with the wanted value must stay # - untouched, no matter which other settings of VAR precede it. { line[NR] = $0 if (is_set($0) && is_wanted($0)) found = 1 } END { for (i = 1; i <= NR; i++) { if (! is_set(line[i])) { print line[i] ; continue } if (is_wanted(line[i])) { print line[i] ; continue } print "#" line[i] if (! found) { print var "=" val ; found = 1 } } if (! found) print var "=" val } ' "$postfwd_default_file" > "$_tmp_file" 2> "$log_file" if [[ $? -ne 0 ]] ; then echo_failed error "$(cat "$log_file")" _rval=2 elif cmp -s "$_tmp_file" "$postfwd_default_file" ; then echo_skipped _rval=1 else # - cat instead of mv/cp keeps owner and permissions of the original file if cat "$_tmp_file" > "$postfwd_default_file" 2> "$log_file" ; then echo_ok _rval=0 else echo_failed error "$(cat "$log_file")" _rval=2 fi fi rm -f "$_tmp_file" return $_rval } # ------------- # --- Main # ------------- [[ "$(id -u)" -eq 0 ]] || fatal "This script must be run as root." command -v postfwd > /dev/null 2>&1 || fatal "postfwd is not installed." if [[ ! -f "$postfwd_default_file" ]] ; then touch "$postfwd_default_file" 2> "$log_file" || fatal "$(cat "$log_file")" fi echo "" echo "Update ${postfwd_default_file}" echo "" file_changed=false errors=false for _setting in "${wanted_settings[@]}" ; do set_postfwd_default "${_setting%%|*}" "${_setting#*|}" case $? in 0) file_changed=true ;; 2) errors=true ;; esac done echo "" if $errors ; then fatal "Errors while updating '${postfwd_default_file}' - postfwd NOT restarted." fi if ! $file_changed ; then echo " Nothing changed - postfwd is left alone." echo "" clean_up 0 fi echononl " Restart postfwd (start parameters changed)" if systemctl restart postfwd > "$log_file" 2>&1 ; then echo_ok else echo_failed error "$(cat "$log_file")" clean_up 1 fi echo "" clean_up 0