1 #include "update_manager.hpp" 2 3 #include "item_updater.hpp" 4 #include "software_utils.hpp" 5 #include "version.hpp" 6 7 #include <phosphor-logging/elog-errors.hpp> 8 #include <phosphor-logging/elog.hpp> 9 #include <phosphor-logging/lg2.hpp> 10 #include <sdbusplus/async.hpp> 11 #include <xyz/openbmc_project/Common/error.hpp> 12 13 #include <filesystem> 14 15 PHOSPHOR_LOG2_USING; 16 17 namespace phosphor::software::update 18 { 19 20 namespace fs = std::filesystem; 21 namespace softwareUtils = phosphor::software::utils; 22 using namespace phosphor::logging; 23 using Version = phosphor::software::manager::Version; 24 using ActivationIntf = phosphor::software::updater::Activation; 25 26 void Manager::processImageFailed(sdbusplus::message::unix_fd image, 27 std::string& id) 28 { 29 close(image); 30 updateInProgress = false; 31 itemUpdater.updateActivationStatus(id, 32 ActivationIntf::Activations::Invalid); 33 } 34 35 // NOLINTNEXTLINE(readability-static-accessed-through-instance) 36 auto Manager::processImage(sdbusplus::message::unix_fd image, 37 ApplyTimeIntf::RequestedApplyTimes applyTime, 38 std::string id, 39 std::string objPath) -> sdbusplus::async::task<> 40 { 41 debug("Processing image {FD}", "FD", image.fd); 42 fs::path tmpDirPath(std::string{IMG_UPLOAD_DIR}); 43 tmpDirPath /= "imageXXXXXX"; 44 auto tmpDir = tmpDirPath.string(); 45 // Create a tmp dir to copy tarball. 46 if (!mkdtemp(tmpDir.data())) 47 { 48 error("Error ({ERRNO}) occurred during mkdtemp", "ERRNO", errno); 49 processImageFailed(image, id); 50 co_return; 51 } 52 53 std::error_code ec; 54 tmpDirPath = tmpDir; 55 softwareUtils::RemovablePath tmpDirToRemove(tmpDirPath); 56 57 // Untar tarball into the tmp dir 58 if (!softwareUtils::unTar(image, tmpDirPath.string())) 59 { 60 error("Error occurred during untar"); 61 processImageFailed(image, id); 62 co_return; 63 } 64 65 fs::path manifestPath = tmpDirPath; 66 manifestPath /= MANIFEST_FILE_NAME; 67 68 // Get version 69 auto version = Version::getValue(manifestPath.string(), "version"); 70 if (version.empty()) 71 { 72 error("Unable to read version from manifest file"); 73 processImageFailed(image, id); 74 co_return; 75 } 76 77 // Get running machine name 78 std::string currMachine = Version::getBMCMachine(OS_RELEASE_FILE); 79 if (currMachine.empty()) 80 { 81 auto path = OS_RELEASE_FILE; 82 error("Failed to read machine name from osRelease: {PATH}", "PATH", 83 path); 84 processImageFailed(image, id); 85 co_return; 86 } 87 88 // Get machine name for image to be upgraded 89 std::string machineStr = 90 Version::getValue(manifestPath.string(), "MachineName"); 91 if (!machineStr.empty()) 92 { 93 if (machineStr != currMachine) 94 { 95 error( 96 "BMC upgrade: Machine name doesn't match: {CURRENT_MACHINE} vs {NEW_MACHINE}", 97 "CURRENT_MACHINE", currMachine, "NEW_MACHINE", machineStr); 98 processImageFailed(image, id); 99 co_return; 100 } 101 } 102 else 103 { 104 warning("No machine name in Manifest file"); 105 } 106 107 // Get purpose 108 auto purposeString = Version::getValue(manifestPath.string(), "purpose"); 109 if (purposeString.empty()) 110 { 111 error("Unable to read purpose from manifest file"); 112 processImageFailed(image, id); 113 co_return; 114 } 115 auto convertedPurpose = 116 sdbusplus::message::convert_from_string<Version::VersionPurpose>( 117 purposeString); 118 if (!convertedPurpose) 119 { 120 warning( 121 "Failed to convert manifest purpose ({PURPOSE}) to enum; setting to Unknown.", 122 "PURPOSE", purposeString); 123 } 124 auto purpose = convertedPurpose.value_or(Version::VersionPurpose::Unknown); 125 126 // Get ExtendedVersion 127 std::string extendedVersion = 128 Version::getValue(manifestPath.string(), "ExtendedVersion"); 129 130 // Get CompatibleNames 131 std::vector<std::string> compatibleNames = 132 Version::getRepeatedValues(manifestPath.string(), "CompatibleName"); 133 134 // Rename IMG_UPLOAD_DIR/imageXXXXXX to IMG_UPLOAD_DIR/id as Manifest 135 // parsing succedded. 136 fs::path imageDirPath = std::string{IMG_UPLOAD_DIR}; 137 imageDirPath /= id; 138 fs::rename(tmpDirPath, imageDirPath, ec); 139 tmpDirToRemove.path.clear(); 140 141 auto filePath = imageDirPath.string(); 142 // Create Version object 143 auto state = itemUpdater.verifyAndCreateObjects( 144 id, objPath, version, purpose, extendedVersion, filePath, 145 compatibleNames); 146 if (state != ActivationIntf::Activations::Ready) 147 { 148 error("Software image is invalid"); 149 processImageFailed(image, id); 150 co_return; 151 } 152 if (applyTime == ApplyTimeIntf::RequestedApplyTimes::Immediate || 153 applyTime == ApplyTimeIntf::RequestedApplyTimes::OnReset) 154 { 155 itemUpdater.requestActivation(id); 156 } 157 158 updateInProgress = false; 159 close(image); 160 co_return; 161 } 162 163 sdbusplus::message::object_path 164 Manager::startUpdate(sdbusplus::message::unix_fd image, 165 ApplyTimeIntf::RequestedApplyTimes applyTime) 166 { 167 info("Starting update for image {FD}", "FD", static_cast<int>(image)); 168 using sdbusplus::xyz::openbmc_project::Common::Error::Unavailable; 169 if (updateInProgress) 170 { 171 error("Failed to start as update is already in progress"); 172 report<Unavailable>(); 173 return sdbusplus::message::object_path(); 174 } 175 updateInProgress = true; 176 177 auto id = Version::getId(std::to_string(randomGen())); 178 auto objPath = std::string{SOFTWARE_OBJPATH} + '/' + id; 179 180 // Create Activation Object 181 itemUpdater.createActivationWithApplyTime(id, objPath, applyTime); 182 183 int newFd = dup(image); 184 ctx.spawn(processImage(newFd, applyTime, id, objPath)); 185 186 return sdbusplus::message::object_path(objPath); 187 } 188 189 } // namespace phosphor::software::update 190