blob: a8f8210aba5404700ca3d274da96915bcd38d0c4 [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 Kobylak447d0da2021-03-15 13:40:52 -050037using ImageFail = Software::Image::ImageFailure;
Adriana Kobylakc98d9122020-05-05 10:36:01 -050038namespace fs = std::filesystem;
Gunnar Mills19e4ce52017-04-27 11:24:19 -050039
40struct RemovablePath
41{
42 fs::path path;
43
Adriana Kobylak2285fe02018-02-27 15:36:59 -060044 RemovablePath(const fs::path& path) : path(path)
Adriana Kobylak58aa7502020-06-08 11:12:11 -050045 {}
Gunnar Mills19e4ce52017-04-27 11:24:19 -050046 ~RemovablePath()
47 {
Lei YU5d930282019-03-08 16:41:51 +080048 if (!path.empty())
Adriana Kobylak2b2cd9f2018-02-12 16:20:13 -060049 {
Lei YU5d930282019-03-08 16:41:51 +080050 std::error_code ec;
51 fs::remove_all(path, ec);
Adriana Kobylak2b2cd9f2018-02-12 16:20:13 -060052 }
Gunnar Mills19e4ce52017-04-27 11:24:19 -050053 }
54};
55
Lei YUf6144e92019-10-18 17:13:49 +080056namespace // anonymous
57{
58
59std::vector<std::string> getSoftwareObjects(sdbusplus::bus::bus& bus)
60{
61 std::vector<std::string> paths;
62 auto method = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
63 MAPPER_INTERFACE, "GetSubTreePaths");
64 method.append(SOFTWARE_OBJPATH);
65 method.append(0); // Depth 0 to search all
66 method.append(std::vector<std::string>({VERSION_BUSNAME}));
67 auto reply = bus.call(method);
68 reply.read(paths);
69 return paths;
70}
71
72} // namespace
73
Gunnar Mills3027bba2017-04-27 15:49:03 -050074int Manager::processImage(const std::string& tarFilePath)
Gunnar Millse91d3212017-04-19 15:42:47 -050075{
Gunnar Mills19e4ce52017-04-27 11:24:19 -050076 if (!fs::is_regular_file(tarFilePath))
77 {
78 log<level::ERR>("Error tarball does not exist",
Adriana Kobylak596466b2018-02-13 14:48:53 -060079 entry("FILENAME=%s", tarFilePath.c_str()));
Gunnar Millsb30cadd2017-10-06 16:07:32 -050080 report<ManifestFileFailure>(ManifestFail::PATH(tarFilePath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -050081 return -1;
Gunnar Mills19e4ce52017-04-27 11:24:19 -050082 }
83 RemovablePath tarPathRemove(tarFilePath);
84 fs::path tmpDirPath(std::string{IMG_UPLOAD_DIR});
85 tmpDirPath /= "imageXXXXXX";
Lei YU5d930282019-03-08 16:41:51 +080086 auto tmpDir = tmpDirPath.string();
Gunnar Mills19e4ce52017-04-27 11:24:19 -050087
Lei YU5d930282019-03-08 16:41:51 +080088 // Create a tmp dir to extract tarball.
89 if (!mkdtemp(tmpDir.data()))
Gunnar Mills19e4ce52017-04-27 11:24:19 -050090 {
Gunnar Mills601bbf02017-10-25 13:46:11 -050091 log<level::ERR>("Error occurred during mkdtemp",
Gunnar Mills19e4ce52017-04-27 11:24:19 -050092 entry("ERRNO=%d", errno));
Gunnar Millsb30cadd2017-10-06 16:07:32 -050093 report<InternalFailure>(InternalFail::FAIL("mkdtemp"));
Gunnar Mills19e4ce52017-04-27 11:24:19 -050094 return -1;
95 }
96
Lei YU5d930282019-03-08 16:41:51 +080097 tmpDirPath = tmpDir;
98 RemovablePath tmpDirToRemove(tmpDirPath);
Gunnar Mills19e4ce52017-04-27 11:24:19 -050099 fs::path manifestPath = tmpDirPath;
100 manifestPath /= MANIFEST_FILE_NAME;
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500101
Lei YU5d930282019-03-08 16:41:51 +0800102 // Untar tarball into the tmp dir
103 auto rc = unTar(tarFilePath, tmpDirPath.string());
104 if (rc < 0)
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500105 {
Lei YU5d930282019-03-08 16:41:51 +0800106 log<level::ERR>("Error occurred during untar");
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500107 return -1;
108 }
109
110 // Verify the manifest file
111 if (!fs::is_regular_file(manifestPath))
112 {
Adriana Kobylak596466b2018-02-13 14:48:53 -0600113 log<level::ERR>("Error No manifest file",
114 entry("FILENAME=%s", tarFilePath.c_str()));
115 report<ManifestFileFailure>(ManifestFail::PATH(tarFilePath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500116 return -1;
117 }
118
119 // Get version
120 auto version = Version::getValue(manifestPath.string(), "version");
121 if (version.empty())
122 {
123 log<level::ERR>("Error unable to read version from manifest file");
Adriana Kobylak596466b2018-02-13 14:48:53 -0600124 report<ManifestFileFailure>(ManifestFail::PATH(tarFilePath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500125 return -1;
126 }
127
Vijay Khemkab7c062e2019-09-18 17:15:57 -0700128 // Get running machine name
129 std::string currMachine = Version::getBMCMachine(OS_RELEASE_FILE);
130 if (currMachine.empty())
131 {
132 log<level::ERR>("Failed to read machine name from osRelease",
133 entry("FILENAME=%s", OS_RELEASE_FILE));
Adriana Kobylak447d0da2021-03-15 13:40:52 -0500134 report<ImageFailure>(ImageFail::FAIL("Failed to read machine name"),
135 ImageFail::PATH("OS_RELEASE_FILE"));
Vijay Khemkab7c062e2019-09-18 17:15:57 -0700136 return -1;
137 }
138
139 // Get machine name for image to be upgraded
140 std::string machineStr =
141 Version::getValue(manifestPath.string(), "MachineName");
142 if (!machineStr.empty())
143 {
144 if (machineStr != currMachine)
145 {
146 log<level::ERR>("BMC upgrade: Machine name doesn't match",
147 entry("CURR_MACHINE=%s", currMachine.c_str()),
148 entry("NEW_MACHINE=%s", machineStr.c_str()));
Adriana Kobylak447d0da2021-03-15 13:40:52 -0500149 report<ImageFailure>(
150 ImageFail::FAIL("Machine name does not match"),
151 ImageFail::PATH(manifestPath.string().c_str()));
Vijay Khemkab7c062e2019-09-18 17:15:57 -0700152 return -1;
153 }
154 }
155 else
156 {
157 log<level::WARNING>("No machine name in Manifest file");
Adriana Kobylak447d0da2021-03-15 13:40:52 -0500158 report<ImageFailure>(
159 ImageFail::FAIL("MANIFEST is missing machine name"),
160 ImageFail::PATH(manifestPath.string().c_str()));
Vijay Khemkab7c062e2019-09-18 17:15:57 -0700161 }
162
Gunnar Mills3027bba2017-04-27 15:49:03 -0500163 // Get purpose
164 auto purposeString = Version::getValue(manifestPath.string(), "purpose");
165 if (purposeString.empty())
166 {
167 log<level::ERR>("Error unable to read purpose from manifest file");
Adriana Kobylak596466b2018-02-13 14:48:53 -0600168 report<ManifestFileFailure>(ManifestFail::PATH(tarFilePath.c_str()));
Gunnar Mills3027bba2017-04-27 15:49:03 -0500169 return -1;
170 }
171
Gunnar Mills3027bba2017-04-27 15:49:03 -0500172 auto purpose = Version::VersionPurpose::Unknown;
Gunnar Millsb30cadd2017-10-06 16:07:32 -0500173 try
174 {
Leonel Gonzalez9a8d14c2017-06-22 11:42:24 -0500175 purpose = Version::convertVersionPurposeFromString(purposeString);
Gunnar Millsb30cadd2017-10-06 16:07:32 -0500176 }
177 catch (const sdbusplus::exception::InvalidEnumString& e)
178 {
Adriana Kobylak2285fe02018-02-27 15:36:59 -0600179 log<level::ERR>("Error: Failed to convert manifest purpose to enum."
Gunnar Millsb30cadd2017-10-06 16:07:32 -0500180 " Setting to Unknown.");
Gunnar Mills3027bba2017-04-27 15:49:03 -0500181 }
182
Chanh Nguyen1fd6ddd2021-01-06 11:09:09 +0700183 // Get ExtendedVersion
184 std::string extendedVersion =
185 Version::getValue(manifestPath.string(), "ExtendedVersion");
186
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500187 // Compute id
188 auto id = Version::getId(version);
189
190 fs::path imageDirPath = std::string{IMG_UPLOAD_DIR};
191 imageDirPath /= id;
192
Gunnar Millsf576bca2017-05-18 13:40:39 -0500193 if (fs::exists(imageDirPath))
194 {
195 fs::remove_all(imageDirPath);
196 }
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500197
Lei YU5d930282019-03-08 16:41:51 +0800198 // Rename the temp dir to image dir
199 fs::rename(tmpDirPath, imageDirPath);
200
201 // Clear the path, so it does not attemp to remove a non-existing path
202 tmpDirToRemove.path.clear();
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500203
Adriana Kobylak2285fe02018-02-27 15:36:59 -0600204 auto objPath = std::string{SOFTWARE_OBJPATH} + '/' + id;
Gunnar Mills3027bba2017-04-27 15:49:03 -0500205
Lei YUf6144e92019-10-18 17:13:49 +0800206 // This service only manages the uploaded versions, and there could be
207 // active versions on D-Bus that is not managed by this service.
208 // So check D-Bus if there is an existing version.
209 auto allSoftwareObjs = getSoftwareObjects(bus);
210 auto it =
211 std::find(allSoftwareObjs.begin(), allSoftwareObjs.end(), objPath);
212 if (versions.find(id) == versions.end() && it == allSoftwareObjs.end())
Saqib Khand377ba12017-10-16 14:32:30 -0500213 {
Lei YUf6144e92019-10-18 17:13:49 +0800214 // Create Version object
Saqib Khanee13e832017-10-23 12:53:11 -0500215 auto versionPtr = std::make_unique<Version>(
Chanh Nguyen1fd6ddd2021-01-06 11:09:09 +0700216 bus, objPath, version, purpose, extendedVersion,
217 imageDirPath.string(),
Adriana Kobylak2285fe02018-02-27 15:36:59 -0600218 std::bind(&Manager::erase, this, std::placeholders::_1));
Saqib Khanee13e832017-10-23 12:53:11 -0500219 versionPtr->deleteObject =
Adriana Kobylak2285fe02018-02-27 15:36:59 -0600220 std::make_unique<phosphor::software::manager::Delete>(bus, objPath,
221 *versionPtr);
Saqib Khanee13e832017-10-23 12:53:11 -0500222 versions.insert(std::make_pair(id, std::move(versionPtr)));
Saqib Khand377ba12017-10-16 14:32:30 -0500223 }
224 else
225 {
226 log<level::INFO>("Software Object with the same version already exists",
Adriana Kobylak596466b2018-02-13 14:48:53 -0600227 entry("VERSION_ID=%s", id.c_str()));
Adriana Kobylak13e09912020-08-31 13:55:10 -0500228 fs::remove_all(imageDirPath);
Saqib Khand377ba12017-10-16 14:32:30 -0500229 }
Gunnar Millse91d3212017-04-19 15:42:47 -0500230 return 0;
231}
232
Leonel Gonzalez50d559c2017-07-07 14:38:25 -0500233void Manager::erase(std::string entryId)
234{
235 auto it = versions.find(entryId);
236 if (it == versions.end())
237 {
238 return;
239 }
Eddie James6d873712017-09-01 11:29:07 -0500240
241 if (it->second->isFunctional())
242 {
Adriana Kobylak2285fe02018-02-27 15:36:59 -0600243 log<level::ERR>(("Error: Version " + entryId +
244 " is currently running on the BMC."
245 " Unable to remove.")
246 .c_str());
Eddie James6d873712017-09-01 11:29:07 -0500247 return;
248 }
249
Leonel Gonzalez50d559c2017-07-07 14:38:25 -0500250 // Delete image dir
251 fs::path imageDirPath = (*(it->second)).path();
252 if (fs::exists(imageDirPath))
253 {
254 fs::remove_all(imageDirPath);
255 }
256 this->versions.erase(entryId);
257}
258
Gunnar Mills3027bba2017-04-27 15:49:03 -0500259int Manager::unTar(const std::string& tarFilePath,
260 const std::string& extractDirPath)
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500261{
262 if (tarFilePath.empty())
263 {
264 log<level::ERR>("Error TarFilePath is empty");
Gunnar Millsb30cadd2017-10-06 16:07:32 -0500265 report<UnTarFailure>(UnTarFail::PATH(tarFilePath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500266 return -1;
267 }
268 if (extractDirPath.empty())
269 {
270 log<level::ERR>("Error ExtractDirPath is empty");
Adriana Kobylak596466b2018-02-13 14:48:53 -0600271 report<UnTarFailure>(UnTarFail::PATH(extractDirPath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500272 return -1;
273 }
274
Adriana Kobylak2285fe02018-02-27 15:36:59 -0600275 log<level::INFO>("Untaring", entry("FILENAME=%s", tarFilePath.c_str()),
Adriana Kobylak596466b2018-02-13 14:48:53 -0600276 entry("EXTRACTIONDIR=%s", extractDirPath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500277 int status = 0;
278 pid_t pid = fork();
279
280 if (pid == 0)
281 {
282 // child process
Adriana Kobylak2285fe02018-02-27 15:36:59 -0600283 execl("/bin/tar", "tar", "-xf", tarFilePath.c_str(), "-C",
284 extractDirPath.c_str(), (char*)0);
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500285 // execl only returns on fail
Adriana Kobylak596466b2018-02-13 14:48:53 -0600286 log<level::ERR>("Failed to execute untar file",
287 entry("FILENAME=%s", tarFilePath.c_str()));
Gunnar Millsb30cadd2017-10-06 16:07:32 -0500288 report<UnTarFailure>(UnTarFail::PATH(tarFilePath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500289 return -1;
290 }
291 else if (pid > 0)
292 {
293 waitpid(pid, &status, 0);
Adriana Kobylak596466b2018-02-13 14:48:53 -0600294 if (WEXITSTATUS(status))
295 {
296 log<level::ERR>("Failed to untar file",
297 entry("FILENAME=%s", tarFilePath.c_str()));
298 report<UnTarFailure>(UnTarFail::PATH(tarFilePath.c_str()));
299 return -1;
300 }
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500301 }
302 else
303 {
304 log<level::ERR>("fork() failed.");
Gunnar Millsb30cadd2017-10-06 16:07:32 -0500305 report<UnTarFailure>(UnTarFail::PATH(tarFilePath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500306 return -1;
307 }
308
309 return 0;
310}
Gunnar Mills3027bba2017-04-27 15:49:03 -0500311
Gunnar Millse91d3212017-04-19 15:42:47 -0500312} // namespace manager
313} // namespace software
Gunnar Millsfa34e022018-09-04 10:05:45 -0500314} // namespace phosphor