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 #include <xyz/openbmc_project/Software/Image/error.hpp>
13
14 #include <filesystem>
15
16 PHOSPHOR_LOG2_USING;
17
18 namespace phosphor::software::update
19 {
20
21 namespace fs = std::filesystem;
22 namespace softwareUtils = phosphor::software::utils;
23 namespace SoftwareLogging = phosphor::logging::xyz::openbmc_project::software;
24 namespace SoftwareErrors =
25 sdbusplus::error::xyz::openbmc_project::software::image;
26 using namespace phosphor::logging;
27 using Version = phosphor::software::manager::Version;
28 using ActivationIntf = phosphor::software::updater::Activation;
29 using ManifestFail = SoftwareLogging::image::ManifestFileFailure;
30 using UnTarFail = SoftwareLogging::image::UnTarFailure;
31 using InternalFail = SoftwareLogging::image::InternalFailure;
32 using ImageFail = SoftwareLogging::image::ImageFailure;
33
processImageFailed(sdbusplus::message::unix_fd image,std::string & id)34 void Manager::processImageFailed(sdbusplus::message::unix_fd image,
35 std::string& id)
36 {
37 close(image);
38 updateInProgress = false;
39 itemUpdater.updateActivationStatus(id,
40 ActivationIntf::Activations::Invalid);
41 }
42
verifyImagePurpose(Version::VersionPurpose purpose,ItemUpdaterIntf::UpdaterType type)43 bool verifyImagePurpose(Version::VersionPurpose purpose,
44 ItemUpdaterIntf::UpdaterType type)
45 {
46 if (purpose == Version::VersionPurpose::Host)
47 {
48 return (type == ItemUpdaterIntf::UpdaterType::BIOS ||
49 type == ItemUpdaterIntf::UpdaterType::ALL);
50 }
51 return true;
52 }
53
processImage(sdbusplus::message::unix_fd image,ApplyTimeIntf::RequestedApplyTimes applyTime,std::string id,std::string objPath)54 auto Manager::processImage(sdbusplus::message::unix_fd image,
55 ApplyTimeIntf::RequestedApplyTimes applyTime,
56 std::string id, std::string objPath)
57 -> sdbusplus::async::task<>
58 {
59 debug("Processing image {FD}", "FD", image.fd);
60 fs::path tmpDirPath(std::string{IMG_UPLOAD_DIR});
61 tmpDirPath /= "imageXXXXXX";
62 auto tmpDir = tmpDirPath.string();
63 // Create a tmp dir to copy tarball.
64 if (!mkdtemp(tmpDir.data()))
65 {
66 error("Error ({ERRNO}) occurred during mkdtemp", "ERRNO", errno);
67 processImageFailed(image, id);
68 report<SoftwareErrors::InternalFailure>(InternalFail::FAIL("mkdtemp"));
69 co_return;
70 }
71
72 std::error_code ec;
73 tmpDirPath = tmpDir;
74 softwareUtils::RemovablePath tmpDirToRemove(tmpDirPath);
75
76 // Untar tarball into the tmp dir
77 if (!softwareUtils::unTar(image, tmpDirPath.string()))
78 {
79 error("Error occurred during untar");
80 processImageFailed(image, id);
81 report<SoftwareErrors::UnTarFailure>(
82 UnTarFail::PATH(tmpDirPath.c_str()));
83 co_return;
84 }
85
86 fs::path manifestPath = tmpDirPath;
87 manifestPath /= MANIFEST_FILE_NAME;
88
89 // Get version
90 auto version = Version::getValue(manifestPath.string(), "version");
91 if (version.empty())
92 {
93 error("Unable to read version from manifest file");
94 processImageFailed(image, id);
95 report<SoftwareErrors::ManifestFileFailure>(
96 ManifestFail::PATH(manifestPath.string().c_str()));
97 co_return;
98 }
99
100 // Get running machine name
101 std::string currMachine = Version::getBMCMachine(OS_RELEASE_FILE);
102 if (currMachine.empty())
103 {
104 auto path = OS_RELEASE_FILE;
105 error("Failed to read machine name from osRelease: {PATH}", "PATH",
106 path);
107 processImageFailed(image, id);
108 report<SoftwareErrors::ImageFailure>(
109 ImageFail::FAIL("Failed to read machine name"),
110 ImageFail::PATH(path));
111 co_return;
112 }
113
114 // Get machine name for image to be upgraded
115 std::string machineStr =
116 Version::getValue(manifestPath.string(), "MachineName");
117 if (!machineStr.empty())
118 {
119 if (machineStr != currMachine)
120 {
121 error(
122 "BMC upgrade: Machine name doesn't match: {CURRENT_MACHINE} vs {NEW_MACHINE}",
123 "CURRENT_MACHINE", currMachine, "NEW_MACHINE", machineStr);
124 processImageFailed(image, id);
125 report<SoftwareErrors::ImageFailure>(
126 ImageFail::FAIL("Machine name does not match"),
127 ImageFail::PATH(manifestPath.string().c_str()));
128 co_return;
129 }
130 }
131 else
132 {
133 warning("No machine name in Manifest file");
134 report<SoftwareErrors::ImageFailure>(
135 ImageFail::FAIL("MANIFEST is missing machine name"),
136 ImageFail::PATH(manifestPath.string().c_str()));
137 }
138
139 // Get purpose
140 auto purposeString = Version::getValue(manifestPath.string(), "purpose");
141 if (purposeString.empty())
142 {
143 error("Unable to read purpose from manifest file");
144 processImageFailed(image, id);
145 report<SoftwareErrors::ManifestFileFailure>(
146 ManifestFail::PATH(manifestPath.string().c_str()));
147 co_return;
148 }
149 auto convertedPurpose =
150 sdbusplus::message::convert_from_string<Version::VersionPurpose>(
151 purposeString);
152 if (!convertedPurpose)
153 {
154 warning(
155 "Failed to convert manifest purpose ({PURPOSE}) to enum; setting to Unknown.",
156 "PURPOSE", purposeString);
157 }
158 auto purpose = convertedPurpose.value_or(Version::VersionPurpose::Unknown);
159
160 if (!verifyImagePurpose(purpose, itemUpdater.type))
161 {
162 error("Purpose ({PURPOSE}) is not supported", "PURPOSE", purpose);
163 processImageFailed(image, id);
164 report<SoftwareErrors::ImageFailure>(
165 ImageFail::FAIL("Purpose is not supported"),
166 ImageFail::PATH(manifestPath.string().c_str()));
167 co_return;
168 }
169
170 // Get ExtendedVersion
171 std::string extendedVersion =
172 Version::getValue(manifestPath.string(), "ExtendedVersion");
173
174 // Get CompatibleNames
175 std::vector<std::string> compatibleNames =
176 Version::getRepeatedValues(manifestPath.string(), "CompatibleName");
177
178 // Rename IMG_UPLOAD_DIR/imageXXXXXX to IMG_UPLOAD_DIR/id as Manifest
179 // parsing succedded.
180 fs::path imageDirPath = std::string{IMG_UPLOAD_DIR};
181 imageDirPath /= id;
182 fs::rename(tmpDirPath, imageDirPath, ec);
183 tmpDirToRemove.path.clear();
184
185 auto filePath = imageDirPath.string();
186 // Create Version object
187 auto state = itemUpdater.verifyAndCreateObjects(
188 id, objPath, version, purpose, extendedVersion, filePath,
189 compatibleNames);
190 if (state != ActivationIntf::Activations::Ready)
191 {
192 error("Software image is invalid");
193 processImageFailed(image, id);
194 report<SoftwareErrors::ImageFailure>(
195 ImageFail::FAIL("Image is invalid"),
196 ImageFail::PATH(filePath.c_str()));
197 co_return;
198 }
199 if (applyTime == ApplyTimeIntf::RequestedApplyTimes::Immediate ||
200 applyTime == ApplyTimeIntf::RequestedApplyTimes::OnReset)
201 {
202 itemUpdater.requestActivation(id);
203 }
204
205 updateInProgress = false;
206 close(image);
207 co_return;
208 }
209
startUpdate(sdbusplus::message::unix_fd image,ApplyTimeIntf::RequestedApplyTimes applyTime)210 sdbusplus::message::object_path Manager::startUpdate(
211 sdbusplus::message::unix_fd image,
212 ApplyTimeIntf::RequestedApplyTimes applyTime)
213 {
214 info("Starting update for image {FD}", "FD", static_cast<int>(image));
215 using sdbusplus::xyz::openbmc_project::Common::Error::Unavailable;
216 if (updateInProgress)
217 {
218 error("Failed to start as update is already in progress");
219 report<Unavailable>();
220 return sdbusplus::message::object_path();
221 }
222 updateInProgress = true;
223
224 auto id = Version::getId(std::to_string(randomGen()));
225 auto objPath = std::string{SOFTWARE_OBJPATH} + '/' + id;
226
227 // Create Activation Object
228 itemUpdater.createActivationWithApplyTime(id, objPath, applyTime);
229
230 int newFd = dup(image);
231 ctx.spawn(processImage(newFd, applyTime, id, objPath));
232
233 return sdbusplus::message::object_path(objPath);
234 }
235
236 } // namespace phosphor::software::update
237