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