blob: d022c96350f137d6b93474ada10672ed4fd87966 [file] [log] [blame]
Saqib Khan7254f0e2017-04-10 21:45:37 -05001#include <string>
Adriana Kobylak5ba6b102017-05-19 09:41:27 -05002#include <experimental/filesystem>
Saqib Khan7254f0e2017-04-10 21:45:37 -05003#include <fstream>
Adriana Kobylakb66ac3a2017-03-28 13:33:20 -05004#include <phosphor-logging/log.hpp>
Adriana Kobylakd6a549e2017-05-10 16:23:01 -05005#include <xyz/openbmc_project/Software/Version/server.hpp>
Saqib Khan167601b2017-06-18 23:33:46 -05006#include "version.hpp"
Adriana Kobylak2d8fa222017-03-15 12:34:32 -05007#include "config.h"
8#include "item_updater.hpp"
Saqib Khana8ade7e2017-04-12 10:27:56 -05009#include "activation.hpp"
Michael Tritz60bc20f2017-07-29 23:32:21 -050010#include "serialize.hpp"
Adriana Kobylak2d8fa222017-03-15 12:34:32 -050011
12namespace openpower
13{
14namespace software
15{
Adriana Kobylakbefe5ce2017-04-05 15:57:44 -050016namespace updater
Adriana Kobylak2d8fa222017-03-15 12:34:32 -050017{
18
Saqib Khana8ade7e2017-04-12 10:27:56 -050019// When you see server:: you know we're referencing our base class
20namespace server = sdbusplus::xyz::openbmc_project::Software::server;
Adriana Kobylak5ba6b102017-05-19 09:41:27 -050021namespace fs = std::experimental::filesystem;
Saqib Khana8ade7e2017-04-12 10:27:56 -050022
Adriana Kobylakb66ac3a2017-03-28 13:33:20 -050023using namespace phosphor::logging;
24
Saqib Khana8ade7e2017-04-12 10:27:56 -050025constexpr auto squashFSImage = "pnor.xz.squashfs";
26
Adriana Kobylakd6a549e2017-05-10 16:23:01 -050027void ItemUpdater::createActivation(sdbusplus::message::message& m)
Adriana Kobylak2d8fa222017-03-15 12:34:32 -050028{
Patrick Williamse4290942017-06-16 05:43:08 -050029 using SVersion = server::Version;
30 using VersionPurpose = SVersion::VersionPurpose;
31 namespace msg = sdbusplus::message;
32 namespace variant_ns = msg::variant_ns;
33
Adriana Kobylakd6a549e2017-05-10 16:23:01 -050034 sdbusplus::message::object_path objPath;
35 std::map<std::string,
Patrick Williamse4290942017-06-16 05:43:08 -050036 std::map<std::string, msg::variant<std::string>>> interfaces;
Adriana Kobylakd6a549e2017-05-10 16:23:01 -050037 m.read(objPath, interfaces);
Patrick Williamse4290942017-06-16 05:43:08 -050038
Adriana Kobylakd6a549e2017-05-10 16:23:01 -050039 std::string path(std::move(objPath));
Adriana Kobylak5ba6b102017-05-19 09:41:27 -050040 std::string filePath;
Patrick Williamse4290942017-06-16 05:43:08 -050041 auto purpose = VersionPurpose::Unknown;
Saqib Khance148702017-06-11 12:01:58 -050042 std::string version;
Adriana Kobylak5ba6b102017-05-19 09:41:27 -050043
Adriana Kobylakd6a549e2017-05-10 16:23:01 -050044 for (const auto& intf : interfaces)
Adriana Kobylakb66ac3a2017-03-28 13:33:20 -050045 {
Adriana Kobylakd6a549e2017-05-10 16:23:01 -050046 if (intf.first == VERSION_IFACE)
47 {
48 for (const auto& property : intf.second)
49 {
50 if (property.first == "Purpose")
51 {
52 // Only process the Host and System images
Patrick Williamse4290942017-06-16 05:43:08 -050053 auto value = SVersion::convertVersionPurposeFromString(
54 variant_ns::get<std::string>(property.second));
55
56 if (value == VersionPurpose::Host ||
57 value == VersionPurpose::System)
Adriana Kobylakd6a549e2017-05-10 16:23:01 -050058 {
Saqib Khance148702017-06-11 12:01:58 -050059 purpose = value;
Adriana Kobylakd6a549e2017-05-10 16:23:01 -050060 }
61 }
Saqib Khance148702017-06-11 12:01:58 -050062 else if (property.first == "Version")
63 {
Patrick Williamse4290942017-06-16 05:43:08 -050064 version = variant_ns::get<std::string>(property.second);
Saqib Khance148702017-06-11 12:01:58 -050065 }
Adriana Kobylakd6a549e2017-05-10 16:23:01 -050066 }
67 }
Adriana Kobylak5ba6b102017-05-19 09:41:27 -050068 else if (intf.first == FILEPATH_IFACE)
69 {
70 for (const auto& property : intf.second)
71 {
72 if (property.first == "Path")
73 {
Patrick Williamse4290942017-06-16 05:43:08 -050074 filePath = variant_ns::get<std::string>(property.second);
Adriana Kobylak5ba6b102017-05-19 09:41:27 -050075 }
76 }
77 }
78 }
Patrick Williamse4290942017-06-16 05:43:08 -050079 if ((filePath.empty()) || (purpose == VersionPurpose::Unknown))
Adriana Kobylak5ba6b102017-05-19 09:41:27 -050080 {
81 return;
Adriana Kobylakb66ac3a2017-03-28 13:33:20 -050082 }
83
Adriana Kobylakd6a549e2017-05-10 16:23:01 -050084 // Version id is the last item in the path
85 auto pos = path.rfind("/");
86 if (pos == std::string::npos)
Adriana Kobylakb66ac3a2017-03-28 13:33:20 -050087 {
Adriana Kobylakd6a549e2017-05-10 16:23:01 -050088 log<level::ERR>("No version id found in object path",
89 entry("OBJPATH=%s", path));
90 return;
91 }
92
93 auto versionId = path.substr(pos + 1);
94
95 if (activations.find(versionId) == activations.end())
96 {
97 // Determine the Activation state by processing the given image dir.
98 auto activationState = server::Activation::Activations::Invalid;
Adriana Kobylak5ba6b102017-05-19 09:41:27 -050099 if (ItemUpdater::validateSquashFSImage(filePath) == 0)
Adriana Kobylakb66ac3a2017-03-28 13:33:20 -0500100 {
Adriana Kobylakd6a549e2017-05-10 16:23:01 -0500101 activationState = server::Activation::Activations::Ready;
Adriana Kobylakb66ac3a2017-03-28 13:33:20 -0500102 }
103
Adriana Kobylak5ba6b102017-05-19 09:41:27 -0500104 fs::path manifestPath(filePath);
105 manifestPath /= MANIFEST_FILE;
Saqib Khan167601b2017-06-18 23:33:46 -0500106 std::string extendedVersion = (Version::getValue(manifestPath.string(),
107 std::map<std::string, std::string>
108 {{"extended_version", ""}})).begin()->second;
Adriana Kobylakd6a549e2017-05-10 16:23:01 -0500109 activations.insert(std::make_pair(
110 versionId,
111 std::make_unique<Activation>(
112 bus,
113 path,
Saqib Khan81bac882017-06-08 12:17:01 -0500114 *this,
Adriana Kobylakd6a549e2017-05-10 16:23:01 -0500115 versionId,
116 extendedVersion,
117 activationState)));
Saqib Khance148702017-06-11 12:01:58 -0500118 versions.insert(std::make_pair(
119 versionId,
120 std::make_unique<Version>(
121 bus,
122 path,
123 version,
124 purpose,
Leonel Gonzalez9c8adfa2017-07-12 11:08:40 -0500125 filePath,
126 *this)));
Saqib Khan00044f42017-07-10 17:24:43 -0500127 }
Saqib Khan3fb2d172017-08-07 12:14:03 -0500128 else
129 {
130 log<level::INFO>("Software Object with the same version already exists",
131 entry("VERSION_ID=%s", versionId));
132 }
Patrick Williams3accb322017-05-30 16:29:52 -0500133 return;
Adriana Kobylak2d8fa222017-03-15 12:34:32 -0500134}
135
Saqib Khan167601b2017-06-18 23:33:46 -0500136void ItemUpdater::processPNORImage()
Saqib Khan7254f0e2017-04-10 21:45:37 -0500137{
Saqib Khan4c5d7442017-07-18 00:43:52 -0500138 // Read pnor.toc from folders under /media/
139 // to get Active Software Versions.
140 for(const auto& iter : fs::directory_iterator(MEDIA_DIR))
141 {
142 auto activationState = server::Activation::Activations::Active;
143
144 static const auto PNOR_RO_PREFIX_LEN = strlen(PNOR_RO_PREFIX);
145
146 // Check if the PNOR_RO_PREFIX is the prefix of the iter.path
147 if (0 == iter.path().native().compare(0, PNOR_RO_PREFIX_LEN,
148 PNOR_RO_PREFIX))
149 {
150 auto pnorTOC = iter.path() / PNOR_TOC_FILE;
151 if (!fs::is_regular_file(pnorTOC))
152 {
153 log<level::ERR>("Failed to read pnorTOC\n",
154 entry("FileName=%s", pnorTOC.string()));
155 activationState = server::Activation::Activations::Invalid;
156 }
157 auto keyValues =
158 Version::getValue(pnorTOC,
159 {{ "version", "" },
160 { "extended_version", "" } });
161 auto& version = keyValues.at("version");
162 if (version.empty())
163 {
164 log<level::ERR>("Failed to read version from pnorTOC",
165 entry("FILENAME=%s", pnorTOC.string()));
166 activationState = server::Activation::Activations::Invalid;
167 }
168
169 auto& extendedVersion = keyValues.at("extended_version");
170 if (extendedVersion.empty())
171 {
172 log<level::ERR>("Failed to read extendedVersion from pnorTOC",
173 entry("FILENAME=%s", pnorTOC.string()));
174 activationState = server::Activation::Activations::Invalid;
175 }
176
177 // The versionId is extracted from the path
178 // for example /media/pnor-ro-2a1022fe
179 auto id = iter.path().native().substr(PNOR_RO_PREFIX_LEN);
180 auto purpose = server::Version::VersionPurpose::Host;
181 auto path = fs::path(SOFTWARE_OBJPATH) / id;
182
183 // Create Activation instance for this version.
184 activations.insert(std::make_pair(
185 id,
186 std::make_unique<Activation>(
187 bus,
188 path,
189 *this,
190 id,
191 extendedVersion,
192 activationState)));
193
194 // If Active, create RedundancyPriority instance for this version.
195 if (activationState == server::Activation::Activations::Active)
196 {
Michael Tritz60bc20f2017-07-29 23:32:21 -0500197 if(fs::is_regular_file(PERSIST_DIR + id))
Saqib Khan4c5d7442017-07-18 00:43:52 -0500198 {
Michael Tritz60bc20f2017-07-29 23:32:21 -0500199 uint8_t priority;
200 restoreFromFile(id, &priority);
201 activations.find(id)->second->redundancyPriority =
202 std::make_unique<RedundancyPriority>(
203 bus,
204 path,
205 *(activations.find(id)->second),
206 priority);
Saqib Khan4c5d7442017-07-18 00:43:52 -0500207 }
Michael Tritz60bc20f2017-07-29 23:32:21 -0500208 else
209 {
210 activations.find(id)->second->activation(
211 server::Activation::Activations::Invalid);
212 }
213
Saqib Khan4c5d7442017-07-18 00:43:52 -0500214 }
215
216 // Create Version instance for this version.
217 versions.insert(std::make_pair(
218 id,
219 std::make_unique<Version>(
220 bus,
221 path,
222 version,
223 purpose,
224 "",
225 *this)));
226 }
227 }
Saqib Khan167601b2017-06-18 23:33:46 -0500228 return;
Saqib Khan7254f0e2017-04-10 21:45:37 -0500229}
230
Adriana Kobylak5ba6b102017-05-19 09:41:27 -0500231int ItemUpdater::validateSquashFSImage(const std::string& filePath)
Saqib Khana8ade7e2017-04-12 10:27:56 -0500232{
Saqib Khan4c5d7442017-07-18 00:43:52 -0500233 auto file = fs::path(filePath) / squashFSImage;
234 if (fs::is_regular_file(file))
Saqib Khana8ade7e2017-04-12 10:27:56 -0500235 {
236 return 0;
237 }
238 else
239 {
240 log<level::ERR>("Failed to find the SquashFS image.");
241 return -1;
242 }
243}
244
Leonel Gonzalez9c8adfa2017-07-12 11:08:40 -0500245void ItemUpdater::removeReadOnlyPartition(std::string versionId)
Michael Tritzdd961b62017-05-17 14:07:03 -0500246{
Leonel Gonzalez9c8adfa2017-07-12 11:08:40 -0500247 auto serviceFile = "obmc-flash-bios-ubiumount-ro@" + versionId +
248 ".service";
249
250 // Remove the read-only partitions.
251 auto method = bus.new_method_call(
252 SYSTEMD_BUSNAME,
253 SYSTEMD_PATH,
254 SYSTEMD_INTERFACE,
255 "StartUnit");
256 method.append(serviceFile, "replace");
257 bus.call_noreply(method);
258}
259
260void ItemUpdater::removeReadWritePartition(std::string versionId)
261{
262 auto serviceFile = "obmc-flash-bios-ubiumount-rw@" + versionId +
Michael Tritzdd961b62017-05-17 14:07:03 -0500263 ".service";
264
265 // Remove the read-write partitions.
Adriana Kobylakd6a549e2017-05-10 16:23:01 -0500266 auto method = bus.new_method_call(
Michael Tritzdd961b62017-05-17 14:07:03 -0500267 SYSTEMD_BUSNAME,
268 SYSTEMD_PATH,
269 SYSTEMD_INTERFACE,
270 "StartUnit");
271 method.append(serviceFile, "replace");
Adriana Kobylakd6a549e2017-05-10 16:23:01 -0500272 bus.call_noreply(method);
Leonel Gonzalez9c8adfa2017-07-12 11:08:40 -0500273}
Michael Tritzdd961b62017-05-17 14:07:03 -0500274
Leonel Gonzalez9c8adfa2017-07-12 11:08:40 -0500275void ItemUpdater::removePreservedPartition()
276{
Michael Tritzdd961b62017-05-17 14:07:03 -0500277 // Remove the preserved partition.
Adriana Kobylakd6a549e2017-05-10 16:23:01 -0500278 auto method = bus.new_method_call(
Michael Tritzdd961b62017-05-17 14:07:03 -0500279 SYSTEMD_BUSNAME,
280 SYSTEMD_PATH,
281 SYSTEMD_INTERFACE,
282 "StartUnit");
283 method.append("obmc-flash-bios-ubiumount-prsv.service", "replace");
Adriana Kobylakd6a549e2017-05-10 16:23:01 -0500284 bus.call_noreply(method);
Michael Tritzdd961b62017-05-17 14:07:03 -0500285
286 return;
287}
288
Leonel Gonzalez9c8adfa2017-07-12 11:08:40 -0500289void ItemUpdater::reset()
290{
291 for(const auto& it : activations)
292 {
293 removeReadWritePartition(it.first);
Michael Tritz60bc20f2017-07-29 23:32:21 -0500294 removeFile(it.first);
Leonel Gonzalez9c8adfa2017-07-12 11:08:40 -0500295 }
296 removePreservedPartition();
297 return;
298}
299
Saqib Khanb8e7f312017-08-12 10:24:10 -0500300void ItemUpdater::freePriority(uint8_t value, const std::string& versionId)
Saqib Khan81bac882017-06-08 12:17:01 -0500301{
302 //TODO openbmc/openbmc#1896 Improve the performance of this function
303 for (const auto& intf : activations)
304 {
305 if(intf.second->redundancyPriority)
306 {
Saqib Khanb8e7f312017-08-12 10:24:10 -0500307 if (intf.second->redundancyPriority.get()->priority() == value &&
308 intf.second->versionId != versionId)
Saqib Khan81bac882017-06-08 12:17:01 -0500309 {
310 intf.second->redundancyPriority.get()->priority(value+1);
311 }
312 }
313 }
314}
315
Saqib Khan2af5c492017-07-17 16:15:13 -0500316bool ItemUpdater::isLowestPriority(uint8_t value)
317{
318 for (const auto& intf : activations)
319 {
320 if(intf.second->redundancyPriority)
321 {
322 if (intf.second->redundancyPriority.get()->priority() < value)
323 {
324 return false;
325 }
326 }
327 }
328 return true;
329}
330
Leonel Gonzalez9c8adfa2017-07-12 11:08:40 -0500331void ItemUpdater::erase(std::string entryId)
332{
333 // Removing partitions
334 removeReadWritePartition(entryId);
335 removeReadOnlyPartition(entryId);
336
337 // Removing entry in versions map
338 auto it = versions.find(entryId);
339 if (it == versions.end())
340 {
341 log<level::ERR>(("Error: Failed to find version " + entryId + \
342 " in item updater versions map." \
343 " Unable to remove.").c_str());
344 return;
345 }
346 versions.erase(entryId);
347
348 // Removing entry in activations map
349 auto ita = activations.find(entryId);
350 if (ita == activations.end())
351 {
352 log<level::ERR>(("Error: Failed to find version " + entryId + \
353 " in item updater activations map." \
354 " Unable to remove.").c_str());
355 return;
356 }
357 activations.erase(entryId);
Michael Tritz60bc20f2017-07-29 23:32:21 -0500358 removeFile(entryId);
Leonel Gonzalez9c8adfa2017-07-12 11:08:40 -0500359}
360
Adriana Kobylakbefe5ce2017-04-05 15:57:44 -0500361} // namespace updater
Adriana Kobylak2d8fa222017-03-15 12:34:32 -0500362} // namespace software
363} // namespace openpower