Skip to content

Commit c6b4c77

Browse files
authored
Merge pull request wled#4953 from LordMike/lordmike/wled-tools-backup-ir
Extend `wled-tools.sh` backup with optional `ir.json`, refactor fetch logic, add timeouts
2 parents 529edfc + 9152d9d commit c6b4c77

File tree

1 file changed

+79
-36
lines changed

1 file changed

+79
-36
lines changed

tools/wled-tools

Lines changed: 79 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -28,28 +28,78 @@ log() {
2828
fi
2929
}
3030

31-
# Generic curl handler function
32-
curl_handler() {
33-
local command="$1"
34-
local hostname="$2"
35-
36-
response=$($command -w "%{http_code}" -o /dev/null)
37-
curl_exit_code=$?
38-
39-
if [ "$response" -ge 200 ] && [ "$response" -lt 300 ]; then
40-
return 0
41-
elif [ $curl_exit_code -ne 0 ]; then
42-
log "ERROR" "$RED" "Connection error during request to $hostname (curl exit code: $curl_exit_code)."
43-
return 1
44-
elif [ "$response" -ge 400 ]; then
45-
log "ERROR" "$RED" "Server error during request to $hostname (HTTP status code: $response)."
46-
return 2
31+
# Fetch a URL to a destination file, validating status codes.
32+
# Usage: fetch "<url>" "<dest or empty>" "200 404"
33+
fetch() {
34+
local url="$1"
35+
local dest="$2"
36+
local accepted="${3:-200}"
37+
38+
# If no dest given, just discard body
39+
local out
40+
if [ -n "$dest" ]; then
41+
# Write to ".tmp" files first, then move when success, to ensure we don't write partial files
42+
out="${dest}.tmp"
4743
else
48-
log "ERROR" "$RED" "Unexpected response from $hostname (HTTP status code: $response)."
49-
return 3
44+
out="/dev/null"
45+
fi
46+
47+
response=$(curl --connect-timeout 5 --max-time 30 -s -w "%{http_code}" -o "$out" "$url")
48+
local curl_exit_code=$?
49+
50+
if [ $curl_exit_code -ne 0 ]; then
51+
[ -n "$dest" ] && rm -f "$out"
52+
log "ERROR" "$RED" "Connection error during request to $url (curl exit code: $curl_exit_code)."
53+
return 1
54+
fi
55+
56+
for code in $accepted; do
57+
if [ "$response" = "$code" ]; then
58+
# Accepted; only persist body for 2xx responses
59+
if [ -n "$dest" ]; then
60+
if [[ "$response" =~ ^2 ]]; then
61+
mv "$out" "$dest"
62+
else
63+
rm -f "$out"
64+
fi
65+
fi
66+
return 0
67+
fi
68+
done
69+
70+
# not accepted
71+
[ -n "$dest" ] && rm -f "$out"
72+
log "ERROR" "$RED" "Unexpected response from $url (HTTP $response)."
73+
return 2
74+
}
75+
76+
77+
# POST a file to a URL, validating status codes.
78+
# Usage: post_file "<url>" "<file>" "200"
79+
post_file() {
80+
local url="$1"
81+
local file="$2"
82+
local accepted="${3:-200}"
83+
84+
response=$(curl --connect-timeout 5 --max-time 300 -s -w "%{http_code}" -o /dev/null -X POST -F "file=@$file" "$url")
85+
local curl_exit_code=$?
86+
87+
if [ $curl_exit_code -ne 0 ]; then
88+
log "ERROR" "$RED" "Connection error during POST to $url (curl exit code: $curl_exit_code)."
89+
return 1
5090
fi
91+
92+
for code in $accepted; do
93+
if [ "$response" -eq "$code" ]; then
94+
return 0
95+
fi
96+
done
97+
98+
log "ERROR" "$RED" "Unexpected response from $url (HTTP $response)."
99+
return 2
51100
}
52101

102+
53103
# Print help message
54104
show_help() {
55105
cat << EOF
@@ -109,33 +159,27 @@ backup_one() {
109159
local address="$2"
110160
local port="$3"
111161

112-
log "INFO" "$YELLOW" "Backing up device config/presets: $hostname ($address:$port)"
162+
log "INFO" "$YELLOW" "Backing up device config/presets/ir: $hostname ($address:$port)"
113163

114164
mkdir -p "$backup_dir"
115165

116-
local cfg_url="http://$address:$port/cfg.json"
117-
local presets_url="http://$address:$port/presets.json"
118-
local cfg_dest="${backup_dir}/${hostname}.cfg.json"
119-
local presets_dest="${backup_dir}/${hostname}.presets.json"
120-
121-
# Write to ".tmp" files first, then move when success, to ensure we don't write partial files
122-
local curl_command_cfg="curl -s "$cfg_url" -o "$cfg_dest.tmp""
123-
local curl_command_presets="curl -s "$presets_url" -o "$presets_dest.tmp""
166+
local file_prefix="${backup_dir}/${hostname}"
124167

125-
if ! curl_handler "$curl_command_cfg" "$hostname"; then
168+
if ! fetch "http://$address:$port/cfg.json" "${file_prefix}.cfg.json"; then
126169
log "ERROR" "$RED" "Failed to backup configuration for $hostname"
127-
rm -f "$cfg_dest.tmp"
128170
return 1
129171
fi
130172

131-
if ! curl_handler "$curl_command_presets" "$hostname"; then
173+
if ! fetch "http://$address:$port/presets.json" "${file_prefix}.presets.json"; then
132174
log "ERROR" "$RED" "Failed to backup presets for $hostname"
133-
rm -f "$presets_dest.tmp"
134175
return 1
135-
fi
176+
fi
177+
178+
# ir.json is optional
179+
if ! fetch "http://$address:$port/ir.json" "${file_prefix}.ir.json" "200 404"; then
180+
log "ERROR" "$RED" "Failed to backup ir configs for $hostname"
181+
fi
136182

137-
mv "$cfg_dest.tmp" "$cfg_dest"
138-
mv "$presets_dest.tmp" "$presets_dest"
139183
log "INFO" "$GREEN" "Successfully backed up config and presets for $hostname"
140184
return 0
141185
}
@@ -150,9 +194,8 @@ update_one() {
150194
log "INFO" "$YELLOW" "Starting firmware update for device: $hostname ($address:$port)"
151195

152196
local url="http://$address:$port/update"
153-
local curl_command="curl -s -X POST -F "file=@$firmware" "$url""
154197

155-
if ! curl_handler "$curl_command" "$hostname"; then
198+
if ! post_file "$url" "$firmware" "200"; then
156199
log "ERROR" "$RED" "Failed to update firmware for $hostname"
157200
return 1
158201
fi

0 commit comments

Comments
 (0)