diff --git a/docs/man/nut-driver-enumerator.txt b/docs/man/nut-driver-enumerator.txt index 72e50df3c3..725691b512 100644 --- a/docs/man/nut-driver-enumerator.txt +++ b/docs/man/nut-driver-enumerator.txt @@ -51,6 +51,12 @@ Update wrapping of devices into services Update wrapping of devices into services in an infinite loop; Default freq is 60 sec. +*nut-driver-enumerator.sh --daemon-after(=freq)*:: +Update wrapping of devices into services in an infinite loop; +first do one run of the loop though, then daemonize (this way +service unit is deemed started only when NUT config and driver +instances are in sync). Default freq is 60 sec. + *nut-driver-enumerator.sh --reconfigure*:: Stop and un-register all service instances and recreate them (e.g. if new dependency template was defined in a new diff --git a/docs/nut.dict b/docs/nut.dict index 0ad61bb8db..5a90674253 100644 --- a/docs/nut.dict +++ b/docs/nut.dict @@ -1,4 +1,4 @@ -personal_ws-1.1 en 2490 utf-8 +personal_ws-1.1 en 2491 utf-8 AAS ACFAIL ACFREQ @@ -1404,6 +1404,7 @@ cx cyberpower d'un da +daemonize daisychain daisychained datacenter diff --git a/scripts/upsdrvsvcctl/nut-driver-enumerator.sh.in b/scripts/upsdrvsvcctl/nut-driver-enumerator.sh.in index 6c434b5387..16b4080192 100755 --- a/scripts/upsdrvsvcctl/nut-driver-enumerator.sh.in +++ b/scripts/upsdrvsvcctl/nut-driver-enumerator.sh.in @@ -1,14 +1,18 @@ #!/bin/sh +# BIG NOTE: Not bash, not any other predetermined shell implementation # # NOTE: This script is intentionally written with portable shell constructs # with the aim and hope to work in different interpreters, so it is a # bit dumber and less efficient than could be achieved with the more -# featured shells in the spectrum. +# featured shells in the spectrum. Also, to minimize the in-memory and +# debug-console traffics, tests for (non-)emptiness of anticipated large +# strings are not done by `test -n/-z`, but by counting the size of the +# string (zero or not). # NOTE ALSO: The configuration parser in this script is not meant to be a # reference or 100% compliant with what the binary code uses; its aim # is to just pick out some strings relevant for tracking config changes. # -# Copyright (C) 2016-2018 Eaton +# Copyright (C) 2016-2019 Eaton # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -182,6 +186,9 @@ hook_listInstances_raw="" hook_validInstanceName="" hook_validFullUnitName="" hook_validInstanceSuffixName="" +hook_getSavedDeviceName="" +hook_findSavedDeviceName="" +hook_setSavedDeviceName="" hook_getSavedMD5="" hook_setSavedMD5="" hook_restart_upsd="" @@ -197,6 +204,9 @@ case "${SERVICE_FRAMEWORK-}" in hook_validInstanceName="smf_validInstanceName" hook_validFullUnitName="smf_validFullUnitName" hook_validInstanceSuffixName="smf_validInstanceSuffixName" + hook_getSavedDeviceName="smf_getSavedDeviceName" + hook_findSavedDeviceName="smf_findSavedDeviceName" + hook_setSavedDeviceName="smf_setSavedDeviceName" hook_getSavedMD5="smf_getSavedMD5" hook_setSavedMD5="smf_setSavedMD5" hook_restart_upsd="smf_restart_upsd" @@ -211,6 +221,9 @@ case "${SERVICE_FRAMEWORK-}" in hook_validInstanceName="systemd_validInstanceName" hook_validFullUnitName="systemd_validFullUnitName" hook_validInstanceSuffixName="systemd_validInstanceSuffixName" + hook_getSavedDeviceName="systemd_getSavedDeviceName" + hook_findSavedDeviceName="systemd_findSavedDeviceName" + hook_setSavedDeviceName="systemd_setSavedDeviceName" hook_getSavedMD5="systemd_getSavedMD5" hook_setSavedMD5="systemd_setSavedMD5" hook_restart_upsd="systemd_restart_upsd" @@ -225,6 +238,9 @@ case "${SERVICE_FRAMEWORK-}" in hook_validInstanceName="selftest_NOOP" hook_validFullUnitName="selftest_NOOP" hook_validInstanceSuffixName="selftest_NOOP" + hook_getSavedDeviceName="selftest_NOOP" + hook_findSavedDeviceName="selftest_NOOP" + hook_setSavedDeviceName="selftest_NOOP" hook_getSavedMD5="selftest_NOOP" hook_setSavedMD5="selftest_NOOP" hook_restart_upsd="selftest_NOOP" @@ -246,7 +262,7 @@ selftest_NOOP() { } common_isFiled() { - [ -n "$UPSLIST_FILE" ] && \ + [ "${#UPSLIST_FILE}" != 0 ] && \ for UPSF in $UPSLIST_FILE ; do [ "$1" = "$UPSF" ] && return 0 [ "`$hook_validInstanceName "$UPSF"`" = "$1" ] && return 0 @@ -255,7 +271,7 @@ common_isFiled() { } common_isRegistered() { - [ -n "$UPSLIST_SVCS" ] && \ + [ "${#UPSLIST_SVCS}" != 0 ] && \ for UPSS in $UPSLIST_SVCS ; do [ "$1" = "$UPSS" ] && return 0 [ "`$hook_validInstanceName "$1"`" = "$UPSS" ] && return 0 @@ -274,8 +290,8 @@ upslist_equals() { # Trivial case 0: one string is empty, another is not # Note: `echo '' | wc -l` == "1" not "0"! - [ -n "$1" -a -z "$2" ] && return 1 - [ -z "$1" -a -n "$2" ] && return 1 + [ "${#1}" != 0 -a "${#2}" = 0 ] && return 1 + [ "${#1}" = 0 -a "${#2}" != 0 ] && return 1 # Trivial case 1: equal strings [ "$1" = "$2" ] && return 0 @@ -284,22 +300,22 @@ upslist_equals() { _TMP_DEV_SVC="" for _DEV in $1 ; do - DEVINST="`$hook_validInstanceName "$_DEV"`" + DEVINST="`$hook_validInstanceName "${_DEV}"`" for _SVC in $2 ; do - [ "$_DEV" = "$_SVC" ] \ - || [ "$DEVINST" = "$_SVC" ] \ - && { [ -z "$_TMP_DEV_SVC" ] \ - && _TMP_DEV_SVC="$_DEV = $_SVC" \ - || _TMP_DEV_SVC="$_TMP_DEV_SVC -$_DEV = $_SVC" ; } + [ "${_DEV}" = "${_SVC}" ] \ + || [ "$DEVINST" = "${_SVC}" ] \ + && { [ "${#_TMP_DEV_SVC}" = 0 ] \ + && _TMP_DEV_SVC="${_DEV} = ${_SVC}" \ + || _TMP_DEV_SVC="${_TMP_DEV_SVC} +${_DEV} = ${_SVC}" ; } done done # Input was not empty; did anything in output fit? - [ -z "$_TMP_DEV_SVC" ] && return 1 + [ "${#_TMP_DEV_SVC}" = 0 ] && return 1 # Exit code : is the built mapping as long as the source list(s)? - [ "`echo "$1" | wc -l`" = "`echo "$_TMP_DEV_SVC" | wc -l`" ] + [ "`echo "$1" | wc -l`" = "`echo "${_TMP_DEV_SVC}" | wc -l`" ] } upssvcconf_checksum_unchanged() { @@ -315,28 +331,72 @@ upslist_checksums_unchanged() { # configuration section from the file and the stashed configuration in # a service instance. Prints a list of mismatching service names that # should get reconfigured. - [ -z "$1" -o -z "$2" ] && return 1 + [ "${#1}" = 0 -o "${#2}" = 0 ] && return 1 _TMP_SVC="" for _DEV in $1 ; do - DEVINST="`$hook_validInstanceName "$_DEV"`" + DEVINST="`$hook_validInstanceName "${_DEV}"`" for _SVC in $2 ; do - if [ "$_DEV" = "$_SVC" ] \ - || [ "$DEVINST" = "$_SVC" ] \ + if [ "${_DEV}" = "${_SVC}" ] \ + || [ "$DEVINST" = "${_SVC}" ] \ ; then - upssvcconf_checksum_unchanged "$_DEV" "$_SVC" || \ - { [ -z "$_TMP_SVC" ] \ - && _TMP_SVC="$_SVC" \ - || _TMP_SVC="$_TMP_SVC -$_SVC" ; } + upssvcconf_checksum_unchanged "${_DEV}" "${_SVC}" || \ + { [ "${#_TMP_SVC}" = 0 ] \ + && _TMP_SVC="${_SVC}" \ + || _TMP_SVC="${_TMP_SVC} +${_SVC}" ; } fi done done - [ -z "$_TMP_SVC" ] && return 0 - echo "$_TMP_SVC" + [ "${#_TMP_SVC}" = 0 ] && return 0 + echo "${_TMP_SVC}" return 1 } +upslist_savednames_find_missing() { + # Verify that all existing service units have a saved DEVICE name + # Report those that do not have a value there (any value) so we can + # amend those quickly after an upgrade. Otherwise we trust these. + + # Get full instance names from system and from props + SVCINSTS="`$hook_listInstances_raw`" && [ "${#SVCINSTS}" != 0 ] || return 1 + # If no props were found, (over)write them all + SVCINST_PROPS="`$hook_findSavedDeviceName`" && [ "${#SVCINST_PROPS}" != 0 ] \ + || { echo $SVCINSTS ; return 2; } + + # Find services which do not have saved names in props + for SVCINST in $SVCINSTS ; do + echo "$SVCINST_PROPS" | egrep "^${SVCINST_PROPS}\t"'$' >/dev/null || echo "$SVCINST" + done +} + +upslist_savednames_find_mismatch() { + # TODO: Make use of this to fsck the enumerator configs + # + # Verify that all existing service units have a saved DEVICE name + # and that such name does match the unit instance's name (original + # or MD5 normalized version). If something does not match, returns + # the unit name so it can be redefined by caller. This does not + # inspect whether such DEVICE is defined in NUT configuration. + # This situation might occur in some errors, but the likely case + # is updating from versions that did not track this info yet (but + # upslist_savednames_find_missing() should have handled those). + + # Get full instance names from system and from props + SVCINSTS="`$hook_listInstances_raw`" && [ "${#SVCINSTS}" != 0 ] || return 1 + SVCINST_PROPS="`$hook_findSavedDeviceName`" && [ "${#SVCINST_PROPS}" != 0 ] || return 2 + + # Find services whose props exist but services do not + echo "$SVCINST_PROPS" | while read SVCINST_PROP DEVNAME_PROP ; do + echo "$SVCINSTS" | egrep "^${SVCINST_PROP}"'$' >/dev/null || echo "$SVCINST_PROP" + done + + # Find services which do not have saved names in props + for SVCINST in $SVCINSTS ; do + echo "$SVCINST_PROPS" | egrep "^${SVCINST_PROP}\t"'$' >/dev/null || echo "$SVCINST" + done +} + upsconf_getSection_content() { # "$1" = name of ups.conf section to display in whole, from whatever # comes on stdin (file or a pre-made normalized variable) @@ -438,7 +498,7 @@ upsconf_getValue() { VALUE="" LINE="`echo "$SECTION_CONTENT" | egrep '(^'"$1"'=|^'"$1"'$)'`" \ - && VALUE="$(echo "$LINE" | sed -e "s,^$1=,," -e 's,^\"\(.*\)\"$,\1,' -e "s,^'\(.*\)'$,\1,")" \ + && VALUE="$(echo "$LINE" | sed -e "s,^$1=,," -e 's,^\"\(.*\)\"$,\1,' -e "s,^'\(.*\)'\$,\1,")" \ || RES_L=$? [ "$RES_L" = 0 ] || { RES="$RES_L" ; echo "ERROR: Section [$CURR_SECTION] or key '$1' in it was not found in the '$UPSCONF' file" >&2 ; } @@ -512,7 +572,7 @@ upsconf_getDriverMedia() { upsconf_getMedia() { _DRVMED="`upsconf_getDriverMedia "$1"`" || return - echo "$_DRVMED" | tail -n +2 + echo "${_DRVMED}" | tail -n +2 return 0 } @@ -522,27 +582,27 @@ upsconf_debug() { _MED="`upsconf_getMedia "$1"`" _MD5="`upsconf_getSection_MD5 "$1"`" NAME_MD5="`calc_md5 "$1"`" - echo "INST: ${NAME_MD5}~[$1]: DRV='$_DRV' PORT='$_PRT' MEDIA='$_MED' SECTIONMD5='$_MD5'" + echo "INST: ${NAME_MD5}~[$1]: DRV='${_DRV}' PORT='${_PRT}' MEDIA='${_MED}' SECTIONMD5='${_MD5}'" } calc_md5() { # Tries several ways to produce an MD5 of the "$1" argument - _MD5="`echo "$1" | md5sum 2>/dev/null | awk '{print $1}'`" && [ -n "$_MD5" ] || \ - { _MD5="`echo "$1" | openssl dgst -md5 2>/dev/null | awk '{print $NF}'`" && [ -n "$_MD5" ]; } || \ + _MD5="`echo "$1" | md5sum 2>/dev/null | awk '{print $1}'`" && [ -n "${_MD5}" ] || \ + { _MD5="`echo "$1" | openssl dgst -md5 2>/dev/null | awk '{print $NF}'`" && [ -n "${_MD5}" ]; } || \ return 1 - echo "$_MD5" + echo "${_MD5}" } calc_md5_file() { # Tries several ways to produce an MD5 of the file named by "$1" argument [ -s "$1" ] || return 2 - _MD5="`md5sum 2>/dev/null < "$1" | awk '{print $1}'`" && [ -n "$_MD5" ] || \ - { _MD5="`openssl dgst -md5 2>/dev/null < "$1" | awk '{print $NF}'`" && [ -n "$_MD5" ]; } || \ + _MD5="`md5sum 2>/dev/null < "$1" | awk '{print $1}'`" && [ -n "${_MD5}" ] || \ + { _MD5="`openssl dgst -md5 2>/dev/null < "$1" | awk '{print $NF}'`" && [ -n "${_MD5}" ]; } || \ return 1 - echo "$_MD5" + echo "${_MD5}" } smf_validFullUnitName() { @@ -563,15 +623,21 @@ smf_validInstanceSuffixName() { smf_registerInstance() { DEVICE="$1" SVCINST="$1" + if /usr/bin/svcs "nut-driver:$SVCINST" >/dev/null 2>&1 ; then + smf_unregisterInstance "$SVCINST" + fi /usr/sbin/svccfg -s nut-driver add "$DEVICE" || \ - { SVCINST="`smf_validInstanceName "$1"`" && \ + { SVCINST="`smf_validInstanceName "$1"`" || return + if /usr/bin/svcs "nut-driver:$SVCINST" >/dev/null 2>&1 ; then + smf_unregisterInstance "$SVCINST" + fi /usr/sbin/svccfg -s nut-driver add "$SVCINST" || return ; } echo "Added instance: 'nut-driver:$SVCINST' for NUT configuration section '$DEVICE'" >&2 DEPSVC="" DEPREQ="" _MED="`upsconf_getMedia "$DEVICE"`" - case "$_MED" in + case "${_MED}" in usb) DEPSVC="$DEPSVC_USB_SMF" DEPREQ="$DEPREQ_USB_SMF" ;; @@ -583,7 +649,7 @@ smf_registerInstance() { DEPREQ="$DEPREQ_NET_FULL_SMF" ;; serial) ;; '') ;; - *) echo "WARNING: Unexpected NUT media type ignored: '$_MED'" >&2 ;; + *) echo "WARNING: Unexpected NUT media type ignored: '${_MED}'" >&2 ;; esac TARGET_FMRI="nut-driver:$SVCINST" @@ -602,6 +668,8 @@ smf_registerInstance() { fi smf_setSavedMD5 "$SVCINST" "`upsconf_getSection_MD5 "$DEVICE"`" + # Save original device (config section) name to speed up some searches + smf_setSavedDeviceName "$SVCINST" "$DEVICE" /usr/sbin/svcadm refresh "${TARGET_FMRI}" || return if [ "$AUTO_START" = yes ] ; then @@ -623,7 +691,8 @@ smf_listInstances_raw() { /usr/bin/svcs -a -H -o fmri | egrep '/nut-driver:' } smf_listInstances() { - smf_listInstances_raw | sed 's/^.*://' | sort -n + # Chop twice, in case there is a leading "svc:/...." + smf_listInstances_raw | sed -e 's/^.*://' -e 's/^.*://' | sort -n } smf_getSavedMD5() { # Query service instance $1 @@ -639,26 +708,81 @@ smf_getSavedMD5() { fi # Note: lookups for GLOBAL cause each service instance to show up - /usr/bin/svcprop -p "$PG/$PROP" "$TARGET_FMRI" | head -1 | awk '{print $NF}' + /usr/bin/svcprop -p "$PG/$PROP" "$TARGET_FMRI" 2>/dev/null | head -1 | awk '{print $NF}' } -smf_setSavedMD5() { - # Save checksum value $2 into service instance $1 - PG="nut-driver-enumerator-generated-checksum" - PROP="SECTION_CHECKSUM" +smf_findSavedDeviceName() { + # Returns long service FMRI which has DEVICE=="$1" + # For empty "$1" returns a list of all recorded "FMRIDEVICE" + if [ -z "$1" ]; then + /usr/bin/svcprop -p "nut-driver-enumerator-generated-devicename/DEVICE" \ + 'svc:/system/power/nut-driver:*' 2>/dev/null \ + | sed 's|^\(svc:/[^:]*:[^/:]*\)/:properties/nut-driver-enumerator-generated-devicename/DEVICE astring \(.*\)$|\1\t\2|' + else + /usr/bin/svcprop -p "nut-driver-enumerator-generated-devicename/DEVICE" \ + "svc:/system/power/nut-driver:$1" 2>/dev/null \ + | sed 's|^\(svc:/[^:]*:[^/:]*\)/:prop.*$|\1|' + fi +} +smf_getSavedDeviceName() { + # Query service instance $1 + PG="nut-driver-enumerator-generated-devicename" + PROP="DEVICE" if [ -n "$1" ]; then TARGET_FMRI="nut-driver:$1" else # Global section - TARGET_FMRI="nut-driver" - PROP="SECTION_CHECKSUM_GLOBAL" + echo "" + return 0 fi - /usr/sbin/svccfg -s "$TARGET_FMRI" delprop "$PG" || true - /usr/sbin/svccfg -s "$TARGET_FMRI" addpg "$PG" application && \ - /usr/sbin/svccfg -s "$TARGET_FMRI" setprop "$PG/$PROP" = astring: "$2" - [ $? = 0 ] && echo "OK" || { echo "FAILED to stash the checksum">&2 ; return 1 ; } - /usr/sbin/svcadm refresh "${TARGET_FMRI}" || return + # Note: lookups for GLOBAL cause each service instance to show up + /usr/bin/svcprop -p "$PG/$PROP" "$TARGET_FMRI" 2>/dev/null | head -1 | awk '{print $NF}' +} +smf_setSavedUniq() { + # Save data value $5 of type $4 into service FMRI $1 + # under (scrapped and) newly created property group $2 + # and property name $3 + __TARGET_FMRI="$1" + __PG="$2" + __PROP="$3" + __TYPE="$4" + case "${__TYPE}" in + *:) ;; + *) __TYPE="${__TYPE}:" ;; + esac + __VAL="$5" + /usr/sbin/svccfg -s "${__TARGET_FMRI}" delprop "${__PG}" 2>/dev/null || true + /usr/sbin/svccfg -s "${__TARGET_FMRI}" addpg "${__PG}" application && \ + /usr/sbin/svccfg -s "${__TARGET_FMRI}" setprop "${__PG}/${__PROP}" = "${__TYPE}" "${__VAL}" + [ $? = 0 ] && echo "OK" || { echo "FAILED to stash the service property ${__PG}/${__PROP}">&2 ; return 1 ; } + + case "${__TARGET_FMRI}" in + svc:/*:*) ;; # A service instance by full FMRI, refresh + svc:/*/nut-driver|nut-driver) return 0 ;; # A base non-instance service item for nut-driver (known multi-instance only) + svc:/*) ;; # A base non-instance service item (not nut-driver) + *:*) ;; # A service instance by short FMRI, refresh + *) ;; # A base non-instance service item (not nut-driver) + esac + /usr/sbin/svcadm refresh "${__TARGET_FMRI}" || return +} +smf_setSavedMD5() { + # Save checksum value $2 into service instance $1 + _PG="nut-driver-enumerator-generated-checksum" + _PROP="SECTION_CHECKSUM" + + if [ -n "$1" ]; then + _TARGET_FMRI="nut-driver:$1" + else + # Global section + _TARGET_FMRI="nut-driver" + _PROP="SECTION_CHECKSUM_GLOBAL" + fi + smf_setSavedUniq "${_TARGET_FMRI}" "${_PG}" "${_PROP}" astring "$2" +} +smf_setSavedDeviceName() { + [ -n "$1" ] || return # No-op for global section + smf_setSavedUniq "nut-driver:$1" "nut-driver-enumerator-generated-devicename" "DEVICE" astring "$2" } smf_restart_upsd() { echo "Restarting NUT data server to make sure it knows new configuration..." @@ -700,7 +824,7 @@ systemd_registerInstance() { DEPSVC="" DEPREQ="" _MED="`upsconf_getMedia "$DEVICE"`" - case "$_MED" in + case "${_MED}" in usb) DEPSVC="$DEPSVC_USB_SYSTEMD" DEPREQ="$DEPREQ_USB_SYSTEMD" ;; @@ -712,7 +836,7 @@ systemd_registerInstance() { DEPREQ="$DEPREQ_NET_FULL_SYSTEMD" ;; serial) ;; '') ;; - *) echo "WARNING: Unexpected NUT media type ignored: '$_MED'" >&2 ;; + *) echo "WARNING: Unexpected NUT media type ignored: '${_MED}'" >&2 ;; esac if [ -n "$DEPSVC" ]; then [ -n "$DEPREQ" ] || DEPREQ="#Wants" @@ -730,6 +854,7 @@ EOF fi systemd_setSavedMD5 "$SVCINST" "`upsconf_getSection_MD5 "$DEVICE"`" + systemd_setSavedDeviceName "$SVCINST" "$DEVICE" if [ "$AUTO_START" = yes ] ; then $TIMEOUT_CMD $TIMEOUT_ARGS /bin/systemctl start --no-block 'nut-driver@'"$SVCINST".service || return @@ -759,16 +884,53 @@ systemd_getSavedMD5() { # Query service instance $1 or global section PROP="SECTION_CHECKSUM" [ -n "$1" ] || PROP="SECTION_CHECKSUM_GLOBAL" - [ -s "${SYSTEMD_CONFPATH}/nut-driver@$1.service.d/nut-driver-enumerator-generated-checksum.conf" ] \ - && grep "Environment='$PROP=" "${SYSTEMD_CONFPATH}/nut-driver@$1.service.d/nut-driver-enumerator-generated-checksum.conf" | sed -e "s,^Environment='$PROP=,," -e "s,'\$,," \ - || { echo "Did not find '${SYSTEMD_CONFPATH}/nut-driver@$1.service.d/nut-driver-enumerator-generated-checksum.conf' with a $PROP" ; return 1; } + PROPFILE="${SYSTEMD_CONFPATH}/nut-driver@$1.service.d/nut-driver-enumerator-generated-checksum.conf" + [ -s "${PROPFILE}" ] \ + && grep "Environment='$PROP=" "${PROPFILE}" | sed -e "s,^Environment='$PROP=,," -e "s,'\$,," \ + || { echo "Did not find '${PROPFILE}' with a $PROP" ; return 1; } +} +systemd_findSavedDeviceName() { + # Returns long service instance name which has DEVICE=="$1" + # For empty "$1" returns a list of all recorded "SVCDEVICE" + if [ -z "$1" ]; then + grep -H "Environment='DEVICE=" \ + /etc/systemd/system/nut-driver@*.service.d/nut-driver-enumerator-generated-devicename.conf \ + | sed 's|^/etc/systemd/system/\(nut-driver@[^/]*\.service\)\.d/.*DEVICE='"[\"']*\([^\"']*\)[\"']*"'$|\1\t\2|' + else + egrep -H "Environment='DEVICE=($1|\"$1\")'" \ + /etc/systemd/system/nut-driver@*.service.d/nut-driver-enumerator-generated-devicename.conf \ + | sed 's|^/etc/systemd/system/\(nut-driver@[^/]*\.service\)\.d/.*$|\1|' + fi +} +systemd_getSavedDeviceName() { + # Query service instance "$1" (quiet NO-OP if empty, for mis-use + # in global sections context) to get the unquoted saved DEVICE + # section name corresponding to this service instance + [ -n "$1" ] || { echo ""; return 0; } + PROP="DEVICE" + PROPFILE="${SYSTEMD_CONFPATH}/nut-driver@$1.service.d/nut-driver-enumerator-generated-devicename.conf" + [ -s "${PROPFILE}" ] \ + && grep "Environment='$PROP=" "${PROPFILE}" | sed -e "s,^Environment='$PROP=,," -e "s,'\$,," -e 's,^"\(.*\)"$,\1,' \ + || { echo "Did not find '${PROPFILE}' with a $PROP" ; return 1; } +} +systemd_setSavedDeviceName() { + # Save device (config section) name $2 into service instance $1 + [ -n "$1" ] || return # No-op for global section + PROPFILE="${SYSTEMD_CONFPATH}/nut-driver@$1.service.d/nut-driver-enumerator-generated-devicename.conf" + mkdir -p "${SYSTEMD_CONFPATH}/nut-driver@$1.service.d" && \ + cat > "${PROPFILE}" << EOF +[Service] +Environment='DEVICE="$2"' +EOF + [ $? = 0 ] && echo "OK" || { echo "FAILED to stash the device name">&2 ; return 1 ; } } systemd_setSavedMD5() { # Save checksum value $2 into service instance $1 PROP="SECTION_CHECKSUM" [ -n "$1" ] || PROP="SECTION_CHECKSUM_GLOBAL" + PROPFILE="${SYSTEMD_CONFPATH}/nut-driver@$1.service.d/nut-driver-enumerator-generated-checksum.conf" mkdir -p "${SYSTEMD_CONFPATH}/nut-driver@$1.service.d" && \ - cat > "${SYSTEMD_CONFPATH}/nut-driver@$1.service.d/nut-driver-enumerator-generated-checksum.conf" << EOF + cat > "${PROPFILE}" << EOF [Service] Environment='$PROP=$2' EOF @@ -851,7 +1013,7 @@ upslist_normalizeFile() { # Also use a SDP subset with just section, driver and port info # for faster parsing when determining driver-required media etc. UPSCONF_DATA="$(upslist_normalizeFile_filter < "$UPSCONF")" \ - && [ -n "$UPSCONF_DATA" ] \ + && [ "${#UPSCONF_DATA}" != 0 ] \ && UPSCONF_DATA_SDP="`egrep '^(\[.*\]|driver=|port=)' << EOF $UPSCONF_DATA EOF`" \ @@ -865,7 +1027,7 @@ upslist_normalizeFile_once() { # Wrapper that ensures that the parsing is only done once # (will re-parse if there were no devices listed on the # first time, though) - [ -z "$UPSCONF_DATA" ] && [ -z "$UPSCONF_DATA_SDP" ] || return 0 + [ "${#UPSCONF_DATA}" = 0 ] && [ "${#UPSCONF_DATA_SDP}" = 0 ] || return 0 upslist_normalizeFile } @@ -881,11 +1043,11 @@ upslist_readFile() { upslist_normalizeFile || return # Propagate errors upwards fi - if [ -n "$UPSCONF_DATA" ] ; then + if [ "${#UPSCONF_DATA}" != 0 ] ; then # Note that section-name brackets should contain a single token UPSLIST_FILE="$(echo "$UPSCONF_DATA_SDP" | egrep '^\[[^'"$TABCHAR"'\ ]*\]$' | sed 's,^\[\(.*\)\]$,\1,' | sort -n)" \ || UPSLIST_FILE="" - if [ -z "$UPSLIST_FILE" ] ; then + if [ "${#UPSLIST_FILE}" = 0 ] ; then echo "Error reading the '$UPSCONF' file or it does not declare any device configurations: no section declarations in parsed normalized contents" >&2 fi fi @@ -896,13 +1058,13 @@ upslist_readFile_once() { # Wrapper that ensures that the parsing is only done once # (will re-parse if there were no devices listed on the # first time, though) - [ -z "$UPSLIST_FILE" ] || return 0 + [ "${#UPSLIST_FILE}" = 0 ] || return 0 DO_NORMALIZE_ONCE=yes upslist_readFile } upslist_readSvcs() { UPSLIST_SVCS="`$hook_listInstances`" || UPSLIST_SVCS="" - if [ -z "$UPSLIST_SVCS" ] && [ "$1" != "-" ] ; then + if [ "${#UPSLIST_SVCS}" = 0 ] && [ "$1" != "-" ] ; then EXPLAIN="" [ -z "$1" ] || EXPLAIN=" - $1" echo "Error reading the list of ${SERVICE_FRAMEWORK-} service instances for UPS drivers, or none are defined${EXPLAIN}" >&2 @@ -993,14 +1155,14 @@ nut_driver_enumerator_main() { # We can only exit quickly if both there are no changed sections and no # new or removed sections since last run. NEW_CHECKSUM="`upslist_checksums_unchanged "$UPSLIST_FILE" "$UPSLIST_SVCS"`" \ - && [ -z "$NEW_CHECKSUM" ] \ + && [ "${#NEW_CHECKSUM}" = 0 ] \ && upslist_equals "$UPSLIST_FILE" "$UPSLIST_SVCS" \ && if [ -z "$DAEMON_SLEEP" -o "${VERBOSE_LOOP}" = yes ] ; then \ echo "`date -u` : OK: No changes to reconcile between ${SERVICE_FRAMEWORK} service instances and device configurations in '$UPSCONF'" ; \ fi \ && [ "$RESTART_ALL" = no ] && return 0 - if [ -n "$NEW_CHECKSUM" ]; then + if [ "${#NEW_CHECKSUM}" != 0 ]; then for UPSS in $NEW_CHECKSUM ; do echo "Dropping old ${SERVICE_FRAMEWORK} service instance ${UPSS} whose section in config file has changed..." >&2 $hook_unregisterInstance "$UPSS" @@ -1008,7 +1170,7 @@ nut_driver_enumerator_main() { upslist_readSvcs "after updating for new config section checksums" fi - if [ -n "$UPSLIST_SVCS" ]; then + if [ "${#UPSLIST_SVCS}" != 0 ]; then # Drop services that are not in config file (any more?) upslist_delSvcs @@ -1024,7 +1186,7 @@ nut_driver_enumerator_main() { $hook_setSavedMD5 "" "`upsconf_getSection_MD5 ""`" fi - if [ -n "$UPSLIST_FILE" ]; then + if [ "${#UPSLIST_FILE}" != 0 ]; then # Add services for sections that are in config file but not yet wrapped upslist_addSvcs $hook_refreshSupervizor @@ -1032,12 +1194,12 @@ nut_driver_enumerator_main() { fi upslist_readSvcs - if [ -n "$UPSLIST_SVCS" ] ; then + if [ "${#UPSLIST_SVCS}" != 0 ] ; then echo "=== The currently defined service instances are:" echo "$UPSLIST_SVCS" fi - if [ -n "$UPSLIST_FILE" ] ; then + if [ "${#UPSLIST_FILE}" != 0 ] ; then echo "=== The currently defined configurations in '$UPSCONF' are:" echo "$UPSLIST_FILE" fi @@ -1078,6 +1240,240 @@ nut_driver_enumerator_main() { return 13 } +nut_driver_enumerator_full_reconfigure() { + # Similar to the main routine for reconciling data, + # but this one removes all service instances and + # defines current mappings from scratch after that + upslist_readFile_once || return $? + upslist_readSvcs "before manipulations" + + if [ "${#UPSLIST_SVCS}" != 0 ]; then + for UPSS in $UPSLIST_SVCS ; do + echo "Dropping old ${SERVICE_FRAMEWORK} service instance for power device [${UPSS}] to reconfigure the service unit..." >&2 + $hook_unregisterInstance "$UPSS" + done + upslist_readSvcs "after dropping" + fi + if [ "${#UPSLIST_FILE}" != 0 ]; then + upslist_addSvcs + upslist_readSvcs "after checking for new config sections to define service instances" + fi + + # Save new checksum of global config + $hook_setSavedMD5 "" "`upsconf_getSection_MD5 ""`" + + # Service units were manipulated, including saving of checksums; + # refresh the service management daemon if needed + $hook_refreshSupervizor + + if [ "${#UPSLIST_SVCS}" != 0 ] ; then + echo "=== The currently defined service instances are:" + echo "$UPSLIST_SVCS" + fi + + if [ "${#UPSLIST_FILE}" != 0 ] ; then + echo "=== The currently defined configurations in '$UPSCONF' are:" + echo "$UPSLIST_FILE" + fi + + # We had some changes to the config file; upsd must be made aware + if [ "$AUTO_START" = yes ] ; then + $hook_restart_upsd + fi + + # Return 42 if there was a change applied succesfully + # (but e.g. some services should restart - upsd, maybe upsmon) + UPSLIST_EQ_RES=0 + upslist_equals "$UPSLIST_FILE" "$UPSLIST_SVCS" || UPSLIST_EQ_RES=$? + + # File processing and service startups take a while; + # make sure upsconf did not change while we worked... + # NOTE: Check this at the last moment to minimize + # the chance of still not noticing the change made + # at just the wrong moment. + UPSCONF_CHECKSUM_END="`calc_md5_file "$UPSCONF"`" || true + if [ "$UPSCONF_CHECKSUM_END" != "$UPSCONF_CHECKSUM_START" ] ; then + echo "`date -u` : '$UPSCONF' changed while $0 $* was processing its older contents; re-running the script to pick up and reconcile the late-coming changes" + nut_driver_enumerator_main ; return $? + # The "main" routine will do REPORT_RESTART_42 logic too + fi + + if [ "$UPSLIST_EQ_RES" = 0 ] ; then + echo "`date -u` : OK: No more changes to reconcile between ${SERVICE_FRAMEWORK} service instances and device configurations in '$UPSCONF'" + [ "${REPORT_RESTART_42-}" = no ] && return 0 || return 42 + fi + + return 13 +} + +list_services_for_devices() { + FINAL_RES=0 + upslist_readFile_once && [ "${#UPSLIST_FILE}" != 0 ] \ + || { echo "No devices detected in '$UPSCONF'" >&2 ; return 1 ; } + upslist_readSvcs "by user request" && [ "${#UPSLIST_SVCS}" != 0 ] \ + || { echo "No service instances detected" >&2 ; return 1; } + UPSLIST_SVCS_RAW="`$hook_listInstances_raw`" && [ "${#UPSLIST_SVCS_RAW}" != 0 ] \ + || { echo "No service units detected" >&2 ; return 1; } + for DEV in $UPSLIST_FILE ; do + vINST="`$hook_validInstanceName "$DEV"`" + vUNITD="`$hook_validFullUnitName "$DEV"`" + vUNITI="`$hook_validFullUnitName "$vINST"`" + # First pass over simple verbatim names + for INST in $UPSLIST_SVCS ; do + if [ "$INST" = "$DEV" ] ; then + for UNIT in $UPSLIST_SVCS_RAW ; do + if [ "$UNIT" = "$vUNITD" ] ; then + printf '%s\t%s\n' "$UNIT" "$DEV" + continue 3 + fi + done + fi + done + for INST in $UPSLIST_SVCS ; do + if [ "$INST" = "$vINST" ] ; then + for UNIT in $UPSLIST_SVCS_RAW ; do + if [ "$UNIT" = "$vUNITI" ] ; then + printf '%s\t%s\n' "$UNIT" "$DEV" + continue 3 + fi + done + fi + done + echo "WARNING: no service instances detected that match device '$DEV'" >&2 + FINAL_RES=1 + done + return $FINAL_RES +} + +SVCS_DEVS_LIST="" +list_services_for_devices_once() { + # On first call, caches the system reponse + # On next calls returns what it got earlier + # Does not return any text, just the exit code + # (0 = data avail, even if empty, in SVCS_DEVS_LIST) + [ "${#SVCS_DEVS_LIST}" != 0 ] && return + + # Pre-cache config file data, if nobody read it yet, + # and keep in main script context for reuse (no subshell) + upslist_readFile_once && \ + SVCS_DEVS_LIST="`list_services_for_devices "$@"`" || return $? +} + +get_device_for_service() { + [ -z "$1" ] && echo "Service (instance) name argument required" >&2 && return 1 + + # Instance name can be a hash or "native" NUT section name + SVC="`$hook_validInstanceSuffixName "$1"`" && [ -n "$SVC" ] \ + || { echo "Error getting SVC name from the inputs" >&2 ; return 1; } + + # Reading the config is too expensive to do for every + # driver management attempt when there are many devices + if [ "${USE_SAVEDINST-}" != false ]; then + # Try to use last-stashed values from service properties first + # (NOTE: saved value is assumed to be valid if present) + SAVEDINST="`$hook_getSavedDeviceName "$SVC"`" || SAVEDINST="" + [ "${#SAVEDINST}" = 0 ] || { echo "$SAVEDINST" ; return 0 ; } + fi + + case "$SVC" in + MD5_*) ;; # fall through to the bulk of code + *) upslist_readFile_once || return $? + echo "$UPSLIST_FILE" | egrep "^$SVC\$" + return $? + ;; + esac + + # Inspect SVC=MD5_* usecase + FINAL_RES=0 + list_services_for_devices_once && [ "${#SVCS_DEVS_LIST}" != 0 ] || FINAL_RES=$? + if [ "$FINAL_RES" = 0 ]; then + echo "$SVCS_DEVS_LIST" | grep "$SVC" | ( \ + while read _SVC _DEV ; do + _SVC="`$hook_validInstanceSuffixName "${_SVC}"`" || exit + [ "${_SVC}" = "${SVC}" ] && echo "${_DEV}" && exit 0 + done ; exit 1 ) && return 0 + fi + echo "No service instance '$1' was detected that matches a NUT device" >&2 + return 1 +} + +get_service_for_device() { + DEV="$1" + [ -z "$DEV" ] && echo "Device name argument required" >&2 && return 1 + + # Cheap check in service instance metadata, if saved + # (NOTE: saved value is assumed to be valid if present) + if [ "${USE_SAVEDSVC-}" != false ]; then + SAVEDSVC="`$hook_findSavedDeviceName "$DEV"`" || SAVEDSVC="" + [ "${#SAVEDSVC}" = 0 ] || { echo "$SAVEDSVC" ; return 0 ; } + fi + + # Trawl all the data we have... + # TODO: Reorder to avoid extra parsing if we have an early hit? + upslist_readSvcs "by user request" && [ "${#UPSLIST_SVCS}" != 0 ] \ + || { echo "No service instances detected" >&2 ; return 1; } + UPSLIST_SVCS_RAW="`$hook_listInstances_raw`" && [ "${#UPSLIST_SVCS_RAW}" != 0 ] \ + || { echo "No service units detected" >&2 ; return 1; } + vINST="`$hook_validInstanceName "$DEV"`" + vUNITD="`$hook_validFullUnitName "$DEV"`" + vUNITI="`$hook_validFullUnitName "$vINST"`" + + # First pass over simple verbatim names + for INST in $UPSLIST_SVCS ; do + if [ "$INST" = "$DEV" ] ; then + for UNIT in $UPSLIST_SVCS_RAW ; do + if [ "$UNIT" = "$vUNITD" ] ; then + echo "$UNIT" + return 0 + fi + done + fi + done + # Second pass over other options + for INST in $UPSLIST_SVCS ; do + if [ "$INST" = "$vINST" ] ; then + for UNIT in $UPSLIST_SVCS_RAW ; do + if [ "$UNIT" = "$vUNITI" ] ; then + echo "$UNIT" + return 0 + fi + done + fi + done + echo "No service instances detected that match device '$DEV'" >&2 + return 1 +} + +update_upslist_savednames_find_missing() { + # Runs once in production modes that inspect and reconcile + # configs, to handle upgraded NUT deployments + SVCINSTS_NO_DEVICE="`upslist_savednames_find_missing`" || \ + case "$?" in + 1) return 0 ;; # No services defined yet + 2) ;; # All service units do not have DEVICE values + esac + # Something found... or not? Or all is populated? + [ "${#SVCINSTS_NO_DEVICE}" != 0 ] || return 0 + + # Make a list of what devices are known in config matched + # to service instances defined in the system, if any + # Note to not check the service instance properties which + # we are validating and currently to not quite trust. + USE_SAVEDINST=false list_services_for_devices_once \ + && [ "${#SVCS_DEVS_LIST}" != 0 ] || return 0 + + # Go over services with no device value saved into properties, + # and write the values learned from mapping above + _MISSING_RES=0 + for SVCINST in $SVCINSTS_NO_DEVICE ; do + _DEV="`printf '%s\n' "$SVCS_DEVS_LIST" | awk '( \$1 == "'"${SVCINST}"'" ) {print \$NF}'`" + echo "Service instance '$SVCINST' did not have a device recorded into properties, setting to '${_DEV}'" + [ -n "${_DEV}" ] || { echo "The device name value for '$SVCINST' is empty, skipping" >&2 ; _MISSING_RES=1 ; continue ; } + $hook_setSavedDeviceName "`$hook_validInstanceSuffixName "$SVCINST"`" "${_DEV}" || _MISSING_RES=$? + done + return ${_MISSING_RES} +} + RECONFIGURE_ASAP=false trap_handle_hup_main() { echo "`date -u` : Received a HUP during processing, scheduling reconfiguration to repeat ASAP (after the current iteration is done)" >&2 @@ -1111,6 +1507,8 @@ daemonize() { RECONFIGURE_ASAP=false trap 'trap_handle_hup_main' $RECONFIGURATION_SIGNALS + update_upslist_savednames_find_missing + # Note: this loop would die on errors with config file or # inability to ensure that it matches the list of services. # If caller did not `export REPORT_RESTART_42=no` then the @@ -1147,6 +1545,7 @@ UPSCONF_CHECKSUM_START="`calc_md5_file "$UPSCONF"`" || true # By default, update wrapping of devices into services if [ $# = 0 ]; then + update_upslist_savednames_find_missing nut_driver_enumerator_main ; exit $? fi @@ -1179,9 +1578,10 @@ $0 --daemon(=freq) Update wrapping of devices into services in an infinite loop Default freq is 60 sec $0 --daemon-after(=freq) - Update wrapping of devices into services in an infinite loop - First do one run of the loop though - Default freq is 60 sec + Update wrapping of devices into services in an infinite loop; + first do one run of the loop though, then daemonize (this way + service unit is deemed started only when NUT config and driver + instances are in sync). Default freq is 60 sec. $0 --reconfigure Stop and un-register all service instances and recreate them (e.g. if new dependency template was defined in a new @@ -1218,78 +1618,49 @@ $0 --show-device-config-value DEV KEY [KEY...] one per line in the same order (including empty lines for missing values); any missing value yields a non-zero exit code. EOF + +# TODO: refactor no-arg and daemonized runs into common loop, set ACTION +# and process that all uniformly. +#COMMON OPTIONS: +# --timeout-cmd service management calls will be time-limited +# --timeout-args by calling the specified program with its args. +# By default, if coreutils timeout is found, it +# would be used to limit service calls by 90 sec. + } while [ $# -gt 0 ]; do case "$1" in --help|-h|-help) usage; exit 0 ;; - --get-service-framework) echo "${SERVICE_FRAMEWORK}" ; exit 0 ;; - --reconfigure) - upslist_readFile_once || exit $? - upslist_readSvcs "before manipulations" - - if [ -n "$UPSLIST_SVCS" ]; then - for UPSS in $UPSLIST_SVCS ; do - echo "Dropping old ${SERVICE_FRAMEWORK} service instance for power device [${UPSS}] to reconfigure the service unit..." >&2 - $hook_unregisterInstance "$UPSS" - done - upslist_readSvcs "after dropping" - fi - - if [ -n "$UPSLIST_FILE" ]; then - upslist_addSvcs - upslist_readSvcs "after checking for new config sections to define service instances" - fi - - # Save new checksum of global config - $hook_setSavedMD5 "" "`upsconf_getSection_MD5 ""`" - - # Service units were manipulated, including saving of checksums; - # refresh the service management daemon if needed - $hook_refreshSupervizor - - if [ -n "$UPSLIST_SVCS" ] ; then - echo "=== The currently defined service instances are:" - echo "$UPSLIST_SVCS" - fi - - if [ -n "$UPSLIST_FILE" ] ; then - echo "=== The currently defined configurations in '$UPSCONF' are:" - echo "$UPSLIST_FILE" - fi - - # We had some changes to the config file; upsd must be made aware - if [ "$AUTO_START" = yes ] ; then - $hook_restart_upsd - fi - - # Return 42 if there was a change applied succesfully - # (but e.g. some services should restart - upsd, maybe upsmon) - UPSLIST_EQ_RES=0 - upslist_equals "$UPSLIST_FILE" "$UPSLIST_SVCS" || UPSLIST_EQ_RES=$? - - # File processing and service startups take a while; - # make sure upsconf did not change while we worked... - # NOTE: Check this at the last moment to minimize - # the chance of still not noticing the change made - # at just the wrong moment. - UPSCONF_CHECKSUM_END="`calc_md5_file "$UPSCONF"`" || true - if [ "$UPSCONF_CHECKSUM_END" != "$UPSCONF_CHECKSUM_START" ] ; then - echo "`date -u` : '$UPSCONF' changed while $0 $* was processing its older contents; re-running the script to pick up the late-coming changes" - $0 ; exit $? - # The "main" routine will do REPORT_RESTART_42 logic too - fi - - if [ "$UPSLIST_EQ_RES" = 0 ] ; then - echo "`date -u` : OK: No more changes to reconcile between ${SERVICE_FRAMEWORK} service instances and device configurations in '$UPSCONF'" - [ "${REPORT_RESTART_42-}" = no ] && exit 0 || exit 42 + --timeout-length|--timeout-len|--timeout-args) + TIMEOUT_ARGS="$2" + shift + ;; + --timeout-cmd) + if [ -n "$2" ] ; then + if [ -x "$2" ] || which "$2" 2>/dev/null >/dev/null ; then + TIMEOUT_CMD="$2" + else + echo "ERROR: Received a '$2' argument for $1, and can not find such program; clearing the option so the basic functionality can proceed" >&2 + TIMEOUT_CMD="" + TIMEOUT_ARGS="" + fi + else + echo "INFO: Received an empty argument for $1, clearing the option" >&2 + TIMEOUT_CMD="" + TIMEOUT_ARGS="" fi + shift + ;; - exit 13 + --get-service-framework) echo "${SERVICE_FRAMEWORK}" ; exit 0 ;; + --reconfigure) + nut_driver_enumerator_full_reconfigure "$@" + exit $? ;; --list-devices) upslist_readFile_once && \ - if [ -n "$UPSLIST_FILE" ] ; then + if [ "${#UPSLIST_FILE}" != 0 ] ; then echo "=== The currently defined configurations in '$UPSCONF' are:" >&2 echo "$UPSLIST_FILE" exit 0 @@ -1299,7 +1670,7 @@ while [ $# -gt 0 ]; do ;; --list-services) UPSLIST_SVCS_RAW="`$hook_listInstances_raw`" && \ - if [ -n "$UPSLIST_SVCS_RAW" ] ; then + if [ "${#UPSLIST_SVCS_RAW}" != 0 ] ; then echo "=== The currently defined service units are:" >&2 echo "$UPSLIST_SVCS_RAW" exit 0 @@ -1309,7 +1680,7 @@ while [ $# -gt 0 ]; do ;; --list-instances) upslist_readSvcs "by user request" && \ - if [ -n "$UPSLIST_SVCS" ] ; then + if [ "${#UPSLIST_SVCS}" != 0 ] ; then echo "=== The currently defined service instances are:" >&2 echo "$UPSLIST_SVCS" exit 0 @@ -1317,104 +1688,26 @@ while [ $# -gt 0 ]; do echo "No service instances detected" >&2 exit 1 ;; - --get-service-for-device) [ -z "$2" ] && echo "Device name argument required" >&2 && exit 1 - DEV="$2" - upslist_readSvcs "by user request" && [ -n "$UPSLIST_SVCS" ] \ - || { echo "No service instances detected" >&2 ; exit 1; } - UPSLIST_SVCS_RAW="`$hook_listInstances_raw`" && [ -n "$UPSLIST_SVCS_RAW" ] \ - || { echo "No service units detected" >&2 ; exit 1; } - vINST="`$hook_validInstanceName "$DEV"`" - vUNITD="`$hook_validFullUnitName "$DEV"`" - vUNITI="`$hook_validFullUnitName "$vINST"`" - # First pass over simple verbatim names - for INST in $UPSLIST_SVCS ; do - if [ "$INST" = "$DEV" ] ; then - for UNIT in $UPSLIST_SVCS_RAW ; do - if [ "$UNIT" = "$vUNITD" ] ; then - echo "$UNIT" - exit 0 - fi - done - fi - done - for INST in $UPSLIST_SVCS ; do - if [ "$INST" = "$vINST" ] ; then - for UNIT in $UPSLIST_SVCS_RAW ; do - if [ "$UNIT" = "$vUNITI" ] ; then - echo "$UNIT" - exit 0 - fi - done - fi - done - echo "No service instances detected that match device '$2'" >&2 - exit 1 + --get-service-for-device) + shift + get_service_for_device "$@" + exit $? ;; - --get-device-for-service) [ -z "$2" ] && echo "Service (instance) name argument required" >&2 && exit 1 - # Instance name can be a hash or "native" NUT section name - SVC="`$hook_validInstanceSuffixName "$2"`" - case "$SVC" in - MD5_*) ;; # fall through to the bulk of code - *) upslist_readFile_once || exit $? - echo "$UPSLIST_FILE" | egrep "^$SVC\$" - exit $? - ;; - esac - FINAL_RES=0 - OUT="`"$0" --list-services-for-devices`" && [ -n "$OUT" ] || FINAL_RES=$? - if [ "$FINAL_RES" = 0 ]; then - echo "$OUT" | grep "$SVC" | ( \ - while read _SVC _DEV ; do - _SVC="`$hook_validInstanceSuffixName "${_SVC}"`" || exit - [ "${_SVC}" = "${SVC}" ] && echo "$_DEV" && exit 0 - done ; exit 1 ) && exit 0 - fi - echo "No service instance '$2' was detected that matches a NUT device" >&2 - exit 1 + --get-device-for-service) + shift + get_device_for_service "$@" + exit $? ;; --list-services-for-devices) - FINAL_RES=0 - upslist_readFile_once && [ -n "$UPSLIST_FILE" ] \ - || { echo "No devices detected in '$UPSCONF'" >&2 ; exit 1 ; } - upslist_readSvcs "by user request" && [ -n "$UPSLIST_SVCS" ] \ - || { echo "No service instances detected" >&2 ; exit 1; } - UPSLIST_SVCS_RAW="`$hook_listInstances_raw`" && [ -n "$UPSLIST_SVCS_RAW" ] \ - || { echo "No service units detected" >&2 ; exit 1; } - for DEV in $UPSLIST_FILE ; do - vINST="`$hook_validInstanceName "$DEV"`" - vUNITD="`$hook_validFullUnitName "$DEV"`" - vUNITI="`$hook_validFullUnitName "$vINST"`" - # First pass over simple verbatim names - for INST in $UPSLIST_SVCS ; do - if [ "$INST" = "$DEV" ] ; then - for UNIT in $UPSLIST_SVCS_RAW ; do - if [ "$UNIT" = "$vUNITD" ] ; then - printf '%s\t%s\n' "$UNIT" "$DEV" - continue 3 - fi - done - fi - done - for INST in $UPSLIST_SVCS ; do - if [ "$INST" = "$vINST" ] ; then - for UNIT in $UPSLIST_SVCS_RAW ; do - if [ "$UNIT" = "$vUNITI" ] ; then - printf '%s\t%s\n' "$UNIT" "$DEV" - continue 3 - fi - done - fi - done - echo "WARNING: no service instances detected that match device '$DEV'" >&2 - FINAL_RES=1 - done - exit $FINAL_RES + shift + list_services_for_devices "$@" + exit $? ;; --show-configs|--show-device-configs|--show-all-configs|--show-all-device-configs) RES=0 upslist_readFile_once || RES=$? [ "$RES" != 0 ] && { echo "ERROR: upslist_readFile_once () failed with code $RES" >&2; exit $RES; } - [ -n "$UPSLIST_FILE" ] \ + [ "${#UPSLIST_FILE}" != 0 ] \ || { echo "WARNING: No devices detected in '$UPSCONF'" >&2 ; RES=1 ; } echo "$UPSCONF_DATA" exit $RES @@ -1443,6 +1736,20 @@ while [ $# -gt 0 ]; do upslist_debug exit $? ;; + upslist_savednames_find_missing) # Not public, not in usage() + upslist_savednames_find_missing + exit $? + ;; + upslist_savednames_find_mismatch) # Not public, not in usage() + upslist_savednames_find_mismatch + exit $? + ;; + update_upslist_savednames_find_missing) # Not public, not in usage() + update_upslist_savednames_find_missing + exit $? + ;; + hook_findSavedDeviceName) shift ; $hook_findSavedDeviceName "$@" ; exit $? ;; + hook_getSavedDeviceName) shift ; $hook_getSavedDeviceName "$@" ; exit $? ;; *) echo "Unrecognized argument: $1" >&2 ; exit 1 ;; esac shift