blob: 230a5d6f7bfe993c98f9e42f64c9c575e4a7c900 [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;
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áš Pecka991e4d52021-01-11 10:03:14 +010088 m_manager->finishRegistration();
89}
Tomáš Pecka9cc00942021-01-14 22:45:10 +010090
91void 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 * */
102void 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
117void 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
160void 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}