Report uncertain preference saves without promising rollback

This commit is contained in:
mikecarper committed 2026-10-16 13:45:48 -07:00
1 parent f6c00be3e0
commit 5fc0943f59
17 files changed
+807 -45

No files matched your search

+36 -25
View File
@@ -2852,6 +2852,9 @@ void CommonCLI::savePrefs(PrefsSaveRouting::Scope scope) {
}
}
// Failure does not prove the previous image is still on disk: publication can
// succeed before readback fails. Callers can restore runtime values, but must
// describe the durable save as unconfirmed rather than promise disk rollback.
bool CommonCLI::trySavePrefs(PrefsSaveRouting::Scope scope) {
const PrefsSaveRouting::Plan plan = PrefsSaveRouting::planFor(scope);
const uint8_t old_advert_interval = _prefs->advert_interval;
@@ -3242,7 +3245,7 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re
StrHelper::strncpy(_prefs->password, &command[9], sizeof(_prefs->password));
if (!trySavePrefs()) {
memcpy(_prefs->password, previous, sizeof(previous));
strcpy(reply, "Error: password not saved; unchanged");
strcpy(reply, "Error: save unconfirmed; reboot may differ");
return;
}
sprintf(reply, "password now: %s", _prefs->password); // echo back just to let admin know for sure!!
@@ -3827,7 +3830,7 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
_prefs->trace_when_repeat_off = strcmp(value, "on") == 0 ? 1 : 0;
if (!trySavePrefs()) {
_prefs->trace_when_repeat_off = previous;
strcpy(reply, "Error: Repeat trace not saved; unchanged");
strcpy(reply, "Error: save unconfirmed; reboot may differ");
return;
}
_callbacks->onRetryConfigChanged();
@@ -3865,7 +3868,7 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
_prefs->gps_sync_interval_hours = hours;
if (!trySavePrefs()) {
_prefs->gps_sync_interval_hours = previous;
strcpy(reply, "Error: GPS sync interval could not be saved");
strcpy(reply, "Error: save unconfirmed; reboot may differ");
} else {
_sensors->applyGpsTimeSyncInterval(hours);
snprintf(reply, 160, "OK - GPS sync interval %u hours (saved)", (unsigned)hours);
@@ -3923,7 +3926,7 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
_prefs->usb_logging_enabled = enabled ? 1 : 0;
if (!trySavePrefs()) {
_prefs->usb_logging_enabled = previous;
strcpy(reply, "Error: USB logging not saved; unchanged");
strcpy(reply, "Error: save unconfirmed; reboot may differ");
return;
}
// Commit before changing the stream's live protocol ownership.
@@ -3945,7 +3948,7 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
_prefs->usb_debug_enabled = enabled ? 1 : 0;
if (!trySavePrefs()) {
_prefs->usb_debug_enabled = previous;
strcpy(reply, "Error: USB debug not saved; unchanged");
strcpy(reply, "Error: save unconfirmed; reboot may differ");
return;
}
mesh::setUsbDebugEnabled(enabled);
@@ -3978,7 +3981,7 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
if (!trySavePrefs()) {
_prefs->usb_logging_enabled = previous_usb;
_prefs->bridge_enabled = previous_bridge;
strcpy(reply, "Error: logging output not saved; unchanged");
strcpy(reply, "Error: save unconfirmed; reboot may differ");
return;
}
mesh::setUsbLoggingEnabled(usb_enabled);
@@ -4053,7 +4056,7 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
// intent first so a failed write leaves the running service unchanged.
if (!trySavePrefs()) {
_prefs->bridge_enabled = previous;
strcpy(reply, "Error: MQTT setting not saved; unchanged");
strcpy(reply, "Error: save unconfirmed; reboot may differ");
return;
}
const bool applied = enable ? _callbacks->setMqttBridgeState(true)
@@ -4075,7 +4078,7 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
_prefs->rs232_heartbeat = enable ? 1 : 0;
if (!trySavePrefs()) {
_prefs->rs232_heartbeat = previous;
strcpy(reply, "Error: RS232 heartbeat not saved; unchanged");
strcpy(reply, "Error: save unconfirmed; reboot may differ");
} else {
strcpy(reply, enable ? "OK - RS232 heartbeat enabled" : "OK - heartbeat and standby disabled");
}
@@ -4092,7 +4095,7 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
_prefs->rs232_autosleep = force ? 2 : enable ? 1 : 0;
if (!trySavePrefs()) {
_prefs->rs232_autosleep = previous;
strcpy(reply, "Error: RS232 autosleep not saved; unchanged");
strcpy(reply, "Error: save unconfirmed; reboot may differ");
} else {
strcpy(reply, force ? "OK - forced standby; stock/unknown peers can miss packets"
: enable ? "OK - standby after peer heartbeat confirmed; stock stays active"
@@ -4112,7 +4115,7 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
_prefs->rs232_idle_seconds = static_cast<uint16_t>(seconds);
if (!trySavePrefs()) {
_prefs->rs232_idle_seconds = previous;
strcpy(reply, "Error: RS232 idle not saved; unchanged");
strcpy(reply, "Error: save unconfirmed; reboot may differ");
} else {
sprintf(reply, "OK - RS232 idle %us", static_cast<unsigned>(seconds));
}
@@ -4147,9 +4150,11 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
// Saved-on may currently be in soft standby. Restore an owner as well
// as its intent, otherwise no object remains to execute the wake timer.
const bool restored = _callbacks->setRs232BridgeState(previous_enabled != 0);
strcpy(reply, restored
? "Error: RS232 change failed; setting unchanged"
: "Error: RS232 change failed; previous runtime could not be restored");
strcpy(reply, applied
? (restored ? "Error: save unconfirmed; reboot may differ"
: "Error: save unconfirmed; runtime restore failed; reboot may differ")
: (restored ? "Error: RS232 change failed; setting unchanged"
: "Error: RS232 change failed; previous runtime could not be restored"));
} else {
strcpy(reply, enable && _sensors->gpsUsesSerialUart(_prefs->bridge_uart)
? "OK - UART GPS paused" : "OK");
@@ -4313,7 +4318,7 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
_prefs->flood_advert_interval = (uint8_t)(hours);
if (!trySavePrefs()) {
_prefs->flood_advert_interval = previous;
strcpy(reply, "Error: interval not saved; unchanged");
strcpy(reply, "Error: save unconfirmed; reboot may differ");
return;
}
_callbacks->updateFloodAdvertTimer();
@@ -4329,7 +4334,7 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
_prefs->advert_interval = (uint8_t)(mins / 2);
if (!trySavePrefs()) {
_prefs->advert_interval = previous;
strcpy(reply, "Error: interval not saved; unchanged");
strcpy(reply, "Error: save unconfirmed; reboot may differ");
return;
}
_callbacks->updateAdvertTimer();
@@ -4341,7 +4346,7 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
StrHelper::strncpy(_prefs->guest_password, &config[15], sizeof(_prefs->guest_password));
if (!trySavePrefs()) {
memcpy(_prefs->guest_password, previous, sizeof(previous));
strcpy(reply, "Error: guest password not saved; unchanged");
strcpy(reply, "Error: save unconfirmed; reboot may differ");
return;
}
strcpy(reply, "OK");
@@ -5103,9 +5108,11 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
// non-running heap-backed bridge instance.
restored = _callbacks->setRs232BridgeState(previous_running);
}
strcpy(reply, restored
? "Error: bridge state change failed; setting unchanged"
: "Error: bridge state change failed; previous runtime state could not be restored");
strcpy(reply, applied
? (restored ? "Error: save unconfirmed; reboot may differ"
: "Error: save unconfirmed; runtime restore failed; reboot may differ")
: (restored ? "Error: bridge state change failed; setting unchanged"
: "Error: bridge state change failed; previous runtime state could not be restored"));
}
#else
// MQTT and ESP-NOW preserve configured intent across missing WiFi
@@ -5176,9 +5183,11 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
const bool restored = previous_running
? _callbacks->restartRs232Bridge()
: _callbacks->setRs232BridgeState(false);
strcpy(reply, restored
? "Error: bridge failed to restart; baud unchanged"
: "Error: bridge failed to restart; previous baud could not be restored");
strcpy(reply, applied
? (restored ? "Error: save unconfirmed; reboot may differ"
: "Error: save unconfirmed; runtime restore failed; reboot may differ")
: (restored ? "Error: bridge failed to restart; baud unchanged"
: "Error: bridge failed to restart; previous baud could not be restored"));
}
} else {
sprintf(reply, "Error: baud rate must be between 9600-%d",BRIDGE_MAX_BAUD);
@@ -5231,9 +5240,11 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
const bool restored = previous_running
? _callbacks->restartRs232Bridge()
: _callbacks->setRs232BridgeState(false);
strcpy(reply, restored
? "Error: bridge failed to restart; UART unchanged"
: "Error: bridge failed to restart; previous UART could not be restored");
strcpy(reply, applied
? (restored ? "Error: save unconfirmed; reboot may differ"
: "Error: save unconfirmed; runtime restore failed; reboot may differ")
: (restored ? "Error: bridge failed to restart; UART unchanged"
: "Error: bridge failed to restart; previous UART could not be restored"));
} else if (
#ifdef WITH_MQTT_BRIDGE
_prefs->rs232_bridge_enabled
+1 -1
View File
@@ -1326,7 +1326,7 @@ bool CommonCLI::handleObserverCommand(uint32_t sender_timestamp, char* command,
_prefs->ota_channel = ch;
if (!saveCommonPrefs()) {
_prefs->ota_channel = previous;
strcpy(reply, "ERR: OTA channel save failed; setting unchanged");
strcpy(reply, "ERR: save unconfirmed; reboot may differ");
} else {
snprintf(reply, 160, "channel set to %s, base %s; run ota update to switch",
ota_channel_name(ch), ota_resolve_base(ch));
+1 -1
View File
@@ -92,7 +92,7 @@ bool handleCommand(uint8_t& shared, const char* command, char* reply,
shared = strcmp(value, "shared") == 0 ? 1 : 0;
if (!save()) {
shared = previous;
snprintf(reply, capacity, "Error: peripheral mode could not be saved");
snprintf(reply, capacity, "Error: save unconfirmed; reboot may differ");
} else {
snprintf(reply, capacity, "OK - %s saved; reboot to apply", value);
}
+1 -1
View File
@@ -15,7 +15,7 @@ public:
: _failures(failures), _start(failures), _reply(reply) {}
~PrefsSaveReplyGuard() {
if (_failures != _start) {
strcpy(_reply, "Error: settings not saved; unsaved changes may remain active until reboot");
strcpy(_reply, "Error: save unconfirmed; changes may be active; reboot may differ");
}
}
};