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