Tomáš Pecka | 991e4d5 | 2021-01-11 10:03:14 +0100 | [diff] [blame] | 1 | #include <sdbus-c++/sdbus-c++.h> |
Tomáš Pecka | 9cc0094 | 2021-01-14 22:45:10 +0100 | [diff] [blame^] | 2 | #include <thread> |
Tomáš Pecka | 991e4d5 | 2021-01-11 10:03:14 +0100 | [diff] [blame] | 3 | #include "dbus_rauc_server.h" |
| 4 | #include "utils/log-init.h" |
| 5 | |
| 6 | using namespace std::literals; |
| 7 | |
| 8 | namespace { |
| 9 | const std::string interfaceManager = "de.pengutronix.rauc.Installer"; |
| 10 | const std::string objectPathManager = "/"; |
| 11 | } |
| 12 | |
Tomáš Pecka | 9cc0094 | 2021-01-14 22:45:10 +0100 | [diff] [blame^] | 13 | #define PROGRESS(perc, msg, depth) \ |
| 14 | m_propProgress = sdbus::make_struct(perc, std::string(msg), depth); \ |
| 15 | m_manager->emitPropertiesChangedSignal(interfaceManager, {"Progress"}); |
| 16 | |
| 17 | #define OPERATION(op) \ |
| 18 | m_propOperation = op; \ |
| 19 | m_manager->emitPropertiesChangedSignal(interfaceManager, {"Operation"}); |
| 20 | |
| 21 | #define LAST_ERROR(msg) \ |
| 22 | m_propLastError = msg; \ |
| 23 | m_manager->emitPropertiesChangedSignal(interfaceManager, {"LastError"}); |
| 24 | |
| 25 | #define WAIT(time) std::this_thread::sleep_for(time); |
| 26 | |
| 27 | #define COMPLETED(retval) m_manager->emitSignal("Completed").onInterface(interfaceManager).withArguments(retval); |
| 28 | |
| 29 | DBusRAUCServer::~DBusRAUCServer() |
| 30 | { |
| 31 | if (m_installThread.joinable()) |
| 32 | m_installThread.join(); |
| 33 | } |
| 34 | |
Tomáš Pecka | 991e4d5 | 2021-01-11 10:03:14 +0100 | [diff] [blame] | 35 | /** @brief Create a dbus server on the connection */ |
| 36 | DBusRAUCServer::DBusRAUCServer(sdbus::IConnection& connection, std::string primarySlot, const std::map<std::string, velia::system::RAUC::SlotProperties>& status) |
| 37 | : m_manager(sdbus::createObject(connection, objectPathManager)) |
| 38 | , m_primarySlot(std::move(primarySlot)) |
Tomáš Pecka | 9cc0094 | 2021-01-14 22:45:10 +0100 | [diff] [blame^] | 39 | , m_propOperation("idle"s) |
| 40 | , m_propLastError(""s) |
| 41 | , m_propProgress(sdbus::make_struct(0, ""s, 0)) |
| 42 | , m_installBehaviour(InstallBehaviour::OK) |
| 43 | , m_operationInProgress(false) |
Tomáš Pecka | 991e4d5 | 2021-01-11 10:03:14 +0100 | [diff] [blame] | 44 | { |
| 45 | for (const auto& [slotName, slotStatus] : status) { |
| 46 | std::map<std::string, sdbus::Variant> m; |
| 47 | for (auto it = slotStatus.begin(); it != slotStatus.end(); ++it) { |
| 48 | // NOTE: I wanted a for-range loop over the map with structured binding [key, val] but this did not compile with clang++. |
| 49 | // According to http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0588r1.html: |
| 50 | // "If a lambda-expression [...] captures a structured binding (explicitly or implicitly), the program is ill-formed." |
| 51 | |
| 52 | const auto& k = it->first; |
| 53 | std::visit([&k, &m](auto&& arg) { m.insert(std::make_pair(k, sdbus::Variant(arg))); }, it->second); |
| 54 | } |
| 55 | m_status.emplace_back(slotName, m); |
| 56 | } |
| 57 | |
| 58 | // create manager object |
Tomáš Pecka | 9cc0094 | 2021-01-14 22:45:10 +0100 | [diff] [blame^] | 59 | m_manager->registerMethod("GetSlotStatus").onInterface(interfaceManager).implementedAs([this]() { |
| 60 | std::lock_guard<std::mutex> lock(m_mtx); |
| 61 | if (!m_operationInProgress) { |
| 62 | return m_status; |
| 63 | } else { |
| 64 | throw sdbus::Error("org.gtk.GDBus.UnmappedGError.Quark._g_2dio_2derror_2dquark.Code30", "Already processing a different method"); |
| 65 | } |
| 66 | }); |
| 67 | m_manager->registerMethod("GetPrimary").onInterface(interfaceManager).implementedAs([this]() { |
| 68 | std::lock_guard<std::mutex> lock(m_mtx); |
| 69 | if (!m_operationInProgress) { |
| 70 | return m_primarySlot; |
| 71 | } else { |
| 72 | throw sdbus::Error("org.gtk.GDBus.UnmappedGError.Quark._g_2dio_2derror_2dquark.Code30", "Already processing a different method"); |
| 73 | } |
| 74 | }); |
| 75 | m_manager->registerMethod("InstallBundle").onInterface(interfaceManager).implementedAs([this]([[maybe_unused]] const std::string& source, [[maybe_unused]] const std::map<std::string, sdbus::Variant>& args) { |
| 76 | std::lock_guard<std::mutex> lock(m_mtx); |
| 77 | if (!m_operationInProgress) { |
| 78 | m_operationInProgress = true; |
| 79 | m_installThread = std::thread(&DBusRAUCServer::installBundle, this); |
| 80 | } else { |
| 81 | throw sdbus::Error("org.gtk.GDBus.UnmappedGError.Quark._g_2dio_2derror_2dquark.Code30", "Already processing a different method"); |
| 82 | } |
| 83 | }); |
| 84 | m_manager->registerProperty("Operation").onInterface(interfaceManager).withGetter([this]() { return m_propOperation; }); |
| 85 | m_manager->registerProperty("LastError").onInterface(interfaceManager).withGetter([this]() { return m_propLastError; }); |
| 86 | m_manager->registerProperty("Progress").onInterface(interfaceManager).withGetter([this]() { return m_propProgress; }); |
| 87 | m_manager->registerSignal("Completed").onInterface(interfaceManager).withParameters<int32_t>(); |
Tomáš Pecka | 991e4d5 | 2021-01-11 10:03:14 +0100 | [diff] [blame] | 88 | m_manager->finishRegistration(); |
| 89 | } |
Tomáš Pecka | 9cc0094 | 2021-01-14 22:45:10 +0100 | [diff] [blame^] | 90 | |
| 91 | void DBusRAUCServer::installBundleBehaviour(DBusRAUCServer::InstallBehaviour b) |
| 92 | { |
| 93 | m_installBehaviour = b; |
| 94 | } |
| 95 | |
| 96 | /** @brief Mimics behaviour of RAUC's InstallBundle DBus method |
| 97 | * |
| 98 | * The behaviour was reverse-engineered from real device by bus monitoring ('busctl monitor de.pengutronix.rauc') |
| 99 | * when issuing 'busctl call de.pengutronix.rauc / de.pengutronix.rauc.Installer InstallBundle sa{sv} "/path/to/source" 0'. |
| 100 | * The reverse-engineered behaviour is implemented by DBusRAUCServer::installBundleOK and DBusRAUCServer::installBundleError. |
| 101 | * */ |
| 102 | void DBusRAUCServer::installBundle() |
| 103 | { |
| 104 | switch (m_installBehaviour) { |
| 105 | case InstallBehaviour::FAILURE: |
| 106 | installBundleError(); |
| 107 | break; |
| 108 | case InstallBehaviour::OK: |
| 109 | installBundleOK(); |
| 110 | break; |
| 111 | } |
| 112 | |
| 113 | std::lock_guard<std::mutex> lock(m_mtx); |
| 114 | m_operationInProgress = false; |
| 115 | } |
| 116 | |
| 117 | void DBusRAUCServer::installBundleOK() |
| 118 | { |
| 119 | OPERATION("installing"); |
| 120 | |
| 121 | m_propLastError = ""; |
| 122 | m_propProgress = sdbus::make_struct(0, "Installing"s, 1); |
| 123 | m_manager->emitPropertiesChangedSignal(interfaceManager, {"LastError", "Progress"}); |
| 124 | |
| 125 | PROGRESS(0, "Determining slot states", 2); |
| 126 | WAIT(25ms); |
| 127 | PROGRESS(20, "Determining slot states done.", 2); |
| 128 | PROGRESS(20, "Checking bundle", 2); |
| 129 | PROGRESS(20, "Veryfing signature", 3); |
| 130 | WAIT(25ms); |
| 131 | PROGRESS(40, "Veryfing signature done.", 3); |
| 132 | PROGRESS(40, "Checking bundle done.", 2); |
| 133 | PROGRESS(40, "Loading manifest file", 2); |
| 134 | WAIT(25ms); |
| 135 | PROGRESS(60, "Loading manifest file done.", 2); |
| 136 | PROGRESS(60, "Determining target install group", 2); |
| 137 | WAIT(25ms); |
| 138 | PROGRESS(80, "Determining target install group done.", 2); |
| 139 | PROGRESS(80, "Updating slots", 2); |
| 140 | PROGRESS(80, "Checking slot rootfs.0", 3); |
| 141 | WAIT(25ms); |
| 142 | PROGRESS(85, "Checking slot rootfs.0 done.", 3); |
| 143 | PROGRESS(85, "Copying image to rootfs.0", 3); |
| 144 | WAIT(500ms); |
| 145 | PROGRESS(90, "Copying image to rootfs.0 done.", 3); |
| 146 | PROGRESS(90, "Checking slot cfg.0", 3); |
| 147 | WAIT(25ms); |
| 148 | PROGRESS(95, "Checking slot cfg.0 done.", 3); |
| 149 | PROGRESS(95, "Copying image to cfg.0", 3); |
| 150 | WAIT(50ms); |
| 151 | PROGRESS(100, "Copying image to cfg.0 done.", 3); |
| 152 | PROGRESS(100, "Updating slots done.", 2); |
| 153 | PROGRESS(100, "Installing done.", 1); |
| 154 | |
| 155 | COMPLETED(0); |
| 156 | |
| 157 | OPERATION("idle"); |
| 158 | } |
| 159 | |
| 160 | void DBusRAUCServer::installBundleError() |
| 161 | { |
| 162 | OPERATION("installing"); |
| 163 | |
| 164 | m_propLastError = ""; |
| 165 | m_propProgress = sdbus::make_struct(0, "Installing"s, 1); |
| 166 | m_manager->emitPropertiesChangedSignal(interfaceManager, {"LastError", "Progress"}); |
| 167 | |
| 168 | PROGRESS(0, "Determining slot states", 2); |
| 169 | WAIT(25ms); |
| 170 | PROGRESS(20, "Determining slot states done.", 2); |
| 171 | PROGRESS(20, "Checking bundle", 2); |
| 172 | PROGRESS(40, "Checking bundle failed.", 2); |
| 173 | PROGRESS(100, "Installing failed.", 1); |
| 174 | |
| 175 | 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"); |
| 176 | COMPLETED(1); |
| 177 | |
| 178 | OPERATION("idle"); |
| 179 | } |