blob: d23075582c0c3a6ba4aa8cb25f2daf502e056c0f [file] [log] [blame]
Saqib Khan35e83f32017-05-22 11:37:32 -05001#include <fstream>
Gunnar Millsec1b41c2017-05-02 12:20:36 -05002#include <string>
Gunnar Mills2ce7da22017-05-04 15:37:56 -05003#include <phosphor-logging/log.hpp>
Gunnar Millsec1b41c2017-05-02 12:20:36 -05004#include "config.h"
Gunnar Mills2ce7da22017-05-04 15:37:56 -05005#include "item_updater.hpp"
6#include "xyz/openbmc_project/Software/Version/server.hpp"
Saqib Khan35e83f32017-05-22 11:37:32 -05007#include <experimental/filesystem>
Saqib Khan705f1bf2017-06-09 23:58:38 -05008#include "version.hpp"
Saqib Khan5d532672017-08-09 10:44:50 -05009#include "serialize.hpp"
Gunnar Millsec1b41c2017-05-02 12:20:36 -050010
11namespace phosphor
12{
13namespace software
14{
15namespace updater
16{
17
Gunnar Mills2ce7da22017-05-04 15:37:56 -050018// When you see server:: you know we're referencing our base class
19namespace server = sdbusplus::xyz::openbmc_project::Software::server;
Michael Tritz0129d922017-08-10 19:33:46 -050020namespace control = sdbusplus::xyz::openbmc_project::Control::server;
Gunnar Mills2ce7da22017-05-04 15:37:56 -050021
22using namespace phosphor::logging;
Saqib Khan35e83f32017-05-22 11:37:32 -050023namespace fs = std::experimental::filesystem;
24
Gunnar Mills9a782242017-08-22 16:23:15 -050025const std::vector<std::string> bmcImages = { "image-kernel",
26 "image-rofs",
27 "image-rwfs",
28 "image-u-boot" };
Gunnar Mills2ce7da22017-05-04 15:37:56 -050029
Patrick Williamse75d10f2017-05-30 16:56:32 -050030void ItemUpdater::createActivation(sdbusplus::message::message& msg)
Gunnar Millsec1b41c2017-05-02 12:20:36 -050031{
Saqib Khan84a0e692017-06-28 17:27:01 -050032
33 using SVersion = server::Version;
34 using VersionPurpose = SVersion::VersionPurpose;
Gunnar Mills9a782242017-08-22 16:23:15 -050035 using VersionClass = phosphor::software::manager::Version;
Saqib Khan84a0e692017-06-28 17:27:01 -050036 namespace mesg = sdbusplus::message;
37 namespace variant_ns = mesg::variant_ns;
38
39 mesg::object_path objPath;
40 auto purpose = VersionPurpose::Unknown;
Saqib Khan705f1bf2017-06-09 23:58:38 -050041 std::string version;
Gunnar Mills2ce7da22017-05-04 15:37:56 -050042 std::map<std::string,
Patrick Williamse75d10f2017-05-30 16:56:32 -050043 std::map<std::string,
Saqib Khan84a0e692017-06-28 17:27:01 -050044 mesg::variant<std::string>>> interfaces;
Patrick Williamse75d10f2017-05-30 16:56:32 -050045 msg.read(objPath, interfaces);
Gunnar Mills2ce7da22017-05-04 15:37:56 -050046 std::string path(std::move(objPath));
Saqib Khan19177d32017-06-20 08:11:49 -050047 std::string filePath;
Gunnar Mills2ce7da22017-05-04 15:37:56 -050048
49 for (const auto& intf : interfaces)
50 {
Saqib Khan705f1bf2017-06-09 23:58:38 -050051 if (intf.first == VERSION_IFACE)
Gunnar Mills2ce7da22017-05-04 15:37:56 -050052 {
Saqib Khan705f1bf2017-06-09 23:58:38 -050053 for (const auto& property : intf.second)
Gunnar Mills2ce7da22017-05-04 15:37:56 -050054 {
Saqib Khan705f1bf2017-06-09 23:58:38 -050055 if (property.first == "Purpose")
Gunnar Mills2ce7da22017-05-04 15:37:56 -050056 {
Saqib Khan84a0e692017-06-28 17:27:01 -050057 auto value = SVersion::convertVersionPurposeFromString(
Gunnar Mills9a782242017-08-22 16:23:15 -050058 variant_ns::get<std::string>(property.second));
Saqib Khan84a0e692017-06-28 17:27:01 -050059 if (value == VersionPurpose::BMC ||
60 value == VersionPurpose::System)
61 {
62 purpose = value;
63 }
Saqib Khan705f1bf2017-06-09 23:58:38 -050064 }
65 else if (property.first == "Version")
66 {
Saqib Khan84a0e692017-06-28 17:27:01 -050067 version = variant_ns::get<std::string>(property.second);
Gunnar Mills2ce7da22017-05-04 15:37:56 -050068 }
69 }
70 }
Saqib Khan19177d32017-06-20 08:11:49 -050071 else if (intf.first == FILEPATH_IFACE)
72 {
73 for (const auto& property : intf.second)
74 {
75 if (property.first == "Path")
76 {
Saqib Khan84a0e692017-06-28 17:27:01 -050077 filePath = variant_ns::get<std::string>(property.second);
Saqib Khan19177d32017-06-20 08:11:49 -050078 }
79 }
80 }
Gunnar Mills2ce7da22017-05-04 15:37:56 -050081 }
Saqib Khan705f1bf2017-06-09 23:58:38 -050082 if (version.empty() ||
Saqib Khan19177d32017-06-20 08:11:49 -050083 filePath.empty() ||
Saqib Khan84a0e692017-06-28 17:27:01 -050084 purpose == VersionPurpose::Unknown)
Saqib Khan705f1bf2017-06-09 23:58:38 -050085 {
86 return;
87 }
Gunnar Mills2ce7da22017-05-04 15:37:56 -050088
89 // Version id is the last item in the path
90 auto pos = path.rfind("/");
91 if (pos == std::string::npos)
92 {
93 log<level::ERR>("No version id found in object path",
94 entry("OBJPATH=%s", path));
Patrick Williamse75d10f2017-05-30 16:56:32 -050095 return;
Gunnar Mills2ce7da22017-05-04 15:37:56 -050096 }
97
98 auto versionId = path.substr(pos + 1);
99
Patrick Williamse75d10f2017-05-30 16:56:32 -0500100 if (activations.find(versionId) == activations.end())
Gunnar Mills2ce7da22017-05-04 15:37:56 -0500101 {
Saqib Khan35e83f32017-05-22 11:37:32 -0500102 // Determine the Activation state by processing the given image dir.
103 auto activationState = server::Activation::Activations::Invalid;
Gunnar Mills9a782242017-08-22 16:23:15 -0500104 ItemUpdater::ActivationStatus result =
105 ItemUpdater::validateSquashFSImage(filePath);
Saqib Khan35e83f32017-05-22 11:37:32 -0500106 if (result == ItemUpdater::ActivationStatus::ready)
107 {
108 activationState = server::Activation::Activations::Ready;
109 }
Gunnar Millsb60add12017-08-24 16:41:42 -0500110
111 // Create an association to the BMC inventory item
112 AssociationList associations{(std::make_tuple(
113 ACTIVATION_FWD_ASSOCIATION,
114 ACTIVATION_REV_ASSOCIATION,
115 bmcInventoryPath))};
116
Saqib Khan35e83f32017-05-22 11:37:32 -0500117 activations.insert(std::make_pair(
Saqib Khan705f1bf2017-06-09 23:58:38 -0500118 versionId,
119 std::make_unique<Activation>(
120 bus,
121 path,
Saqib Khan4c1aec02017-07-06 11:46:13 -0500122 *this,
Saqib Khan35e83f32017-05-22 11:37:32 -0500123 versionId,
Gunnar Millsb60add12017-08-24 16:41:42 -0500124 activationState,
125 associations)));
Saqib Khan705f1bf2017-06-09 23:58:38 -0500126 versions.insert(std::make_pair(
127 versionId,
Gunnar Mills9a782242017-08-22 16:23:15 -0500128 std::make_unique<VersionClass>(
Saqib Khan705f1bf2017-06-09 23:58:38 -0500129 bus,
130 path,
131 version,
132 purpose,
Leonel Gonzalez3526ef72017-07-07 14:38:25 -0500133 filePath,
134 std::bind(&ItemUpdater::erase,
135 this,
136 std::placeholders::_1))));
Gunnar Mills2ce7da22017-05-04 15:37:56 -0500137 }
Saqib Khan7b5010f2017-08-09 10:03:11 -0500138 else
139 {
140 log<level::INFO>("Software Object with the same version already exists",
Gunnar Mills9a782242017-08-22 16:23:15 -0500141 entry("VERSION_ID=%s", versionId));
Saqib Khan7b5010f2017-08-09 10:03:11 -0500142 }
Patrick Williamse75d10f2017-05-30 16:56:32 -0500143 return;
Gunnar Millsec1b41c2017-05-02 12:20:36 -0500144}
145
Saqib Khanba239882017-05-26 08:41:54 -0500146void ItemUpdater::processBMCImage()
147{
Gunnar Millsb60add12017-08-24 16:41:42 -0500148 // Create an association to the BMC inventory item
149 AssociationList associations{(std::make_tuple(
150 ACTIVATION_FWD_ASSOCIATION,
151 ACTIVATION_REV_ASSOCIATION,
152 bmcInventoryPath))};
153
Saqib Khan1eef62d2017-08-10 15:29:34 -0500154 // Read os-release from folders under /media/ to get
155 // BMC Software Versions.
156 for(const auto& iter : fs::directory_iterator(MEDIA_DIR))
157 {
158 auto activationState = server::Activation::Activations::Active;
Saqib Khan6fab70d2017-09-07 00:13:50 -0500159 static const auto BMC_RO_PREFIX_LEN = strlen(BMC_ROFS_PREFIX);
Saqib Khan1eef62d2017-08-10 15:29:34 -0500160
161 // Check if the BMC_RO_PREFIXis the prefix of the iter.path
162 if (0 == iter.path().native().compare(0, BMC_RO_PREFIX_LEN,
Saqib Khan6fab70d2017-09-07 00:13:50 -0500163 BMC_ROFS_PREFIX))
Saqib Khan1eef62d2017-08-10 15:29:34 -0500164 {
165 auto osRelease = iter.path() / OS_RELEASE_FILE;
166 if (!fs::is_regular_file(osRelease))
167 {
168 log<level::ERR>("Failed to read osRelease\n",
169 entry("FileName=%s", osRelease.string()));
170 activationState = server::Activation::Activations::Invalid;
171 }
172 auto version =
173 phosphor::software::manager::Version::
174 getBMCVersion(osRelease);
175 if (version.empty())
176 {
177 log<level::ERR>("Failed to read version from osRelease",
178 entry("FILENAME=%s", osRelease.string()));
179 activationState = server::Activation::Activations::Invalid;
180 }
181 // The versionId is extracted from the path
182 // for example /media/ro-2a1022fe
183 auto id = iter.path().native().substr(BMC_RO_PREFIX_LEN);
184 auto purpose = server::Version::VersionPurpose::BMC;
185 auto path = fs::path(SOFTWARE_OBJPATH) / id;
186
187 // Create Activation instance for this version.
188 activations.insert(std::make_pair(
189 id,
190 std::make_unique<Activation>(
191 bus,
192 path,
193 *this,
194 id,
195 server::Activation::Activations::Active,
196 associations)));
197
198 // If Active, create RedundancyPriority instance for this version.
199 if (activationState == server::Activation::Activations::Active)
200 {
201 uint8_t priority = std::numeric_limits<uint8_t>::max();
202 if (!restoreFromFile(id, priority))
203 {
204 log<level::ERR>("Unable to restore priority from file.",
205 entry("VERSIONID=%s", id));
206 }
207 activations.find(id)->second->redundancyPriority =
208 std::make_unique<RedundancyPriority>(
Saqib Khanba239882017-05-26 08:41:54 -0500209 bus,
210 path,
Saqib Khan1eef62d2017-08-10 15:29:34 -0500211 *(activations.find(id)->second),
212 priority);
213 }
214
215 // Create Version instance for this version.
216 versions.insert(std::make_pair(
217 id,
218 std::make_unique<
219 phosphor::software::manager::Version>(
220 bus,
221 path,
222 version,
223 purpose,
224 "",
225 std::bind(&ItemUpdater::erase,
Leonel Gonzalez3526ef72017-07-07 14:38:25 -0500226 this,
227 std::placeholders::_1))));
Saqib Khan1eef62d2017-08-10 15:29:34 -0500228 }
229 }
Saqib Khanba239882017-05-26 08:41:54 -0500230 return;
231}
232
Leonel Gonzalez3526ef72017-07-07 14:38:25 -0500233void ItemUpdater::erase(std::string entryId)
234{
Saqib Khan0c2eb262017-08-19 11:01:18 -0500235 // Delete ReadOnly partitions
Leonel Gonzalez3526ef72017-07-07 14:38:25 -0500236 removeReadOnlyPartition(entryId);
Saqib Khan1eef62d2017-08-10 15:29:34 -0500237 removeFile(entryId);
238
239 // Remove the priority environment variable.
240 auto serviceFile = "obmc-flash-bmc-setenv@" + entryId + ".service";
241 auto method = bus.new_method_call(
242 SYSTEMD_BUSNAME,
243 SYSTEMD_PATH,
244 SYSTEMD_INTERFACE,
245 "StartUnit");
246 method.append(serviceFile, "replace");
247 bus.call_noreply(method);
Leonel Gonzalez3526ef72017-07-07 14:38:25 -0500248
249 // Removing entry in versions map
250 auto it = versions.find(entryId);
251 if (it == versions.end())
252 {
253 log<level::ERR>(("Error: Failed to find version " + entryId + \
Gunnar Mills9a782242017-08-22 16:23:15 -0500254 " in item updater versions map." \
255 " Unable to remove.").c_str());
Leonel Gonzalez3526ef72017-07-07 14:38:25 -0500256 return;
257 }
258 this->versions.erase(entryId);
259
260 // Removing entry in activations map
261 auto ita = activations.find(entryId);
262 if (ita == activations.end())
263 {
264 log<level::ERR>(("Error: Failed to find version " + entryId + \
Gunnar Mills9a782242017-08-22 16:23:15 -0500265 " in item updater activations map." \
266 " Unable to remove.").c_str());
Leonel Gonzalez3526ef72017-07-07 14:38:25 -0500267 return;
268 }
269 // TODO: openbmc/openbmc#1986
270 // Test if this is the currently running image
271 // If not, don't continue.
272
273 this->activations.erase(entryId);
274}
275
Saqib Khan35e83f32017-05-22 11:37:32 -0500276ItemUpdater::ActivationStatus ItemUpdater::validateSquashFSImage(
Gunnar Mills9a782242017-08-22 16:23:15 -0500277 const std::string& filePath)
Saqib Khan35e83f32017-05-22 11:37:32 -0500278{
Michael Tritzb1cfdf92017-08-14 14:33:30 -0500279 bool invalid = false;
Saqib Khan35e83f32017-05-22 11:37:32 -0500280
Michael Tritzb1cfdf92017-08-14 14:33:30 -0500281 for (auto& bmcImage : bmcImages)
Saqib Khan35e83f32017-05-22 11:37:32 -0500282 {
Michael Tritzb1cfdf92017-08-14 14:33:30 -0500283 fs::path file(filePath);
284 file /= bmcImage;
285 std::ifstream efile(file.c_str());
286 if (efile.good() != 1)
287 {
288 log<level::ERR>("Failed to find the BMC image.",
Gunnar Mills9a782242017-08-22 16:23:15 -0500289 entry("IMAGE=%s", bmcImage.c_str()));
Michael Tritzb1cfdf92017-08-14 14:33:30 -0500290 invalid = true;
291 }
Saqib Khan35e83f32017-05-22 11:37:32 -0500292 }
Michael Tritzb1cfdf92017-08-14 14:33:30 -0500293
294 if (invalid)
Saqib Khan35e83f32017-05-22 11:37:32 -0500295 {
Saqib Khan35e83f32017-05-22 11:37:32 -0500296 return ItemUpdater::ActivationStatus::invalid;
297 }
Michael Tritzb1cfdf92017-08-14 14:33:30 -0500298
299 return ItemUpdater::ActivationStatus::ready;
Saqib Khan35e83f32017-05-22 11:37:32 -0500300}
301
Saqib Khan4c1aec02017-07-06 11:46:13 -0500302void ItemUpdater::freePriority(uint8_t value)
303{
304 //TODO openbmc/openbmc#1896 Improve the performance of this function
305 for (const auto& intf : activations)
306 {
Gunnar Mills9a782242017-08-22 16:23:15 -0500307 if (intf.second->redundancyPriority)
Saqib Khan4c1aec02017-07-06 11:46:13 -0500308 {
309 if (intf.second->redundancyPriority.get()->priority() == value)
310 {
Gunnar Mills9a782242017-08-22 16:23:15 -0500311 intf.second->redundancyPriority.get()->priority(value + 1);
Saqib Khan4c1aec02017-07-06 11:46:13 -0500312 }
313 }
314 }
315}
316
Michael Tritz37a59042017-07-12 13:44:53 -0500317void ItemUpdater::reset()
318{
319 // Mark the read-write partition for recreation upon reboot.
320 auto method = bus.new_method_call(
321 SYSTEMD_BUSNAME,
322 SYSTEMD_PATH,
323 SYSTEMD_INTERFACE,
324 "StartUnit");
Michael Tritz0129d922017-08-10 19:33:46 -0500325 method.append("obmc-flash-bmc-setenv@rwreset\\x3dtrue.service", "replace");
Michael Tritz37a59042017-07-12 13:44:53 -0500326 bus.call_noreply(method);
327
328 log<level::INFO>("BMC factory reset will take effect upon reboot.");
329
330 return;
331}
332
Leonel Gonzalez3526ef72017-07-07 14:38:25 -0500333void ItemUpdater::removeReadOnlyPartition(std::string versionId)
334{
335 auto serviceFile = "obmc-flash-bmc-ubiro-remove@" + versionId +
336 ".service";
337
338 // Remove the read-only partitions.
339 auto method = bus.new_method_call(
340 SYSTEMD_BUSNAME,
341 SYSTEMD_PATH,
342 SYSTEMD_INTERFACE,
343 "StartUnit");
344 method.append(serviceFile, "replace");
345 bus.call_noreply(method);
346}
347
Michael Tritz0129d922017-08-10 19:33:46 -0500348bool ItemUpdater::fieldModeEnabled(bool value)
349{
350 // enabling field mode is intended to be one way: false -> true
351 if (value && !control::FieldMode::fieldModeEnabled())
352 {
353 control::FieldMode::fieldModeEnabled(value);
354
355 auto method = bus.new_method_call(
356 SYSTEMD_BUSNAME,
357 SYSTEMD_PATH,
358 SYSTEMD_INTERFACE,
359 "StartUnit");
360 method.append("obmc-flash-bmc-setenv@fieldmode\\x3dtrue.service",
Gunnar Mills9a782242017-08-22 16:23:15 -0500361 "replace");
Michael Tritz0129d922017-08-10 19:33:46 -0500362 bus.call_noreply(method);
363
364 method = bus.new_method_call(
365 SYSTEMD_BUSNAME,
366 SYSTEMD_PATH,
367 SYSTEMD_INTERFACE,
368 "StopUnit");
369 method.append("usr-local.mount", "replace");
370 bus.call_noreply(method);
371
372 std::vector<std::string> usrLocal = {"usr-local.mount"};
373
374 method = bus.new_method_call(
375 SYSTEMD_BUSNAME,
376 SYSTEMD_PATH,
377 SYSTEMD_INTERFACE,
378 "MaskUnitFiles");
379 method.append(usrLocal, false, true);
380 bus.call_noreply(method);
381 }
382
383 return control::FieldMode::fieldModeEnabled();
384}
385
386void ItemUpdater::restoreFieldModeStatus()
387{
388 std::ifstream input("/run/fw_env");
389 std::string envVar;
390 std::getline(input, envVar);
391
Gunnar Mills9a782242017-08-22 16:23:15 -0500392 if (envVar.find("fieldmode=true") != std::string::npos)
Michael Tritz0129d922017-08-10 19:33:46 -0500393 {
394 ItemUpdater::fieldModeEnabled(true);
395 }
396}
397
Gunnar Millsb60add12017-08-24 16:41:42 -0500398void ItemUpdater::setBMCInventoryPath()
399{
400 //TODO: openbmc/openbmc#1786 - Get the BMC path by looking for objects
401 // that implement the BMC inventory interface
402 auto depth = 0;
403 auto mapperCall = bus.new_method_call(MAPPER_BUSNAME,
404 MAPPER_PATH,
405 MAPPER_INTERFACE,
406 "GetSubTreePaths");
407
408 mapperCall.append(CHASSIS_INVENTORY_PATH);
409 mapperCall.append(depth);
410
411 // TODO: openbmc/openbmc#2226 - Add Inventory Item filter when
412 // mapper is fixed.
413 std::vector<std::string> filter = {};
414 mapperCall.append(filter);
415
416 auto response = bus.call(mapperCall);
417 if (response.is_method_error())
418 {
419 log<level::ERR>("Error in mapper GetSubTreePath");
420 return;
421 }
422
423 using ObjectPaths = std::vector<std::string>;
424 ObjectPaths result;
425 response.read(result);
426
427 if (result.empty())
428 {
429 log<level::ERR>("Invalid response from mapper");
430 return;
431 }
432
433 for (auto& iter : result)
434 {
435 const auto& path = iter;
436 if (path.substr(path.find_last_of('/') + 1).compare("bmc") == 0)
437 {
438 bmcInventoryPath = path;
439 return;
440 }
441 }
442}
443
Gunnar Millsded875d2017-08-28 16:44:52 -0500444void ItemUpdater::createActiveAssociation(std::string path)
445{
446 assocs.emplace_back(std::make_tuple(ACTIVE_FWD_ASSOCIATION,
447 ACTIVE_REV_ASSOCIATION,
448 path));
449 associations(assocs);
450}
451
452void ItemUpdater::removeActiveAssociation(std::string path)
453{
454 for (auto iter = assocs.begin(); iter != assocs.end();)
455 {
456 if ((std::get<2>(*iter)).compare(path) == 0)
457 {
458 iter = assocs.erase(iter);
459 associations(assocs);
460 }
461 else
462 {
463 ++iter;
464 }
465 }
466}
467
Gunnar Millsec1b41c2017-05-02 12:20:36 -0500468} // namespace updater
469} // namespace software
470} // namespace phosphor