#!/usr/bin/env bash set -e set -u source /usr/local/bin/bashdb trap 'echo && echo "Aborting.." && exit' SIGINT INT _scriptName=confCare _scriptVersion="Master 2.21" password="4indoorpos2B" logFile="/tmp/confCare_$(date +%Y-%m-%dT%H%M%S).log" declare -A ips obj=() devices=() ips_ip="" pwsr_mac="" pwsr_ip="" sniff_url="" cred="admin:admin1234" display=0 setConf=0 hwType="" factory=0 db="/tmp/.db" upgrade=0 dataPath="/opt/sensio/indoorpos/Data" fwPath="$dataPath/firmware" txPower= sniffer= stm=0 lastSTMupdate= txPowerState=1 threshold= txPowerArr=( "Strong" "Moderatly_Strong" "Moderate" "Moderatly_Weak" "Weak" ) stm=0 stmUpgrade=0 stmPID= declare -A configList declare -A txPowerList txPowerList["Strong"]=80 txPowerList["Moderatly_Strong"]=55 txPowerList["Moderate"]=38 txPowerList["Moderatly_Weak"]=29 txPowerList["Weak"]=23 red=$(tput setaf 1) green=$(tput setaf 2) yellow=$(tput setaf 3) normal=$(tput sgr0) function init { [[ -d $fwPath ]] || mkdir -p "$fwPath" } function updateFW { local _MAC=${1:-} local _fileName=${2:-} /usr/bin/expect <" { send "CLIMAXFIRMWARE $_MAC $_fileName\r" expect { "Upgrading firmware for" { return 0 } "ERROR: Not upgrading firmware for" { return 1 } "timeout" { return 2 } } } } EOD } function getClimaxDevices { local ip [[ -f $db ]] && rm $db echo "Building device database from IPS.." devices=$(/usr/bin/expect <" { log_user 1 send "climaxconnections\r" expect { "\n" { exp_continue } "IPS>" { exit 0 } } } } EOD ) deviceFile=$(grep -rl "Devices" $dataPath/OfflineAPI/* | xargs ls -1tr | tail -n1) zoneFile=$(grep -rl "ParentZoneId" $dataPath/OfflineAPI/* | xargs ls -1tr | tail -n1) [[ -f $db ]] || touch $db while IFS=\n; read line; do if [[ $line =~ "Device:" ]]; then while read l; do if ! [[ -z $l ]]; then split "$l" ":" case $arg1 in "Device") _device="$arg2" continue ;; "Ip") ip="$arg2" ;; "Firmware") getFW $ip [[ -z $_hw ]] && _hw="Missing" db_set -f "$db" -k "$_device" -c "Hardware" -v "$_hw" [[ -z $_fw ]] && _fw="Missing" db_set -f "$db" -k "$_device" -c "Firmware" -v "$_fw" continue ;; "Id") IFS="" _zoneID=$(jq -r --arg arg2 "$arg2" '.Devices | map(select(.DeviceId == $arg2).ZoneId)[0]' <$deviceFile) _zoneName=$(jq -r --arg _zoneID "$_zoneID" '.Children | map(select(.ZoneId == $_zoneID).Name)[0]' <$zoneFile) IFS=\n ;; "ShouldBeConnected") #Set Zone name [[ -z $_zoneName ]] || db_set -f "$db" -k "$_device" -c "Zone Name" -v "$_zoneName" arg1="" arg2="" break ;; esac db_set -f "$db" -k "$_device" -c "$arg1" -v "$arg2" fi _hw="" _fw="" done< <(tr "'" "\n" <<<"$line" | sed ':a;N;$!ba;s/: \n/:/g' | sed 's/^ *//g') fi done<<<$devices IFS= } function setOBJ { (( $# == 3 )) || ( echo "setOBJ missing argument(s)" && exit 1 ) local _MAC=${1:-} local _IP=${2:-} local _PAGE=${3:-} url="http://${_IP}/${_PAGE}" str="" i=1 for o in "${obj[@]}"; do if (( $i == 1 )); then str="$o" else str="${str}&${o}" fi ((++i)) done curl --connect-timeout 2 -sS -X POST -H "Content-Type: text/html" -d "$str" -u "$cred" "$url" 1>/dev/null || return 1 } function getLocalIP { interface=$(route | grep default | awk '{ print $8 }') /sbin/ip -o -4 addr list $interface | awk '{print $4}' | cut -d/ -f1 } function help { cat <. Must be runned with -s Available settings: 200 = Unlimited 80-104 -u Update fw. Arguments: all=update all devices with firmware(s) in /opt/sensio/indoorpos/Data/firmware update spesific beacon host -x Override TX power for one or all beacon hosts. Usage all: confCare -x all Usage single beacon: confCare -x EOD } function split { _line=${1:-} _delim=${2:-} arg1=$(cut -d$_delim -f1 <<<$_line) arg2=$(cut -d$_delim -f2- <<<$_line) } function findDevices { db_dump -f "$db" } function setConfig { ii=0 while read key; do ((++ii)) _IP=$(db_get -f $db -k $key -c "IP" || :) _hwType=$(db_get -f $db -k $key -c "MODEL" | tr [:lower:] [:upper:] || :) [[ -z $_IP ]] && echo "$ii - No IP for $_hwType ${key}" && continue case "$_hwType" in PWSR|POE) echo "$ii - Changing RF config for $_hwType ${key} ${_IP}" readHTML "${key}" "${_IP}" "rfsniffer.htm" if setOBJ "${key}" "${_IP}" "rfsniffer.htm"; then echo "$ii - Changing SR config for $_hwType ${key} ${_IP}" readHTML "${key}" "${_IP}" "srp.htm" setOBJ "${key}" "${_IP}" "srp.htm" else echo "$ii - Timeout: Could not reach ${_IP}" fi ;; esac done< <(db_keys -f $db -h) } function getXML () { local IFS='>' read -d '<' TAG VALUE } function readHTML { local _MAC=${1:-} local _IP=${2:-} local _PAGE=${3:-} local _NAME="" local _VALUE="" local _TYPE="" local _ALLOWEMPTY=0 local _selectName="" ips_ip=$(getLocalIP) obj=() while getXML; do case "$TAG" in *name=*) #If no selected if [[ $_selectName == "tx_pwr" ]]; then str="${_selectName}=55" obj+=( "${str}" ) _selectName="" fi _NAME=$(sed 's/\(.*name=\)\(.*\)/\2/g' <<<"$TAG" | awk '{ print $1 }' | tr -d \") if [[ "$TAG" == *"value"* ]]; then _VALUE=$(sed 's/\(.*value=\)\(.*\)/\2/g' <<<"$TAG" | awk '{ print $1 }' | tr -d \") _TYPE=$(sed 's/\(.*type=\)\(.*\)/\2/g' <<<"$TAG" | awk '{ print $1 }' | tr -d \") _ALLOWEMPTY=1 else #Expecting a selected value _selectName=$_NAME continue fi ;; *selected*) _selectName="" _VALUE=$(sed 's/\(.*value=\)\(.*\)/\2/g' <<<"$TAG" | awk '{ print $1 }' | tr -d \") if [[ "$_NAME" == "tx_pwr" ]]; then if ! [[ -z $VALUE ]]; then if [[ -v ${txPowerList[$VALUE]-} ]]; then (( ${txPowerList[$VALUE]-} != $_VALUE )) && txPower=$_VALUE fi fi fi ;; esac if ! [[ -z ${_NAME} ]] && ( ! [[ -z ${_VALUE} ]] || (( $_ALLOWEMPTY == 1 )) ); then #OVERRIDES case "$_NAME" in anti|anto) str="${_NAME}=1" ;; sniff_url) str="${_NAME}=${_MAC}%40${ips_ip}%3A53076" ;; sniffer) if [[ -z $sniffer ]]; then str=${_NAME}=1 else str=${_NAME}=$sniffer fi ;; srp_en) str=${_NAME}=1 ;; srp_url) str="${_NAME}=${ips_ip}:53077" ;; chk|chko) str="${_NAME}=1" ;; tx_pwr) if [[ -z $txPower ]]; then str="${_NAME}=${_VALUE}" else str="${_NAME}=${txPower}" fi ;; rf_flt) if [[ -z $threshold ]]; then str="${_NAME}=${_VALUE}" else str="$_NAME=$threshold" fi ;; wu_b) str="${_NAME}=$txPowerState" ;; in2out|out2in) str="${_NAME}=0" ;; *|?) str="${_NAME}=${_VALUE}" ;; esac obj+=( "${str}" ) _NAME="" _VALUE="" _TYPE="" _ALLOWEMPTY=0 _str="" fi done< <(curl --connect-timeout 2 -s -u "$cred" http://${_IP}/${_PAGE}) || return 1 } function checkFactoryHW { local hw local mac mac=${1:-} hw=${2:-} case "$hw" in PWSR_sensio|PWSR_256_sensio) url="http://${ip}/firmware2.htm" return 0 ;; PWSR_STM_sensio) url="http://${ip}/system.htm" return 0 ;; *|?) return 1 ;; esac } function factoryReset { local mac local ip local hw local url read -p "Set single device or all? : " singleMulti if [[ $singleMulti == [aA] ]]; then read -p "Absolute sure? All devices will loose all of their config : " suresure if [[ $suresure == [yY] ]]; then ii=0 while read key; do mac="$key" ip=$(db_get -f $db -k $key -c IP) hw=$(db_get -f $db -k $key -c HARDWARE) [[ -z $ip ]] && echo "$ii - No IP for $hw ${key}" && continue if checkFactoryHW "$mac" "$hw"; then ((++ii)) echo "$ii - Factory resetting for $hwType ${mac} ${ip}" if curl --connect-timeout 2 -u "$cred" -X POST -d "act=Factory+Reset" $url >/dev/null 2>&1; then continue else return 1 fi else continue fi done< <(db_keys -f $db -h) else echo "Abortin factory reset" fi else mac="" while [[ -z $mac ]]; do read -p "Enter serial: " mac done ip=$(db_get -f $db -k $mac -c IP) hw=$(db_get -f $db -k $mac -c HARDWARE) if checkFactoryHW "$mac" "$hw"; then echo "Factory resetting for $hw ${mac} ${ip}" if curl --connect-timeout 2 -u "$cred" -X POST -d "act=Factory+Reset" $url >/dev/null 2>&1; then return 0 else return 1 fi fi fi } function resetAll { local key="" local i=0 while read key; do local _IP=$(db_get -f $db -k $key -c IP -h || :) [[ -z $_IP ]] && echo "$i - Missing IP for $key" && continue echo "$i - Resetting $key ($_IP)" if curl --connect-timeout 2 -Ss -u "$cred" -X POST -d "act=Reset" http://${_IP}/firmware2.htm 1>/dev/null; then echo "$i - OK" else echo "$i - Failed" fi ((++i)) done< <(db_keys -f $db -h) } function findFWFiles { mapfile -t fwFiles< <(find $fwPath -maxdepth 1 -type f -name "*.bin") } function getFW { local _ip=${1:-} next=0 while getXML; do [[ -z $(sed '/^$/d' <<<$VALUE) ]] && continue if (( $next == 1 )); then _hw=$(cut -d " " -f1 <<<$VALUE) _fw=$(cut -d " " -f2 <<<$VALUE) break fi ( [[ $VALUE =~ "Firmware version" ]] || [[ $VALUE =~ "Firmware revision" ]] ) && next=1 done< <(curl --connect-timeout 2 -s -u "$cred" http://${_ip}/index.htm) } function STMmonitor { local mac local ip local epoc while sleep 5; do if [[ -s $stmFile ]]; then while read host; do if (( $(wc -l <$stmFile) == 1 )); then [[ $host == "FINISHED" ]] && return 0 else [[ $host == "FINISHED" ]] && continue fi mac=$(cut -d "|" -f1 <<<$host) ip=$(cut -d "|" -f2 <<<$host) epoc=$(cut -d "|" -f3 <<<$host) if (( $EPOCHSECONDS - $epoc >= 60 )); then echo "Enabling rf sniffer for $mac - $ip" sniffer=1 if readHTML "$mac" "$ip" "rfsniffer.htm" 2>/dev/null || :; then if setOBJ "$mac" "$ip" "rfsniffer.htm" 2>/dev/null || :; then sed '/'$ip'/d' -i $stmFile else echo "Read HTML failed for $mac" continue fi else echo "Set RF sniffer failed for $mac" continue fi fi done<$stmFile fi done } (( $# == 0 )) && help && exit 0 while getopts ":abdDfhiolrst:u:vx:" o; do case "${o}" in b) mapfile -t keys< <(db_keys -f $db -h) jsonObj="{" for ((k=0;k<${#keys[@]};k++)); do _hw=$(db_get -f "$db" -k ${keys[$k]} -c HARDWARE -h || :) _fw=$(db_get -f $db -k ${keys[$k]} -c FIRMWARE -h || :) _ip=$(db_get -f $db -k ${keys[$k]} -c IP -h || :) _model=$(db_get -f $db -k ${keys[$k]} -c MODEL -h || :) [[ $_model != "Pwsr" ]] && continue readHTML "${keys[$k]}" "$_ip" "rfsniffer.htm" jsonObj="$jsonObj\"${keys[$k]}\":{" for ((i=0;i<${#obj[@]};i++)); do object=$(cut -d= -f1 <<<${obj[$i]}) value="$(cut -d= -f2 <<<${obj[$i]})" [[ -z $value ]] && value="null" if (( $i == ${#obj[@]}-1 )); then jsonObj="$jsonObj\"$object\":\"$value\"" else jsonObj="$jsonObj\"$object\":\"$value\"," fi done jsonObj="$jsonObj}," done jsonObj=$(sed 's/.$//' <<<"$jsonObj") json="$jsonObj}" bakFile="/root/confCare_$(date +%Y-%m-%dT%H%M%S).bak" jq <<<"$json" >"$bakFile" echo "Backup file found here: $bakFile" exit 0 ;; d) #Display found devices init getClimaxDevices findDevices echo "Found: $(db_keys -f $db -h | wc -l) devices" exit 0 ;; D) db_dump -f $db exit 0 ;; f) #Factory Reset dbUpdate=n read -p "Update database now?: [y/N] " dbUpdate [[ $dbUpdate == [Yy] ]] && getClimaxDevices && findDevices [[ -f /tmp/.db ]] || ( echo "Database is missing. Update database when upgrading or run confCare -d" && exit 1 ) factoryReset echo -e "\n\nRemeber to run confCare -s to set deafult config" exit 0 ;; h) help exit 0 ;; i) #Info on single MAC getClimaxDevices mac=${OPTARG} db_get -f $db ;; l) findFWFiles echo "FW found in $fwPath:" for fw in ${fwFiles[@]}; do basename $fw done ;; o) txPower=${OPTARG} ;; r) while IFS='\n' read d; do echo "$d" done< <(/usr/bin/expect <" { log_user 1 send "climaxconnections\r" expect { "\n" { exp_continue } "IPS>" { exit 0 } } } } EOD ) ;; s) read -p "Update database now?: [y/N]" dbUpdate [[ $dbUpdate == [Yy] ]] && getClimaxDevices setConf=1 ;; t) threshold=${OPTARG} ;; u) #Update FW read -p "Update database now?: [y/N] " dbUpdate [[ $dbUpdate == [Yy] ]] && getClimaxDevices && findDevices [[ -f /tmp/.db ]] || ( echo "Database is missing. Update database when upgrading or run confCare -d" && exit 1 ) printf "\n\n" IFS= _mac=${OPTARG} stmFile=$(mktemp) STMmonitor >/tmp/.STMmonitor.log 2>&1 & stmPID=$! findFWFiles declare -A updated declare -A _found if [[ $_mac != "all" ]]; then #Update single keys=( "$_mac" ) else files=() for f in ${fwFiles[@]}; do _fileHW=$(basename $f | sed 's/\(.*\)\(_\)\([0-9].*\)\(.bin\)/\1/g') _fileFW=$(basename $f | sed 's/\(.*\)\(_\)\([0-9].*\)\(.bin\)/\1/g') if [[ ${files[*]} =~ "$_fileHW" ]]; then echo "Multiple upgrade files found for $_fileHW. Please clean up manually" exit 1 else files+=( $_fileHW ) fi done< <(find $fwPath -maxdepth 1 -type f -name "*.bin") mapfile -t keys< <(db_keys -f $db -h) fi (( ${#fwFiles[@]} == 0 )) && echo "Cannot find any firmware files in $fwPath" && exit 1 for key in ${keys[@]}; do _hw=$(db_get -f "$db" -k $key -c HARDWARE -h || :) _fw=$(db_get -f $db -k $key -c FIRMWARE -h || :) _ip=$(db_get -f $db -k $key -c IP -h || :) if [[ -z $_ip ]]; then echo "Missing IP. Cannot continue with $key.." | tee -a $logFile continue fi #FW Files found=0 for f in "${fwFiles[@]}"; do _fwFile=$(basename $f) _fileFW=$(basename "$_fwFile" .bin | sed -r 's/(.*)(_)(.*)/\3/') _fileHW=$(basename "$_fwFile" _${_fileFW}.bin) if [[ $_fileHW == $_hw ]] || ( [[ "$_hw" =~ "PoE_STM_std" ]] && [[ $_fwFile =~ "PoE_STM_sensio" ]] ) || ( [[ "$_hw" == "PWSR" ]] && [[ "$_fwFile" =~ "PWSR_sensio" ]] ); then found=1 break fi done if (( $found == 0 )); then echo "No FW file matched for MAC: $key HW:$_hw FW:$_fw" | tee -a $logFile continue fi #Upgrade if [[ $_fileFW != $_fw ]]; then if [[ $_mac == "all" ]]; then install="y" else read -p "Install $_fwFile over $_fw on $key? : " install /dev/null; then stmUpgrade=1 stm=1 declare -A listSTM sniffer=0 readHTML "$key" "$_ip" "rfsniffer.htm" setOBJ "$key" "$_ip" "rfsniffer.htm" listSTM[$key]=$_ip sleep 5 fi if [[ $install == [yY] ]]; then printf '%s\r' "${yellow}SENDING${normal}: FW $_fwFile to $key ($_ip)" if updateFW "$key" "$_fwFile"; then updated[$_hw]=$(( ${updated[$_hw]} + 1 )) configList[$key]="$_ip" if (( $stm == 1 )); then lastSTMupdate=$EPOCHSECONDS echo "${key}|${_ip}|$lastSTMupdate" >>$stmFile stm=0 fi tput el echo "${green}SUCCESS${normal}: FW $_fwFile sent to $key ($_ip)" | tee -a $logFile else tput el echo "${red}FAILED${normal}: sending FW $_fwFile to $key ($_ip)" | tee -a $logFile fi fi else echo "${green}SUCCESS${normal}: FW Already up to date on $key" | tee -a $logFile fi done if (( $stmUpgrade == 1 )); then echo "FINISHED" >>$stmFile if kill -0 $stmPID &>/dev/null || :; then printf '%s\r' "Waiting for STM devices to be enabled" wait tput el fi [[ -f $stmFile ]] && rm $stmFile fi printf "\n\n\n" #Result if [[ -z ${updated[@]-} ]]; then echo "All devices are up to date" else printf "\n\n" printf '%-20s%s\n' "UPDATED HW:" COUNT: for i in "${!updated[@]}"; do printf '%-22s%s\n' "$i" "${updated[$i]}" done printf '\n\n' read -p "Send new config to upgraded devices?: y/N " _sendConfig if [[ $_sendConfig == [yY] ]]; then echo "Deploying new config" ii=1 for c in "${!configList[@]}"; do echo "$ii - Changing RF config for ${c} ${configList[$c]}" readHTML "${c}" "${configList[$c]}" "rfsniffer.htm" setOBJ "${c}" "${configList[$c]}" "rfsniffer.htm" echo "$ii - Changing SR config for ${c} ${configList[$c]}" readHTML "${c}" "${configList[$c]}" "srp.htm" setOBJ "${c}" "${configList[$c]}" "srp.htm" ((++ii)) done fi read -p "Restart IPS service to update device list?: y/N " _restartIPS if [[ $_restartIPS == [yY] ]]; then echo "Restarting IPS service" docker service update --force swell_ips 1>/dev/null fi fi printf '\n\n' echo "Log file found here: $logFile" exit 0 ;; v) echo "Master version: $_scriptVersion" exit 0 ;; x) mac=${OPTARG} read -p "Update database now?: [y/N]" dbUpdate [[ $dbUpdate == [Yy] ]] && getClimaxDevices && findDevices for ((i=0;i<${#txPowerArr[@]};++i)); do printf '%-20d%s\n' "$i" "${txPowerArr[$i]}" done txPower= while [[ -z $txPower ]]; do read -p "Enter desired index: " txPower done txPower=${txPowerList[${txPowerArr[$txPower]}]} if [[ $mac == "all" ]]; then setConfig else _IP=$(db_get -f $db -k $mac -c "IP" || :) echo "Changing RF config for ${mac} ${_IP}" readHTML "${mac}" "${_IP}" "rfsniffer.htm" setOBJ "${mac}" "${_IP}" "rfsniffer.htm" fi ;; "") help exit 0 ;; ?|*) help exit 0 ;; esac done shift $((OPTIND-1)) (( $factory == 1 )) && (( $setConf == 1 )) && echo "Factory reset and setConfig can not be runned at the same time" && exit 1 (( $setConf == 1 )) && setConfig