openwrt-configsync/uci-fetch-commit.sh

134 lines
3.2 KiB
Bash
Executable File

#!/bin/sh
# Check that everything is present
for bin in mktemp sha256sum rm rmdir curl printf chmod cat uci; do
if ! which "$bin" >/dev/null 2>&1; then
echo "Failed to find $bin. Make sure it resides in \$PATH" >&2
return 1
fi
done
CONFDIR="$(mktemp -p /tmp -d "configsync.XXXXXXXXX")" || return 1
CFGPATH="$CONFDIR/config"
CSUMPATH="$CONFDIR/checksum"
CAPATH="$CONFDIR/ca.crt"
CERTPATH="$CONFDIR/cert.crt"
KEYPATH="$CONFDIR/key.crt"
ROLLBACKPATH="/etc/uci-defaults/00-rollback-config"
CONFIGSAVE="configsync.configdata"
TIMEOUT="10"
cleanup() {
trap - EXIT
rm -v "$CFGPATH" "$CSUMPATH" "$CAPATH" "$CERTPATH" "$KEYPATH"
rmdir "$CONFDIR"
}
trap 'cleanup' EXIT INT HUP
exp_cfg() {
# Export config for rollback
EXPORT="$(uci export)" || return
printf '%s\n%s\n%s\n%s\n%s\n' \
'#!/bin/sh' \
'uci import <<EOF' \
"$EXPORT" \
'EOF' \
'reload_config' > "$ROLLBACKPATH"
chmod +x "$ROLLBACKPATH"
}
run_cfg() {
chmod +x "$CFGPATH"
uci set "$CONFIGSAVE".currentcsum="$(cat "$CSUMPATH")"
"$CFGPATH"
}
rollback() {
local badcsum="$(uci -q get "$CONFIGSAVE".currentcsum)"
# Rollback if config commit has been attempted
if [ -x "$ROLLBACKPATH" ]; then
rm -rv /tmp/.uci # Clean up any pending changes not commited yet
if "$ROLLBACKPATH"; then
echo "rolled back last known working config" >&2
uci set "$CONFIGSAVE".lastbadcsum="$badcsum" # Mark current checksum as bad
uci commit
rm -v "$ROLLBACKPATH"
fi
fi
}
ret=1 ca="" cert="" key=""
uci get "$CONFIGSAVE".ca > "$CAPATH" && ca="$CAPATH"
uci get "$CONFIGSAVE".cert > "$CERTPATH" && cert="$CERTPATH"
uci get "$CONFIGSAVE".key > "$KEYPATH" && key="$KEYPATH"
currentcsum="$(uci -q get "$CONFIGSAVE".currentcsum)"
badcsum="$(uci -q get "$CONFIGSAVE".lastbadcsum)"
for endpoint in $(uci get "$CONFIGSAVE".endpoints); do
if ! curl -sSfL \
${ca:+'--cacert' "$ca"} \
${cert:+'--cert' "$cert"} \
${key:+'--key' "$key"} \
-m "$TIMEOUT" \
"$endpoint/checksum" -o "$CSUMPATH"; then
echo "${endpoint}: failed to fetch csum, trying next endpoint" >&2
continue
fi
expcsum="$(cat "$CSUMPATH")"
if [ "$expcsum" == "$badcsum" ]; then
echo "${endpoint}/${expcsum}: known bad config, not re-fetching" >&2
ret=0
break
fi
if [ "$expcsum" == "$currentcsum" ]; then
echo "${endpoint}/${expcsum}: config already applied, not re-fetching" >&2
ret=0
break
fi
if ! curl -sSfL \
${ca:+'--cacert' "$ca"} \
${cert:+'--cert' "$cert"} \
${key:+'--key' "$key"} \
-m "$TIMEOUT" \
"$endpoint/config" -o "$CFGPATH"; then
echo "${endpoint}/${expcsum}: failed to fetch cfg, trying next endpoint" >&2
continue
fi
cfgcsum="$(sha256sum "$CFGPATH" | cut -d' ' -f1)"
if [ "$expcsum" != "$cfgcsum" ]; then
echo "${endpoint}/${expcsum}: sha256 checksum mismatched, discarding" >&2
ret=0
break
fi
if ! exp_cfg; then
echo "failed to export current config, exiting" >&2
break
fi
if ! run_cfg; then
echo "${endpoint}/${expcsum}: failed to apply new config, attempting rollback" >&2
break
fi
echo "${endpoint}/${expcsum}: applied" >&2
ret=0
break
done
if [ "$ret" -ne 0 ]; then
if ! rollback; then
echo "failed to rollback previous config, resetting to factory" >&2
firstboot -y
reboot
fi
fi
return $ret