#!/bin/sh # Script is sourced not executed, but shebang helps tools recognize this as # a shell script. # In recent (relevant) versions of shellcheck busybox is a valid shell type # shellcheck shell=busybox # uci-defaults script to migrate old uci config for notifications and monitor sections, # to the new. Applied during install or on first boot after install, if setting on install # fails # IPKG_INSTROOT is intentionally only set when building an image and # is intentionally empty on a live OpenWrt device # Only run this uci-defaults script on a live OpenWrt device [ -z "${IPKG_INSTROOT}" ] || exit 0 # Loads needed OpenWrt functions # shellcheck source=net/nut/files/functions.sh.functions . /lib/functions.sh || { # As the uci-defaults environment in which this runs does not have logging # available, nor is stderr captured or displayed on the console, these messages # exist only to assist when debugging manual runs of the script. printf "'%s': ERROR: '%s'\n" nut-monitor-migrate "Unable to source 'functions.sh' in 'nut-monitor-migrate' uci-default script. Bailing" exit 1 } # Adds notification flag (value) to 'notify_flags' from caller # shellcheck disable=SC2329,SC2317 get_notify_flags() { local value="$1" append notify_flags "$value" "+" } # shellcheck disable=SC2329,SC2317 convert_flags() { local notify_flags="$1" local working_flags="" local flag_val # In input flags can be in any order. If any of the input flags are IGNORE, # the output should only contain IGNORE. If the input contains only SYSLOG # or only EXEC, use as is. If the input contains both SYSLOG and EXEC (in # any order) the output must be SYSLOG+EXEC for flag_val in IGNORE SYSLOG EXEC; do # If the notify_flags contains $flag_val case "$notify_flags" in "$flag_val"+* | *+"$flag_val" | "$flag_val") case "$flag_val" in IGNORE) working_flags="IGNORE" printf "%s" "$working_flags" return ;; SYSLOG) # If working flags exists, it already has a non SYSLOG value # SYSLOG must come first in the final flags, so append # old working_flags to SYSLOG with delimiter '+', and make # that the new working_flags if [ "$working_flags" = "EXEC" ]; then working_flags="${flag_val}+${working_flags}" else working_flags="$flag_val" fi ;; EXEC) # append to an empty variable just returns the value to be # appended without a delimiter, so we do not need to check # for already having a value in 'working_flags' append working_flags "$flag_val" "+" ;; esac ;; esac done printf "%s" "$working_flags" } # As the uci-defaults environment in which this runs does not have logging # available, nor is stderr captured or displayed on the console, these messages # exist only to assist when debugging manual runs of the script. # shellcheck disable=SC2329,SC2317 log_migration_error() { local reason="$1" printf "'%s': ERROR: '%s'\n" "nut-monitor-migrate" "$reason" >&2 } # shellcheck disable=SC2329,SC2317 get_notification_flags() { local uci_option="$1" local cfg="$2" local notify_flags="" # accumulates flags in notify_flags via get_notify_flags config_list_foreach "$cfg" "$uci_option" get_notify_flags convert_flags "$notify_flags" } # shellcheck disable=SC2329,SC2317 process_upsmon_section() { local cfg="$1" local notify_type flag_name notify_msg notify_section val local notification_name notify_flags_as_var upsmon_final_flags # List of notification messages options in existing (to be converted) # UCI config for nut_monitor local nut_notify_message_types="onlinemsg onbattmsg lowbattmsg fsdmsg commokmsg" append nut_notify_message_types "commbadmsg shutdownmsg replbattmsg" append nut_notify_message_types "nocommmsmsg nocommmsg noparentmsg" local nocomm_exists="false" # Get hotsync (typo) value if present config_get val "$cfg" hotsync # Get hostsync value, only using hotsync as default if hostsync does not exist config_get val "$cfg" hostsync "$val" if [ -n "$val" ]; then if ! uci -q set nut_monitor."$cfg".hostsync="$val"; then log_migration_error "Failed to set hostsync value" fi fi # Remove invalid hotsync option unconditionally uci -q delete nut_monitor."$cfg".hotsync || true if uci -q get nut_monitor.NOCOMM; then nocomm_exists="true" fi # We need word-splitting to iterate over nut_notify_message_types # This is a local variable defined by us, so it is safe to do this. # We still disable globbing of the word-split set -f for notify_type in $nut_notify_message_types; do # Transform msg into notify (flag_name) and (notification_name) flag_name="${notify_type/msg/notify}" notification_name="${notify_type/msg/}" # busybox ash does not support case transformation via variable parameter substitution, # so we use tr. notification_name="$(printf '%s' "$notification_name" | tr '[:lower:]' '[:upper:]')" # Transform wrong NOCOMMS to NOCOMM (typo in nocommsmsg which should # have been nocommmsg in previous script), preferring NOCOMM (correct # version) if both exist if [ "$notification_name" = "NOCOMMMS" ]; then notification_name="NOCOMM" fi if [ "$notification_name" = "NOCOMM" ]; then if [ "$nocomm_exists" = "true" ]; then # Do not delete or overwrite pre-existing NOCOMM section continue fi if uci -q get nut_monitor."$notification_name"; then uci -q delete nut_monitor."$notification_name" || { log_migration_error "Failed to remove duplicate NOCOMM section" # We don't want to prevent other sections from being processed # if this one fails, so do not error exit, but keep going. continue } printf "'%s': WARN: '%s'\n" "nut-monitor-migrate" "Removed duplicate NOCOMM section, likely from NOCOMMS typo" >&2 fi fi config_get notify_msg "$cfg" "$notify_type" config_get notify_flags_as_var "$cfg" "$flag_name" if [ -n "$notify_msg" ] || [ -n "$notify_flags_as_var" ]; then notify_section="$(uci -q add nut_monitor notifications)" if [ -z "$notify_section" ]; then log_migration_error "Failed to add notifications section" # We don't want to prevent other sections from being processed # if this one fails, so do not error exit, but keep going. continue fi if ! uci -q rename nut_monitor."$notify_section"="$notification_name"; then log_migration_error "Failed to rename '$notify_section' to '$notification_name'" # We don't want to prevent other sections from being processed # if this one fails, so do not error exit, but keep going. continue fi fi if [ -n "$notify_msg" ]; then if ! uci -q set nut_monitor."$notification_name".message="$notify_msg"; then log_migration_error "Failed to add message for '$notification_name'" continue fi fi upsmon_final_flags="$(get_notification_flags "$flag_name" "$cfg")" if [ -n "$upsmon_final_flags" ]; then if ! uci -q set nut_monitor."$notification_name".flag="$upsmon_final_flags"; then log_migration_error "Failed to add flag(s) to '$notification_name'" continue fi fi # Keep going even if deleting old flag fails. It won't be used # anyway, and partial success is better than none. if [ -n "$cfg" ] && [ -n "$notify_flags_as_var" ] && ! uci -q delete nut_monitor."$cfg"."$flag_name"; then log_migration_error "Failed to delete old flag '$flag_name' from '$cfg'" fi # Keep going even if deleting old notification message fails. It # won't be used anyway, and partial success is better than none. if [ -n "$cfg" ] && [ -n "$notify_msg" ] && ! uci -q delete nut_monitor."$cfg"."$notify_type"; then log_migration_error "Failed to delete old notification type '$notify_type' from '$cfg'" fi done set +f upsmon_final_flags="$(get_notification_flags "defaultnotify" "$cfg")" if [ -n "$upsmon_final_flags" ]; then if ! uci -q set nut_monitor."$cfg".defaultnotify="$upsmon_final_flags"; then log_migration_error "Failed to update defaultnotify for '$cfg'" fi fi } # shellcheck disable=SC2329,SC2317 process_primary_secondary() { local cfg="$1" local monitor_type="$2" local upsname hostname port powervalue username password section section="$(uci -q add nut_monitor monitor)" || { log_migration_error "Failed to add monitor section to replace '$cfg'" # Do not error exit as we do not want to stop other sections from being # processed return 0 } if [ -n "$section" ]; then { uci -q set nut_monitor."$section".type="$monitor_type" || return 1 # Empty values are allowed and should remain as such in the new # section. config_get upsname "$cfg" upsname config_get hostname "$cfg" hostname config_get port "$cfg" port config_get powervalue "$cfg" powervalue config_get username "$cfg" username config_get password "$cfg" password uci -q set nut_monitor."$section".upsname="$upsname" || return 1 uci -q set nut_monitor."$section".hostname="$hostname" || return 1 uci -q set nut_monitor."$section".port="$port" || return 1 uci -q set nut_monitor."$section".powervalue="$powervalue" || return 1 uci -q set nut_monitor."$section".username="$username" || return 1 uci -q set nut_monitor."$section".password="$password" || return 1 # Delete the old section with name config (of type primary # or secondary) uci -q delete nut_monitor."$cfg" || return 1 # Rename the nut_monitor section '$section' we created, above, with # the name of the old section. This requires that the old section # with the same name has already been removed. # In the event of delete failure, the new section is present, but # with the name assigned when the new section was created. uci -q rename nut_monitor."$section"="$cfg" || return 1 return 0 } || { log_migration_error "Error converting primary/secondary section '$cfg'" # Do not error exit as we do not want to stop other sections from # being processed return 0 } else log_migration_error "Name of monitor section to replace '$cfg' was empty" # Do not error exit as we do not want to stop other sections from being # processed return 0 fi } config_load nut_monitor || { echo "nut-monitor-migrate: FATAL: Failed to load nut_monitor" exit 1 } config_foreach process_upsmon_section upsmon config_foreach process_primary_secondary master primary config_foreach process_primary_secondary slave secondary uci commit nut_monitor exit 0