Network UPS Tools Script Help

Edited to include quotes around variables used as suggested by MichaIng

Hello,

I’ve been trying to get the timing and sequencing ironed out in the upssched-cmd script, but I just can’t seem to find the right path. I have a RPi3b+ running Raspbian OS that is operating as a Controller for my Unifi network and I also have an EdgeRouter Pro. I can SSH into both systems, but I cannot (or maybe too afraid to) install the NUT program on the EdgeRouter. Since I can ssh into each device, I had planed to simply deliver the halt command to the pi and shutdown command to the ER when the DietPi running as a NUT server detected the UPS moving from OnBattery to LowBattery. I could then set a timer for say 60 sec prior to the FSD being initiated and killing the DietPi and eventually rebooting the UPS prior to it going completely dead. I set the LowBattery point to 4 mins and/or 33%, so hopefully I would have plenty of time. I’ve never written a script before, so I have only copied from other examples and attempted to determine what it’s doing:

#! /bin/sh

# SSH connection settings

ssh_host1='ControllerUser@ControllerIP'
ssh_host2='RouterUser@RouterIP'

# Misc logging
UPS="apc"
STATUS=$( upsc $UPS ups.status )
CHARGE=$( upsc $UPS battery.charge )
CHMSG="[$STATUS]:$CHARGE%"

logger -i -t upssched-cmd Calling upssched-cmd $1

case $1 in
    onbatt)
        message="Power Failure on UPS ${UPSNAME}!"
        echo -e "Warning: UPS $UPSNAME experienced a power failure and is now running on battery!" \
        | mail -s"Warning: $message" root
        remote_cmd="log warning message=\"${message}\""
        #ssh $SSH_HOST -l $SSH_USER -i $SSH_KEY $remote_cmd
	ssh "$ssh_host1" "$remote_cmd"
	;;
    online)
        message="Power restored on UPS $UPSNAME"
        echo -e "Power on UPS $UPSNAME has been restored." \
        | mail -s"$message" root
        remote_cmd="log info message=\"${message}\""
        #ssh $SSH_HOST -l $SSH_USER -i $SSH_KEY $remote_cmd
        ssh "$ssh_host1" "$remote_cmd"
        ;;
    lowbatt)
        message="Low battery on UPS ${UPSNAME}!"
        echo -e "Warning: UPS $UPSNAME is low on battery! All connected Systems will be shut down soon." \
        | mail -s"Warning: $message" root
        remote_cmd="log warning message=\"${message}\""
        #ssh $SSH_HOST -l $SSH_USER -i $SSH_KEY $remote_cmd
        ssh "$ssh_host1" "$remote_cmd"
        ;;
    fsd)
        message="Forced Shutdown from UPS ${UPSNAME}!"
        echo -e "Warning: All Systems connected to UPS $UPSNAME will be shut down now!" \
        | mail -s"Warning: $message" root
        remote_cmd="log error message=\"${message}\" ; beep 0.5 ; delay 4000ms ; beep 0.5 ; system shutdown!"
        #ssh $SSH_HOST -l $SSH_USER -i $SSH_KEY $remote_cmd
        ssh "$ssh_host1" "'sudo halt'"
        ssh "$ssh_host2" "'sudo shutdown'"
	ssh "$ssh_host1" "$remote_cmd"
        ;;
    commok)
        message="Communications restored with UPS $UPSNAME"
        echo -e "Communications with UPS $UPSNAME have been restored." \
        | mail -s"$message" root
        remote_cmd="log info message=\"${message}\""
        #ssh $SSH_HOST -l $SSH_USER -i $SSH_KEY $remote_cmd
        ssh "$ssh_host1" "$remote_cmd"
        ;;
    commbad)
        message=""
        echo -e "Warning: Lost communications with UPS ${UPSNAME}!" \
        | mail -s"Warning: Lost communications with UPS ${UPSNAME}!" root
        remote_cmd="log warning message=\"${message}\""
        #ssh $SSH_HOST -l $SSH_USER -i $SSH_KEY $remote_cmd
        ssh "$ssh_host1" "$remote_cmd"
        ;;
    shutdown)
        message="System $HOST is shutting down now!"
        echo -e "Warning: System $HOST is shutting down now!" \
        | mail -s"Warning: $message" root
        remote_cmd="log warning message=\"${message}\""
        #ssh $SSH_HOST -l $SSH_USER -i $SSH_KEY $remote_cmd
        ssh "$ssh_host1" "'sudo halt'"
        ssh "$ssh_host2" "'sudo shutdown'"
	ssh "$ssh_host1" "$remote_cmd"
	;;
    replbatt)
        message="Replace battery on UPS ${UPSNAME}!"
        echo -e "Warning: The UPS $UPSNAME needs to have its battery replaced!" \
        | mail -s"Warning: $message" root
        remote_cmd="log warning message=\"${message}\""
        #ssh $SSH_HOST -l $SSH_USER -i $SSH_KEY $remote_cmd
        ssh "$ssh_host1" "$remote_cmd"
        ;;
    nocomm)
        message="The UPS $UPSNAME can’t be contacted for monitoring!"
        echo -e "Warning: The UPS $UPSNAME can’t be contacted for monitoring!" \
        | mail -s"Warning: $message" root
        remote_cmd="log warning message=\"${message}\""
        #ssh $SSH_HOST -l $SSH_USER -i $SSH_KEY $remote_cmd
        ssh "$ssh_host1" "$remote_cmd"
        ;;
    *)
        logger -t upssched-cmd "Unrecognized command: $1"
        ;;

esac

logger -i -t upssched-cmd "$message"

Any guidance would be greatly appreciated!!

Thanks in advance!

I’ve not much experience with UPS and NUT, but one thing about the script: You should double quote $remote_cmd when passing it to ssh to prevent word splitting and shell globbing. Also for other variables meant to be passed as single argument it is good practice to quote them:

ssh "$ssh_host1" "$remote_cmd"

MichaIng

Do the single and double quotes contained within the variable definition not get passed to prevent the splitting/globbing? I thought it did, but this is my first attempt at any kind of a script…

Thanks!!

Nope, when assigning a string to a variable, quotes are mandatory to not cause a syntax error. When calling a variable, quoting is again required to prevent word splitting and globbing.

E.g. it is possible to do something like that:

arguments='--user dietpi --files *.ext'
command $arguments

And the individual “words” of the arguments variable are added as individual arguments (what is wanted in this case) and even the asterisk is expanded so that all files in the current directory with .ext extension are added as individual arguments as well. Better practice in such case is to create an array and quote the individual array entries like “${arguments[@]}”, but the above works as well.

MichaIng

So if I understand you correctly, your example works but is not the best practice.

arguments='--user dietpi --files *.ext'
command $arguments

The best practice is to use double quotes("") to contain the variable, curly brackets ({}) to define an array, and I’ guessing the brackets and @ sign ([@]) indicate the individual strings separated by spaces?

I appreciate you taking the time to help me understand!!

Yes exactly, it was more to show how the shell works. If you want to add variable arguments to a command, in bash, better is an array, which also allows spaces and special characters in single arguments:

arguments=('--user' 'dietpi' '--files' '/path/with space/to/file_with_*_asterisk')
command "${arguments[@]}"

And yes bash basically duplicates ${array[@]} statements for each array entry, including surrounding double quotes. So in the upper example, the command is expanded to, and then directly executed (no further variable expansion etc):

command "--user" "dietpi" "--files" "/path/with space/to/file_with_*_asterisk"

Without the double quotes, the path would be separated by the space into two arguments, the asterisk would be interpreted as glob wildcard and in case $ would be expanded as variable calls, and after that, the command would be executed :wink:.