xref: /openbmc/phosphor-bmc-code-mgmt/item_updater.cpp (revision 44b9fef9bf49eacfa0743e93316c31c6c9645870)
1 #include "config.h"
2 
3 #include "item_updater.hpp"
4 
5 #include "images.hpp"
6 #include "serialize.hpp"
7 #include "version.hpp"
8 #include "xyz/openbmc_project/Software/ExtendedVersion/server.hpp"
9 #include "xyz/openbmc_project/Software/Version/server.hpp"
10 
11 #include <phosphor-logging/elog-errors.hpp>
12 #include <phosphor-logging/elog.hpp>
13 #include <phosphor-logging/lg2.hpp>
14 #include <xyz/openbmc_project/Common/error.hpp>
15 #include <xyz/openbmc_project/Software/Image/error.hpp>
16 
17 #include <filesystem>
18 #include <fstream>
19 #include <queue>
20 #include <set>
21 #include <string>
22 #include <system_error>
23 
24 namespace phosphor
25 {
26 namespace software
27 {
28 namespace updater
29 {
30 
31 // When you see server:: you know we're referencing our base class
32 namespace server = sdbusplus::xyz::openbmc_project::Software::server;
33 namespace control = sdbusplus::xyz::openbmc_project::Control::server;
34 
35 PHOSPHOR_LOG2_USING;
36 using namespace phosphor::logging;
37 using namespace sdbusplus::xyz::openbmc_project::Software::Image::Error;
38 using namespace phosphor::software::image;
39 namespace fs = std::filesystem;
40 using NotAllowed = sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed;
41 
42 void ItemUpdater::createActivation(sdbusplus::message_t& msg)
43 {
44 
45     using SVersion = server::Version;
46     using VersionPurpose = SVersion::VersionPurpose;
47     using VersionClass = phosphor::software::manager::Version;
48 
49     sdbusplus::message::object_path objPath;
50     auto purpose = VersionPurpose::Unknown;
51     std::string extendedVersion;
52     std::string version;
53     std::map<std::string,
54              std::map<std::string,
55                       std::variant<std::string, std::vector<std::string>>>>
56         interfaces;
57     msg.read(objPath, interfaces);
58     std::string path(std::move(objPath));
59     std::string filePath;
60     std::vector<std::string> compatibleNames;
61 
62     for (const auto& intf : interfaces)
63     {
64         if (intf.first == VERSION_IFACE)
65         {
66             for (const auto& property : intf.second)
67             {
68                 if (property.first == "Purpose")
69                 {
70                     auto value = SVersion::convertVersionPurposeFromString(
71                         std::get<std::string>(property.second));
72                     if (value == VersionPurpose::BMC ||
73 #ifdef HOST_BIOS_UPGRADE
74                         value == VersionPurpose::Host ||
75 #endif
76                         value == VersionPurpose::System)
77                     {
78                         purpose = value;
79                     }
80                 }
81                 else if (property.first == "Version")
82                 {
83                     version = std::get<std::string>(property.second);
84                 }
85             }
86         }
87         else if (intf.first == FILEPATH_IFACE)
88         {
89             for (const auto& property : intf.second)
90             {
91                 if (property.first == "Path")
92                 {
93                     filePath = std::get<std::string>(property.second);
94                 }
95             }
96         }
97         else if (intf.first == EXTENDED_VERSION_IFACE)
98         {
99             for (const auto& property : intf.second)
100             {
101                 if (property.first == "ExtendedVersion")
102                 {
103                     extendedVersion = std::get<std::string>(property.second);
104                 }
105             }
106         }
107         else if (intf.first == COMPATIBLE_IFACE)
108         {
109             for (const auto& property : intf.second)
110             {
111                 if (property.first == "Names")
112                 {
113                     compatibleNames =
114                         std::get<std::vector<std::string>>(property.second);
115                 }
116             }
117         }
118     }
119     if (version.empty() || filePath.empty() ||
120         purpose == VersionPurpose::Unknown)
121     {
122         return;
123     }
124 
125     // Version id is the last item in the path
126     auto pos = path.rfind("/");
127     if (pos == std::string::npos)
128     {
129         error("No version id found in object path: {PATH}", "PATH", path);
130         return;
131     }
132 
133     auto versionId = path.substr(pos + 1);
134 
135     if (activations.find(versionId) == activations.end())
136     {
137         // Determine the Activation state by processing the given image dir.
138         auto activationState = server::Activation::Activations::Invalid;
139         ItemUpdater::ActivationStatus result;
140         if (purpose == VersionPurpose::BMC || purpose == VersionPurpose::System)
141             result = ItemUpdater::validateSquashFSImage(filePath);
142         else
143             result = ItemUpdater::ActivationStatus::ready;
144 
145         AssociationList associations = {};
146 
147         if (result == ItemUpdater::ActivationStatus::ready)
148         {
149             activationState = server::Activation::Activations::Ready;
150             // Create an association to the BMC inventory item
151             associations.emplace_back(
152                 std::make_tuple(ACTIVATION_FWD_ASSOCIATION,
153                                 ACTIVATION_REV_ASSOCIATION, bmcInventoryPath));
154         }
155 
156         auto versionPtr = std::make_unique<VersionClass>(
157             bus, path, version, purpose, extendedVersion, filePath,
158             compatibleNames,
159             std::bind(&ItemUpdater::erase, this, std::placeholders::_1),
160             versionId);
161         versionPtr->deleteObject =
162             std::make_unique<phosphor::software::manager::Delete>(bus, path,
163                                                                   *versionPtr);
164         versions.insert(std::make_pair(versionId, std::move(versionPtr)));
165 
166         activations.insert(std::make_pair(
167             versionId,
168             std::make_unique<Activation>(bus, path, *this, versionId,
169                                          activationState, associations)));
170     }
171     return;
172 }
173 
174 void ItemUpdater::processBMCImage()
175 {
176     using VersionClass = phosphor::software::manager::Version;
177 
178     // Check MEDIA_DIR and create if it does not exist
179     try
180     {
181         if (!fs::is_directory(MEDIA_DIR))
182         {
183             fs::create_directory(MEDIA_DIR);
184         }
185     }
186     catch (const fs::filesystem_error& e)
187     {
188         error("Failed to prepare dir: {ERROR}", "ERROR", e);
189         return;
190     }
191 
192     // Functional images are mounted as rofs-<location>-functional
193     constexpr auto functionalSuffix = "-functional";
194     bool functionalFound = false;
195 
196     // Read os-release from folders under /media/ to get
197     // BMC Software Versions.
198     std::error_code ec;
199     for (const auto& iter : fs::directory_iterator(MEDIA_DIR, ec))
200     {
201         auto activationState = server::Activation::Activations::Active;
202         static const auto BMC_RO_PREFIX_LEN = strlen(BMC_ROFS_PREFIX);
203 
204         // Check if the BMC_RO_PREFIXis the prefix of the iter.path
205         if (0 ==
206             iter.path().native().compare(0, BMC_RO_PREFIX_LEN, BMC_ROFS_PREFIX))
207         {
208             // Get the version to calculate the id
209             fs::path releaseFile(OS_RELEASE_FILE);
210             auto osRelease = iter.path() / releaseFile.relative_path();
211             if (!fs::is_regular_file(osRelease, ec))
212             {
213 #ifdef BMC_STATIC_DUAL_IMAGE
214                 // For dual image, it is possible that the secondary image is
215                 // empty or contains invalid data, ignore such case.
216                 info("Unable to find osRelease: {PATH}: {ERROR_MSG}", "PATH",
217                      osRelease, "ERROR_MSG", ec.message());
218 #else
219                 error("Failed to read osRelease: {PATH}: {ERROR_MSG}", "PATH",
220                       osRelease, "ERROR_MSG", ec.message());
221 
222                 // Try to get the version id from the mount directory name and
223                 // call to delete it as this version may be corrupted. Dynamic
224                 // volumes created by the UBI layout for example have the id in
225                 // the mount directory name. The worst that can happen is that
226                 // erase() is called with an non-existent id and returns.
227                 auto id = iter.path().native().substr(BMC_RO_PREFIX_LEN);
228                 ItemUpdater::erase(id);
229 #endif
230 
231                 continue;
232             }
233             auto version = VersionClass::getBMCVersion(osRelease);
234             if (version.empty())
235             {
236                 error("Failed to read version from osRelease: {PATH}", "PATH",
237                       osRelease);
238 
239                 // Try to delete the version, same as above if the
240                 // OS_RELEASE_FILE does not exist.
241                 auto id = iter.path().native().substr(BMC_RO_PREFIX_LEN);
242                 ItemUpdater::erase(id);
243 
244                 continue;
245             }
246 
247             // The flash location is part of the mount name: rofs-<location>
248             auto flashId = iter.path().native().substr(BMC_RO_PREFIX_LEN);
249 
250             auto id = VersionClass::getId(version + flashId);
251 
252             // Check if the id has already been added. This can happen if the
253             // BMC partitions / devices were manually flashed with the same
254             // image.
255             if (versions.find(id) != versions.end())
256             {
257                 continue;
258             }
259 
260             auto functional = false;
261             if (iter.path().native().find(functionalSuffix) !=
262                 std::string::npos)
263             {
264                 // Set functional to true and remove the functional suffix
265                 functional = true;
266                 flashId.erase(flashId.length() - strlen(functionalSuffix));
267                 functionalFound = true;
268             }
269 
270             auto purpose = server::Version::VersionPurpose::BMC;
271             restorePurpose(flashId, purpose);
272 
273             // Read os-release from /etc/ to get the BMC extended version
274             std::string extendedVersion =
275                 VersionClass::getBMCExtendedVersion(osRelease);
276 
277             auto path = fs::path(SOFTWARE_OBJPATH) / id;
278 
279             // Create functional association if this is the functional
280             // version
281             if (functional)
282             {
283                 createFunctionalAssociation(path);
284             }
285 
286             AssociationList associations = {};
287 
288             if (activationState == server::Activation::Activations::Active)
289             {
290                 // Create an association to the BMC inventory item
291                 associations.emplace_back(std::make_tuple(
292                     ACTIVATION_FWD_ASSOCIATION, ACTIVATION_REV_ASSOCIATION,
293                     bmcInventoryPath));
294 
295                 // Create an active association since this image is active
296                 createActiveAssociation(path);
297             }
298 
299             // All updateable firmware components must expose the updateable
300             // association.
301             createUpdateableAssociation(path);
302 
303             // Create Version instance for this version.
304             auto versionPtr = std::make_unique<VersionClass>(
305                 bus, path, version, purpose, extendedVersion, flashId,
306                 std::vector<std::string>(),
307                 std::bind(&ItemUpdater::erase, this, std::placeholders::_1),
308                 id);
309             if (functional)
310             {
311                 versionPtr->setFunctional(true);
312             }
313             else
314             {
315                 versionPtr->deleteObject =
316                     std::make_unique<phosphor::software::manager::Delete>(
317                         bus, path, *versionPtr);
318             }
319             versions.insert(std::make_pair(id, std::move(versionPtr)));
320 
321             // Create Activation instance for this version.
322             activations.insert(std::make_pair(
323                 id, std::make_unique<Activation>(
324                         bus, path, *this, id, activationState, associations)));
325 
326 #ifdef BMC_STATIC_DUAL_IMAGE
327             uint8_t priority;
328             if ((functional && (runningImageSlot == 0)) ||
329                 (!functional && (runningImageSlot == 1)))
330             {
331                 priority = 0;
332             }
333             else
334             {
335                 priority = 1;
336             }
337             activations.find(id)->second->redundancyPriority =
338                 std::make_unique<RedundancyPriority>(
339                     bus, path, *(activations.find(id)->second), priority,
340                     false);
341 #else
342             // If Active, create RedundancyPriority instance for this
343             // version.
344             if (activationState == server::Activation::Activations::Active)
345             {
346                 uint8_t priority = std::numeric_limits<uint8_t>::max();
347                 if (!restorePriority(flashId, priority))
348                 {
349                     if (functional)
350                     {
351                         priority = 0;
352                     }
353                     else
354                     {
355                         error(
356                             "Unable to restore priority from file for {VERSIONID}",
357                             "VERSIONID", id);
358                     }
359                 }
360                 activations.find(id)->second->redundancyPriority =
361                     std::make_unique<RedundancyPriority>(
362                         bus, path, *(activations.find(id)->second), priority,
363                         false);
364             }
365 #endif
366         }
367     }
368 
369     if (!functionalFound)
370     {
371         // If there is no functional version found, read the /etc/os-release and
372         // create rofs-<versionId>-functional under MEDIA_DIR, then call again
373         // processBMCImage() to create the D-Bus interface for it.
374         auto version = VersionClass::getBMCVersion(OS_RELEASE_FILE);
375         auto id = phosphor::software::manager::Version::getId(version +
376                                                               functionalSuffix);
377         auto versionFileDir = BMC_ROFS_PREFIX + id + functionalSuffix + "/etc/";
378         try
379         {
380             if (!fs::is_directory(versionFileDir))
381             {
382                 fs::create_directories(versionFileDir);
383             }
384             auto versionFilePath =
385                 BMC_ROFS_PREFIX + id + functionalSuffix + OS_RELEASE_FILE;
386             fs::create_directory_symlink(OS_RELEASE_FILE, versionFilePath);
387             ItemUpdater::processBMCImage();
388         }
389         catch (const std::exception& e)
390         {
391             error("Exception during processing: {ERROR}", "ERROR", e);
392         }
393     }
394 
395     mirrorUbootToAlt();
396     return;
397 }
398 
399 void ItemUpdater::erase(std::string entryId)
400 {
401     // Find entry in versions map
402     auto it = versions.find(entryId);
403     if (it != versions.end())
404     {
405         if (it->second->isFunctional() && ACTIVE_BMC_MAX_ALLOWED > 1)
406         {
407             error(
408                 "Version ({VERSIONID}) is currently running on the BMC; unable to remove.",
409                 "VERSIONID", entryId);
410             return;
411         }
412     }
413 
414     // First call resetUbootEnvVars() so that the BMC points to a valid image to
415     // boot from. If resetUbootEnvVars() is called after the image is actually
416     // deleted from the BMC flash, there'd be a time window where the BMC would
417     // be pointing to a non-existent image to boot from.
418     // Need to remove the entries from the activations map before that call so
419     // that resetUbootEnvVars() doesn't use the version to be deleted.
420     auto iteratorActivations = activations.find(entryId);
421     if (iteratorActivations == activations.end())
422     {
423         error(
424             "Failed to find version ({VERSIONID}) in item updater activations map; unable to remove.",
425             "VERSIONID", entryId);
426     }
427     else
428     {
429         removeAssociations(iteratorActivations->second->path);
430         iteratorActivations->second->deleteImageManagerObject();
431         this->activations.erase(entryId);
432     }
433     ItemUpdater::resetUbootEnvVars();
434 
435     if (it != versions.end())
436     {
437         auto flashId = it->second->path();
438 
439         // Delete version data if it has been installed on flash (path is not
440         // the upload directory)
441         if (flashId.find(IMG_UPLOAD_DIR) == std::string::npos)
442         {
443             removeReadOnlyPartition(entryId);
444             removePersistDataDirectory(flashId);
445             helper.clearEntry(flashId);
446         }
447 
448         // Removing entry in versions map
449         this->versions.erase(entryId);
450     }
451 
452     return;
453 }
454 
455 void ItemUpdater::deleteAll()
456 {
457     std::vector<std::string> deletableVersions;
458 
459     for (const auto& versionIt : versions)
460     {
461         if (!versionIt.second->isFunctional())
462         {
463             deletableVersions.push_back(versionIt.first);
464         }
465     }
466 
467     for (const auto& deletableIt : deletableVersions)
468     {
469         ItemUpdater::erase(deletableIt);
470     }
471 
472     helper.cleanup();
473 }
474 
475 ItemUpdater::ActivationStatus
476     ItemUpdater::validateSquashFSImage(const std::string& filePath)
477 {
478     bool valid = true;
479 
480     // Record the images which are being updated
481     // First check for the fullimage, then check for images with partitions
482     imageUpdateList.push_back(bmcFullImages);
483     valid = checkImage(filePath, imageUpdateList);
484     if (!valid)
485     {
486         imageUpdateList.clear();
487         imageUpdateList.assign(bmcImages.begin(), bmcImages.end());
488         valid = checkImage(filePath, imageUpdateList);
489         if (!valid)
490         {
491             error("Failed to find the needed BMC images.");
492             return ItemUpdater::ActivationStatus::invalid;
493         }
494     }
495 
496     return ItemUpdater::ActivationStatus::ready;
497 }
498 
499 void ItemUpdater::savePriority(const std::string& versionId, uint8_t value)
500 {
501     auto flashId = versions.find(versionId)->second->path();
502     storePriority(flashId, value);
503     helper.setEntry(flashId, value);
504 }
505 
506 void ItemUpdater::freePriority(uint8_t value, const std::string& versionId)
507 {
508     std::map<std::string, uint8_t> priorityMap;
509 
510     // Insert the requested version and priority, it may not exist yet.
511     priorityMap.insert(std::make_pair(versionId, value));
512 
513     for (const auto& intf : activations)
514     {
515         if (intf.second->redundancyPriority)
516         {
517             priorityMap.insert(std::make_pair(
518                 intf.first, intf.second->redundancyPriority.get()->priority()));
519         }
520     }
521 
522     // Lambda function to compare 2 priority values, use <= to allow duplicates
523     typedef std::function<bool(std::pair<std::string, uint8_t>,
524                                std::pair<std::string, uint8_t>)>
525         cmpPriority;
526     cmpPriority cmpPriorityFunc =
527         [](std::pair<std::string, uint8_t> priority1,
528            std::pair<std::string, uint8_t> priority2) {
529             return priority1.second <= priority2.second;
530         };
531 
532     // Sort versions by ascending priority
533     std::set<std::pair<std::string, uint8_t>, cmpPriority> prioritySet(
534         priorityMap.begin(), priorityMap.end(), cmpPriorityFunc);
535 
536     auto freePriorityValue = value;
537     for (auto& element : prioritySet)
538     {
539         if (element.first == versionId)
540         {
541             continue;
542         }
543         if (element.second == freePriorityValue)
544         {
545             ++freePriorityValue;
546             auto it = activations.find(element.first);
547             it->second->redundancyPriority.get()->sdbusPriority(
548                 freePriorityValue);
549         }
550     }
551 
552     auto lowestVersion = prioritySet.begin()->first;
553     if (value == prioritySet.begin()->second)
554     {
555         lowestVersion = versionId;
556     }
557     updateUbootEnvVars(lowestVersion);
558 }
559 
560 void ItemUpdater::reset()
561 {
562     helper.factoryReset();
563 
564     info("BMC factory reset will take effect upon reboot.");
565 }
566 
567 void ItemUpdater::removeReadOnlyPartition(std::string versionId)
568 {
569     auto flashId = versions.find(versionId)->second->path();
570     helper.removeVersion(flashId);
571 }
572 
573 bool ItemUpdater::fieldModeEnabled(bool value)
574 {
575     // enabling field mode is intended to be one way: false -> true
576     if (value && !control::FieldMode::fieldModeEnabled())
577     {
578         control::FieldMode::fieldModeEnabled(value);
579 
580         auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH,
581                                           SYSTEMD_INTERFACE, "StartUnit");
582         method.append("obmc-flash-bmc-setenv@fieldmode\\x3dtrue.service",
583                       "replace");
584         bus.call_noreply(method);
585 
586         method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH,
587                                      SYSTEMD_INTERFACE, "StopUnit");
588         method.append("usr-local.mount", "replace");
589         bus.call_noreply(method);
590 
591         std::vector<std::string> usrLocal = {"usr-local.mount"};
592 
593         method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH,
594                                      SYSTEMD_INTERFACE, "MaskUnitFiles");
595         method.append(usrLocal, false, true);
596         bus.call_noreply(method);
597     }
598     else if (!value && control::FieldMode::fieldModeEnabled())
599     {
600         elog<NotAllowed>(xyz::openbmc_project::Common::NotAllowed::REASON(
601             "FieldMode is not allowed to be cleared"));
602     }
603 
604     return control::FieldMode::fieldModeEnabled();
605 }
606 
607 void ItemUpdater::restoreFieldModeStatus()
608 {
609     // The fieldmode u-boot environment variable may not exist since it is not
610     // part of the default environment, run fw_printenv with 2>&1 to ignore the
611     // error message in the journal "Error: "fieldmode" not defined"
612     std::pair<int, std::string> ret =
613         utils::execute("/sbin/fw_printenv", "-n", "fieldmode", "2>&1");
614 
615     if (ret.first != 0)
616     {
617         return;
618     }
619 
620     // truncate any extra characters off the end to compare against a "true" str
621     std::string result = ret.second.substr(0, 4);
622     if (result == "true")
623     {
624         ItemUpdater::fieldModeEnabled(true);
625     }
626 }
627 
628 void ItemUpdater::setBMCInventoryPath()
629 {
630     auto depth = 0;
631     auto mapperCall = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
632                                           MAPPER_INTERFACE, "GetSubTreePaths");
633 
634     mapperCall.append(INVENTORY_PATH);
635     mapperCall.append(depth);
636     std::vector<std::string> filter = {BMC_INVENTORY_INTERFACE};
637     mapperCall.append(filter);
638 
639     try
640     {
641         auto response = bus.call(mapperCall);
642 
643         using ObjectPaths = std::vector<std::string>;
644         ObjectPaths result;
645         response.read(result);
646 
647         if (!result.empty())
648         {
649             bmcInventoryPath = result.front();
650         }
651     }
652     catch (const sdbusplus::exception_t& e)
653     {
654         error("Error in mapper GetSubTreePath: {ERROR}", "ERROR", e);
655         return;
656     }
657 
658     return;
659 }
660 
661 void ItemUpdater::createActiveAssociation(const std::string& path)
662 {
663     assocs.emplace_back(
664         std::make_tuple(ACTIVE_FWD_ASSOCIATION, ACTIVE_REV_ASSOCIATION, path));
665     associations(assocs);
666 }
667 
668 void ItemUpdater::createFunctionalAssociation(const std::string& path)
669 {
670     assocs.emplace_back(std::make_tuple(FUNCTIONAL_FWD_ASSOCIATION,
671                                         FUNCTIONAL_REV_ASSOCIATION, path));
672     associations(assocs);
673 }
674 
675 void ItemUpdater::createUpdateableAssociation(const std::string& path)
676 {
677     assocs.emplace_back(std::make_tuple(UPDATEABLE_FWD_ASSOCIATION,
678                                         UPDATEABLE_REV_ASSOCIATION, path));
679     associations(assocs);
680 }
681 
682 void ItemUpdater::removeAssociations(const std::string& path)
683 {
684     for (auto iter = assocs.begin(); iter != assocs.end();)
685     {
686         if ((std::get<2>(*iter)).compare(path) == 0)
687         {
688             iter = assocs.erase(iter);
689             associations(assocs);
690         }
691         else
692         {
693             ++iter;
694         }
695     }
696 }
697 
698 bool ItemUpdater::isLowestPriority(uint8_t value)
699 {
700     for (const auto& intf : activations)
701     {
702         if (intf.second->redundancyPriority)
703         {
704             if (intf.second->redundancyPriority.get()->priority() < value)
705             {
706                 return false;
707             }
708         }
709     }
710     return true;
711 }
712 
713 void ItemUpdater::updateUbootEnvVars(const std::string& versionId)
714 {
715     auto it = versions.find(versionId);
716     if (it == versions.end())
717     {
718         return;
719     }
720     auto flashId = it->second->path();
721     helper.updateUbootVersionId(flashId);
722 }
723 
724 void ItemUpdater::resetUbootEnvVars()
725 {
726     decltype(activations.begin()->second->redundancyPriority.get()->priority())
727         lowestPriority = std::numeric_limits<uint8_t>::max();
728     decltype(activations.begin()->second->versionId) lowestPriorityVersion;
729     for (const auto& intf : activations)
730     {
731         if (!intf.second->redundancyPriority.get())
732         {
733             // Skip this version if the redundancyPriority is not initialized.
734             continue;
735         }
736 
737         if (intf.second->redundancyPriority.get()->priority() <= lowestPriority)
738         {
739             lowestPriority = intf.second->redundancyPriority.get()->priority();
740             lowestPriorityVersion = intf.second->versionId;
741         }
742     }
743 
744     // Update the U-boot environment variable to point to the lowest priority
745     updateUbootEnvVars(lowestPriorityVersion);
746 }
747 
748 void ItemUpdater::freeSpace([[maybe_unused]] const Activation& caller)
749 {
750 #ifdef BMC_STATIC_DUAL_IMAGE
751     // For the golden image case, always remove the version on the primary side
752     std::string versionIDtoErase;
753     for (const auto& iter : activations)
754     {
755         if (iter.second->redundancyPriority &&
756             iter.second->redundancyPriority.get()->priority() == 0)
757         {
758             versionIDtoErase = iter.second->versionId;
759             break;
760         }
761     }
762     if (!versionIDtoErase.empty())
763     {
764         erase(versionIDtoErase);
765     }
766     else
767     {
768         warning("Failed to find version to erase");
769     }
770 #else
771     //  Versions with the highest priority in front
772     std::priority_queue<std::pair<int, std::string>,
773                         std::vector<std::pair<int, std::string>>,
774                         std::less<std::pair<int, std::string>>>
775         versionsPQ;
776 
777     std::size_t count = 0;
778     for (const auto& iter : activations)
779     {
780         if ((iter.second.get()->activation() ==
781              server::Activation::Activations::Active) ||
782             (iter.second.get()->activation() ==
783              server::Activation::Activations::Failed))
784         {
785             count++;
786             // Don't put the functional version on the queue since we can't
787             // remove the "running" BMC version.
788             // If ACTIVE_BMC_MAX_ALLOWED <= 1, there is only one active BMC,
789             // so remove functional version as well.
790             // Don't delete the the Activation object that called this function.
791             if ((versions.find(iter.second->versionId)
792                      ->second->isFunctional() &&
793                  ACTIVE_BMC_MAX_ALLOWED > 1) ||
794                 (iter.second->versionId == caller.versionId))
795             {
796                 continue;
797             }
798 
799             // Failed activations don't have priority, assign them a large value
800             // for sorting purposes.
801             auto priority = 999;
802             if (iter.second.get()->activation() ==
803                     server::Activation::Activations::Active &&
804                 iter.second->redundancyPriority)
805             {
806                 priority = iter.second->redundancyPriority.get()->priority();
807             }
808 
809             versionsPQ.push(std::make_pair(priority, iter.second->versionId));
810         }
811     }
812 
813     // If the number of BMC versions is over ACTIVE_BMC_MAX_ALLOWED -1,
814     // remove the highest priority one(s).
815     while ((count >= ACTIVE_BMC_MAX_ALLOWED) && (!versionsPQ.empty()))
816     {
817         erase(versionsPQ.top().second);
818         versionsPQ.pop();
819         count--;
820     }
821 #endif
822 }
823 
824 void ItemUpdater::mirrorUbootToAlt()
825 {
826     helper.mirrorAlt();
827 }
828 
829 bool ItemUpdater::checkImage(const std::string& filePath,
830                              const std::vector<std::string>& imageList)
831 {
832     bool valid = true;
833 
834     for (auto& bmcImage : imageList)
835     {
836         fs::path file(filePath);
837         file /= bmcImage;
838         std::ifstream efile(file.c_str());
839         if (efile.good() != 1)
840         {
841             valid = false;
842             break;
843         }
844     }
845 
846     return valid;
847 }
848 
849 #ifdef HOST_BIOS_UPGRADE
850 void ItemUpdater::createBIOSObject()
851 {
852     std::string path = BIOS_OBJPATH;
853     // Get version id from last item in the path
854     auto pos = path.rfind("/");
855     if (pos == std::string::npos)
856     {
857         error("No version id found in object path {PATH}", "PATH", path);
858         return;
859     }
860 
861     createActiveAssociation(path);
862     createFunctionalAssociation(path);
863 
864     auto versionId = path.substr(pos + 1);
865     auto version = "null";
866     AssociationList assocs = {};
867     biosActivation = std::make_unique<Activation>(
868         bus, path, *this, versionId, server::Activation::Activations::Active,
869         assocs);
870     auto dummyErase = [](std::string /*entryId*/) {
871         // Do nothing;
872     };
873     biosVersion = std::make_unique<VersionClass>(
874         bus, path, version, VersionPurpose::Host, "", "",
875         std::vector<std::string>(),
876         std::bind(dummyErase, std::placeholders::_1), "");
877     biosVersion->deleteObject =
878         std::make_unique<phosphor::software::manager::Delete>(bus, path,
879                                                               *biosVersion);
880 }
881 #endif
882 
883 void ItemUpdater::getRunningSlot()
884 {
885     // Check /run/media/slot to get the slot number
886     constexpr auto slotFile = "/run/media/slot";
887     std::fstream f(slotFile, std::ios_base::in);
888     f >> runningImageSlot;
889 }
890 
891 } // namespace updater
892 } // namespace software
893 } // namespace phosphor
894