blob: 94930c33052f35f7a635369e86c524eb3b92fab4 [file] [log] [blame]
Tomáš Pecka991e4d52021-01-11 10:03:14 +01001#include <sdbus-c++/sdbus-c++.h>
Tomáš Pecka9cc00942021-01-14 22:45:10 +01002#include <thread>
Tomáš Pecka991e4d52021-01-11 10:03:14 +01003#include "dbus_rauc_server.h"
4#include "utils/log-init.h"
5
6using namespace std::literals;
7
8namespace {
9const std::string interfaceManager = "de.pengutronix.rauc.Installer";
10const std::string objectPathManager = "/";
11}
12
Tomáš Pecka9cc00942021-01-14 22:45:10 +010013#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
29DBusRAUCServer::~DBusRAUCServer()
30{
31 if (m_installThread.joinable())
32 m_installThread.join();
33}
34
Tomáš Pecka991e4d52021-01-11 10:03:14 +010035/** @brief Create a dbus server on the connection */
36DBusRAUCServer::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áš Pecka9cc00942021-01-14 22:45:10 +010039 , 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áš Pecka991e4d52021-01-11 10:03:14 +010044{
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áš Pecka9cc00942021-01-14 22:45:10 +010059 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;
Tomáš Peckaf614c1b2021-02-17 19:25:13 +010079
80 if (m_installThread.joinable())
81 m_installThread.join();
Tomáš Pecka9cc00942021-01-14 22:45:10 +010082 m_installThread = std::thread(&DBusRAUCServer::installBundle, this);
83 } else {
84 throw sdbus::Error("org.gtk.GDBus.UnmappedGError.Quark._g_2dio_2derror_2dquark.Code30", "Already processing a different method");
85 }
86 });
87 m_manager->registerProperty("Operation").onInterface(interfaceManager).withGetter([this]() { return m_propOperation; });
88 m_manager->registerProperty("LastError").onInterface(interfaceManager).withGetter([this]() { return m_propLastError; });
89 m_manager->registerProperty("Progress").onInterface(interfaceManager).withGetter([this]() { return m_propProgress; });
90 m_manager->registerSignal("Completed").onInterface(interfaceManager).withParameters<int32_t>();
Tomáš Pecka991e4d52021-01-11 10:03:14 +010091 m_manager->finishRegistration();
92}
Tomáš Pecka9cc00942021-01-14 22:45:10 +010093
94void DBusRAUCServer::installBundleBehaviour(DBusRAUCServer::InstallBehaviour b)
95{
96 m_installBehaviour = b;
97}
98
99/** @brief Mimics behaviour of RAUC's InstallBundle DBus method
100 *
101 * The behaviour was reverse-engineered from real device by bus monitoring ('busctl monitor de.pengutronix.rauc')
102 * when issuing 'busctl call de.pengutronix.rauc / de.pengutronix.rauc.Installer InstallBundle sa{sv} "/path/to/source" 0'.
103 * The reverse-engineered behaviour is implemented by DBusRAUCServer::installBundleOK and DBusRAUCServer::installBundleError.
104 * */
105void DBusRAUCServer::installBundle()
106{
107 switch (m_installBehaviour) {
108 case InstallBehaviour::FAILURE:
109 installBundleError();
110 break;
111 case InstallBehaviour::OK:
112 installBundleOK();
113 break;
114 }
115
116 std::lock_guard<std::mutex> lock(m_mtx);
117 m_operationInProgress = false;
118}
119
120void DBusRAUCServer::installBundleOK()
121{
122 OPERATION("installing");
123
124 m_propLastError = "";
125 m_propProgress = sdbus::make_struct(0, "Installing"s, 1);
126 m_manager->emitPropertiesChangedSignal(interfaceManager, {"LastError", "Progress"});
127
128 PROGRESS(0, "Determining slot states", 2);
129 WAIT(25ms);
130 PROGRESS(20, "Determining slot states done.", 2);
131 PROGRESS(20, "Checking bundle", 2);
132 PROGRESS(20, "Veryfing signature", 3);
133 WAIT(25ms);
134 PROGRESS(40, "Veryfing signature done.", 3);
135 PROGRESS(40, "Checking bundle done.", 2);
136 PROGRESS(40, "Loading manifest file", 2);
137 WAIT(25ms);
138 PROGRESS(60, "Loading manifest file done.", 2);
139 PROGRESS(60, "Determining target install group", 2);
140 WAIT(25ms);
141 PROGRESS(80, "Determining target install group done.", 2);
142 PROGRESS(80, "Updating slots", 2);
143 PROGRESS(80, "Checking slot rootfs.0", 3);
144 WAIT(25ms);
145 PROGRESS(85, "Checking slot rootfs.0 done.", 3);
146 PROGRESS(85, "Copying image to rootfs.0", 3);
147 WAIT(500ms);
148 PROGRESS(90, "Copying image to rootfs.0 done.", 3);
149 PROGRESS(90, "Checking slot cfg.0", 3);
150 WAIT(25ms);
151 PROGRESS(95, "Checking slot cfg.0 done.", 3);
152 PROGRESS(95, "Copying image to cfg.0", 3);
153 WAIT(50ms);
154 PROGRESS(100, "Copying image to cfg.0 done.", 3);
155 PROGRESS(100, "Updating slots done.", 2);
156 PROGRESS(100, "Installing done.", 1);
157
158 COMPLETED(0);
159
160 OPERATION("idle");
161}
162
163void DBusRAUCServer::installBundleError()
164{
165 OPERATION("installing");
166
167 m_propLastError = "";
168 m_propProgress = sdbus::make_struct(0, "Installing"s, 1);
169 m_manager->emitPropertiesChangedSignal(interfaceManager, {"LastError", "Progress"});
170
171 PROGRESS(0, "Determining slot states", 2);
172 WAIT(25ms);
173 PROGRESS(20, "Determining slot states done.", 2);
174 PROGRESS(20, "Checking bundle", 2);
175 PROGRESS(40, "Checking bundle failed.", 2);
176 PROGRESS(100, "Installing failed.", 1);
177
178 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");
179 COMPLETED(1);
180
181 OPERATION("idle");
182}