~aleteoryx/muditaos

b4814861b02051c9b402627577eec80aeb258830 — Roman Kubiak 5 years ago 253ff86
[EGD-4318] enable service desktop (#973)

* [EGD-3688] Relax retries count for send
enable ServiceDesktop

* [EGD-3688] Relax retries count for send
enable ServiceDesktop

* [EGD-4318] enable service desktop and USB communication
CDC for serial port (communication with Mudita Center)
MTP for file transfer

* [EGD-4318] include path fix

* [egd-4318] constexpr brought back

* [EGD-4318]: review changes
- all BSP specific code moved to bsp files
- added a device listener class for USB
- simplified WorkerDesktop

* [EGD-4318] more reivew fixes mostly include paths and enums

* [EGD-4319] review fixes for SP2FET

* [EGD-4318] updates for large messages sent from phone

* [EGD-4318] more review fixes
- error checks on linux (ptms open and ptsname)
- removed all vfs
- service-desktop won't start if worker init fails

* [EGD-4318] updated return values for usbInit

* Update module-services/service-desktop/WorkerDesktop.cpp

Co-authored-by: Piotr Tanski <piotr.tanski@mudita.com>

* Update module-services/service-desktop/endpoints/update/UpdateMuditaOS.hpp

Co-authored-by: Piotr Tanski <piotr.tanski@mudita.com>

* [EGD-4318] review fixes
- removed #define
- return values for usbInit

* Update module-bsp/board/linux/usb_cdc/usb_cdc.cpp

Co-authored-by: Piotr Tanski <piotr.tanski@mudita.com>

* [EGD-4318] updated taglib

* [EGD-4318] style fixes

* Update module-bsp/board/linux/usb_cdc/usb_cdc.cpp

Co-authored-by: Alek Rudnik <54846206+alekrudnik@users.noreply.github.com>

* [EGD-4318] switches from freertos Timer class to sys::Timer

* Update module-services/service-desktop/WorkerDesktop.cpp

Co-authored-by: Piotr Tanski <piotr.tanski@mudita.com>

* Update module-services/service-desktop/WorkerDesktop.cpp

Co-authored-by: Piotr Tanski <piotr.tanski@mudita.com>

* [EGD-4318] added mutex/lock during raw data transfers

* [EGD-4318] if the TAR file is zero size or invalid
mtar_close causes HF. This is a workaround

* [EGD-4318] timer should not start on constructor

* [EGD-4318] getRawMode is const noecept

Co-authored-by: unknown <atom@prostate.local>
Co-authored-by: Piotr Tanski <piotr.tanski@mudita.com>
Co-authored-by: Alek Rudnik <54846206+alekrudnik@users.noreply.github.com>
M module-bsp/board/linux/usb_cdc/usb_cdc.cpp => module-bsp/board/linux/usb_cdc/usb_cdc.cpp +23 -8
@@ 10,7 10,12 @@ namespace bsp
    int fd;
    xQueueHandle USBReceiveQueue;

    void usbCDCReceive(void *)
    void usbDeviceTask(void *ptr)
    {
        usbCDCReceive(ptr);
    }

    int usbCDCReceive(void *)
    {
        LOG_INFO("[ServiceDesktop:BSP_Driver] Start reading on fd:%d", fd);
        uint8_t inputData[SERIAL_BUFFER_LEN];


@@ 51,20 56,25 @@ namespace bsp
        }
    }

    void *usbInit(xQueueHandle receiveQueue)
    int usbInit(xQueueHandle receiveQueue, USBDeviceListener *)
    {

        fd = 0;
        fd = open("/dev/ptmx", O_RDWR | O_NOCTTY);
        if (fd == -1) {
            return (nullptr);
            LOG_ERROR("bsp::usbInit Failed to open /dev/ptmx, can't allocate new pseudo terminal");
            return -1;
        }

        grantpt(fd);
        unlockpt(fd);

        char *pts_name = ptsname(fd);
        LOG_INFO("[ServiceDesktop:BSP_Driver] VCOMAPPInit linux ptsname: %s", pts_name);
        if (pts_name == nullptr) {
            LOG_ERROR("bsp::usbInit ptsname returned NULL, no pseudo terminal allocated");
            return -1;
        }
        LOG_INFO("bsp::usbInit linux ptsname: %s", pts_name);
        struct termios newtio;
        memset(&newtio, 0, sizeof(newtio));
        struct termios oldtio;


@@ 86,13 96,18 @@ namespace bsp
        xTaskHandle taskHandleReceive;
        USBReceiveQueue = receiveQueue;

        BaseType_t task_error = xTaskCreate(
            usbCDCReceive, "USBLinuxReceive", SERIAL_BUFFER_LEN * 8, (void *)1, tskIDLE_PRIORITY, &taskHandleReceive);
        BaseType_t task_error = xTaskCreate(&bsp::usbDeviceTask,
                                            "USBLinuxReceive",
                                            SERIAL_BUFFER_LEN * 8,
                                            nullptr,
                                            tskIDLE_PRIORITY,
                                            &taskHandleReceive);

        if (task_error != pdPASS) {
            LOG_ERROR("[ServiceDesktop:BSP_Driver] Failed to start freertos USB_Linux_Receive");
            LOG_ERROR("bsp::usbInit Failed to start freertos USB_Linux_Receive");
            return -1;
        }

        return (&fd);
        return 0;
    }
} // namespace bsp

M module-bsp/board/rt1051/bsp/usb => module-bsp/board/rt1051/bsp/usb +1 -1
@@ 1,1 1,1 @@
Subproject commit 7061a014757a5dc3b6d9207a7408d5e3f8591bab
Subproject commit 9b7a0a468b28c185d94024823f844b2d64dc6efe

M module-bsp/bsp/usb/usb.hpp => module-bsp/bsp/usb/usb.hpp +12 -7
@@ 14,15 14,20 @@ extern "C"
#include <string.h>
#include <unistd.h>

#define SERIAL_TRANSMISSION_START 0x02
#define SERIAL_TRANSMISSION_END 0x03
#define SERIAL_SHELL_START 0x33
#define SERIAL_BAUDRATE 115200
#define SERIAL_BUFFER_LEN 512 // this matches the buffer length in rt1051 cdc implementaion
inline constexpr auto SERIAL_BUFFER_LEN = 512;
inline constexpr auto  SERIAL_BAUDRATE = 115200;

namespace bsp
{
    void *usbInit(xQueueHandle);
    void usbCDCReceive(void *ptr);
    class USBDeviceListener {
      public:
        virtual bool getRawMode() const noexcept{
            return false;
        }
        virtual void rawDataReceived(void *dataPtr, uint32_t dataLen) = 0;
    };

    int usbInit(xQueueHandle, USBDeviceListener *deviceListener = nullptr);
    int usbCDCReceive(void *ptr);
    int usbCDCSend(std::string *sendMsg);
} // namespace bsp

M module-services/service-desktop/CMakeLists.txt => module-services/service-desktop/CMakeLists.txt +1 -0
@@ 18,6 18,7 @@ set(SOURCES
    endpoints/restore/RestoreEndpoint.cpp
    endpoints/update/UpdateEndpoint.cpp
    endpoints/update/UpdateMuditaOS.cpp
    endpoints/filesystem/FilesystemEndpoint.cpp

    parser/ParserUtils.cpp
    parser/ParserFSM.cpp

M module-services/service-desktop/ServiceDesktop.cpp => module-services/service-desktop/ServiceDesktop.cpp +10 -5
@@ 36,11 36,17 @@ ServiceDesktop::~ServiceDesktop()
sys::ReturnCodes ServiceDesktop::InitHandler()
{
    desktopWorker = std::make_unique<WorkerDesktop>(this);
    desktopWorker->init(
        {{desktopWorker->RECEIVE_QUEUE_BUFFER_NAME, sizeof(std::string), sdesktop::cdc_queue_len},
         {desktopWorker->SEND_QUEUE_BUFFER_NAME, sizeof(std::string *), sdesktop::cdc_queue_object_size}});
    desktopWorker->run();
    const bool ret = desktopWorker->init(
        {{sdesktop::RECEIVE_QUEUE_BUFFER_NAME, sizeof(std::string), sdesktop::cdc_queue_len},
         {sdesktop::SEND_QUEUE_BUFFER_NAME, sizeof(std::string *), sdesktop::cdc_queue_object_size}});

    if (ret == false) {
        LOG_ERROR("!!! service-desktop InitHandler failed to initialize worker, service-desktop won't work");
        return sys::ReturnCodes::Failure;
    }
    else {
        desktopWorker->run();
    }
    connect(sdesktop::developerMode::DeveloperModeRequest(), [&](sys::Message *msg) {
        auto request = static_cast<sdesktop::developerMode::DeveloperModeRequest *>(msg);
        if (request->event != nullptr) {


@@ 104,7 110,6 @@ sys::ReturnCodes ServiceDesktop::InitHandler()
        return std::make_shared<sys::ResponseMessage>();
    });

    vfs.updateTimestamp();
    return (sys::ReturnCodes::Success);
}


M module-services/service-desktop/WorkerDesktop.cpp => module-services/service-desktop/WorkerDesktop.cpp +164 -26
@@ 1,9 1,11 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "service-desktop/ServiceDesktop.hpp"
#include "service-desktop/WorkerDesktop.hpp"
#include "parser/MessageHandler.hpp"
#include "parser/ParserFSM.hpp"
#include "endpoints/Context.hpp"

#include <bsp/usb/usb.hpp>
#include <log/log.hpp>


@@ 13,55 15,191 @@
#include <map>
#include <vector>

bool WorkerDesktop::handleMessage(uint32_t queueID)
inline constexpr auto uploadFailedMessage = "file upload terminated before all data transferred";

WorkerDesktop::WorkerDesktop(sys::Service *ownerServicePtr)
    : sys::Worker(ownerServicePtr), ownerService(ownerServicePtr), parser(ownerServicePtr), fileDes(nullptr)
{
    transferTimer =
        std::make_unique<sys::Timer>("WorkerDesktop file upload", ownerServicePtr, sdesktop::file_transfer_timeout);
    transferTimer->connect([=](sys::Timer &) { timerHandler(); });
}

    QueueHandle_t queue = queues[queueID];
bool WorkerDesktop::init(std::list<sys::WorkerQueueInfo> queues)
{
    Worker::init(queues);

    std::string qname = queueNameMap[queue];
    LOG_INFO("[ServiceDesktop:Worker] Received data from queue: %s", qname.c_str());
    receiveQueue                         = Worker::getQueueByName(sdesktop::RECEIVE_QUEUE_BUFFER_NAME);
    parserFSM::MessageHandler::sendQueue = Worker::getQueueByName(sdesktop::SEND_QUEUE_BUFFER_NAME);

    static std::string receiveMsg;
    static std::string *sendMsg;
    return (bsp::usbInit(receiveQueue, this) < 0) ? false : true;
}

    if (qname == SERVICE_QUEUE_NAME) {
        LOG_ERROR("[ServiceDesktop:Worker] Service Queue invoked but not implemented!");
bool WorkerDesktop::deinit(void)
{
    LOG_DEBUG("deinit");

    if (fileDes != nullptr) {
        LOG_DEBUG("deinit close opened fileDes");
        fclose(fileDes);
    }

    if (qname == RECEIVE_QUEUE_BUFFER_NAME) {
        if (xQueueReceive(queue, &receiveMsg, 0) != pdTRUE)
            return false;
    Worker::deinit();

        parser.processMessage(receiveMsg);
    }
    LOG_DEBUG("deinit end");
    return true;
}

bool WorkerDesktop::handleMessage(uint32_t queueID)
{
    QueueHandle_t queue = queues[queueID];

    // TODO: Consider moving sendBuffer receive to bsp driver
    if (qname == SEND_QUEUE_BUFFER_NAME) {
        if (xQueueReceive(queue, &sendMsg, 0) != pdTRUE)
    std::string qname = queueNameMap[queue];
    LOG_INFO("handleMessage received data from queue: %s", qname.c_str());
    static std::string *sendMsg = nullptr;
    static std::string receivedMsg;

    if (qname == sdesktop::RECEIVE_QUEUE_BUFFER_NAME) {
        if (xQueueReceive(queue, &receivedMsg, 0) != pdTRUE) {
            LOG_ERROR("handleMessage failed to receive from \"%s\"", sdesktop::RECEIVE_QUEUE_BUFFER_NAME);
            return false;
        }
        else {
            parser.processMessage(receivedMsg);
        }
    }
    else if (qname == sdesktop::SEND_QUEUE_BUFFER_NAME) {
        if (xQueueReceive(queue, &sendMsg, 0) != pdTRUE) {
            LOG_ERROR("handleMessage xQueueReceive failed for %s size %d bytes",
                      sdesktop::SEND_QUEUE_BUFFER_NAME,
                      static_cast<unsigned int>(sendMsg->length()));
            return false;
        }
        else {
            LOG_DEBUG("handeMessage sending %d bytes using usbCDCSend", static_cast<unsigned int>(sendMsg->length()));
        }

        bsp::usbCDCSend(sendMsg);
    }
    else {
        LOG_INFO("handeMessage got message on an unhandled queue");
    }

    return true;
}

bool WorkerDesktop::init(std::list<sys::WorkerQueueInfo> queues)
sys::ReturnCodes WorkerDesktop::startDownload(const std::filesystem::path &destinationPath, uint32_t fileSize)
{
    Worker::init(queues);
    filePath = destinationPath;
    fileDes  = fopen(filePath.c_str(), "w");

    if ((bsp::usbInit(Worker::getQueueByName(WorkerDesktop::RECEIVE_QUEUE_BUFFER_NAME)) == nullptr)) {
        LOG_ERROR("won't start desktop service without serial port");
        return false;
    if (fileDes == nullptr)
        return sys::ReturnCodes::Failure;

    if (fileSize <= 0)
        return sys::ReturnCodes::Failure;

    transferTimer->start();

    writeFileSizeExpected = fileSize;
    rawModeEnabled        = true;

    LOG_DEBUG("startDownload all checks passed starting download");
    return sys::ReturnCodes::Success;
}

void WorkerDesktop::stopTransfer(const TransferFailAction action)
{
    LOG_DEBUG("stopTransfer %s",
              action == TransferFailAction::removeDesitnationFile ? "remove desination file" : "do nothing");
    parser.setState(parserFSM::State::NoMsg);
    rawModeEnabled = false;

    parserFSM::Context responseContext;
    responseContext.setResponseStatus((action == TransferFailAction::removeDesitnationFile)
                                          ? parserFSM::http::Code::NotAcceptable
                                          : parserFSM::http::Code::Accepted);
    responseContext.setEndpoint(parserFSM::EndpointType::filesystemUpload);
    json11::Json responseJson = json11::Json::object{{parserFSM::json::fileSize, std::to_string(writeFileDataWritten)},
                                                     {parserFSM::json::fileName, filePath.filename().c_str()}};
    responseContext.setResponseBody(responseJson);

    // close the file descriptor
    fclose(fileDes);

    // stop the timeout timer
    transferTimer->stop();

    // reset all counters
    writeFileSizeExpected = 0;
    writeFileDataWritten  = 0;

    if (action == TransferFailAction::removeDesitnationFile) {
        if (remove(filePath.c_str()) != 0) {
            LOG_ERROR("stopTransfer can't delete file(requested) %s", filePath.c_str());
        }
    }

    parserFSM::MessageHandler::sendQueue = Worker::getQueueByName(WorkerDesktop::SEND_QUEUE_BUFFER_NAME);
    parserFSM::MessageHandler::putToSendQueue(responseContext.createSimpleResponse());
}

    return true;
void WorkerDesktop::rawDataReceived(void *dataPtr, uint32_t dataLen)
{
    if (getRawMode()) {
        transferTimer->reload();

        if (dataPtr == nullptr || dataLen == 0) {
            LOG_ERROR("transferDataReceived invalid data");
            return;
        }

        const uint32_t bytesWritten = fwrite(dataPtr, 1, dataLen, fileDes);

        if (bytesWritten != dataLen) {
            LOG_ERROR("transferDataReceived vfs write failed bytesWritten=%" PRIu32 " != dataLen=%" PRIu32,
                      bytesWritten,
                      dataLen);
            return;
        }

        writeFileDataWritten += dataLen;

        if (writeFileDataWritten >= writeFileSizeExpected) {
            LOG_INFO("transferDataReceived all data transferred, stop now");
            stopTransfer(TransferFailAction::doNothing);
        }
    }
    else {
        LOG_DEBUG("transferDataReceived not in a transfer state");
    }
}

bool WorkerDesktop::deinit(void)
void WorkerDesktop::timerHandler()
{
    Worker::deinit();
    return true;
    LOG_DEBUG("timeout timer: run");

    if (getRawMode()) {
        LOG_DEBUG("timeout timer: stopping transfer");
        uploadFileFailedResponse();
        stopTransfer(TransferFailAction::removeDesitnationFile);
    }
}

bool WorkerDesktop::getRawMode() const noexcept
{
    return rawModeEnabled;
}

void WorkerDesktop::uploadFileFailedResponse()
{
    LOG_ERROR("Upload file failed, timeout");
    parserFSM::Context responseContext;
    responseContext.setResponseStatus(parserFSM::http::Code::InternalServerError);
    responseContext.setEndpoint(parserFSM::EndpointType::filesystemUpload);

    json11::Json responseJson = json11::Json::object{
        {parserFSM::json::status, uploadFailedMessage},
    };
    responseContext.setResponseBody(responseJson);
    parserFSM::MessageHandler::putToSendQueue(responseContext.createSimpleResponse());
}

M module-services/service-desktop/endpoints/Context.hpp => module-services/service-desktop/endpoints/Context.hpp +4 -4
@@ 97,6 97,10 @@ namespace parserFSM
        {
            responseContext.status = status;
        }
        auto setEndpoint(EndpointType endpointTypeToSet)
        {
            endpoint = endpointTypeToSet;
        }
        auto setResponseBody(json11::Json respBody)
        {
            responseContext.body = respBody;


@@ 109,10 113,6 @@ namespace parserFSM
        {
            return endpoint;
        }
        void setEndpoint(EndpointType newEndpoint)
        {
            endpoint = newEndpoint;
        }
        auto getUuid() -> uint32_t
        {
            return uuid;

M module-services/service-desktop/endpoints/EndpointFactory.hpp => module-services/service-desktop/endpoints/EndpointFactory.hpp +6 -2
@@ 7,11 7,13 @@

#include "Service/Service.hpp"
#include "backup/BackupEndpoint.hpp"
#include "deviceInfo/DeviceInfoEndpoint.hpp"
#include "update/UpdateEndpoint.hpp"
#include "filesystem/FilesystemEndpoint.hpp"
#include "factoryReset/FactoryResetEndpoint.hpp"
#include "calllog/CalllogEndpoint.hpp"
#include "contacts/ContactsEndpoint.hpp"
#include "developerMode/DeveloperModeEndpoint.hpp"
#include "deviceInfo/DeviceInfoEndpoint.hpp"
#include "factoryReset/FactoryResetEndpoint.hpp"
#include "messages/MessagesEndpoint.hpp"
#include "restore/RestoreEndpoint.hpp"
#include "update/UpdateEndpoint.hpp"


@@ 27,6 29,8 @@ class EndpointFactory
        switch (context.getEndpoint()) {
        case EndpointType::update:
            return std::make_unique<UpdateEndpoint>(ownerServicePtr);
        case EndpointType::filesystemUpload:
            return std::make_unique<FilesystemEndpoint>(ownerServicePtr);
        case EndpointType::backup:
            return std::make_unique<BackupEndpoint>(ownerServicePtr);
        case EndpointType::deviceInfo:

A module-services/service-desktop/endpoints/filesystem/FilesystemEndpoint.cpp => module-services/service-desktop/endpoints/filesystem/FilesystemEndpoint.cpp +71 -0
@@ 0,0 1,71 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "FilesystemEndpoint.hpp"
#include "service-desktop/DesktopMessages.hpp"
#include "service-desktop/ServiceDesktop.hpp"
#include <module-vfs/include/user/purefs/filesystem_paths.hpp>

auto FilesystemEndpoint::handle(Context &context) -> void
{
    LOG_DEBUG("handle");
    switch (context.getMethod()) {
    case http::Method::post:
        run(context);
        break;
    default:
        break;
    }
}
static bool isWritable(const fs::path file)
{
    auto lamb = [](vfs::FILE *stream) { vfs.fclose(stream); };

    std::unique_ptr<vfs::FILE, decltype(lamb)> sf(vfs.fopen(file.c_str(), "w"), lamb);

    if (sf.get() != nullptr) {
        return true;
    }
    else {
        return false;
    }
}

auto FilesystemEndpoint::run(Context &context) -> sys::ReturnCodes
{
    LOG_DEBUG("running");
    sys::ReturnCodes returnCode = sys::ReturnCodes::Failure;
    std::string cmd             = context.getBody()[parserFSM::json::filesystem::command].string_value();

    context.setResponseBody(
        json11::Json::object({{json::status, std::to_string(static_cast<int>(sys::ReturnCodes::Failure))}}));

    auto owner = static_cast<ServiceDesktop *>(ownerServicePtr);

    if (cmd == parserFSM::json::filesystem::commands::download) {
        fs::path filePath    = context.getBody()[parserFSM::json::fileName].string_value();
        fs::path tmpFilePath = purefs::dir::getUpdatesOSPath() / filePath;

        uint32_t fileSize = context.getBody()[parserFSM::json::fileSize].int_value();

        LOG_DEBUG("got owner, file %s", tmpFilePath.c_str());

        if (isWritable(tmpFilePath)) {
            LOG_INFO("download %" PRIu32 " bytes to: %s", fileSize, tmpFilePath.c_str());

            if (owner->desktopWorker->startDownload(tmpFilePath, fileSize) == sys::ReturnCodes::Success) {
                context.setResponseStatus(parserFSM::http::Code::Accepted);
                returnCode = sys::ReturnCodes::Success;
            }
        }
        else {
            LOG_ERROR("download command failed, can't write %" PRIu32 " bytes to: %s", fileSize, tmpFilePath.c_str());
        }
    }
    else {
        LOG_ERROR("unknown command: %s", cmd.c_str());
    }

    MessageHandler::putToSendQueue(context.createSimpleResponse());
    return returnCode;
}
\ No newline at end of file

A module-services/service-desktop/endpoints/filesystem/FilesystemEndpoint.hpp => module-services/service-desktop/endpoints/filesystem/FilesystemEndpoint.hpp +19 -0
@@ 0,0 1,19 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include <module-services/service-desktop/endpoints/Endpoint.hpp>
#include "Service/Service.hpp"

using namespace parserFSM;

class FilesystemEndpoint : public Endpoint
{
  public:
    FilesystemEndpoint(sys::Service *ownerServicePtr) : Endpoint(ownerServicePtr)
    {}
    auto handle(Context &context) -> void override;
    auto run(Context &context) -> sys::ReturnCodes;
    auto getUpdates(Context &context) -> sys::ReturnCodes;
};

M module-services/service-desktop/endpoints/update/UpdateMuditaOS.cpp => module-services/service-desktop/endpoints/update/UpdateMuditaOS.cpp +147 -91
@@ 17,6 17,9 @@

#if defined(TARGET_RT1051)
#include <board/cross/eMMC/eMMC.hpp>
#include "bsp/watchdog/watchdog.hpp"
#include "vfs_globals.hpp"
#include <module-vfs/include/user/purefs/filesystem_paths.hpp>
#endif

#include <array>


@@ 56,13 59,15 @@ updateos::UpdateError UpdateMuditaOS::setUpdateFile(fs::path updateFileToUse)
            totalBytes = vfs.filelength(updateTar.stream);
        }
        else {
            informError("UpdateMuditaOS::setUpdateFile can't open TAR file %s", updateFile.c_str());
            return updateos::UpdateError::CantOpenUpdateFile;
            return informError(updateos::UpdateError::CantOpenUpdateFile,
                               "UpdateMuditaOS::setUpdateFile can't open TAR file %s",
                               updateFile.c_str());
        }
    }
    else {
        informError("UpdateMuditaOS::setUpdateFile %s does not exist", updateFile.c_str());
        return updateos::UpdateError::CantOpenUpdateFile;
        return informError(updateos::UpdateError::CantOpenUpdateFile,
                           "UpdateMuditaOS::setUpdateFile %s does not exist",
                           updateFile.c_str());
    }

    status = updateos::UpdateState::UpdateFileSet;


@@ 75,52 80,47 @@ updateos::UpdateError UpdateMuditaOS::runUpdate()

    updateos::UpdateError err = prepareTempDirForUpdate();
    if (err != updateos::UpdateError::NoError) {
        informError("runUpdate can't prepare temp directory for update");
        return err;
        return informError(err, "runUpdate can't prepare temp directory for update");
    }

    informDebug("Unpacking update");
    if ((err = unpackUpdate()) == updateos::UpdateError::NoError) {
        informUpdate("Unpacked");
        informUpdate(status, "Unpacked");
    }
    else {
        informError("%s can't be unpacked", updateFile.c_str());
        return err;
        return informError(err, "%s can't be unpacked", updateFile.c_str());
    }

    if ((err = verifyChecksums()) == updateos::UpdateError::NoError) {
        informUpdate("Verify checksums");
        informUpdate(status, "Verify checksums");
    }
    else {
        informError("Checksum verification failed");
        return err;
        return informError(err, "Checksum verification failed");
    }

    if ((err = verifyVersion()) == updateos::UpdateError::NoError) {
        informUpdate("Verify version");
        informUpdate(status, "Verify version");
    }
    else {
        informError("Can't verify version");
        return err;
        return informError(err, "Can't verify version");
    }

    if ((err = updateBootloader()) == updateos::UpdateError::NoError) {
        informUpdate("Update bootloader");
        informUpdate(status, "Update bootloader");
    }
    else {
        informError("Failed to update the bootloader");
        return err;
        return informError(err, "Failed to update the bootloader");
    }

    if ((err = prepareRoot()) == updateos::UpdateError::NoError) {
        informUpdate("Ready for reset");
        informUpdate(status, "Ready for reset");
    }
    else {
        informError("Can't prepare root dir for reset");
        informError(err, "Can't prepare root dir for reset");
    }

    if ((err = cleanupAfterUpdate()) != updateos::UpdateError::NoError) {
        informError("runUpdate cleanupAfterUpdate failed, resetting anyway");
        informError(err, "runUpdate cleanupAfterUpdate failed, resetting anyway");
    }

    // reboot always


@@ 141,14 141,16 @@ updateos::UpdateError UpdateMuditaOS::unpackUpdate()
        if (tarHeader.type == MTAR_TDIR) {
            fs::path tmpPath = getUpdateTmpChild(tarHeader.name);
            if (vfs.mkdir(tmpPath.c_str()) != 0) {
                informError("unpackUpdate failed to create %s when extracting update tar", tmpPath.c_str());
                return (updateos::UpdateError::CantCreateExtractedFile);
                return informError(updateos::UpdateError::CantCreateExtractedFile,
                                   "unpackUpdate failed to create %s when extracting update tar",
                                   tmpPath.c_str());
            }
        }
        else {
            if (unpackFileToTemp(tarHeader, &fileCRC32) == false) {
                informError("unpackUpdate failed to extract update file %s", tarHeader.name);
                return (updateos::UpdateError::CantCreateExtractedFile);
                return informError(updateos::UpdateError::CantCreateExtractedFile,
                                   "unpackUpdate failed to extract update file %s",
                                   tarHeader.name);
            }
            filesInUpdatePackage.emplace_back(FileInfo(tarHeader, fileCRC32));
        }


@@ 169,8 171,9 @@ updateos::UpdateError UpdateMuditaOS::verifyChecksums()
    vfs::FILE *fpChecksums = vfs.fopen(checksumsFile.c_str(), "r");

    if (fpChecksums == nullptr) {
        informError("verifyChecksums can't open checksums file %s", checksumsFile.c_str());
        return updateos::UpdateError::CantOpenChecksumsFile;
        return informError(updateos::UpdateError::CantOpenChecksumsFile,
                           "verifyChecksums can't open checksums file %s",
                           checksumsFile.c_str());
    }

    while (!vfs.eof(fpChecksums)) {


@@ 184,9 187,12 @@ updateos::UpdateError UpdateMuditaOS::verifyChecksums()
        getChecksumInfo(line, filePath, &fileCRC32);
        unsigned long computedCRC32 = getExtractedFileCRC32(filePath);
        if (computedCRC32 != fileCRC32) {
            informError("verifyChecksums %s crc32 match FAIL %lX != %lX", filePath.c_str(), fileCRC32, computedCRC32);
            vfs.fclose(fpChecksums);
            return updateos::UpdateError::VerifyChecksumsFailure;
            return informError(updateos::UpdateError::VerifyChecksumsFailure,
                               "verifyChecksums %s crc32 match FAIL %lX != %lX",
                               filePath.c_str(),
                               fileCRC32,
                               computedCRC32);
        }
    }
    vfs.fclose(fpChecksums);


@@ 198,16 204,17 @@ updateos::UpdateError UpdateMuditaOS::verifyVersion()
    status = updateos::UpdateState::VersionVerificiation;

    if (!vfs.fileExists(getUpdateTmpChild(updateos::file::version).c_str())) {
        informError("verifyVersion %s does not exist", getUpdateTmpChild(updateos::file::version).c_str());
        return updateos::UpdateError::VerifyVersionFailure;
        return informError(updateos::UpdateError::VerifyVersionFailure,
                           "verifyVersion %s does not exist",
                           getUpdateTmpChild(updateos::file::version).c_str());
    }

    std::string versionJsonString = vfs.loadFileAsString(getUpdateTmpChild(updateos::file::version));
    std::string parserError;
    json11::Json updateVersionInformation = json11::Json::parse(versionJsonString, parserError);
    if (parserError != "") {
        informUpdate("verifyVersion parse json error: %s", parserError.c_str());
        return updateos::UpdateError::VerifyVersionFailure;
        return informError(
            updateos::UpdateError::VerifyVersionFailure, "verifyVersion parse json error: %s", parserError.c_str());
    }
    else {
    }


@@ 269,24 276,34 @@ updateos::UpdateError UpdateMuditaOS::prepareRoot()
    ret = vfs.deltree(previousOSPath.c_str());

    if (ret != 0) {
        informError(
            "prepareRoot ff_deltree on %s caused an error %s", previousOSPath.c_str(), vfs.lastErrnoToStr().c_str());
        informError(updateos::UpdateError::CantDeletePreviousOS,
                    "prepareRoot ff_deltree on %s caused an error %s",
                    previousOSPath.c_str(),
                    vfs.lastErrnoToStr().c_str());
    }

    if (vfs.isDir(previousOSPath.c_str())) {
        informError("prepareRoot %s still exists, we can't continue", previousOSPath.c_str());
        return updateos::UpdateError::CantDeletePreviousOS;
    if (vfs.isDir(purefs::dir::getPreviousOSPath().c_str())) {
        return informError(updateos::UpdateError::CantDeletePreviousOS,
                           "prepareRoot ff_deltree on %s caused an error %s",
                           previousOSPath.c_str(),
                           vfs.lastErrnoToStr().c_str());
    }

    if (vfs.isDir(purefs::dir::getPreviousOSPath().c_str())) {
        return informError(updateos::UpdateError::CantDeletePreviousOS,
                           "prepareRoot %s still exists, we can't continue",
                           purefs::dir::getPreviousOSPath().c_str());
    }
    // rename the current OS to previous on partition
    informDebug("prepareRoot rename: %s->%s", currentOSPath.c_str(), previousOSPath.c_str());
    ret = vfs.rename(currentOSPath.c_str(), previousOSPath.c_str());

    if (ret != 0) {
        informError("prepareRoot can't rename %s -> %s error %s",
                    currentOSPath.c_str(),
                    previousOSPath.c_str(),
                    vfs.lastErrnoToStr().c_str());
        return updateos::UpdateError::CantRenameCurrentToPrevious;
        return informError(updateos::UpdateError::CantRenameCurrentToPrevious,
                           "prepareRoot can't rename %s -> %s error %s",
                           purefs::dir::getCurrentOSPath().c_str(),
                           purefs::dir::getPreviousOSPath().c_str(),
                           vfs.lastErrnoToStr().c_str());
    }

    // rename the temp directory to current (extracted update)


@@ 294,11 311,11 @@ updateos::UpdateError UpdateMuditaOS::prepareRoot()
    ret = vfs.rename(updateTempDirectory.c_str(), currentOSPath.c_str());

    if (ret != 0) {
        informError("prepareRoot can't rename %s -> %s error %s",
                    updateTempDirectory.c_str(),
                    currentOSPath.c_str(),
                    vfs.lastErrnoToStr().c_str());
        return updateos::UpdateError::CantRenameTempToCurrent;
        return informError(updateos::UpdateError::CantRenameTempToCurrent,
                           "prepareRoot can't rename %s -> %s error %s",
                           updateTempDirectory.c_str(),
                           purefs::dir::getCurrentOSPath().c_str(),
                           vfs.lastErrnoToStr().c_str());
    }

    // move the contents of /sys/current/user if it exists to /user


@@ 350,7 367,7 @@ bool UpdateMuditaOS::unpackFileToTemp(mtar_header_t &h, unsigned long *crc32)
    fileExtracted         = h.name;
    fileExtractedSize     = h.size;

    informUpdate("Unpack %s", fullPath.filename().c_str());
    informUpdate(status, "Unpack %s", fullPath.filename().c_str());

    if (crc32 != nullptr) {
        *crc32 = 0;


@@ 362,7 379,8 @@ bool UpdateMuditaOS::unpackFileToTemp(mtar_header_t &h, unsigned long *crc32)
    int errCode   = MTAR_ESUCCESS;
    vfs::FILE *fp = vfs.fopen(fullPath.c_str(), "w+");
    if (fp == nullptr) {
        informError("unpackFileToTemp %s can't open for writing", fullPath.c_str());
        informError(
            updateos::UpdateError::CantWriteToFile, "unpackFileToTemp %s can't open for writing", fullPath.c_str());
        return false;
    }



@@ 378,14 396,17 @@ bool UpdateMuditaOS::unpackFileToTemp(mtar_header_t &h, unsigned long *crc32)
            break;

        if ((errCode = mtar_read_data(&updateTar, readBuf.get(), sizeToRead)) != MTAR_ESUCCESS) {
            informError("unpackFileToTemp mtar_read_data failed, errCode=%d", errCode);
            informError(
                updateos::UpdateError::CantWriteToFile, "unpackFileToTemp mtar_read_data failed, errCode=%d", errCode);
            return false;
        }

        const uint32_t dataWritten = vfs.fwrite(readBuf.get(), 1, sizeToRead, fp);
        if (dataWritten != sizeToRead) {
            informError(
                "unpackFileToTemp %s can't write to file error: %s", fullPath.c_str(), vfs.lastErrnoToStr().c_str());
            informError(updateos::UpdateError::CantWriteToFile,
                        "unpackFileToTemp %s can't write to file error: %s",
                        fullPath.c_str(),
                        vfs.lastErrnoToStr().c_str());
            vfs.fclose(fp);
            return false;
        }


@@ 401,13 422,12 @@ bool UpdateMuditaOS::unpackFileToTemp(mtar_header_t &h, unsigned long *crc32)
updateos::UpdateError UpdateMuditaOS::cleanupAfterUpdate()
{
    if (vfs.isDir(updateTempDirectory.c_str()) && vfs.deltree(updateTempDirectory.c_str())) {
        informError("ff_deltree failed on %s", updateTempDirectory.c_str());
        return updateos::UpdateError::CantRemoveUniqueTmpDir;
        return informError(
            updateos::UpdateError::CantRemoveUniqueTmpDir, "ff_deltree failed on %s", updateTempDirectory.c_str());
    }
    mtar_close(&updateTar);
    if (vfs.remove(updateFile.c_str())) {
        informError("Failed to delete %s", updateFile.c_str());
        return updateos::UpdateError::CantRemoveUpdateFile;
        return informError(updateos::UpdateError::CantRemoveUpdateFile, "Failed to delete %s", updateFile.c_str());
    }
    status = updateos::UpdateState::ReadyForReset;
    return updateos::UpdateError::NoError;


@@ 429,37 449,50 @@ updateos::UpdateError UpdateMuditaOS::prepareTempDirForUpdate()
    const auto updatesOSPath = purefs::dir::getUpdatesOSPath();
    if (vfs.isDir(updatesOSPath.c_str()) == false) {
        if (vfs.mkdir(updatesOSPath.c_str()) != 0) {
            informError("%s can't create it %s", updatesOSPath.c_str(), vfs.lastErrnoToStr().c_str());
            return updateos::UpdateError::CantCreateUpdatesDir;
            return informError(updateos::UpdateError::CantCreateUpdatesDir,
                               "%s can't create it %s",
                               updatesOSPath.c_str(),
                               vfs.lastErrnoToStr().c_str());
        }
    }

    if (vfs.isDir(purefs::dir::getUpdatesOSPath().c_str()) == false) {
        if (vfs.mkdir(purefs::dir::getUpdatesOSPath().c_str()) != 0) {
            return informError(updateos::UpdateError::CantCreateUpdatesDir,
                               "%s can't create it %s",
                               purefs::dir::getUpdatesOSPath().c_str(),
                               vfs.lastErrnoToStr().c_str());
        }
        else {
            informDebug("prepareTempDirForUpdate %s created", updatesOSPath.c_str());
            informDebug("prepareTempDirForUpdate %s created", purefs::dir::getUpdatesOSPath().c_str());
        }
    }
    else {
        informDebug("prepareTempDirForUpdate %s exists", updatesOSPath.c_str());
        informDebug("prepareTempDirForUpdate %s exists", purefs::dir::getUpdatesOSPath().c_str());
    }

    const auto tmpPath = purefs::dir::getTemporaryPath();
    if (vfs.isDir(tmpPath.c_str()) == false) {
        informDebug("prepareTempDirForUpdate %s is not a directory", tmpPath.c_str());
        if (vfs.mkdir(tmpPath.c_str()) != 0) {
            informError("%s can't create it %s", tmpPath.c_str(), vfs.lastErrnoToStr().c_str());
            return updateos::UpdateError::CantCreateTempDir;
    if (vfs.isDir(purefs::dir::getTemporaryPath().c_str()) == false) {
        informDebug("prepareTempDirForUpdate %s is not a directory", purefs::dir::getTemporaryPath().c_str());
        if (vfs.mkdir(purefs::dir::getTemporaryPath().c_str()) != 0) {
            return informError(updateos::UpdateError::CantCreateTempDir,
                               "%s can't create it %s",
                               purefs::dir::getTemporaryPath().c_str(),
                               vfs.lastErrnoToStr().c_str());
        }
        else {
            informDebug("prepareTempDirForUpdate %s created", tmpPath.c_str());
            informDebug("prepareTempDirForUpdate %s created", purefs::dir::getTemporaryPath().c_str());
        }
    }
    else {
        informDebug("prepareTempDirForUpdate %s exists", tmpPath.c_str());
        informDebug("prepareTempDirForUpdate %s exists", purefs::dir::getTemporaryPath().c_str());
    }

    if (vfs.isDir(updateTempDirectory.c_str())) {
        informDebug("prepareTempDirForUpdate %s exists already, try to remove it", updateTempDirectory.c_str());
        if (vfs.deltree(updateTempDirectory.c_str()) != 0) {
            informError("prepareTempDirForUpdate can't remove %s", updateTempDirectory.c_str());
            return updateos::UpdateError::CantRemoveUniqueTmpDir;
            return informError(updateos::UpdateError::CantRemoveUniqueTmpDir,
                               "prepareTempDirForUpdate can't remove %s",
                               updateTempDirectory.c_str());
        }
        else {
            informDebug("prepareTempDirForUpdate %s removed", updateTempDirectory.c_str());


@@ 468,58 501,61 @@ updateos::UpdateError UpdateMuditaOS::prepareTempDirForUpdate()

    informDebug("prepareTempDirForUpdate trying to create %s as tempDir", updateTempDirectory.c_str());
    if (vfs.mkdir(updateTempDirectory.c_str()) != 0) {
        informError("prepareTempDirForUpdate failed to create: %s error: %s",
        informError(updateos::UpdateError::CantCreateUniqueTmpDir,
                    "prepareTempDirForUpdate failed to create: %s error: %s",
                    updateTempDirectory.c_str(),
                    vfs.lastErrnoToStr().c_str());
        return updateos::UpdateError::CantCreateUniqueTmpDir;
    }

    return updateos::UpdateError::NoError;
}

updateos::BootloaderUpdateError UpdateMuditaOS::writeBootloader(fs::path bootloaderFile)
updateos::UpdateError UpdateMuditaOS::writeBootloader(fs::path bootloaderFile)
{
    status = updateos::UpdateState::UpdatingBootloader;

#if defined(TARGET_Linux)
    return updateos::BootloaderUpdateError::NoError;
    return updateos::UpdateError::NoError;
#else

    if (vfs.fileExists(bootloaderFile.c_str()) == false) {
        informError("[Bootloader Update] File %s doesn't exist!\n", bootloaderFile.c_str());
        return updateos::BootloaderUpdateError::NoBootloaderFile;
        return informError(updateos::UpdateError::NoBootloaderFile,
                           "[Bootloader Update] File %s doesn't exist!\n",
                           bootloaderFile.c_str());
    }

    auto fileHandler = vfs.fopen(bootloaderFile.c_str(), "r");
    if (fileHandler == nullptr) {
        informError("[Bootloader Update] Failed to open file %s\n", bootloaderFile.c_str());
        return updateos::BootloaderUpdateError::CantOpenBootloaderFile;
        return informError(updateos::UpdateError::CantOpenBootloaderFile,
                           "[Bootloader Update] Failed to open file %s\n",
                           bootloaderFile.c_str());
    }

    unsigned long fileLen = vfs.filelength(fileHandler);
    auto fileBuf          = std::make_unique<uint8_t[]>(fileLen);
    if (fileBuf == nullptr) {
        informError("[Bootloader Update] Failed to allocate buffer\n");
        return updateos::BootloaderUpdateError::CantAllocateBuffer;
        return informError(updateos::UpdateError::CantAllocateBuffer,
                           "[Bootloader Update] Failed to allocate buffer\n");
    }

    auto filesLoaded = vfs.fread(fileBuf.get(), fileLen, 1, fileHandler);
    if (filesLoaded == 0) {
        informError("[Bootloader Update] Failed to load file %s\n", bootloaderFile.c_str());
        return updateos::BootloaderUpdateError::CantOpenBootloaderFile;
        return informError(updateos::UpdateError::CantOpenBootloaderFile,
                           "[Bootloader Update] Failed to load file %s\n",
                           bootloaderFile.c_str());
    }

    informUpdate("[Bootloader Update] File size: %lu B, Writing...", fileLen);
    informUpdate(status, "[Bootloader Update] File size: %lu B, Writing...", fileLen);

    bsp::eMMC emmc;
    emmc.Init();
    emmc.SwitchPartition(bsp::eMMC::Partition::Boot1);
    emmc.WriteBlocks(fileBuf.get(), 0, std::ceil(fileLen / FSL_SDMMC_DEFAULT_BLOCK_SIZE));

    informUpdate("[Bootloader Update] DONE!\n");
    informUpdate(status, "[Bootloader Update] DONE!\n");
    emmc.SwitchPartition(bsp::eMMC::Partition::UserArea);

    return updateos::BootloaderUpdateError::NoError;
    return updateos::UpdateError::NoError;
#endif
}



@@ 552,7 588,7 @@ const json11::Json UpdateMuditaOS::getVersionInfoFromFile(const fs::path &update
                     updateos::file::version,
                     updateFile.c_str());

            mtar_close(&tar);
            // mtar_close(&tar);
            return json11::Json();
        }



@@ 608,7 644,7 @@ updateos::UpdateError UpdateMuditaOS::updateUserData()
    return updateos::UpdateError::NoError;
}

void UpdateMuditaOS::informError(const char *format, ...)
updateos::UpdateError UpdateMuditaOS::informError(const updateos::UpdateError errorCode, const char *format, ...)
{
    va_list argptr;
    std::unique_ptr<char[]> readBuf(new char[purefs::buffer::tar_buf]);


@@ 616,12 652,23 @@ void UpdateMuditaOS::informError(const char *format, ...)
    vsnprintf(readBuf.get(), purefs::buffer::tar_buf, format, argptr);
    va_end(argptr);

    LOG_ERROR("UPDATE_ERRROR %s", readBuf.get());
    LOG_ERROR("UPDATE_ERRROR [%d] %s", static_cast<uint8_t>(errorCode), readBuf.get());

    auto msgToSend         = std::make_shared<sdesktop::UpdateOsMessage>(updateos::UpdateMessageType::UpdateError);
    messageText            = std::string(readBuf.get());
    msgToSend->updateStats = (updateos::UpdateStats)(*this);
    sys::Bus::SendUnicast(msgToSend, app::name_desktop, owner);

    parserFSM::Context responseContext;
    responseContext.setResponseStatus(parserFSM::http::Code::InternalServerError);
    responseContext.setEndpoint(parserFSM::EndpointType::update);
    json11::Json responseJson = json11::Json::object{{parserFSM::json::update, parserFSM::json::updateError},
                                                     {parserFSM::json::status, messageText},
                                                     {parserFSM::json::errorCode, static_cast<uint8_t>(errorCode)}};
    responseContext.setResponseBody(responseJson);
    parserFSM::MessageHandler::putToSendQueue(responseContext.createSimpleResponse());

    return errorCode;
}

void UpdateMuditaOS::informDebug(const char *format, ...)


@@ 635,7 682,7 @@ void UpdateMuditaOS::informDebug(const char *format, ...)
    LOG_DEBUG("UPDATE_DEBUG %s", readBuf.get());
}

void UpdateMuditaOS::informUpdate(const char *format, ...)
void UpdateMuditaOS::informUpdate(const updateos::UpdateState statusCode, const char *format, ...)
{
    va_list argptr;
    std::unique_ptr<char[]> readBuf(new char[purefs::buffer::tar_buf]);


@@ 643,10 690,19 @@ void UpdateMuditaOS::informUpdate(const char *format, ...)
    vsnprintf(readBuf.get(), purefs::buffer::tar_buf, format, argptr);
    va_end(argptr);

    LOG_INFO("UPDATE_INFO %s", readBuf.get());
    LOG_INFO("UPDATE_INFO [%d] %s", static_cast<uint8_t>(statusCode), readBuf.get());

    auto msgToSend         = std::make_shared<sdesktop::UpdateOsMessage>(updateos::UpdateMessageType::UpdateInform);
    messageText            = std::string(readBuf.get());
    msgToSend->updateStats = (updateos::UpdateStats)(*this);
    sys::Bus::SendUnicast(msgToSend, app::name_desktop, owner);

    parserFSM::Context responseContext;
    responseContext.setResponseStatus(parserFSM::http::Code::Accepted);
    responseContext.setEndpoint(parserFSM::EndpointType::update);
    json11::Json responseJson = json11::Json::object{{parserFSM::json::update, parserFSM::json::updateInfo},
                                                     {parserFSM::json::status, messageText},
                                                     {parserFSM::json::statusCode, static_cast<uint8_t>(statusCode)}};
    responseContext.setResponseBody(responseJson);
    parserFSM::MessageHandler::putToSendQueue(responseContext.createSimpleResponse());
}

M module-services/service-desktop/endpoints/update/UpdateMuditaOS.hpp => module-services/service-desktop/endpoints/update/UpdateMuditaOS.hpp +7 -11
@@ 14,7 14,6 @@
#include <vector>

class ServiceDesktop;

namespace fs = std::filesystem;
namespace updateos
{


@@ 31,7 30,7 @@ namespace updateos
        inline constexpr auto update = ".tar";
    }

    const inline int prefix_len = 8;
    inline constexpr auto prefix_len = 8;

    enum class UpdateError
    {


@@ 52,12 51,9 @@ namespace updateos
        CantRenameTempToCurrent,
        CantUpdateJSON,
        CantSaveJSON,
        CantUpdateCRC32JSON
    };

    enum class BootloaderUpdateError
    {
        NoError,
        CantUpdateCRC32JSON,
        CantDeltreePreviousOS,
        CantWriteToFile,
        NoBootloaderFile,
        CantOpenBootloaderFile,
        CantAllocateBuffer,


@@ 128,11 124,11 @@ class UpdateMuditaOS : public updateos::UpdateStats
    updateos::UpdateError cleanupAfterUpdate();
    updateos::UpdateError updateUserData();

    void informError(const char *format, ...);
    updateos::UpdateError informError(updateos::UpdateError errorCode, const char *format, ...);
    void informDebug(const char *format, ...);
    void informUpdate(const char *format, ...);
    void informUpdate(const updateos::UpdateState statusCode, const char *format, ...);

    updateos::BootloaderUpdateError writeBootloader(fs::path bootloaderFile);
    updateos::UpdateError writeBootloader(fs::path bootloaderFile);

    void getChecksumInfo(const std::string &infoLine, std::string &filePath, unsigned long *fileCRC32Long);
    unsigned long getExtractedFileCRC32(const std::string &filePath);

M module-services/service-desktop/parser/ParserFSM.cpp => module-services/service-desktop/parser/ParserFSM.cpp +15 -7
@@ 3,17 3,25 @@

#include "MessageHandler.hpp"
#include "ParserFSM.hpp"
#include "ParserUtils.hpp"

#include <log/log.hpp>
namespace sys
{
    class Service;
} // namespace sys

#include <service-desktop/ServiceDesktop.hpp>
#include <log/log.hpp>
#include <json/json11.hpp>
#include <memory>
#include <string>

#include "MessageHandler.hpp" // for MessageHandler
#include "ParserUtils.hpp" // for eraseFront, calcPayloadLength, extractPayload, getHeader, removeHeader, size_header, endpointChar, parserFSM

namespace sys
{
    class Service;
} // namespace sys
}

using namespace parserFSM;



@@ 52,7 60,7 @@ void StateMachine::parseHeader()

    auto messageStart = receivedMsgPtr->find(message::endpointChar);
    if (messageStart == std::string::npos) {
        LOG_ERROR("This is not a valid endpoint message! Type=%c\n", receivedMsgPtr->at(0));
        LOG_ERROR("This is not a valid endpoint message! Type=%c", receivedMsgPtr->at(0));
        return;
    }



@@ 72,7 80,7 @@ void StateMachine::parseHeader()
        return;
    }

    LOG_DEBUG("Payload length: %lu\n", payloadLength);
    LOG_DEBUG("Payload length: %lu", payloadLength);

    message::removeHeader(*receivedMsgPtr);
    parseNewMessage();


@@ 144,9 152,9 @@ void StateMachine::parsePartialMessage()

void StateMachine::parsePayload()
{
    LOG_DEBUG("Payload: %s\n", payload.c_str());
    LOG_DEBUG("Payload: %s", payload.c_str());
    if (payload.empty()) {
        LOG_ERROR("Empty payload!\n");
        LOG_ERROR("Empty payload!");
        state = State::NoMsg;
        return;
    }

M module-services/service-desktop/parser/ParserFSM.hpp => module-services/service-desktop/parser/ParserFSM.hpp +5 -5
@@ 10,11 10,6 @@

#include <string>

namespace sys
{
    class Service;
} // namespace sys

namespace parserFSM
{
    enum class State


@@ 35,6 30,11 @@ namespace parserFSM
            return state;
        };

        void setState(const parserFSM::State newState)
        {
            state = newState;
        }

      private:
        std::string *receivedMsgPtr = nullptr;
        parserFSM::State state      = State::NoMsg;

M module-services/service-desktop/parser/ParserUtils.hpp => module-services/service-desktop/parser/ParserUtils.hpp +46 -26
@@ 2,13 2,11 @@
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include <log/log.hpp>

#include <bits/exception.h>
#include <cstddef>
#include <cstdint>
#include <string>
#include <stdint.h>         // for uint8_t
#include <log/log.hpp>      // for LOG_ERROR
#include <bits/exception.h> // for exception
#include <stddef.h>         // for size_t
#include <string>           // for string, allocator, basic_string, stol
#include <vector>

namespace parserFSM


@@ 20,6 18,7 @@ namespace parserFSM
        invalid = 0,
        deviceInfo,
        update,
        filesystemUpload,
        backup,
        restore,
        factory,


@@ 80,7 79,9 @@ namespace parserFSM
        enum class Code
        {
            OK                  = 200,
            Accepted            = 202,
            BadRequest          = 400,
            NotAcceptable       = 406,
            InternalServerError = 500
        };



@@ 99,27 100,46 @@ namespace parserFSM

    namespace json
    {
        inline constexpr auto batteryLevel   = "batteryLevel";
        inline constexpr auto batteryState   = "batteryState";
        inline constexpr auto selectedSim    = "selectedSim";
        inline constexpr auto trayState      = "trayState";
        inline constexpr auto signalStrength = "signalStrength";
        inline constexpr auto fsTotal        = "fsTotal";
        inline constexpr auto fsFreePercent  = "fsFreePercent";
        inline constexpr auto fsFree         = "fsFree";
        inline constexpr auto gitRevision    = "gitRevision";
        inline constexpr auto gitBranch      = "gitBranch";
        inline constexpr auto gitTag         = "gitTag";
        inline constexpr auto currentRTCTime = "currentRTCTime";
        inline constexpr auto updateReady    = "updateReady";
        inline constexpr auto updateFileList = "updateFileList";
        inline constexpr auto backupRequest  = "backupRequest";
        inline constexpr auto backupReady    = "backupReady";
        inline constexpr auto backupUpload   = "backupUpload";
        inline constexpr auto restoreRequest = "restoreRequest";
        inline constexpr auto factoryRequest = "factoryRequest";
        inline constexpr auto batteryLevel     = "batteryLevel";
        inline constexpr auto batteryState     = "batteryState";
        inline constexpr auto selectedSim      = "selectedSim";
        inline constexpr auto trayState        = "trayState";
        inline constexpr auto signalStrength   = "signalStrength";
        inline constexpr auto fsTotal          = "fsTotal";
        inline constexpr auto fsFreePercent    = "fsFreePercent";
        inline constexpr auto fsFree           = "fsFree";
        inline constexpr auto gitRevision      = "gitRevision";
        inline constexpr auto gitBranch        = "gitBranch";
        inline constexpr auto gitTag           = "gitTag";
        inline constexpr auto currentRTCTime   = "currentRTCTime";
        inline constexpr auto updateReady      = "updateReady";
        inline constexpr auto updateFileList   = "updateFileList";
        inline constexpr auto backupRequest    = "backupRequest";
        inline constexpr auto backupReady      = "backupReady";
        inline constexpr auto backupUpload     = "backupUpload";
        inline constexpr auto restoreRequest   = "restoreRequest";
        inline constexpr auto factoryRequest   = "factoryRequest";
        inline constexpr auto networkStatus    = "networkStatus";
        inline constexpr auto accessTechnology = "accessTechnology";
        inline constexpr auto fileName         = "fileName";
        inline constexpr auto fileSize         = "fileSize";

        inline constexpr auto update      = "update";
        inline constexpr auto updateInfo  = "updateInfo";
        inline constexpr auto updateError = "updateError";
        inline constexpr auto errorCode   = "errorCode";
        inline constexpr auto statusCode  = "statusCode";

        namespace filesystem
        {
            inline constexpr auto command = "command";
            namespace commands
            {
                inline constexpr auto upload   = "upload";
                inline constexpr auto rm       = "rm";
                inline constexpr auto download = "download";
            } // namespace commands
        }     // namespace filesystem

        namespace messages
        {

M module-services/service-desktop/service-desktop/ServiceDesktop.hpp => module-services/service-desktop/service-desktop/ServiceDesktop.hpp +12 -7
@@ 3,25 3,30 @@

#pragma once

#include <memory> // for allocator, unique_ptr

#include "WorkerDesktop.hpp"
#include "endpoints/update/UpdateMuditaOS.hpp"
#include "Service/Common.hpp"  // for ReturnCodes, ServicePowerMode
#include "Service/Message.hpp" // for MessagePointer, DataMessage (ptr only), ResponseMessage (ptr only)
#include "Service/Service.hpp" // for Service
#include "Constants.hpp"
#include "WorkerDesktop.hpp"

#include <endpoints/update/UpdateMuditaOS.hpp>

#include <Service/Common.hpp>
#include <Service/Message.hpp>
#include <Service/Service.hpp>

#include <memory>

class UpdateMuditaOS;
class WorkerDesktop;

namespace sdesktop
{
    inline constexpr auto service_stack         = 8192;
    inline constexpr auto cdc_queue_len         = 10;
    inline constexpr auto cdc_queue_object_size = 10;
    inline constexpr auto cdc_queue_len             = 32;
    inline constexpr auto cdc_queue_object_size     = 1024;
    inline constexpr auto file_transfer_timeout     = 5000;
    inline constexpr auto RECEIVE_QUEUE_BUFFER_NAME = "receiveQueueBuffer";
    inline constexpr auto SEND_QUEUE_BUFFER_NAME    = "sendQueueBuffer";
}; // namespace sdesktop

class ServiceDesktop : public sys::Service

M module-services/service-desktop/service-desktop/WorkerDesktop.hpp => module-services/service-desktop/service-desktop/WorkerDesktop.hpp +39 -26
@@ 3,39 3,52 @@

#pragma once

#include <Service/Message.hpp>
#include <Service/Service.hpp>
#include <Service/Worker.hpp>
#include <bsp/usb/usb.hpp>
#include <parser/ParserFSM.hpp>
#include <string.h>
#include <stdio.h>
#include <filesystem>
#include "Service/Message.hpp"
#include "Service/Service.hpp"
#include "Service/Worker.hpp"
#include "Service/Timer.hpp"
#include "parser/ParserFSM.hpp"
#include "bsp/usb/usb.hpp"

extern "C"
class WorkerDesktop : public sys::Worker, public bsp::USBDeviceListener
{
#include <FreeRTOS.h>
#include <task.h>
}

#include <cstdint>
#include <list>
#include <string>

namespace sys
{
    class Service;
} // namespace sys

class WorkerDesktop : public sys::Worker
{
  private:
  public:
    const std::string RECEIVE_QUEUE_BUFFER_NAME = "receiveQueueBuffer";
    const std::string SEND_QUEUE_BUFFER_NAME    = "sendQueueBuffer";
    enum TransferFailAction
    {
        doNothing,
        removeDesitnationFile
    };
    WorkerDesktop(sys::Service *ownerServicePtr);

    WorkerDesktop(sys::Service *ownerServicePtr)
        : sys::Worker(ownerServicePtr), ownerService(ownerServicePtr), parser(ownerServicePtr){};
    virtual bool init(std::list<sys::WorkerQueueInfo> queues) override;
    virtual bool deinit() override;
    bool handleMessage(uint32_t queueID) override final;

    sys::Service *ownerService = nullptr;
    parserFSM::StateMachine parser;

    xQueueHandle getReceiveQueue()
    {
        return receiveQueue;
    }
    sys::ReturnCodes startDownload(const std::filesystem::path &destinationPath, const uint32_t fileSize);
    void stopTransfer(const TransferFailAction action);

    void timerHandler(void);

    void rawDataReceived(void *dataPtr, uint32_t dataLen) override;
    bool getRawMode() const noexcept override;

  private:
    void uploadFileFailedResponse();
    xQueueHandle receiveQueue;
    FILE *fileDes                  = nullptr;
    uint32_t writeFileSizeExpected = 0;
    uint32_t writeFileDataWritten  = 0;
    std::filesystem::path filePath;
    volatile bool rawModeEnabled = false;
    std::unique_ptr<sys::Timer> transferTimer;
};

M module-sys/Service/Timer.hpp => module-sys/Service/Timer.hpp +1 -1
@@ 5,7 5,7 @@

#include "FreeRTOS.h"
#include "portmacro.h" // for TickType_t
#include <timer.hpp>   // for Timer
#include <module-os/RTOSWrapper/include/timer.hpp> // for Timer
#include <functional>  // for function
#include <string>      // for string
namespace sys