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 
processImageFailed(sdbusplus::message::unix_fd image,std::string & id)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)
processImage(sdbusplus::message::unix_fd image,ApplyTimeIntf::RequestedApplyTimes applyTime,std::string id,std::string objPath)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
startUpdate(sdbusplus::message::unix_fd image,ApplyTimeIntf::RequestedApplyTimes applyTime)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