1*cab87e9cSJagpal Singh Gill #include "config.h"
2*cab87e9cSJagpal Singh Gill
3*cab87e9cSJagpal Singh Gill #include "image_manager.hpp"
4*cab87e9cSJagpal Singh Gill
5*cab87e9cSJagpal Singh Gill #include "version.hpp"
6*cab87e9cSJagpal Singh Gill #include "watch.hpp"
7*cab87e9cSJagpal Singh Gill
8*cab87e9cSJagpal Singh Gill #include <stdio.h>
9*cab87e9cSJagpal Singh Gill #include <stdlib.h>
10*cab87e9cSJagpal Singh Gill #include <sys/stat.h>
11*cab87e9cSJagpal Singh Gill #include <sys/wait.h>
12*cab87e9cSJagpal Singh Gill #include <unistd.h>
13*cab87e9cSJagpal Singh Gill
14*cab87e9cSJagpal Singh Gill #include <phosphor-logging/elog-errors.hpp>
15*cab87e9cSJagpal Singh Gill #include <phosphor-logging/elog.hpp>
16*cab87e9cSJagpal Singh Gill #include <phosphor-logging/lg2.hpp>
17*cab87e9cSJagpal Singh Gill #include <xyz/openbmc_project/Software/Image/error.hpp>
18*cab87e9cSJagpal Singh Gill
19*cab87e9cSJagpal Singh Gill #include <algorithm>
20*cab87e9cSJagpal Singh Gill #include <cstring>
21*cab87e9cSJagpal Singh Gill #include <ctime>
22*cab87e9cSJagpal Singh Gill #include <filesystem>
23*cab87e9cSJagpal Singh Gill #include <random>
24*cab87e9cSJagpal Singh Gill #include <string>
25*cab87e9cSJagpal Singh Gill #include <system_error>
26*cab87e9cSJagpal Singh Gill
27*cab87e9cSJagpal Singh Gill namespace phosphor
28*cab87e9cSJagpal Singh Gill {
29*cab87e9cSJagpal Singh Gill namespace software
30*cab87e9cSJagpal Singh Gill {
31*cab87e9cSJagpal Singh Gill namespace manager
32*cab87e9cSJagpal Singh Gill {
33*cab87e9cSJagpal Singh Gill
34*cab87e9cSJagpal Singh Gill PHOSPHOR_LOG2_USING;
35*cab87e9cSJagpal Singh Gill using namespace phosphor::logging;
36*cab87e9cSJagpal Singh Gill using namespace sdbusplus::error::xyz::openbmc_project::software::image;
37*cab87e9cSJagpal Singh Gill namespace Software = phosphor::logging::xyz::openbmc_project::software;
38*cab87e9cSJagpal Singh Gill using ManifestFail = Software::image::ManifestFileFailure;
39*cab87e9cSJagpal Singh Gill using UnTarFail = Software::image::UnTarFailure;
40*cab87e9cSJagpal Singh Gill using InternalFail = Software::image::InternalFailure;
41*cab87e9cSJagpal Singh Gill using ImageFail = Software::image::ImageFailure;
42*cab87e9cSJagpal Singh Gill namespace fs = std::filesystem;
43*cab87e9cSJagpal Singh Gill
44*cab87e9cSJagpal Singh Gill struct RemovablePath
45*cab87e9cSJagpal Singh Gill {
46*cab87e9cSJagpal Singh Gill fs::path path;
47*cab87e9cSJagpal Singh Gill
RemovablePathphosphor::software::manager::RemovablePath48*cab87e9cSJagpal Singh Gill explicit RemovablePath(const fs::path& path) : path(path) {}
~RemovablePathphosphor::software::manager::RemovablePath49*cab87e9cSJagpal Singh Gill ~RemovablePath()
50*cab87e9cSJagpal Singh Gill {
51*cab87e9cSJagpal Singh Gill if (!path.empty())
52*cab87e9cSJagpal Singh Gill {
53*cab87e9cSJagpal Singh Gill std::error_code ec;
54*cab87e9cSJagpal Singh Gill fs::remove_all(path, ec);
55*cab87e9cSJagpal Singh Gill }
56*cab87e9cSJagpal Singh Gill }
57*cab87e9cSJagpal Singh Gill
58*cab87e9cSJagpal Singh Gill RemovablePath(const RemovablePath& other) = delete;
59*cab87e9cSJagpal Singh Gill RemovablePath& operator=(const RemovablePath& other) = delete;
60*cab87e9cSJagpal Singh Gill RemovablePath(RemovablePath&&) = delete;
61*cab87e9cSJagpal Singh Gill RemovablePath& operator=(RemovablePath&&) = delete;
62*cab87e9cSJagpal Singh Gill };
63*cab87e9cSJagpal Singh Gill
64*cab87e9cSJagpal Singh Gill namespace // anonymous
65*cab87e9cSJagpal Singh Gill {
66*cab87e9cSJagpal Singh Gill
getSoftwareObjects(sdbusplus::bus_t & bus)67*cab87e9cSJagpal Singh Gill std::vector<std::string> getSoftwareObjects(sdbusplus::bus_t& bus)
68*cab87e9cSJagpal Singh Gill {
69*cab87e9cSJagpal Singh Gill std::vector<std::string> paths;
70*cab87e9cSJagpal Singh Gill auto method = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
71*cab87e9cSJagpal Singh Gill MAPPER_INTERFACE, "GetSubTreePaths");
72*cab87e9cSJagpal Singh Gill method.append(SOFTWARE_OBJPATH);
73*cab87e9cSJagpal Singh Gill method.append(0); // Depth 0 to search all
74*cab87e9cSJagpal Singh Gill method.append(std::vector<std::string>({VERSION_BUSNAME}));
75*cab87e9cSJagpal Singh Gill auto reply = bus.call(method);
76*cab87e9cSJagpal Singh Gill reply.read(paths);
77*cab87e9cSJagpal Singh Gill return paths;
78*cab87e9cSJagpal Singh Gill }
79*cab87e9cSJagpal Singh Gill
80*cab87e9cSJagpal Singh Gill } // namespace
81*cab87e9cSJagpal Singh Gill
processImage(const std::string & tarFilePath)82*cab87e9cSJagpal Singh Gill int Manager::processImage(const std::string& tarFilePath)
83*cab87e9cSJagpal Singh Gill {
84*cab87e9cSJagpal Singh Gill std::error_code ec;
85*cab87e9cSJagpal Singh Gill if (!fs::is_regular_file(tarFilePath, ec))
86*cab87e9cSJagpal Singh Gill {
87*cab87e9cSJagpal Singh Gill error("Tarball {PATH} does not exist: {ERROR_MSG}", "PATH", tarFilePath,
88*cab87e9cSJagpal Singh Gill "ERROR_MSG", ec.message());
89*cab87e9cSJagpal Singh Gill report<ManifestFileFailure>(ManifestFail::PATH(tarFilePath.c_str()));
90*cab87e9cSJagpal Singh Gill return -1;
91*cab87e9cSJagpal Singh Gill }
92*cab87e9cSJagpal Singh Gill RemovablePath tarPathRemove(tarFilePath);
93*cab87e9cSJagpal Singh Gill fs::path tmpDirPath(std::string{IMG_UPLOAD_DIR});
94*cab87e9cSJagpal Singh Gill tmpDirPath /= "imageXXXXXX";
95*cab87e9cSJagpal Singh Gill auto tmpDir = tmpDirPath.string();
96*cab87e9cSJagpal Singh Gill
97*cab87e9cSJagpal Singh Gill // Create a tmp dir to extract tarball.
98*cab87e9cSJagpal Singh Gill if (!mkdtemp(tmpDir.data()))
99*cab87e9cSJagpal Singh Gill {
100*cab87e9cSJagpal Singh Gill error("Error ({ERRNO}) occurred during mkdtemp", "ERRNO", errno);
101*cab87e9cSJagpal Singh Gill report<InternalFailure>(InternalFail::FAIL("mkdtemp"));
102*cab87e9cSJagpal Singh Gill return -1;
103*cab87e9cSJagpal Singh Gill }
104*cab87e9cSJagpal Singh Gill
105*cab87e9cSJagpal Singh Gill tmpDirPath = tmpDir;
106*cab87e9cSJagpal Singh Gill RemovablePath tmpDirToRemove(tmpDirPath);
107*cab87e9cSJagpal Singh Gill fs::path manifestPath = tmpDirPath;
108*cab87e9cSJagpal Singh Gill manifestPath /= MANIFEST_FILE_NAME;
109*cab87e9cSJagpal Singh Gill
110*cab87e9cSJagpal Singh Gill // Untar tarball into the tmp dir
111*cab87e9cSJagpal Singh Gill auto rc = unTar(tarFilePath, tmpDirPath.string());
112*cab87e9cSJagpal Singh Gill if (rc < 0)
113*cab87e9cSJagpal Singh Gill {
114*cab87e9cSJagpal Singh Gill error("Error ({RC}) occurred during untar", "RC", rc);
115*cab87e9cSJagpal Singh Gill return -1;
116*cab87e9cSJagpal Singh Gill }
117*cab87e9cSJagpal Singh Gill
118*cab87e9cSJagpal Singh Gill // Verify the manifest file
119*cab87e9cSJagpal Singh Gill if (!fs::is_regular_file(manifestPath, ec))
120*cab87e9cSJagpal Singh Gill {
121*cab87e9cSJagpal Singh Gill error("No manifest file {PATH}: {ERROR_MSG}", "PATH", tarFilePath,
122*cab87e9cSJagpal Singh Gill "ERROR_MSG", ec.message());
123*cab87e9cSJagpal Singh Gill report<ManifestFileFailure>(ManifestFail::PATH(tarFilePath.c_str()));
124*cab87e9cSJagpal Singh Gill return -1;
125*cab87e9cSJagpal Singh Gill }
126*cab87e9cSJagpal Singh Gill
127*cab87e9cSJagpal Singh Gill // Get version
128*cab87e9cSJagpal Singh Gill auto version = Version::getValue(manifestPath.string(), "version");
129*cab87e9cSJagpal Singh Gill if (version.empty())
130*cab87e9cSJagpal Singh Gill {
131*cab87e9cSJagpal Singh Gill error("Unable to read version from manifest file {PATH}", "PATH",
132*cab87e9cSJagpal Singh Gill tarFilePath);
133*cab87e9cSJagpal Singh Gill report<ManifestFileFailure>(ManifestFail::PATH(tarFilePath.c_str()));
134*cab87e9cSJagpal Singh Gill return -1;
135*cab87e9cSJagpal Singh Gill }
136*cab87e9cSJagpal Singh Gill
137*cab87e9cSJagpal Singh Gill // Get running machine name
138*cab87e9cSJagpal Singh Gill std::string currMachine = Version::getBMCMachine(OS_RELEASE_FILE);
139*cab87e9cSJagpal Singh Gill if (currMachine.empty())
140*cab87e9cSJagpal Singh Gill {
141*cab87e9cSJagpal Singh Gill auto path = OS_RELEASE_FILE;
142*cab87e9cSJagpal Singh Gill error("Failed to read machine name from osRelease: {PATH}", "PATH",
143*cab87e9cSJagpal Singh Gill path);
144*cab87e9cSJagpal Singh Gill report<ImageFailure>(ImageFail::FAIL("Failed to read machine name"),
145*cab87e9cSJagpal Singh Gill ImageFail::PATH(path));
146*cab87e9cSJagpal Singh Gill return -1;
147*cab87e9cSJagpal Singh Gill }
148*cab87e9cSJagpal Singh Gill
149*cab87e9cSJagpal Singh Gill // Get machine name for image to be upgraded
150*cab87e9cSJagpal Singh Gill std::string machineStr =
151*cab87e9cSJagpal Singh Gill Version::getValue(manifestPath.string(), "MachineName");
152*cab87e9cSJagpal Singh Gill if (!machineStr.empty())
153*cab87e9cSJagpal Singh Gill {
154*cab87e9cSJagpal Singh Gill if (machineStr != currMachine)
155*cab87e9cSJagpal Singh Gill {
156*cab87e9cSJagpal Singh Gill error(
157*cab87e9cSJagpal Singh Gill "BMC upgrade: Machine name doesn't match: {CURRENT_MACHINE} vs {NEW_MACHINE}",
158*cab87e9cSJagpal Singh Gill "CURRENT_MACHINE", currMachine, "NEW_MACHINE", machineStr);
159*cab87e9cSJagpal Singh Gill report<ImageFailure>(
160*cab87e9cSJagpal Singh Gill ImageFail::FAIL("Machine name does not match"),
161*cab87e9cSJagpal Singh Gill ImageFail::PATH(manifestPath.string().c_str()));
162*cab87e9cSJagpal Singh Gill return -1;
163*cab87e9cSJagpal Singh Gill }
164*cab87e9cSJagpal Singh Gill }
165*cab87e9cSJagpal Singh Gill else
166*cab87e9cSJagpal Singh Gill {
167*cab87e9cSJagpal Singh Gill warning("No machine name in Manifest file");
168*cab87e9cSJagpal Singh Gill report<ImageFailure>(
169*cab87e9cSJagpal Singh Gill ImageFail::FAIL("MANIFEST is missing machine name"),
170*cab87e9cSJagpal Singh Gill ImageFail::PATH(manifestPath.string().c_str()));
171*cab87e9cSJagpal Singh Gill }
172*cab87e9cSJagpal Singh Gill
173*cab87e9cSJagpal Singh Gill // Get purpose
174*cab87e9cSJagpal Singh Gill auto purposeString = Version::getValue(manifestPath.string(), "purpose");
175*cab87e9cSJagpal Singh Gill if (purposeString.empty())
176*cab87e9cSJagpal Singh Gill {
177*cab87e9cSJagpal Singh Gill error("Unable to read purpose from manifest file {PATH}", "PATH",
178*cab87e9cSJagpal Singh Gill tarFilePath);
179*cab87e9cSJagpal Singh Gill report<ManifestFileFailure>(ManifestFail::PATH(tarFilePath.c_str()));
180*cab87e9cSJagpal Singh Gill return -1;
181*cab87e9cSJagpal Singh Gill }
182*cab87e9cSJagpal Singh Gill
183*cab87e9cSJagpal Singh Gill auto convertedPurpose =
184*cab87e9cSJagpal Singh Gill sdbusplus::message::convert_from_string<Version::VersionPurpose>(
185*cab87e9cSJagpal Singh Gill purposeString);
186*cab87e9cSJagpal Singh Gill
187*cab87e9cSJagpal Singh Gill if (!convertedPurpose)
188*cab87e9cSJagpal Singh Gill {
189*cab87e9cSJagpal Singh Gill error(
190*cab87e9cSJagpal Singh Gill "Failed to convert manifest purpose ({PURPOSE}) to enum; setting to Unknown.",
191*cab87e9cSJagpal Singh Gill "PURPOSE", purposeString);
192*cab87e9cSJagpal Singh Gill }
193*cab87e9cSJagpal Singh Gill auto purpose = convertedPurpose.value_or(Version::VersionPurpose::Unknown);
194*cab87e9cSJagpal Singh Gill
195*cab87e9cSJagpal Singh Gill // Get ExtendedVersion
196*cab87e9cSJagpal Singh Gill std::string extendedVersion =
197*cab87e9cSJagpal Singh Gill Version::getValue(manifestPath.string(), "ExtendedVersion");
198*cab87e9cSJagpal Singh Gill
199*cab87e9cSJagpal Singh Gill // Get CompatibleNames
200*cab87e9cSJagpal Singh Gill std::vector<std::string> compatibleNames =
201*cab87e9cSJagpal Singh Gill Version::getRepeatedValues(manifestPath.string(), "CompatibleName");
202*cab87e9cSJagpal Singh Gill
203*cab87e9cSJagpal Singh Gill // Compute id
204*cab87e9cSJagpal Singh Gill auto salt = std::to_string(randomGen());
205*cab87e9cSJagpal Singh Gill auto id = Version::getId(version + salt);
206*cab87e9cSJagpal Singh Gill
207*cab87e9cSJagpal Singh Gill fs::path imageDirPath = std::string{IMG_UPLOAD_DIR};
208*cab87e9cSJagpal Singh Gill imageDirPath /= id;
209*cab87e9cSJagpal Singh Gill
210*cab87e9cSJagpal Singh Gill auto objPath = std::string{SOFTWARE_OBJPATH} + '/' + id;
211*cab87e9cSJagpal Singh Gill
212*cab87e9cSJagpal Singh Gill // This service only manages the uploaded versions, and there could be
213*cab87e9cSJagpal Singh Gill // active versions on D-Bus that is not managed by this service.
214*cab87e9cSJagpal Singh Gill // So check D-Bus if there is an existing version.
215*cab87e9cSJagpal Singh Gill auto allSoftwareObjs = getSoftwareObjects(bus);
216*cab87e9cSJagpal Singh Gill auto it =
217*cab87e9cSJagpal Singh Gill std::find(allSoftwareObjs.begin(), allSoftwareObjs.end(), objPath);
218*cab87e9cSJagpal Singh Gill if (versions.find(id) == versions.end() && it == allSoftwareObjs.end())
219*cab87e9cSJagpal Singh Gill {
220*cab87e9cSJagpal Singh Gill // Rename the temp dir to image dir
221*cab87e9cSJagpal Singh Gill fs::rename(tmpDirPath, imageDirPath, ec);
222*cab87e9cSJagpal Singh Gill // Clear the path, so it does not attempt to remove a non-existing path
223*cab87e9cSJagpal Singh Gill tmpDirToRemove.path.clear();
224*cab87e9cSJagpal Singh Gill
225*cab87e9cSJagpal Singh Gill // Create Version object
226*cab87e9cSJagpal Singh Gill auto versionPtr = std::make_unique<Version>(
227*cab87e9cSJagpal Singh Gill bus, objPath, version, purpose, extendedVersion,
228*cab87e9cSJagpal Singh Gill imageDirPath.string(), compatibleNames,
229*cab87e9cSJagpal Singh Gill std::bind(&Manager::erase, this, std::placeholders::_1), id);
230*cab87e9cSJagpal Singh Gill versionPtr->deleteObject =
231*cab87e9cSJagpal Singh Gill std::make_unique<phosphor::software::manager::Delete>(
232*cab87e9cSJagpal Singh Gill bus, objPath, *versionPtr);
233*cab87e9cSJagpal Singh Gill versions.insert(std::make_pair(id, std::move(versionPtr)));
234*cab87e9cSJagpal Singh Gill }
235*cab87e9cSJagpal Singh Gill else
236*cab87e9cSJagpal Singh Gill {
237*cab87e9cSJagpal Singh Gill info("Software Object with the same version ({VERSION}) already exists",
238*cab87e9cSJagpal Singh Gill "VERSION", id);
239*cab87e9cSJagpal Singh Gill }
240*cab87e9cSJagpal Singh Gill return 0;
241*cab87e9cSJagpal Singh Gill }
242*cab87e9cSJagpal Singh Gill
erase(const std::string & entryId)243*cab87e9cSJagpal Singh Gill void Manager::erase(const std::string& entryId)
244*cab87e9cSJagpal Singh Gill {
245*cab87e9cSJagpal Singh Gill auto it = versions.find(entryId);
246*cab87e9cSJagpal Singh Gill if (it == versions.end())
247*cab87e9cSJagpal Singh Gill {
248*cab87e9cSJagpal Singh Gill return;
249*cab87e9cSJagpal Singh Gill }
250*cab87e9cSJagpal Singh Gill
251*cab87e9cSJagpal Singh Gill // Delete image dir
252*cab87e9cSJagpal Singh Gill fs::path imageDirPath = (*(it->second)).path();
253*cab87e9cSJagpal Singh Gill std::error_code ec;
254*cab87e9cSJagpal Singh Gill if (fs::exists(imageDirPath, ec))
255*cab87e9cSJagpal Singh Gill {
256*cab87e9cSJagpal Singh Gill fs::remove_all(imageDirPath, ec);
257*cab87e9cSJagpal Singh Gill }
258*cab87e9cSJagpal Singh Gill this->versions.erase(entryId);
259*cab87e9cSJagpal Singh Gill }
260*cab87e9cSJagpal Singh Gill
unTar(const std::string & tarFilePath,const std::string & extractDirPath)261*cab87e9cSJagpal Singh Gill int Manager::unTar(const std::string& tarFilePath,
262*cab87e9cSJagpal Singh Gill const std::string& extractDirPath)
263*cab87e9cSJagpal Singh Gill {
264*cab87e9cSJagpal Singh Gill if (tarFilePath.empty())
265*cab87e9cSJagpal Singh Gill {
266*cab87e9cSJagpal Singh Gill error("TarFilePath is empty");
267*cab87e9cSJagpal Singh Gill report<UnTarFailure>(UnTarFail::PATH(tarFilePath.c_str()));
268*cab87e9cSJagpal Singh Gill return -1;
269*cab87e9cSJagpal Singh Gill }
270*cab87e9cSJagpal Singh Gill if (extractDirPath.empty())
271*cab87e9cSJagpal Singh Gill {
272*cab87e9cSJagpal Singh Gill error("ExtractDirPath is empty");
273*cab87e9cSJagpal Singh Gill report<UnTarFailure>(UnTarFail::PATH(extractDirPath.c_str()));
274*cab87e9cSJagpal Singh Gill return -1;
275*cab87e9cSJagpal Singh Gill }
276*cab87e9cSJagpal Singh Gill
277*cab87e9cSJagpal Singh Gill info("Untaring {PATH} to {EXTRACTIONDIR}", "PATH", tarFilePath,
278*cab87e9cSJagpal Singh Gill "EXTRACTIONDIR", extractDirPath);
279*cab87e9cSJagpal Singh Gill int status = 0;
280*cab87e9cSJagpal Singh Gill pid_t pid = fork();
281*cab87e9cSJagpal Singh Gill
282*cab87e9cSJagpal Singh Gill if (pid == 0)
283*cab87e9cSJagpal Singh Gill {
284*cab87e9cSJagpal Singh Gill // child process
285*cab87e9cSJagpal Singh Gill execl("/bin/tar", "tar", "-xf", tarFilePath.c_str(), "-C",
286*cab87e9cSJagpal Singh Gill extractDirPath.c_str(), (char*)0);
287*cab87e9cSJagpal Singh Gill // execl only returns on fail
288*cab87e9cSJagpal Singh Gill error("Failed to execute untar on {PATH}", "PATH", tarFilePath);
289*cab87e9cSJagpal Singh Gill report<UnTarFailure>(UnTarFail::PATH(tarFilePath.c_str()));
290*cab87e9cSJagpal Singh Gill return -1;
291*cab87e9cSJagpal Singh Gill }
292*cab87e9cSJagpal Singh Gill else if (pid > 0)
293*cab87e9cSJagpal Singh Gill {
294*cab87e9cSJagpal Singh Gill waitpid(pid, &status, 0);
295*cab87e9cSJagpal Singh Gill if (WEXITSTATUS(status))
296*cab87e9cSJagpal Singh Gill {
297*cab87e9cSJagpal Singh Gill error("Failed ({STATUS}) to untar file {PATH}", "STATUS", status,
298*cab87e9cSJagpal Singh Gill "PATH", tarFilePath);
299*cab87e9cSJagpal Singh Gill report<UnTarFailure>(UnTarFail::PATH(tarFilePath.c_str()));
300*cab87e9cSJagpal Singh Gill return -1;
301*cab87e9cSJagpal Singh Gill }
302*cab87e9cSJagpal Singh Gill }
303*cab87e9cSJagpal Singh Gill else
304*cab87e9cSJagpal Singh Gill {
305*cab87e9cSJagpal Singh Gill error("fork() failed: {ERRNO}", "ERRNO", errno);
306*cab87e9cSJagpal Singh Gill report<UnTarFailure>(UnTarFail::PATH(tarFilePath.c_str()));
307*cab87e9cSJagpal Singh Gill return -1;
308*cab87e9cSJagpal Singh Gill }
309*cab87e9cSJagpal Singh Gill
310*cab87e9cSJagpal Singh Gill return 0;
311*cab87e9cSJagpal Singh Gill }
312*cab87e9cSJagpal Singh Gill
313*cab87e9cSJagpal Singh Gill } // namespace manager
314*cab87e9cSJagpal Singh Gill } // namespace software
315*cab87e9cSJagpal Singh Gill } // namespace phosphor
316