M module-services/service-desktop/ServiceDesktop.cpp => module-services/service-desktop/ServiceDesktop.cpp +30 -8
@@ 111,10 111,13 @@ sys::ReturnCodes ServiceDesktop::InitHandler()
sdesktop::BackupMessage *backupMessage = dynamic_cast<sdesktop::BackupMessage *>(msg);
if (backupMessage != nullptr) {
RemountFS();
- backupStatus.state = BackupRestore::BackupUserFiles(this, backupStatus.backupTempDir);
- backupStatus.location =
- (purefs::dir::getBackupOSPath() / backupStatus.task).replace_extension(purefs::extension::tar);
- desktopWorker->reinit(backupStatus.location.parent_path());
+
+ backupRestoreStatus.state = OperationState::Running;
+ backupRestoreStatus.lastOperationResult =
+ BackupRestore::BackupUserFiles(this, backupRestoreStatus.backupTempDir);
+ backupRestoreStatus.location =
+ (purefs::dir::getBackupOSPath() / backupRestoreStatus.task).replace_extension(purefs::extension::tar);
+ backupRestoreStatus.state = OperationState::Stopped;
}
return sys::MessageNone{};
});
@@ 123,7 126,17 @@ sys::ReturnCodes ServiceDesktop::InitHandler()
sdesktop::RestoreMessage *restoreMessage = dynamic_cast<sdesktop::RestoreMessage *>(msg);
if (restoreMessage != nullptr) {
RemountFS();
- BackupRestore::RestoreUserFiles(this);
+ backupRestoreStatus.state = OperationState::Running;
+ backupRestoreStatus.lastOperationResult =
+ BackupRestore::RestoreUserFiles(this, backupRestoreStatus.location);
+
+ backupRestoreStatus.state = OperationState::Stopped;
+ if (backupRestoreStatus.lastOperationResult == true) {
+ sys::SystemManager::Reboot(this);
+ }
+ else {
+ LOG_ERROR("Restore failed");
+ }
}
return sys::MessageNone{};
});
@@ 322,9 335,10 @@ void ServiceDesktop::storeHistory(const std::string &historyValue)
void ServiceDesktop::prepareBackupData()
{
- backupStatus.task = std::to_string(static_cast<uint32_t>(utils::time::getCurrentTimestamp().getTime()));
- backupStatus.state = false;
- backupStatus.backupTempDir = purefs::dir::getTemporaryPath() / backupStatus.task;
+ backupRestoreStatus.operation = ServiceDesktop::Operation::Backup;
+ backupRestoreStatus.task = std::to_string(static_cast<uint32_t>(utils::time::getCurrentTimestamp().getTime()));
+ backupRestoreStatus.state = OperationState::Stopped;
+ backupRestoreStatus.backupTempDir = purefs::dir::getTemporaryPath() / backupRestoreStatus.task;
}
void ServiceDesktop::processUSBHandshake(sdesktop::usb::USBHandshake *msg)
@@ 341,3 355,11 @@ void ServiceDesktop::processUSBHandshake(sdesktop::usb::USBHandshake *msg)
parserFSM::MessageHandler::putToSendQueue(responseContext.createSimpleResponse());
}
+
+void ServiceDesktop::prepareRestoreData(const std::filesystem::path &restoreLocation)
+{
+ backupRestoreStatus.operation = ServiceDesktop::Operation::Restore;
+ backupRestoreStatus.location = purefs::dir::getBackupOSPath() / restoreLocation;
+ backupRestoreStatus.state = OperationState::Stopped;
+ backupRestoreStatus.task = restoreLocation.filename();
+}
M module-services/service-desktop/endpoints/backup/BackupEndpoint.cpp => module-services/service-desktop/endpoints/backup/BackupEndpoint.cpp +5 -5
@@ 37,19 37,19 @@ auto BackupEndpoint::request(Context &context) -> sys::ReturnCodes
auto owner = static_cast<ServiceDesktop *>(ownerServicePtr);
if (context.getBody()[json::task].is_string()) {
- if (owner->getBackupStatus().task == context.getBody()[json::task].string_value()) {
- if (owner->getBackupStatus().state == true) {
+ if (owner->getBackupRestoreStatus().task == context.getBody()[json::task].string_value()) {
+ if (owner->getBackupRestoreStatus().state != ServiceDesktop::OperationState::Running) {
context.setResponseStatus(parserFSM::http::Code::SeeOther);
}
- context.setResponseBody(owner->getBackupStatus());
+ context.setResponseBody(owner->getBackupRestoreStatus());
}
else {
context.setResponseStatus(parserFSM::http::Code::NotFound);
}
}
else if (context.getBody()[json::request] == true) {
- if (owner->getBackupStatus().state == true) {
+ if (owner->getBackupRestoreStatus().state == ServiceDesktop::OperationState::Running) {
// a backup is already running, don't start a second task
context.setResponseStatus(parserFSM::http::Code::NotAcceptable);
}
@@ 62,7 62,7 @@ auto BackupEndpoint::request(Context &context) -> sys::ReturnCodes
service::name::service_desktop);
// return new generated backup info
- context.setResponseBody(owner->getBackupStatus());
+ context.setResponseBody(owner->getBackupRestoreStatus());
}
}
else {
M module-services/service-desktop/endpoints/backup/BackupRestore.cpp => module-services/service-desktop/endpoints/backup/BackupRestore.cpp +195 -170
@@ 26,7 26,10 @@ namespace sys
} // namespace sys
static const long unsigned int empty_dirlist_size = 2;
-
+static bool isValidDirentry(const std::filesystem::directory_entry &direntry)
+{
+ return direntry.path() != "." && direntry.path() != ".." && direntry.path() != "...";
+}
// this replaces std::filesystem::copy_file that is broken at
// the time this is implemented (chown issues after copy)
// once fixed this can be replaced
@@ 50,7 53,7 @@ static bool copyFile(const std::filesystem::path &from, const std::filesystem::p
return false;
}
- std::unique_ptr<unsigned char[]> buffer(new unsigned char[purefs::buffer::tar_buf]);
+ std::unique_ptr<unsigned char[]> buffer = std::make_unique<unsigned char[]>(purefs::buffer::tar_buf);
size_t bytes;
while ((bytes = std::fread(buffer.get(), 1, purefs::buffer::tar_buf, fromFp)) != 0) {
@@ 99,18 102,18 @@ bool BackupRestore::WriteBackupInfo(sys::Service *ownerService, const std::files
if (std::filesystem::is_directory(path)) {
try {
- copyFile(purefs::dir::getRootDiskPath() / purefs::file::boot_json, path / purefs::file::boot_json);
+ copyFile(purefs::dir::getRootDiskPath() / purefs::file::boot_json, path / bkp::backupInfo);
LOG_DEBUG("%s copied to %s",
(purefs::dir::getRootDiskPath() / purefs::file::boot_json).c_str(),
- (path / "backup.json").c_str());
+ (path / bkp::backupInfo).c_str());
return true;
}
catch (std::filesystem::filesystem_error &e) {
LOG_ERROR("failed to copy %s->%s error:\"%s\"",
(purefs::dir::getRootDiskPath() / purefs::file::boot_json).c_str(),
- (path / "backup.json").c_str(),
+ (path / bkp::backupInfo).c_str(),
e.what());
return false;
@@ 124,27 127,31 @@ bool BackupRestore::WriteBackupInfo(sys::Service *ownerService, const std::files
return true;
}
-void BackupRestore::RestoreUserFiles(sys::Service *ownerService)
+bool BackupRestore::RestoreUserFiles(sys::Service *ownerService, const std::filesystem::path &path)
{
assert(ownerService != nullptr);
+ LOG_INFO("RestoreUserFiles: restore started from %s", path.c_str());
- LOG_INFO("RestoreUserFiles: restore started...");
-
- if (BackupRestore::UnpackBackupFile() == false) {
- return;
+ if (BackupRestore::UnpackBackupFile(path) == false) {
+ return false;
}
- /* close user files to be restored */
- LOG_INFO("RestoreUserFiles: closing ServiceDB...");
- std::string dbServiceName = service::name::db;
- sys::SystemManager::DestroySystemService(dbServiceName, ownerService);
+ if (sys::SystemManager::Restore(ownerService) == false) {
+ LOG_ERROR("Can't enter update system state");
+ return false;
+ }
+ else {
+ LOG_INFO("entered update state");
- BackupRestore::ReplaceUserFiles();
+ if (BackupRestore::ReplaceUserFiles(path) == false) {
+ LOG_ERROR("can't restore user files");
+ return false;
+ }
+ }
// BackupRestore::RemoveBackupDir(path);
- LOG_INFO("RestoreUserFiles: restoring finished, rebooting...");
- sys::SystemManager::Reboot(ownerService);
+ return true;
}
bool BackupRestore::RemoveBackupDir(std::filesystem::path &path)
@@ 168,41 175,31 @@ bool BackupRestore::RemoveBackupDir(std::filesystem::path &path)
bool BackupRestore::CreateBackupDir(std::filesystem::path &path)
{
LOG_INFO("CreateBackupDir: creating backup directory %s...", path.c_str());
- std::error_code e;
-
- struct statvfs stat;
- if (statvfs(path.c_str(), &stat)) {
- LOG_ERROR("statvdfs failed on %s", path.c_str());
- return false;
- }
-
- if (stat.f_flag & ST_RDONLY) {
- LOG_ERROR("%s is on a RO fs", path.c_str());
- return false;
- }
+ std::error_code errorCode;
if (!std::filesystem::is_directory(purefs::dir::getBackupOSPath())) {
- if (!std::filesystem::create_directory(purefs::dir::getBackupOSPath(), e)) {
+ if (!std::filesystem::create_directory(purefs::dir::getBackupOSPath(), errorCode)) {
LOG_ERROR("CreateBackupDir: creating backup directory %s failed. \"%s\"",
purefs::dir::getBackupOSPath().c_str(),
- e.message().c_str());
+ errorCode.message().c_str());
return false;
}
}
if (!std::filesystem::is_directory(purefs::dir::getTemporaryPath())) {
- if (!std::filesystem::create_directory(purefs::dir::getTemporaryPath(), e)) {
+ if (!std::filesystem::create_directory(purefs::dir::getTemporaryPath(), errorCode)) {
LOG_ERROR("CreateBackupDir: creating backup directory %s failed. \"%s\"",
purefs::dir::getTemporaryPath().c_str(),
- e.message().c_str());
+ errorCode.message().c_str());
return false;
}
}
if (!std::filesystem::is_directory(path)) {
- if (!std::filesystem::create_directory(path, e)) {
- LOG_ERROR(
- "CreateBackupDir: creating backup directory %s failed. \"%s\"", path.c_str(), e.message().c_str());
+ if (!std::filesystem::create_directory(path, errorCode)) {
+ LOG_ERROR("CreateBackupDir: creating backup directory %s failed. \"%s\"",
+ path.c_str(),
+ errorCode.message().c_str());
return false;
}
}
@@ 213,7 210,7 @@ bool BackupRestore::CreateBackupDir(std::filesystem::path &path)
bool BackupRestore::PackUserFiles(std::filesystem::path &path)
{
if (std::filesystem::is_empty(path)) {
- LOG_ERROR("PackUserFiles: backup dir %s is empty, nothing to backup, quitting...", path.c_str());
+ LOG_ERROR("backup dir %s is empty, nothing to backup, quitting...", path.c_str());
BackupRestore::RemoveBackupDir(path);
return false;
}
@@ 222,103 219,102 @@ bool BackupRestore::PackUserFiles(std::filesystem::path &path)
(purefs::dir::getBackupOSPath() / path.filename()).replace_extension(purefs::extension::tar);
mtar_t tarFile;
- LOG_INFO("PackUserFiles: opening file %s...", tarFilePath.c_str());
+ LOG_INFO("opening file %s...", tarFilePath.c_str());
int ret = mtar_open(&tarFile, tarFilePath.c_str(), "w");
if (ret != MTAR_ESUCCESS) {
- LOG_ERROR(
- "PackUserFiles: opening file %s failed, error: %s, quitting...", tarFilePath.c_str(), mtar_strerror(ret));
+ LOG_ERROR("opening file %s failed, error: %s, quitting...", tarFilePath.c_str(), mtar_strerror(ret));
BackupRestore::RemoveBackupDir(path);
return false;
}
- std::unique_ptr<unsigned char[]> buffer(new unsigned char[purefs::buffer::tar_buf]);
+
+ auto buffer = std::make_unique<unsigned char[]>(purefs::buffer::tar_buf);
std::error_code e;
for (auto &direntry : std::filesystem::directory_iterator(path)) {
- if ((direntry.path().string() != ".") && (direntry.path().string() != "..") &&
- (direntry.path().string() != "...")) {
-
- LOG_INFO("PackUserFiles: archiving file %s...", direntry.path().c_str());
- auto *file = std::fopen(direntry.path().string().c_str(), "r");
-
- if (file == nullptr) {
- LOG_ERROR("PackUserFiles: archiving file %s failed, cannot open file, quitting...",
- direntry.path().c_str());
- mtar_close(&tarFile);
- BackupRestore::RemoveBackupDir(path);
- return false;
- }
-
- LOG_DEBUG("PackUserFiles: writting tar header for %s...", direntry.path().c_str());
+ if (!isValidDirentry(direntry)) {
+ continue;
+ }
- if (mtar_write_file_header(&tarFile,
- direntry.path().filename().c_str(),
- static_cast<unsigned>(std::filesystem::file_size(direntry))) != MTAR_ESUCCESS) {
- LOG_ERROR("PackUserFiles: writing tar header for %s failed", direntry.path().filename().c_str());
- std::fclose(file);
- mtar_close(&tarFile);
- BackupRestore::RemoveBackupDir(path);
- return false;
- }
+ LOG_INFO("archiving file %s...", direntry.path().c_str());
+ auto *file = std::fopen(direntry.path().string().c_str(), "r");
- uintmax_t filesize = std::filesystem::file_size(direntry.path(), e);
- if (e) {
- LOG_ERROR("failed to get size for file: %s \"%s\"", path.c_str(), e.message().c_str());
- BackupRestore::RemoveBackupDir(path);
- return false;
- }
- uint32_t loopcount = (filesize / purefs::buffer::tar_buf) + 1u;
- uint32_t readsize = 0u;
+ if (file == nullptr) {
+ LOG_ERROR("archiving file %s failed, cannot open file, quitting...", direntry.path().c_str());
+ mtar_close(&tarFile);
+ BackupRestore::RemoveBackupDir(path);
+ return false;
+ }
- for (uint32_t i = 0u; i < loopcount; i++) {
- if (i + 1u == loopcount) {
- readsize = filesize % purefs::buffer::tar_buf;
- }
- else {
- readsize = purefs::buffer::tar_buf;
- }
+ LOG_DEBUG("writting tar header for %s...", direntry.path().c_str());
- LOG_DEBUG("PackUserFiles: reading file %s...", direntry.path().c_str());
+ if (mtar_write_file_header(&tarFile,
+ direntry.path().filename().c_str(),
+ static_cast<unsigned>(std::filesystem::file_size(direntry))) != MTAR_ESUCCESS) {
+ LOG_ERROR("writing tar header for %s failed", direntry.path().filename().c_str());
+ std::fclose(file);
+ mtar_close(&tarFile);
+ BackupRestore::RemoveBackupDir(path);
+ return false;
+ }
- if (std::fread(buffer.get(), 1, readsize, file) != readsize) {
- LOG_ERROR("PackUserFiles: reading file %s failed, quitting...", direntry.path().c_str());
- std::fclose(file);
- mtar_close(&tarFile);
- BackupRestore::RemoveBackupDir(path);
- return false;
- }
+ uintmax_t filesize = std::filesystem::file_size(direntry.path(), e);
+ if (e) {
+ LOG_ERROR("failed to get size for file: %s \"%s\"", path.c_str(), e.message().c_str());
+ BackupRestore::RemoveBackupDir(path);
+ return false;
+ }
+ uint32_t loopcount = (filesize / purefs::buffer::tar_buf) + 1u;
+ uint32_t readsize;
- LOG_DEBUG("PackUserFiles: writting %s into backup...", direntry.path().c_str());
- if (mtar_write_data(&tarFile, buffer.get(), readsize) != MTAR_ESUCCESS) {
- LOG_ERROR("PackUserFiles: writting %s into backup failed, quitting...", direntry.path().c_str());
- std::fclose(file);
- mtar_close(&tarFile);
- BackupRestore::RemoveBackupDir(path);
- return false;
- }
+ for (uint32_t i = 0u; i < loopcount; i++) {
+ if (i + 1u == loopcount) {
+ readsize = filesize % purefs::buffer::tar_buf;
+ }
+ else {
+ readsize = purefs::buffer::tar_buf;
}
- LOG_INFO("PackUserFiles: closing file %s...", direntry.path().c_str());
- if (std::fclose(file) != 0) {
- LOG_ERROR("PackUserFiles: closing file %s failed, quitting...", direntry.path().c_str());
+ LOG_DEBUG("reading file %s...", direntry.path().c_str());
+
+ if (std::fread(buffer.get(), 1, readsize, file) != readsize) {
+ LOG_ERROR("reading file %s failed, quitting...", direntry.path().c_str());
+ std::fclose(file);
mtar_close(&tarFile);
BackupRestore::RemoveBackupDir(path);
return false;
}
- LOG_INFO("PackUserFiles: deleting file %s...", direntry.path().c_str());
-
- if (std::remove(direntry.path().c_str()) != 0) {
- LOG_ERROR("PackUserFiles: deleting file %s failed, quitting...", direntry.path().c_str());
+ LOG_DEBUG("writting %s into backup...", direntry.path().c_str());
+ if (mtar_write_data(&tarFile, buffer.get(), readsize) != MTAR_ESUCCESS) {
+ LOG_ERROR("PackUserFiles: writting %s into backup failed, quitting...", direntry.path().c_str());
+ std::fclose(file);
mtar_close(&tarFile);
BackupRestore::RemoveBackupDir(path);
return false;
}
}
+
+ LOG_INFO("closing file %s...", direntry.path().c_str());
+ if (std::fclose(file) != 0) {
+ LOG_ERROR("PackUserFiles: closing file %s failed, quitting...", direntry.path().c_str());
+ mtar_close(&tarFile);
+ BackupRestore::RemoveBackupDir(path);
+ return false;
+ }
+
+ LOG_INFO("deleting file %s...", direntry.path().c_str());
+
+ if (std::remove(direntry.path().c_str()) != 0) {
+ LOG_ERROR("PackUserFiles: deleting file %s failed, quitting...", direntry.path().c_str());
+ mtar_close(&tarFile);
+ BackupRestore::RemoveBackupDir(path);
+ return false;
+ }
}
- LOG_INFO("PackUserFiles: finalizing file %s...", tarFilePath.c_str());
+ LOG_INFO("finalizing file %s...", tarFilePath.c_str());
if (mtar_finalize(&tarFile) != MTAR_ESUCCESS) {
LOG_ERROR("PackUserFiles: finalizing file %s failed, quitting....", tarFilePath.c_str());
mtar_close(&tarFile);
@@ 326,7 322,7 @@ bool BackupRestore::PackUserFiles(std::filesystem::path &path)
return false;
}
- LOG_INFO("PackUserFiles: closing file %s...", tarFilePath.c_str());
+ LOG_INFO("closing file %s...", tarFilePath.c_str());
if (mtar_close(&tarFile) != MTAR_ESUCCESS) {
LOG_ERROR("PackUserFiles: closing file %s failed, quitting...", tarFilePath.c_str());
BackupRestore::RemoveBackupDir(path);
@@ 336,41 332,46 @@ bool BackupRestore::PackUserFiles(std::filesystem::path &path)
return true;
}
-bool BackupRestore::UnpackBackupFile()
+bool BackupRestore::UnpackBackupFile(const std::filesystem::path &tarFilePath)
{
- std::string tarFilePath = purefs::dir::getBackupOSPath();
- tarFilePath += "/";
-
mtar_t tarFile;
mtar_header_t tarHeader;
+ std::error_code e;
+
+ auto extractDestination = purefs::dir::getTemporaryPath() / tarFilePath.stem();
+
+ LOG_INFO("creating temporary directory %s", extractDestination.c_str());
+ if (!std::filesystem::is_directory(extractDestination, e)) {
+ std::filesystem::create_directory(extractDestination, e);
+ if (e) {
+ LOG_ERROR("Can't create temporary directory %s \"%s\"", extractDestination.c_str(), e.message().c_str());
+ return false;
+ }
+ }
- LOG_INFO("UnpackBackupFile: opening file %s...", tarFilePath.c_str());
+ LOG_INFO("opening file %s...", tarFilePath.c_str());
int ret = mtar_open(&tarFile, tarFilePath.c_str(), "r");
if (ret != MTAR_ESUCCESS) {
- LOG_ERROR("UnpackBackupFile: opening file %s failed, error: %s, quitting...",
- tarFilePath.c_str(),
- mtar_strerror(ret));
+ LOG_ERROR("opening file %s failed, error: %s, quitting...", tarFilePath.c_str(), mtar_strerror(ret));
return false;
}
- std::unique_ptr<unsigned char[]> buffer(new unsigned char[purefs::buffer::tar_buf]);
+ auto buffer = std::make_unique<unsigned char[]>(purefs::buffer::tar_buf);
do {
ret = mtar_read_header(&tarFile, &tarHeader);
- LOG_INFO("UnpackBackupFile: reading tar header name %s...", tarHeader.name);
+ LOG_DEBUG("reading tar header name %s...", tarHeader.name);
if ((tarHeader.type == MTAR_TREG) && (ret == MTAR_ESUCCESS)) {
- LOG_INFO("UnpackBackupFile: extracting file %s...", tarHeader.name);
+ LOG_DEBUG("extracting file %s...", tarHeader.name);
- std::string restoreFilePath = purefs::dir::getBackupOSPath();
- restoreFilePath += "/";
- restoreFilePath += tarHeader.name;
- auto *file = std::fopen(restoreFilePath.c_str(), "w");
+ std::filesystem::path extractedFile = extractDestination / tarHeader.name;
+ auto *file = std::fopen(extractedFile.c_str(), "w");
if (file == nullptr) {
- LOG_ERROR("UnpackBackupFile: extracting file %s failed, quitting...", tarHeader.name);
+ LOG_ERROR("can't open %s for writing", extractedFile.c_str());
mtar_close(&tarFile);
return false;
}
@@ 388,90 389,114 @@ bool BackupRestore::UnpackBackupFile()
}
if (mtar_read_data(&tarFile, buffer.get(), readsize) != MTAR_ESUCCESS) {
- LOG_ERROR("UnpackBackupFile: extracting file %s failed, quitting...", tarHeader.name);
+ LOG_ERROR("extracting file %s failed, quitting...", extractedFile.c_str());
mtar_close(&tarFile);
std::fclose(file);
- std::remove(restoreFilePath.c_str());
+ std::remove(extractedFile.c_str());
return false;
}
if (std::fwrite(buffer.get(), 1, readsize, file) != readsize) {
- LOG_ERROR("UnpackBackupFile: writting file %s failed, quitting...", restoreFilePath.c_str());
+ LOG_ERROR("writting file %s failed, quitting...", extractedFile.c_str());
mtar_close(&tarFile);
std::fclose(file);
- std::remove(restoreFilePath.c_str());
+ std::remove(extractedFile.c_str());
return false;
}
}
- LOG_INFO("UnpackBackupFile: extracting file %s succeeded", tarHeader.name);
+ LOG_INFO("extracting file %s succeeded", extractedFile.c_str());
std::fclose(file);
}
else {
- LOG_INFO("UnpackBackupFile: found header %d, skipping", tarHeader.type);
+ LOG_DEBUG("found header %d, skipping", tarHeader.type);
}
ret = mtar_next(&tarFile);
- LOG_INFO("UnpackBackupFile: reading tar next status %s", mtar_strerror(ret));
+ LOG_DEBUG("reading tar next status %s", mtar_strerror(ret));
} while (ret == MTAR_ESUCCESS);
- LOG_INFO("UnpackBackupFile: cleaning directory from tar file...");
+ LOG_DEBUG("cleanup %s", tarFilePath.c_str());
mtar_close(&tarFile);
std::remove(tarFilePath.c_str());
+ if (e) {
+ LOG_WARN("can't cleanup temporary dir %s \"%s\"", extractDestination.c_str(), e.message().c_str());
+ }
+
return true;
}
-bool BackupRestore::ReplaceUserFiles()
+bool BackupRestore::ReplaceUserFiles(const std::filesystem::path &path)
{
/* replace existing files that have respective backup files existing */
- const auto backupOSPath = purefs::dir::getBackupOSPath();
- if (std::filesystem::is_directory(backupOSPath) && std::filesystem::is_empty(backupOSPath)) {
- LOG_INFO("ReplaceUserFiles: dir emtpy, nothing to restore, quitting...");
+ const auto tempDir = purefs::dir::getTemporaryPath() / path.stem();
+
+ if (std::filesystem::is_directory(tempDir) && std::filesystem::is_empty(tempDir)) {
+ LOG_INFO("dir empty, nothing to restore, quitting...");
return false;
}
- std::string userFilePath = purefs::dir::getUserDiskPath();
- userFilePath += "/";
-
- std::string backupFilePath = purefs::dir::getBackupOSPath();
- backupFilePath += "/";
-
- for (auto &direntry : std::filesystem::directory_iterator(backupOSPath.c_str())) {
- if ((direntry.path().compare(".") != 0) && (direntry.path().compare("..") != 0) &&
- (direntry.path().compare("...") != 0)) {
- LOG_INFO("ReplaceUserFiles: restoring backup file %s...", direntry.path().c_str());
-
- if (std::filesystem::exists((userFilePath + direntry.path().string()))) {
- if (std::filesystem::remove((userFilePath + direntry.path().string()))) {
- try {
- std::filesystem::rename(backupFilePath + direntry.path().string(),
- userFilePath + direntry.path().string());
- LOG_INFO("ReplaceUserFiles: restoring backup file %s succeeded", direntry.path().c_str());
- }
- catch (const std::exception &e) {
- LOG_ERROR("ReplaceUserFiles: restoring backup file %s failed on error %s",
- direntry.path().c_str(),
- e.what());
- }
- }
- else {
- LOG_ERROR("ReplaceUserFiles: restoring backup file %s", direntry.path().c_str());
- }
+
+ const std::filesystem::path userDir = purefs::dir::getUserDiskPath();
+ const std::filesystem::path backupDir = purefs::dir::getBackupOSPath();
+ std::error_code e;
+
+ for (auto &direntry : std::filesystem::directory_iterator(tempDir, e)) {
+ if (e) {
+ LOG_INFO("Can't list contents of %s \"%s\"", tempDir.c_str(), e.message().c_str());
+ return false;
+ }
+
+ if (!isValidDirentry(direntry)) {
+ continue;
+ }
+
+ // dont restore the information file
+ if (direntry.path().filename() == bkp::backupInfo) {
+ continue;
+ }
+
+ LOG_INFO("restoring backup file %s...", direntry.path().c_str());
+
+ if (std::filesystem::remove(userDir / direntry.path().filename(), e)) {
+ std::filesystem::rename(tempDir / direntry.path().filename(), userDir / direntry.path().filename(), e);
+ if (e) {
+ LOG_ERROR("can't rename %s->%s restore failed \"%s\"",
+ (tempDir / direntry.path().filename()).c_str(),
+ (userDir / direntry.path().filename()).c_str(),
+ e.message().c_str());
+ return false;
}
else {
- try {
- std::filesystem::rename(backupFilePath + direntry.path().string(),
- userFilePath + direntry.path().string());
- LOG_INFO("ReplaceUserFiles: restoring backup file %s succeeded", direntry.path().c_str());
- }
- catch (const std::filesystem::filesystem_error &e) {
- LOG_ERROR("ReplaceUserFiles: restoring backup file %s failed on error %s",
- direntry.path().c_str(),
- e.what());
- }
+ LOG_INFO("restored %s->%s",
+ (tempDir / direntry.path().filename()).c_str(),
+ (userDir / direntry.path().filename()).c_str());
}
}
+ else {
+ LOG_WARN("can't remove %s \"%s\"", (userDir / direntry.path().filename()).c_str(), e.message().c_str());
+ // we should continue, there can be new files in the backup
+ }
}
return true;
}
+
+json11::Json BackupRestore::GetBackupFiles()
+{
+ auto dirEntryVector = std::vector<std::string>();
+ std::error_code e;
+ for (const auto &p : std::filesystem::directory_iterator(purefs::dir::getBackupOSPath(), e)) {
+ if (e) {
+ LOG_ERROR(
+ "Can't get directory %s contents \"%s\"", purefs::dir::getBackupOSPath().c_str(), e.message().c_str());
+ return json11::Json();
+ }
+ if (!p.is_directory() && p.path().extension() == purefs::extension::tar) {
+ LOG_DEBUG("possible restore file %s", p.path().filename().c_str());
+ dirEntryVector.push_back(p.path().filename());
+ }
+ }
+
+ return dirEntryVector;
+}
M module-services/service-desktop/endpoints/backup/BackupRestore.hpp => module-services/service-desktop/endpoints/backup/BackupRestore.hpp +10 -3
@@ 5,25 5,32 @@
#include <Service/Service.hpp>
#include <filesystem>
+#include <json/json11.hpp>
namespace sys
{
class Service;
} // namespace sys
+namespace bkp
+{
+ inline constexpr auto backupInfo = "backup.json";
+};
+
class BackupRestore
{
public:
BackupRestore(){};
~BackupRestore(){};
static bool BackupUserFiles(sys::Service *ownerService, std::filesystem::path &path);
- static void RestoreUserFiles(sys::Service *ownerService);
+ static bool RestoreUserFiles(sys::Service *ownerService, const std::filesystem::path &path);
+ static json11::Json GetBackupFiles();
private:
static bool RemoveBackupDir(std::filesystem::path &path);
static bool CreateBackupDir(std::filesystem::path &path);
static bool PackUserFiles(std::filesystem::path &path);
- static bool UnpackBackupFile();
- static bool ReplaceUserFiles();
+ static bool UnpackBackupFile(const std::filesystem::path &path);
+ static bool ReplaceUserFiles(const std::filesystem::path &path);
static bool WriteBackupInfo(sys::Service *ownerService, const std::filesystem::path &path);
};
M module-services/service-desktop/endpoints/restore/RestoreEndpoint.cpp => module-services/service-desktop/endpoints/restore/RestoreEndpoint.cpp +67 -10
@@ 1,4 1,4 @@
-// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
#include "RestoreEndpoint.hpp"
@@ 8,6 8,7 @@
#include <parser/ParserUtils.hpp>
#include <service-desktop/DesktopMessages.hpp>
#include <service-desktop/ServiceDesktop.hpp>
+#include <service-desktop/endpoints/backup/BackupRestore.hpp>
#include <json/json11.hpp>
@@ 17,23 18,79 @@ using namespace parserFSM;
auto RestoreEndpoint::handle(Context &context) -> void
{
- if (context.getMethod() == parserFSM::http::Method::post) {
+ switch (context.getMethod()) {
+ case http::Method::get:
+ context.setResponseBody(BackupRestore::GetBackupFiles());
+ break;
+ case http::Method::post:
+ request(context);
+ break;
+ case http::Method::put:
+ case http::Method::del:
+ context.setResponseStatus(http::Code::BadRequest);
+ break;
+ }
+
+ MessageHandler::putToSendQueue(context.createSimpleResponse());
+}
- if (context.getBody()[parserFSM::json::restoreRequest] == true) {
- auto msg = std::make_shared<sdesktop::RestoreMessage>();
- ownerServicePtr->bus.sendUnicast(msg, service::name::service_desktop);
+auto RestoreEndpoint::request(Context &context) -> sys::ReturnCodes
+{
+ json11::Json responseBodyJson;
+ auto owner = static_cast<ServiceDesktop *>(ownerServicePtr);
- context.setResponseBody(json11::Json::object({{parserFSM::json::restoreRequest, true}}));
+ if (context.getBody()[json::task].is_string()) {
+ if (owner->getBackupRestoreStatus().task == context.getBody()[json::task].string_value()) {
+ if (owner->getBackupRestoreStatus().state == ServiceDesktop::OperationState::Running) {
+ LOG_WARN("looks like a previous job is running can't start a new one");
+ context.setResponseStatus(parserFSM::http::Code::SeeOther);
+ }
+
+ context.setResponseBody(owner->getBackupRestoreStatus());
}
else {
- context.setResponseBody(json11::Json::object({{parserFSM::json::restoreRequest, false}}));
+ context.setResponseStatus(parserFSM::http::Code::NotFound);
+ }
+ }
+ else if (context.getBody()[json::request] == true) {
+ if (owner->getBackupRestoreStatus().state == ServiceDesktop::OperationState::Running) {
+ LOG_WARN("looks like a job is running, try again later");
+ context.setResponseStatus(parserFSM::http::Code::NotAcceptable);
}
+ else {
+ const std::filesystem::path location(context.getBody()[json::location].string_value());
+ if (location.empty()) {
+ LOG_ERROR("no location in request");
+ context.setResponseBody(json11::Json::object({{"msg", "no location passed"}}));
+ context.setResponseStatus(parserFSM::http::Code::NotAcceptable);
+
+ return sys::ReturnCodes::Failure;
+ }
+ if (!std::filesystem::exists(purefs::dir::getBackupOSPath() / location)) {
+ LOG_ERROR("file %s does not exist", (purefs::dir::getBackupOSPath() / location).c_str());
+ context.setResponseBody(json11::Json::object({{"msg", "passed location is not readable"}}));
+ context.setResponseStatus(parserFSM::http::Code::NotFound);
+
+ return sys::ReturnCodes::Failure;
+ }
+ // initialize new restore information
+ owner->prepareRestoreData(location);
- MessageHandler::putToSendQueue(context.createSimpleResponse());
+ // start the request process
+ ownerServicePtr->bus.sendUnicast(std::make_shared<sdesktop::RestoreMessage>(),
+ service::name::service_desktop);
- return;
+ // return new generated restore info
+ context.setResponseBody(owner->getBackupRestoreStatus());
+ }
}
else {
- return;
+ // unknown request for backup endpoint
+ context.setResponseStatus(parserFSM::http::Code::BadRequest);
}
+
+ LOG_DEBUG("responding: %s", context.createSimpleResponse().c_str());
+ MessageHandler::putToSendQueue(context.createSimpleResponse());
+
+ return sys::ReturnCodes::Success;
}
M module-services/service-desktop/endpoints/restore/RestoreEndpoint.hpp => module-services/service-desktop/endpoints/restore/RestoreEndpoint.hpp +1 -0
@@ 25,4 25,5 @@ class RestoreEndpoint : public parserFSM::Endpoint
debugName = "RestoreEndpoint";
}
auto handle(parserFSM::Context &context) -> void override;
+ auto request(parserFSM::Context &context) -> sys::ReturnCodes;
};
M module-services/service-desktop/parser/ParserUtils.hpp => module-services/service-desktop/parser/ParserUtils.hpp +0 -1
@@ 94,7 94,6 @@ namespace parserFSM
inline constexpr auto currentRTCTime = "currentRTCTime";
inline constexpr auto updateReady = "updateReady";
inline constexpr auto updateFileList = "updateFileList";
- inline constexpr auto restoreRequest = "restoreRequest";
inline constexpr auto factoryRequest = "factoryRequest";
inline constexpr auto networkStatus = "networkStatus";
inline constexpr auto accessTechnology = "accessTechnology";
M module-services/service-desktop/service-desktop/ServiceDesktop.hpp => module-services/service-desktop/service-desktop/ServiceDesktop.hpp +36 -9
@@ 47,20 47,46 @@ class ServiceDesktop : public sys::Service
ServiceDesktop();
~ServiceDesktop() override;
- struct BackupStatus
+ enum class Operation
+ {
+ Backup,
+ Restore
+ };
+ enum class OperationState
+ {
+ Stopped,
+ Running,
+ Error
+ };
+
+ static const std::string opToString(const OperationState &op)
+ {
+ switch (op) {
+ case OperationState::Stopped:
+ return "stopped";
+ case OperationState::Running:
+ return "running";
+ case OperationState::Error:
+ return "error";
+ default:
+ return "unkown";
+ }
+ }
+ struct BackupRestoreStatus
{
std::filesystem::path backupTempDir;
std::filesystem::path location;
+ bool lastOperationResult = false;
std::string task;
- bool state = false;
+ OperationState state = OperationState::Stopped;
+ Operation operation = Operation::Backup;
json11::Json to_json() const
{
- return json11::Json::object{
- {parserFSM::json::task, task},
- {parserFSM::json::state, state ? parserFSM::json::finished : parserFSM::json::pending},
- {parserFSM::json::location, location.string()}};
+ return json11::Json::object{{parserFSM::json::task, task},
+ {parserFSM::json::state, opToString(state)},
+ {parserFSM::json::location, location.string()}};
}
- } backupStatus;
+ } backupRestoreStatus;
sys::ReturnCodes InitHandler() override;
sys::ReturnCodes DeinitHandler() override;
@@ 73,9 99,10 @@ class ServiceDesktop : public sys::Service
void storeHistory(const std::string &historyValue);
void prepareBackupData();
- const BackupStatus getBackupStatus()
+ void prepareRestoreData(const std::filesystem::path &restoreLocation);
+ const BackupRestoreStatus getBackupRestoreStatus()
{
- return backupStatus;
+ return backupRestoreStatus;
}
const sdesktop::USBSecurityModel *getSecurity()
{
M module-sys/SystemManager/SystemManager.cpp => module-sys/SystemManager/SystemManager.cpp +97 -25
@@ 46,6 46,34 @@ namespace sys
constexpr std::chrono::milliseconds lowBatteryShutdownDelayTime{5000};
} // namespace
+ namespace state
+ {
+ namespace update
+ {
+ static const std::set<std::string> whitelist{service::name::service_desktop,
+ service::name::evt_manager,
+ service::name::gui,
+ service::name::db,
+ service::name::eink,
+ app::manager::ApplicationManager::ServiceName};
+ }
+
+ namespace restore
+ {
+ static const std::set<std::string> whitelist{service::name::service_desktop,
+ service::name::evt_manager,
+ service::name::gui,
+ service::name::eink,
+ app::manager::ApplicationManager::ServiceName};
+ }
+
+ static bool isOnWhitelist(const std::set<std::string> &list, const std::string &serviceName)
+ {
+ return list.find(serviceName) != list.end();
+ }
+
+ } // namespace state
+
using namespace cpp_freertos;
using namespace std;
using namespace sys;
@@ 164,9 192,9 @@ namespace sys
void SystemManager::StartSystem(InitFunction sysInit, InitFunction appSpaceInit)
{
- powerManager = std::make_unique<PowerManager>();
- cpuStatistics = std::make_unique<CpuStatistics>();
- deviceManager = std::make_unique<DeviceManager>();
+ powerManager = std::make_unique<PowerManager>();
+ cpuStatistics = std::make_unique<CpuStatistics>();
+ deviceManager = std::make_unique<DeviceManager>();
phoneModeSubject = std::make_unique<phone_modes::Subject>(this);
systemInit = std::move(sysInit);
@@ 180,7 208,13 @@ namespace sys
cpuStatisticsTimer.start();
}
- bool SystemManager::Update(Service *s, const std::string &updateOSVer, std::string ¤tOSVer)
+ bool SystemManager::CloseSystem(Service *s)
+ {
+ s->bus.sendUnicast(std::make_shared<SystemManagerCmd>(Code::CloseSystem), service::name::system_manager);
+ return true;
+ }
+
+ bool SystemManager::Update(Service *s, const std::string &updateOSVer, const std::string ¤tOSVer)
{
// set update OS version (and also current os version) in Settings
storeOsVersion(s, updateOSVer, currentOSVer);
@@ 195,6 229,25 @@ namespace sys
return true;
}
+ bool SystemManager::Restore(Service *s)
+ {
+ LOG_DEBUG("trying to enter restore state");
+ auto ret = s->bus.sendUnicast(std::make_shared<SystemManagerCmd>(Code::Restore),
+ service::name::system_manager,
+ sys::constants::restoreTimeout);
+ if (ret.first != ReturnCodes::Success) {
+ LOG_WARN("Can't stop all services, %d ms wait time", sys::constants::restoreTimeout);
+ }
+ auto msgCloseApplications = std::make_shared<app::manager::UpdateInProgress>(service::name::system_manager);
+ ret = s->bus.sendUnicast(std::move(msgCloseApplications),
+ app::manager::ApplicationManager::ServiceName,
+ sys::constants::restoreTimeout);
+ if (ret.first != ReturnCodes::Success) {
+ LOG_WARN("Can't stop all applications, %d ms wait time", sys::constants::restoreTimeout);
+ }
+ return true;
+ }
+
void SystemManager::storeOsVersion(Service *s, const std::string &updateOSVer, const std::string ¤tOSVer)
{
// store OS version in Settings
@@ 411,6 464,9 @@ namespace sys
case Code::Update:
UpdateSystemHandler();
break;
+ case Code::Restore:
+ RestoreSystemHandler();
+ break;
case Code::Reboot:
RebootHandler();
break;
@@ 418,6 474,7 @@ namespace sys
break;
}
}
+
return MessageNone{};
});
@@ 603,9 660,9 @@ namespace sys
set(State::Shutdown);
}
- void SystemManager::UpdateSystemHandler()
+ void SystemManager::RestoreSystemHandler()
{
- LOG_DEBUG("Starting system update procedure...");
+ LOG_INFO("Entering restore system state");
// We are going to remove services in reversed order of creation
CriticalSection::Enter();
@@ 614,34 671,49 @@ namespace sys
for (bool retry{};; retry = false) {
for (auto &service : servicesList) {
- if (service->GetName() == service::name::evt_manager) {
- LOG_DEBUG("Delay closing %s", service::name::evt_manager);
- continue;
- }
- if (service->GetName() == service::name::service_desktop) {
- LOG_DEBUG("Delay closing %s", service::name::service_desktop);
+ if (sys::state::isOnWhitelist(sys::state::restore::whitelist, service->GetName())) {
continue;
}
- if (service->GetName() == service::name::db) {
- LOG_DEBUG("Delay closing %s", service::name::db);
- continue;
+ if (service->parent.empty()) {
+ LOG_DEBUG("destroy service: %s", service->GetName().c_str());
+ const auto ret = DestroySystemService(service->GetName(), this);
+ if (!ret) {
+ // no response to exit message,
+ LOG_FATAL("%s failed to respond to exit message", service->GetName().c_str());
+ kill(service);
+ }
+ else {
+ LOG_DEBUG("%s destroyed", service->GetName().c_str());
+ }
+ retry = true;
+ break;
}
+ }
+ if (!retry) {
+ break;
+ }
+ }
- if (service->GetName() == service::name::gui) {
- LOG_DEBUG("Delay closing %s", service::name::gui);
- continue;
- }
+ LOG_INFO("entered restore state");
+ }
- if (service->GetName() == service::name::eink) {
- LOG_DEBUG("Delay closing %s", service::name::eink);
- continue;
- }
+ void SystemManager::UpdateSystemHandler()
+ {
+ LOG_DEBUG("Starting system update procedure...");
+
+ // We are going to remove services in reversed order of creation
+ CriticalSection::Enter();
+ std::reverse(servicesList.begin(), servicesList.end());
+ CriticalSection::Exit();
- if (service->GetName() == app::manager::ApplicationManager::ServiceName) {
- LOG_DEBUG("Delay closing %s", app::manager::ApplicationManager::ServiceName);
+ for (bool retry{};; retry = false) {
+ for (auto &service : servicesList) {
+ if (sys::state::isOnWhitelist(sys::state::update::whitelist, service->GetName())) {
+ LOG_DEBUG("Delay closing %s", service->GetName().c_str());
continue;
}
+
if (service->parent.empty()) {
const auto ret = DestroySystemService(service->GetName(), this);
if (!ret) {
M module-sys/SystemManager/SystemManager.hpp => module-sys/SystemManager/SystemManager.hpp +11 -2
@@ 36,6 36,7 @@ namespace sys
using namespace std::chrono_literals;
inline constexpr std::chrono::milliseconds timerInitInterval{30s};
inline constexpr std::chrono::milliseconds timerPeriodInterval{100ms};
+ inline constexpr auto restoreTimeout{5000};
} // namespace constants
class PhoneModeRequest; // Forward declaration
@@ 45,6 46,7 @@ namespace sys
{
CloseSystem,
Update,
+ Restore,
Reboot,
None,
};
@@ 77,7 79,7 @@ namespace sys
Suspend,
Shutdown,
ShutdownReady,
- Reboot,
+ Reboot
} state = State::Running;
explicit SystemManager(std::vector<std::unique_ptr<BaseServiceCreator>> &&creators);
@@ 89,7 91,12 @@ namespace sys
void StartSystem(InitFunction sysInit, InitFunction appSpaceInit);
- static bool Update(Service *s, const std::string &updateOSVer, std::string ¤tOSVer);
+ // Invoke system close procedure
+ static bool CloseSystem(Service *s);
+
+ static bool Update(Service *s, const std::string &updateOSVer, const std::string ¤tOSVer);
+
+ static bool Restore(Service *s);
static bool Reboot(Service *s);
@@ 167,6 174,8 @@ namespace sys
void UpdateSystemHandler();
+ void RestoreSystemHandler();
+
void RebootHandler();
/// loop to handle prior to full system close
M test/pytest/conftest.py => test/pytest/conftest.py +4 -0
@@ 199,3 199,7 @@ def pytest_configure(config):
"usb_cdc_echo: mark test if it's intended for usb-cdc echo mode")
config.addinivalue_line("markers",
"two_sim_cards: mark test in case when two sim cards are required")
+ config.addinivalue_line("markers",
+ "backup: subset of backup user data tests")
+ config.addinivalue_line("markers",
+ "restore: subset of restore user data tests")
M test/pytest/service-desktop/test_backup.py => test/pytest/service-desktop/test_backup.py +58 -3
@@ 1,12 1,67 @@
# Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
# For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
import pytest
+import time
+import os
+from harness import log
from harness.interface.defs import status
-@pytest.mark.skip("rkubiak01 is fixing this issue")
@pytest.mark.service_desktop_test
+@pytest.mark.rt1051
@pytest.mark.usefixtures("usb_unlocked")
+@pytest.mark.backup
def test_backup(harness):
body = { "request": True }
- ret = harness.endpoint_request("backup", "get", body)
- assert ret["status"] == status["BadRequest"]
+ log.debug("backup testing");
+
+ # this requests a backup start
+ response = harness.endpoint_request("backup", "get", body)
+ assert response["body"] != ""
+ assert response["body"]["task"] != ""
+ task = response["body"]["task"]
+ assert response["status"] == status["OK"]
+
+ # in response we get a task ID and status 200
+ log.debug("backup started, waiting for results")
+
+ # start polling for backup status and wait for it to end
+ i = 0
+ while True:
+ i = i+1
+ time.sleep(1) # wait for the endpoint to be ready
+
+ # now that we know the task ID we can poll for it's status
+ body = { "request": True, "task": task }
+ response = harness.endpoint_request("backup", "get", body)
+
+ # backup is still running
+ if response["body"]["state"] == "running":
+ assert response["status"] == 200
+ log.debug("backup is running...")
+
+ # backup has stopped, should be OK and finished, status is 303
+ # and redirects to a location as per the documentation
+ if response["body"]["state"] == "stopped":
+ log.debug("backup ended, check results")
+ assert response["status"] == 303
+ assert response["body"]["location"] != ""
+
+ # see if the location is a valid backup path, extensions is .tar
+ # and starts with a /
+ p = response["body"]["location"]
+ p_split = os.path.splitext(p)
+ assert p_split[1] == ".tar"
+ assert p_split[0][0] == "/"
+
+ break
+
+ # wait for a moment
+ log.debug("sleeping for 1 second")
+ time.sleep(1)
+
+ # max 30 second timeout
+ if i > 30:
+ # this is bas, and the test fails here
+ log.error("backup timeout reached")
+ assert False
+ break
A test/pytest/service-desktop/test_restore.py => test/pytest/service-desktop/test_restore.py +39 -0
@@ 0,0 1,39 @@
+# Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
+# For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+import pytest
+import time
+from harness.interface.defs import status
+from harness import log
+
+@pytest.mark.service_desktop_test
+@pytest.mark.rt1051
+@pytest.mark.usefixtures("usb_unlocked")
+@pytest.mark.restore
+def test_restore(harness):
+ # this requests the list of available files
+ body = { "request": True }
+ response = harness.endpoint_request("restore", "get", body)
+
+ # should be ok 200
+ assert response["status"] == status["OK"]
+ log.debug("check if body is an array")
+
+ # body should be an array of files
+ assert isinstance(response["body"], list) == True
+
+ # chose the first entry for test if array is > 0
+ if len(response["body"]) > 0:
+ restore_task = response["body"][0]
+ log.debug("there are possible backup files on target, test restore %s" % (restore_task))
+ # this starts a restore process with a file as parameter
+ body = { "request":True, "location": restore_task }
+ response = harness.endpoint_request("restore", "post", body)
+
+ # we can't really test for results here, as the phone should reset
+ # in case the restore process lasts longer we should be able to poll
+ # but only on rt1051
+ assert response["body"]["location"] != ""
+ assert response["body"]["state"] != ""
+ else:
+ log.error("no possisble backup files on phone, run backup first")
+ assert False
M test/pytest/service-desktop/test_update.py => test/pytest/service-desktop/test_update.py +1 -2
@@ 1,11 1,10 @@
-# Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
+# Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
# For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
import pytest
from harness.interface.defs import status
@pytest.mark.service_desktop_test
-@pytest.mark.rt1051
@pytest.mark.usefixtures("usb_unlocked")
@pytest.mark.rt1051
def test_update(harness):