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