blob: a9a5a694af3d0ee08f49fae73642e97124332e1f [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>
Patrick Williamsc9bb6422021-08-27 06:18:35 -050016#include <phosphor-logging/lg2.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
Patrick Williamsc9bb6422021-08-27 06:18:35 -050031PHOSPHOR_LOG2_USING;
Gunnar Mills19e4ce52017-04-27 11:24:19 -050032using namespace phosphor::logging;
Adriana Kobylak43699ca2018-10-17 14:56:29 -050033using namespace sdbusplus::xyz::openbmc_project::Software::Image::Error;
Gunnar Millsb30cadd2017-10-06 16:07:32 -050034namespace Software = phosphor::logging::xyz::openbmc_project::Software;
Adriana Kobylak43699ca2018-10-17 14:56:29 -050035using ManifestFail = Software::Image::ManifestFileFailure;
36using UnTarFail = Software::Image::UnTarFailure;
37using InternalFail = Software::Image::InternalFailure;
Adriana Kobylak447d0da2021-03-15 13:40:52 -050038using ImageFail = Software::Image::ImageFailure;
Adriana Kobylakc98d9122020-05-05 10:36:01 -050039namespace fs = std::filesystem;
Gunnar Mills19e4ce52017-04-27 11:24:19 -050040
41struct RemovablePath
42{
43 fs::path path;
44
Adriana Kobylak2285fe02018-02-27 15:36:59 -060045 RemovablePath(const fs::path& path) : path(path)
Adriana Kobylak58aa7502020-06-08 11:12:11 -050046 {}
Gunnar Mills19e4ce52017-04-27 11:24:19 -050047 ~RemovablePath()
48 {
Lei YU5d930282019-03-08 16:41:51 +080049 if (!path.empty())
Adriana Kobylak2b2cd9f2018-02-12 16:20:13 -060050 {
Lei YU5d930282019-03-08 16:41:51 +080051 std::error_code ec;
52 fs::remove_all(path, ec);
Adriana Kobylak2b2cd9f2018-02-12 16:20:13 -060053 }
Gunnar Mills19e4ce52017-04-27 11:24:19 -050054 }
55};
56
Lei YUf6144e92019-10-18 17:13:49 +080057namespace // anonymous
58{
59
60std::vector<std::string> getSoftwareObjects(sdbusplus::bus::bus& bus)
61{
62 std::vector<std::string> paths;
63 auto method = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
64 MAPPER_INTERFACE, "GetSubTreePaths");
65 method.append(SOFTWARE_OBJPATH);
66 method.append(0); // Depth 0 to search all
67 method.append(std::vector<std::string>({VERSION_BUSNAME}));
68 auto reply = bus.call(method);
69 reply.read(paths);
70 return paths;
71}
72
73} // namespace
74
Gunnar Mills3027bba2017-04-27 15:49:03 -050075int Manager::processImage(const std::string& tarFilePath)
Gunnar Millse91d3212017-04-19 15:42:47 -050076{
Gunnar Mills19e4ce52017-04-27 11:24:19 -050077 if (!fs::is_regular_file(tarFilePath))
78 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -050079 error("Tarball {PATH} does not exist", "PATH", tarFilePath);
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 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -050091 error("Error ({ERRNO}) occurred during mkdtemp", "ERRNO", 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 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500105 error("Error ({RC}) occurred during untar", "RC", rc);
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 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500112 error("No manifest file {PATH}", "PATH", tarFilePath);
Adriana Kobylak596466b2018-02-13 14:48:53 -0600113 report<ManifestFileFailure>(ManifestFail::PATH(tarFilePath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500114 return -1;
115 }
116
117 // Get version
118 auto version = Version::getValue(manifestPath.string(), "version");
119 if (version.empty())
120 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500121 error("Unable to read version from manifest file {PATH}", "PATH",
122 tarFilePath);
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 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500131 auto path = OS_RELEASE_FILE;
132 error("Failed to read machine name from osRelease: {PATH}", "PATH",
133 path);
Adriana Kobylak447d0da2021-03-15 13:40:52 -0500134 report<ImageFailure>(ImageFail::FAIL("Failed to read machine name"),
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500135 ImageFail::PATH(path));
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 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500146 error(
147 "BMC upgrade: Machine name doesn't match: {CURRENT_MACHINE} vs {NEW_MACHINE}",
148 "CURRENT_MACHINE", currMachine, "NEW_MACHINE", machineStr);
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 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500157 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 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500167 error("Unable to read purpose from manifest file {PATH}", "PATH",
168 tarFilePath);
Adriana Kobylak596466b2018-02-13 14:48:53 -0600169 report<ManifestFileFailure>(ManifestFail::PATH(tarFilePath.c_str()));
Gunnar Mills3027bba2017-04-27 15:49:03 -0500170 return -1;
171 }
172
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500173 auto convertedPurpose =
174 sdbusplus::message::convert_from_string<Version::VersionPurpose>(
175 purposeString);
176
177 if (!convertedPurpose)
Gunnar Millsb30cadd2017-10-06 16:07:32 -0500178 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500179 error(
180 "Failed to convert manifest purpose ({PURPOSE}) to enum; setting to Unknown.",
181 "PURPOSE", purposeString);
Gunnar Millsb30cadd2017-10-06 16:07:32 -0500182 }
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500183 auto purpose = convertedPurpose.value_or(Version::VersionPurpose::Unknown);
Gunnar Mills3027bba2017-04-27 15:49:03 -0500184
Chanh Nguyen1fd6ddd2021-01-06 11:09:09 +0700185 // Get ExtendedVersion
186 std::string extendedVersion =
187 Version::getValue(manifestPath.string(), "ExtendedVersion");
188
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500189 // Compute id
190 auto id = Version::getId(version);
191
192 fs::path imageDirPath = std::string{IMG_UPLOAD_DIR};
193 imageDirPath /= id;
194
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 {
selvaganapathim3ea1e872021-09-09 22:55:30 +0530205 // Rename the temp dir to image dir
206 fs::rename(tmpDirPath, imageDirPath);
207 // Clear the path, so it does not attemp to remove a non-existing path
208 tmpDirToRemove.path.clear();
209
Lei YUf6144e92019-10-18 17:13:49 +0800210 // Create Version object
Saqib Khanee13e832017-10-23 12:53:11 -0500211 auto versionPtr = std::make_unique<Version>(
Chanh Nguyen1fd6ddd2021-01-06 11:09:09 +0700212 bus, objPath, version, purpose, extendedVersion,
213 imageDirPath.string(),
Adriana Kobylak2285fe02018-02-27 15:36:59 -0600214 std::bind(&Manager::erase, this, std::placeholders::_1));
Saqib Khanee13e832017-10-23 12:53:11 -0500215 versionPtr->deleteObject =
Adriana Kobylak2285fe02018-02-27 15:36:59 -0600216 std::make_unique<phosphor::software::manager::Delete>(bus, objPath,
217 *versionPtr);
Saqib Khanee13e832017-10-23 12:53:11 -0500218 versions.insert(std::make_pair(id, std::move(versionPtr)));
Saqib Khand377ba12017-10-16 14:32:30 -0500219 }
220 else
221 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500222 info("Software Object with the same version ({VERSION}) already exists",
223 "VERSION", id);
Saqib Khand377ba12017-10-16 14:32:30 -0500224 }
Gunnar Millse91d3212017-04-19 15:42:47 -0500225 return 0;
226}
227
Leonel Gonzalez50d559c2017-07-07 14:38:25 -0500228void Manager::erase(std::string entryId)
229{
230 auto it = versions.find(entryId);
231 if (it == versions.end())
232 {
233 return;
234 }
Eddie James6d873712017-09-01 11:29:07 -0500235
236 if (it->second->isFunctional())
237 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500238 error(
239 "Version {VERSION} is currently running on the BMC; unable to remove",
240 "VERSION", entryId);
Eddie James6d873712017-09-01 11:29:07 -0500241 return;
242 }
243
Leonel Gonzalez50d559c2017-07-07 14:38:25 -0500244 // Delete image dir
245 fs::path imageDirPath = (*(it->second)).path();
246 if (fs::exists(imageDirPath))
247 {
248 fs::remove_all(imageDirPath);
249 }
250 this->versions.erase(entryId);
251}
252
Gunnar Mills3027bba2017-04-27 15:49:03 -0500253int Manager::unTar(const std::string& tarFilePath,
254 const std::string& extractDirPath)
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500255{
256 if (tarFilePath.empty())
257 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500258 error("TarFilePath is empty");
Gunnar Millsb30cadd2017-10-06 16:07:32 -0500259 report<UnTarFailure>(UnTarFail::PATH(tarFilePath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500260 return -1;
261 }
262 if (extractDirPath.empty())
263 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500264 error("ExtractDirPath is empty");
Adriana Kobylak596466b2018-02-13 14:48:53 -0600265 report<UnTarFailure>(UnTarFail::PATH(extractDirPath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500266 return -1;
267 }
268
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500269 info("Untaring {PATH} to {EXTRACTIONDIR}", "PATH", tarFilePath,
270 "EXTRACTIONDIR", extractDirPath);
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500271 int status = 0;
272 pid_t pid = fork();
273
274 if (pid == 0)
275 {
276 // child process
Adriana Kobylak2285fe02018-02-27 15:36:59 -0600277 execl("/bin/tar", "tar", "-xf", tarFilePath.c_str(), "-C",
278 extractDirPath.c_str(), (char*)0);
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500279 // execl only returns on fail
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500280 error("Failed to execute untar on {PATH}", "PATH", tarFilePath);
Gunnar Millsb30cadd2017-10-06 16:07:32 -0500281 report<UnTarFailure>(UnTarFail::PATH(tarFilePath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500282 return -1;
283 }
284 else if (pid > 0)
285 {
286 waitpid(pid, &status, 0);
Adriana Kobylak596466b2018-02-13 14:48:53 -0600287 if (WEXITSTATUS(status))
288 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500289 error("Failed ({STATUS}) to untar file {PATH}", "STATUS", status,
290 "PATH", tarFilePath);
Adriana Kobylak596466b2018-02-13 14:48:53 -0600291 report<UnTarFailure>(UnTarFail::PATH(tarFilePath.c_str()));
292 return -1;
293 }
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500294 }
295 else
296 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500297 error("fork() failed: {ERRNO}", "ERRNO", errno);
Gunnar Millsb30cadd2017-10-06 16:07:32 -0500298 report<UnTarFailure>(UnTarFail::PATH(tarFilePath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500299 return -1;
300 }
301
302 return 0;
303}
Gunnar Mills3027bba2017-04-27 15:49:03 -0500304
Gunnar Millse91d3212017-04-19 15:42:47 -0500305} // namespace manager
306} // namespace software
Gunnar Millsfa34e022018-09-04 10:05:45 -0500307} // namespace phosphor