blob: b5436ad0d38218cfd8cc4cc1e261bf5c875d6a67 [file] [log] [blame]
#include <sdbus-c++/sdbus-c++.h>
#include <thread>
#include "dbus_rauc_server.h"
#include "utils/log-init.h"
using namespace std::literals;
namespace {
const std::string interfaceManager = "de.pengutronix.rauc.Installer";
const std::string objectPathManager = "/";
}
#define PROGRESS(perc, msg, depth) \
m_propProgress = sdbus::make_struct(perc, std::string(msg), depth); \
m_manager->emitPropertiesChangedSignal(interfaceManager, {"Progress"});
#define OPERATION(op) \
m_propOperation = op; \
m_manager->emitPropertiesChangedSignal(interfaceManager, {"Operation"});
#define LAST_ERROR(msg) \
m_propLastError = msg; \
m_manager->emitPropertiesChangedSignal(interfaceManager, {"LastError"});
#define WAIT(time) std::this_thread::sleep_for(time);
#define COMPLETED(retval) m_manager->emitSignal("Completed").onInterface(interfaceManager).withArguments(retval);
DBusRAUCServer::~DBusRAUCServer()
{
if (m_installThread.joinable())
m_installThread.join();
}
/** @brief Create a dbus server on the connection */
DBusRAUCServer::DBusRAUCServer(sdbus::IConnection& connection, std::string primarySlot, const std::map<std::string, velia::system::RAUC::SlotProperties>& status)
: m_manager(sdbus::createObject(connection, objectPathManager))
, m_primarySlot(std::move(primarySlot))
, m_propOperation("idle"s)
, m_propLastError(""s)
, m_propProgress(sdbus::make_struct(0, ""s, 0))
, m_installBehaviour(InstallBehaviour::OK)
, m_operationInProgress(false)
{
for (const auto& [slotName, slotStatus] : status) {
std::map<std::string, sdbus::Variant> m;
for (auto it = slotStatus.begin(); it != slotStatus.end(); ++it) {
// NOTE: I wanted a for-range loop over the map with structured binding [key, val] but this did not compile with clang++.
// According to http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0588r1.html:
// "If a lambda-expression [...] captures a structured binding (explicitly or implicitly), the program is ill-formed."
const auto& k = it->first;
std::visit([&k, &m](auto&& arg) { m.insert(std::make_pair(k, sdbus::Variant(arg))); }, it->second);
}
m_status.emplace_back(slotName, m);
}
// create manager object
m_manager->registerMethod("GetSlotStatus").onInterface(interfaceManager).implementedAs([this]() {
std::lock_guard<std::mutex> lock(m_mtx);
if (!m_operationInProgress) {
return m_status;
} else {
throw sdbus::Error("org.gtk.GDBus.UnmappedGError.Quark._g_2dio_2derror_2dquark.Code30", "Already processing a different method");
}
});
m_manager->registerMethod("GetPrimary").onInterface(interfaceManager).implementedAs([this]() {
std::lock_guard<std::mutex> lock(m_mtx);
if (!m_operationInProgress) {
return m_primarySlot;
} else {
throw sdbus::Error("org.gtk.GDBus.UnmappedGError.Quark._g_2dio_2derror_2dquark.Code30", "Already processing a different method");
}
});
m_manager->registerMethod("InstallBundle").onInterface(interfaceManager).implementedAs(
[this]([[maybe_unused]] const std::string& source, [[maybe_unused]] const std::map<std::string, sdbus::Variant>& args) {
std::lock_guard<std::mutex> lock(m_mtx);
if (!m_operationInProgress) {
m_operationInProgress = true;
if (m_installThread.joinable())
m_installThread.join();
m_installThread = std::thread(&DBusRAUCServer::installBundle, this);
} else {
throw sdbus::Error("org.gtk.GDBus.UnmappedGError.Quark._g_2dio_2derror_2dquark.Code30", "Already processing a different method");
}
});
m_manager->registerProperty("Operation").onInterface(interfaceManager).withGetter([this]() { return m_propOperation; });
m_manager->registerProperty("LastError").onInterface(interfaceManager).withGetter([this]() { return m_propLastError; });
m_manager->registerProperty("Progress").onInterface(interfaceManager).withGetter([this]() { return m_propProgress; });
m_manager->registerSignal("Completed").onInterface(interfaceManager).withParameters<int32_t>();
m_manager->registerMethod("Mark").onInterface(interfaceManager).implementedAs([this](const std::string& how, const std::string& what) {
impl_Mark(how, what);
});
m_manager->finishRegistration();
}
void DBusRAUCServer::installBundleBehaviour(DBusRAUCServer::InstallBehaviour b)
{
m_installBehaviour = b;
}
/** @brief Mimics behaviour of RAUC's InstallBundle DBus method
*
* The behaviour was reverse-engineered from real device by bus monitoring ('busctl monitor de.pengutronix.rauc')
* when issuing 'busctl call de.pengutronix.rauc / de.pengutronix.rauc.Installer InstallBundle sa{sv} "/path/to/source" 0'.
* The reverse-engineered behaviour is implemented by DBusRAUCServer::installBundleOK and DBusRAUCServer::installBundleError.
* */
void DBusRAUCServer::installBundle()
{
switch (m_installBehaviour) {
case InstallBehaviour::FAILURE:
installBundleError();
break;
case InstallBehaviour::OK:
installBundleOK();
break;
}
std::lock_guard<std::mutex> lock(m_mtx);
m_operationInProgress = false;
}
void DBusRAUCServer::installBundleOK()
{
OPERATION("installing");
m_propLastError = "";
m_propProgress = sdbus::make_struct(0, "Installing"s, 1);
m_manager->emitPropertiesChangedSignal(interfaceManager, {"LastError", "Progress"});
PROGRESS(0, "Determining slot states", 2);
WAIT(25ms);
PROGRESS(20, "Determining slot states done.", 2);
PROGRESS(20, "Checking bundle", 2);
PROGRESS(20, "Veryfing signature", 3);
WAIT(25ms);
PROGRESS(40, "Veryfing signature done.", 3);
PROGRESS(40, "Checking bundle done.", 2);
PROGRESS(40, "Loading manifest file", 2);
WAIT(25ms);
PROGRESS(60, "Loading manifest file done.", 2);
PROGRESS(60, "Determining target install group", 2);
WAIT(25ms);
PROGRESS(80, "Determining target install group done.", 2);
PROGRESS(80, "Updating slots", 2);
PROGRESS(80, "Checking slot rootfs.0", 3);
WAIT(25ms);
PROGRESS(85, "Checking slot rootfs.0 done.", 3);
PROGRESS(85, "Copying image to rootfs.0", 3);
WAIT(500ms);
PROGRESS(90, "Copying image to rootfs.0 done.", 3);
PROGRESS(90, "Checking slot cfg.0", 3);
WAIT(25ms);
PROGRESS(95, "Checking slot cfg.0 done.", 3);
PROGRESS(95, "Copying image to cfg.0", 3);
WAIT(50ms);
PROGRESS(100, "Copying image to cfg.0 done.", 3);
PROGRESS(100, "Updating slots done.", 2);
PROGRESS(100, "Installing done.", 1);
COMPLETED(0);
OPERATION("idle");
}
void DBusRAUCServer::installBundleError()
{
OPERATION("installing");
m_propLastError = "";
m_propProgress = sdbus::make_struct(0, "Installing"s, 1);
m_manager->emitPropertiesChangedSignal(interfaceManager, {"LastError", "Progress"});
PROGRESS(0, "Determining slot states", 2);
WAIT(500ms);
PROGRESS(20, "Determining slot states done.", 2);
PROGRESS(20, "Checking bundle", 2);
PROGRESS(40, "Checking bundle failed.", 2);
PROGRESS(100, "Installing failed.", 1);
LAST_ERROR("Failed to download bundle https://10.88.3.11:8000/update.raucb: Transfer failed: error:1408F10B:SSL routines:ssl3_get_record:wrong version number");
COMPLETED(1);
OPERATION("idle");
}