blob: 9d90c95f3c6b13a411344dc63abd8f2e55ac01a9 [file] [log] [blame]
Jagpal Singh Gill6d131aa2024-04-07 23:56:48 -07001#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
15PHOSPHOR_LOG2_USING;
16
17namespace phosphor::software::update
18{
19
20namespace fs = std::filesystem;
21namespace softwareUtils = phosphor::software::utils;
22using namespace phosphor::logging;
23using Version = phosphor::software::manager::Version;
24using ActivationIntf = phosphor::software::updater::Activation;
25
26void 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)
36auto 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
163sdbusplus::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