blob: dae8fdfd06e27e566b9aa13903f13cd48934dff1 [file] [log] [blame]
Gunnar Mills19e4ce52017-04-27 11:24:19 -05001#include "config.h"
Gunnar Millsb0ce9962018-09-07 13:39:10 -05002
3#include "image_manager.hpp"
4
Gunnar Mills19e4ce52017-04-27 11:24:19 -05005#include "version.hpp"
Gunnar Mills3027bba2017-04-27 15:49:03 -05006#include "watch.hpp"
Gunnar Millsb0ce9962018-09-07 13:39:10 -05007
8#include <stdio.h>
9#include <stdlib.h>
10#include <sys/stat.h>
11#include <sys/wait.h>
12#include <unistd.h>
13
Gunnar Millsb0ce9962018-09-07 13:39:10 -050014#include <elog-errors.hpp>
Gunnar Millsb0ce9962018-09-07 13:39:10 -050015#include <phosphor-logging/elog.hpp>
16#include <phosphor-logging/log.hpp>
Adriana Kobylak43699ca2018-10-17 14:56:29 -050017#include <xyz/openbmc_project/Software/Image/error.hpp>
Gunnar Millse91d3212017-04-19 15:42:47 -050018
Adriana Kobylak58aa7502020-06-08 11:12:11 -050019#include <algorithm>
20#include <cstring>
21#include <filesystem>
22#include <string>
23
Gunnar Millse91d3212017-04-19 15:42:47 -050024namespace phosphor
25{
26namespace software
27{
28namespace manager
29{
30
Gunnar Mills19e4ce52017-04-27 11:24:19 -050031using namespace phosphor::logging;
Adriana Kobylak43699ca2018-10-17 14:56:29 -050032using namespace sdbusplus::xyz::openbmc_project::Software::Image::Error;
Gunnar Millsb30cadd2017-10-06 16:07:32 -050033namespace Software = phosphor::logging::xyz::openbmc_project::Software;
Adriana Kobylak43699ca2018-10-17 14:56:29 -050034using ManifestFail = Software::Image::ManifestFileFailure;
35using UnTarFail = Software::Image::UnTarFailure;
36using InternalFail = Software::Image::InternalFailure;
Adriana Kobylakc98d9122020-05-05 10:36:01 -050037namespace fs = std::filesystem;
Gunnar Mills19e4ce52017-04-27 11:24:19 -050038
39struct RemovablePath
40{
41 fs::path path;
42
Adriana Kobylak2285fe02018-02-27 15:36:59 -060043 RemovablePath(const fs::path& path) : path(path)
Adriana Kobylak58aa7502020-06-08 11:12:11 -050044 {}
Gunnar Mills19e4ce52017-04-27 11:24:19 -050045 ~RemovablePath()
46 {
Lei YU5d930282019-03-08 16:41:51 +080047 if (!path.empty())
Adriana Kobylak2b2cd9f2018-02-12 16:20:13 -060048 {
Lei YU5d930282019-03-08 16:41:51 +080049 std::error_code ec;
50 fs::remove_all(path, ec);
Adriana Kobylak2b2cd9f2018-02-12 16:20:13 -060051 }
Gunnar Mills19e4ce52017-04-27 11:24:19 -050052 }
53};
54
Lei YUf6144e92019-10-18 17:13:49 +080055namespace // anonymous
56{
57
58std::vector<std::string> getSoftwareObjects(sdbusplus::bus::bus& bus)
59{
60 std::vector<std::string> paths;
61 auto method = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
62 MAPPER_INTERFACE, "GetSubTreePaths");
63 method.append(SOFTWARE_OBJPATH);
64 method.append(0); // Depth 0 to search all
65 method.append(std::vector<std::string>({VERSION_BUSNAME}));
66 auto reply = bus.call(method);
67 reply.read(paths);
68 return paths;
69}
70
71} // namespace
72
Gunnar Mills3027bba2017-04-27 15:49:03 -050073int Manager::processImage(const std::string& tarFilePath)
Gunnar Millse91d3212017-04-19 15:42:47 -050074{
Gunnar Mills19e4ce52017-04-27 11:24:19 -050075 if (!fs::is_regular_file(tarFilePath))
76 {
77 log<level::ERR>("Error tarball does not exist",
Adriana Kobylak596466b2018-02-13 14:48:53 -060078 entry("FILENAME=%s", tarFilePath.c_str()));
Gunnar Millsb30cadd2017-10-06 16:07:32 -050079 report<ManifestFileFailure>(ManifestFail::PATH(tarFilePath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -050080 return -1;
Gunnar Mills19e4ce52017-04-27 11:24:19 -050081 }
82 RemovablePath tarPathRemove(tarFilePath);
83 fs::path tmpDirPath(std::string{IMG_UPLOAD_DIR});
84 tmpDirPath /= "imageXXXXXX";
Lei YU5d930282019-03-08 16:41:51 +080085 auto tmpDir = tmpDirPath.string();
Gunnar Mills19e4ce52017-04-27 11:24:19 -050086
Lei YU5d930282019-03-08 16:41:51 +080087 // Create a tmp dir to extract tarball.
88 if (!mkdtemp(tmpDir.data()))
Gunnar Mills19e4ce52017-04-27 11:24:19 -050089 {
Gunnar Mills601bbf02017-10-25 13:46:11 -050090 log<level::ERR>("Error occurred during mkdtemp",
Gunnar Mills19e4ce52017-04-27 11:24:19 -050091 entry("ERRNO=%d", errno));
Gunnar Millsb30cadd2017-10-06 16:07:32 -050092 report<InternalFailure>(InternalFail::FAIL("mkdtemp"));
Gunnar Mills19e4ce52017-04-27 11:24:19 -050093 return -1;
94 }
95
Lei YU5d930282019-03-08 16:41:51 +080096 tmpDirPath = tmpDir;
97 RemovablePath tmpDirToRemove(tmpDirPath);
Gunnar Mills19e4ce52017-04-27 11:24:19 -050098 fs::path manifestPath = tmpDirPath;
99 manifestPath /= MANIFEST_FILE_NAME;
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500100
Lei YU5d930282019-03-08 16:41:51 +0800101 // Untar tarball into the tmp dir
102 auto rc = unTar(tarFilePath, tmpDirPath.string());
103 if (rc < 0)
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500104 {
Lei YU5d930282019-03-08 16:41:51 +0800105 log<level::ERR>("Error occurred during untar");
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500106 return -1;
107 }
108
109 // Verify the manifest file
110 if (!fs::is_regular_file(manifestPath))
111 {
Adriana Kobylak596466b2018-02-13 14:48:53 -0600112 log<level::ERR>("Error No manifest file",
113 entry("FILENAME=%s", tarFilePath.c_str()));
114 report<ManifestFileFailure>(ManifestFail::PATH(tarFilePath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500115 return -1;
116 }
117
118 // Get version
119 auto version = Version::getValue(manifestPath.string(), "version");
120 if (version.empty())
121 {
122 log<level::ERR>("Error unable to read version from manifest file");
Adriana Kobylak596466b2018-02-13 14:48:53 -0600123 report<ManifestFileFailure>(ManifestFail::PATH(tarFilePath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500124 return -1;
125 }
126
Vijay Khemkab7c062e2019-09-18 17:15:57 -0700127 // Get running machine name
128 std::string currMachine = Version::getBMCMachine(OS_RELEASE_FILE);
129 if (currMachine.empty())
130 {
131 log<level::ERR>("Failed to read machine name from osRelease",
132 entry("FILENAME=%s", OS_RELEASE_FILE));
133 return -1;
134 }
135
136 // Get machine name for image to be upgraded
137 std::string machineStr =
138 Version::getValue(manifestPath.string(), "MachineName");
139 if (!machineStr.empty())
140 {
141 if (machineStr != currMachine)
142 {
143 log<level::ERR>("BMC upgrade: Machine name doesn't match",
144 entry("CURR_MACHINE=%s", currMachine.c_str()),
145 entry("NEW_MACHINE=%s", machineStr.c_str()));
146 return -1;
147 }
148 }
149 else
150 {
151 log<level::WARNING>("No machine name in Manifest file");
152 }
153
Gunnar Mills3027bba2017-04-27 15:49:03 -0500154 // Get purpose
155 auto purposeString = Version::getValue(manifestPath.string(), "purpose");
156 if (purposeString.empty())
157 {
158 log<level::ERR>("Error unable to read purpose from manifest file");
Adriana Kobylak596466b2018-02-13 14:48:53 -0600159 report<ManifestFileFailure>(ManifestFail::PATH(tarFilePath.c_str()));
Gunnar Mills3027bba2017-04-27 15:49:03 -0500160 return -1;
161 }
162
Gunnar Mills3027bba2017-04-27 15:49:03 -0500163 auto purpose = Version::VersionPurpose::Unknown;
Gunnar Millsb30cadd2017-10-06 16:07:32 -0500164 try
165 {
Leonel Gonzalez9a8d14c2017-06-22 11:42:24 -0500166 purpose = Version::convertVersionPurposeFromString(purposeString);
Gunnar Millsb30cadd2017-10-06 16:07:32 -0500167 }
168 catch (const sdbusplus::exception::InvalidEnumString& e)
169 {
Adriana Kobylak2285fe02018-02-27 15:36:59 -0600170 log<level::ERR>("Error: Failed to convert manifest purpose to enum."
Gunnar Millsb30cadd2017-10-06 16:07:32 -0500171 " Setting to Unknown.");
Gunnar Mills3027bba2017-04-27 15:49:03 -0500172 }
173
Chanh Nguyen1fd6ddd2021-01-06 11:09:09 +0700174 // Get ExtendedVersion
175 std::string extendedVersion =
176 Version::getValue(manifestPath.string(), "ExtendedVersion");
177
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500178 // Compute id
179 auto id = Version::getId(version);
180
181 fs::path imageDirPath = std::string{IMG_UPLOAD_DIR};
182 imageDirPath /= id;
183
Gunnar Millsf576bca2017-05-18 13:40:39 -0500184 if (fs::exists(imageDirPath))
185 {
186 fs::remove_all(imageDirPath);
187 }
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500188
Lei YU5d930282019-03-08 16:41:51 +0800189 // Rename the temp dir to image dir
190 fs::rename(tmpDirPath, imageDirPath);
191
192 // Clear the path, so it does not attemp to remove a non-existing path
193 tmpDirToRemove.path.clear();
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500194
Adriana Kobylak2285fe02018-02-27 15:36:59 -0600195 auto objPath = std::string{SOFTWARE_OBJPATH} + '/' + id;
Gunnar Mills3027bba2017-04-27 15:49:03 -0500196
Lei YUf6144e92019-10-18 17:13:49 +0800197 // This service only manages the uploaded versions, and there could be
198 // active versions on D-Bus that is not managed by this service.
199 // So check D-Bus if there is an existing version.
200 auto allSoftwareObjs = getSoftwareObjects(bus);
201 auto it =
202 std::find(allSoftwareObjs.begin(), allSoftwareObjs.end(), objPath);
203 if (versions.find(id) == versions.end() && it == allSoftwareObjs.end())
Saqib Khand377ba12017-10-16 14:32:30 -0500204 {
Lei YUf6144e92019-10-18 17:13:49 +0800205 // Create Version object
Saqib Khanee13e832017-10-23 12:53:11 -0500206 auto versionPtr = std::make_unique<Version>(
Chanh Nguyen1fd6ddd2021-01-06 11:09:09 +0700207 bus, objPath, version, purpose, extendedVersion,
208 imageDirPath.string(),
Adriana Kobylak2285fe02018-02-27 15:36:59 -0600209 std::bind(&Manager::erase, this, std::placeholders::_1));
Saqib Khanee13e832017-10-23 12:53:11 -0500210 versionPtr->deleteObject =
Adriana Kobylak2285fe02018-02-27 15:36:59 -0600211 std::make_unique<phosphor::software::manager::Delete>(bus, objPath,
212 *versionPtr);
Saqib Khanee13e832017-10-23 12:53:11 -0500213 versions.insert(std::make_pair(id, std::move(versionPtr)));
Saqib Khand377ba12017-10-16 14:32:30 -0500214 }
215 else
216 {
217 log<level::INFO>("Software Object with the same version already exists",
Adriana Kobylak596466b2018-02-13 14:48:53 -0600218 entry("VERSION_ID=%s", id.c_str()));
Adriana Kobylak13e09912020-08-31 13:55:10 -0500219 fs::remove_all(imageDirPath);
Saqib Khand377ba12017-10-16 14:32:30 -0500220 }
Gunnar Millse91d3212017-04-19 15:42:47 -0500221 return 0;
222}
223
Leonel Gonzalez50d559c2017-07-07 14:38:25 -0500224void Manager::erase(std::string entryId)
225{
226 auto it = versions.find(entryId);
227 if (it == versions.end())
228 {
229 return;
230 }
Eddie James6d873712017-09-01 11:29:07 -0500231
232 if (it->second->isFunctional())
233 {
Adriana Kobylak2285fe02018-02-27 15:36:59 -0600234 log<level::ERR>(("Error: Version " + entryId +
235 " is currently running on the BMC."
236 " Unable to remove.")
237 .c_str());
Eddie James6d873712017-09-01 11:29:07 -0500238 return;
239 }
240
Leonel Gonzalez50d559c2017-07-07 14:38:25 -0500241 // Delete image dir
242 fs::path imageDirPath = (*(it->second)).path();
243 if (fs::exists(imageDirPath))
244 {
245 fs::remove_all(imageDirPath);
246 }
247 this->versions.erase(entryId);
248}
249
Gunnar Mills3027bba2017-04-27 15:49:03 -0500250int Manager::unTar(const std::string& tarFilePath,
251 const std::string& extractDirPath)
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500252{
253 if (tarFilePath.empty())
254 {
255 log<level::ERR>("Error TarFilePath is empty");
Gunnar Millsb30cadd2017-10-06 16:07:32 -0500256 report<UnTarFailure>(UnTarFail::PATH(tarFilePath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500257 return -1;
258 }
259 if (extractDirPath.empty())
260 {
261 log<level::ERR>("Error ExtractDirPath is empty");
Adriana Kobylak596466b2018-02-13 14:48:53 -0600262 report<UnTarFailure>(UnTarFail::PATH(extractDirPath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500263 return -1;
264 }
265
Adriana Kobylak2285fe02018-02-27 15:36:59 -0600266 log<level::INFO>("Untaring", entry("FILENAME=%s", tarFilePath.c_str()),
Adriana Kobylak596466b2018-02-13 14:48:53 -0600267 entry("EXTRACTIONDIR=%s", extractDirPath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500268 int status = 0;
269 pid_t pid = fork();
270
271 if (pid == 0)
272 {
273 // child process
Adriana Kobylak2285fe02018-02-27 15:36:59 -0600274 execl("/bin/tar", "tar", "-xf", tarFilePath.c_str(), "-C",
275 extractDirPath.c_str(), (char*)0);
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500276 // execl only returns on fail
Adriana Kobylak596466b2018-02-13 14:48:53 -0600277 log<level::ERR>("Failed to execute untar file",
278 entry("FILENAME=%s", tarFilePath.c_str()));
Gunnar Millsb30cadd2017-10-06 16:07:32 -0500279 report<UnTarFailure>(UnTarFail::PATH(tarFilePath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500280 return -1;
281 }
282 else if (pid > 0)
283 {
284 waitpid(pid, &status, 0);
Adriana Kobylak596466b2018-02-13 14:48:53 -0600285 if (WEXITSTATUS(status))
286 {
287 log<level::ERR>("Failed to untar file",
288 entry("FILENAME=%s", tarFilePath.c_str()));
289 report<UnTarFailure>(UnTarFail::PATH(tarFilePath.c_str()));
290 return -1;
291 }
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500292 }
293 else
294 {
295 log<level::ERR>("fork() failed.");
Gunnar Millsb30cadd2017-10-06 16:07:32 -0500296 report<UnTarFailure>(UnTarFail::PATH(tarFilePath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500297 return -1;
298 }
299
300 return 0;
301}
Gunnar Mills3027bba2017-04-27 15:49:03 -0500302
Gunnar Millse91d3212017-04-19 15:42:47 -0500303} // namespace manager
304} // namespace software
Gunnar Millsfa34e022018-09-04 10:05:45 -0500305} // namespace phosphor