diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index b40c7153a..e8efa7566 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -1598,6 +1598,94 @@ UserLicenseStatus NodeDB::getLicenseStatus(uint32_t nodeNum) return info->user.is_licensed ? UserLicenseStatus::Licensed : UserLicenseStatus::NotLicensed; } +bool NodeDB::backupPreferences(meshtastic_AdminMessage_BackupLocation location) +{ + bool success = false; + lastBackupAttempt = millis(); +#ifdef FSCom + if (location == meshtastic_AdminMessage_BackupLocation_FLASH) { + meshtastic_BackupPreferences backup = meshtastic_BackupPreferences_init_zero; + backup.version = DEVICESTATE_CUR_VER; + backup.timestamp = getValidTime(RTCQuality::RTCQualityDevice, false); + backup.has_config = true; + backup.config = config; + backup.has_module_config = true; + backup.module_config = moduleConfig; + backup.has_channels = true; + backup.channels = channelFile; + backup.has_owner = true; + backup.owner = owner; + + size_t backupSize; + pb_get_encoded_size(&backupSize, meshtastic_BackupPreferences_fields, &backup); + + spiLock->lock(); + FSCom.mkdir("/backups"); + spiLock->unlock(); + success = saveProto(backupFileName, backupSize, &meshtastic_BackupPreferences_msg, &backup); + + if (success) { + LOG_INFO("Saved backup preferences"); + } else { + LOG_ERROR("Failed to save backup preferences to file"); + } + } else if (location == meshtastic_AdminMessage_BackupLocation_SD) { + // TODO: After more mainline SD card support + } +#endif + return success; +} + +bool NodeDB::restorePreferences(meshtastic_AdminMessage_BackupLocation location, int restoreWhat) +{ + bool success = false; +#ifdef FSCom + if (location == meshtastic_AdminMessage_BackupLocation_FLASH) { + spiLock->lock(); + if (!FSCom.exists(backupFileName)) { + spiLock->unlock(); + LOG_WARN("Could not restore. No backup file found"); + return false; + } else { + spiLock->unlock(); + } + meshtastic_BackupPreferences backup = meshtastic_BackupPreferences_init_zero; + success = loadProto(backupFileName, meshtastic_BackupPreferences_size, sizeof(meshtastic_BackupPreferences), + &meshtastic_BackupPreferences_msg, &backup); + if (success) { + if (restoreWhat & SEGMENT_CONFIG) { + config = backup.config; + LOG_DEBUG("Restored config"); + } + if (restoreWhat & SEGMENT_MODULECONFIG) { + moduleConfig = backup.module_config; + LOG_DEBUG("Restored module config"); + } + if (restoreWhat & SEGMENT_DEVICESTATE) { + devicestate.owner = backup.owner; + LOG_DEBUG("Restored device state"); + } + if (restoreWhat & SEGMENT_CHANNELS) { + channelFile = backup.channels; + LOG_DEBUG("Restored channels"); + } + + success = saveToDisk(restoreWhat); + if (success) { + LOG_INFO("Restored preferences from backup"); + } else { + LOG_ERROR("Failed to save restored preferences to flash"); + } + } else { + LOG_ERROR("Failed to restore preferences from backup file"); + } + } else if (location == meshtastic_AdminMessage_BackupLocation_SD) { + // TODO: After more mainline SD card support + } + return success; +#endif +} + /// Record an error that should be reported via analytics void recordCriticalError(meshtastic_CriticalErrorCode code, uint32_t address, const char *filename) { diff --git a/src/mesh/NodeDB.h b/src/mesh/NodeDB.h index a31f33250..291c3804b 100644 --- a/src/mesh/NodeDB.h +++ b/src/mesh/NodeDB.h @@ -48,6 +48,7 @@ static constexpr const char *configFileName = "/prefs/config.proto"; static constexpr const char *uiconfigFileName = "/prefs/uiconfig.proto"; static constexpr const char *moduleConfigFileName = "/prefs/module.proto"; static constexpr const char *channelFileName = "/prefs/channels.proto"; +static constexpr const char *backupFileName = "/backups/backup.proto"; /// Given a node, return how many seconds in the past (vs now) that we last heard from it uint32_t sinceLastSeen(const meshtastic_NodeInfoLite *n); @@ -202,8 +203,13 @@ class NodeDB bool hasValidPosition(const meshtastic_NodeInfoLite *n); + bool backupPreferences(meshtastic_AdminMessage_BackupLocation location); + bool restorePreferences(meshtastic_AdminMessage_BackupLocation location, + int restoreWhat = SEGMENT_CONFIG | SEGMENT_MODULECONFIG | SEGMENT_DEVICESTATE | SEGMENT_CHANNELS); + private: - uint32_t lastNodeDbSave = 0; // when we last saved our db to flash + uint32_t lastNodeDbSave = 0; // when we last saved our db to flash + uint32_t lastBackupAttempt = 0; // when we last tried a backup automatically or manually /// Find a node in our DB, create an empty NodeInfoLite if missing meshtastic_NodeInfoLite *getOrCreateMeshNode(NodeNum n); diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index 9503109db..992f38ff4 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -372,7 +372,7 @@ DecodeState perhapsDecode(meshtastic_MeshPacket *p) p->pki_encrypted = true; memcpy(&p->public_key.bytes, nodeDB->getMeshNode(p->from)->user.public_key.bytes, 32); p->public_key.size = 32; - memcpy(&p->decoded, &decodedtmp, sizeof(meshtastic_Data_msg)); + p->decoded = decodedtmp; p->which_payload_variant = meshtastic_MeshPacket_decoded_tag; // change type to decoded } else { LOG_ERROR("PKC Decrypted, but pb_decode failed!"); diff --git a/src/modules/AdminModule.cpp b/src/modules/AdminModule.cpp index a765fb0b1..ae25ea3fc 100644 --- a/src/modules/AdminModule.cpp +++ b/src/modules/AdminModule.cpp @@ -370,6 +370,42 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta LOG_DEBUG("Failed to delete file"); } spiLock->unlock(); +#endif + break; + } + case meshtastic_AdminMessage_backup_preferences_tag: { + LOG_INFO("Client requesting to backup preferences"); + if (nodeDB->backupPreferences(r->backup_preferences)) { + myReply = allocErrorResponse(meshtastic_Routing_Error_NONE, &mp); + } else { + myReply = allocErrorResponse(meshtastic_Routing_Error_BAD_REQUEST, &mp); + } + break; + } + case meshtastic_AdminMessage_restore_preferences_tag: { + LOG_INFO("Client requesting to restore preferences"); + if (nodeDB->restorePreferences(r->backup_preferences, + SEGMENT_DEVICESTATE | SEGMENT_CONFIG | SEGMENT_MODULECONFIG | SEGMENT_CHANNELS)) { + myReply = allocErrorResponse(meshtastic_Routing_Error_NONE, &mp); + LOG_DEBUG("Rebooting after successful restore of preferences"); + reboot(1000); + disableBluetooth(); + } else { + myReply = allocErrorResponse(meshtastic_Routing_Error_BAD_REQUEST, &mp); + } + break; + } + case meshtastic_AdminMessage_remove_backup_preferences_tag: { + LOG_INFO("Client requesting to remove backup preferences"); +#ifdef FSCom + if (r->remove_backup_preferences == meshtastic_AdminMessage_BackupLocation_FLASH) { + spiLock->lock(); + FSCom.remove(backupFileName); + spiLock->unlock(); + } else if (r->remove_backup_preferences == meshtastic_AdminMessage_BackupLocation_SD) { + // TODO: After more mainline SD card support + LOG_ERROR("SD backup removal not implemented yet"); + } #endif break; }