blob: 93fac667156bfbc7b63553bf79af12384997e32b [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>
Adriana Kobylak59b640b2022-01-21 19:45:22 +000021#include <ctime>
Adriana Kobylak58aa7502020-06-08 11:12:11 -050022#include <filesystem>
Adriana Kobylak59b640b2022-01-21 19:45:22 +000023#include <random>
Adriana Kobylak58aa7502020-06-08 11:12:11 -050024#include <string>
25
Gunnar Millse91d3212017-04-19 15:42:47 -050026namespace phosphor
27{
28namespace software
29{
30namespace manager
31{
32
Patrick Williamsc9bb6422021-08-27 06:18:35 -050033PHOSPHOR_LOG2_USING;
Gunnar Mills19e4ce52017-04-27 11:24:19 -050034using namespace phosphor::logging;
Adriana Kobylak43699ca2018-10-17 14:56:29 -050035using namespace sdbusplus::xyz::openbmc_project::Software::Image::Error;
Gunnar Millsb30cadd2017-10-06 16:07:32 -050036namespace Software = phosphor::logging::xyz::openbmc_project::Software;
Adriana Kobylak43699ca2018-10-17 14:56:29 -050037using ManifestFail = Software::Image::ManifestFileFailure;
38using UnTarFail = Software::Image::UnTarFailure;
39using InternalFail = Software::Image::InternalFailure;
Adriana Kobylak447d0da2021-03-15 13:40:52 -050040using ImageFail = Software::Image::ImageFailure;
Adriana Kobylakc98d9122020-05-05 10:36:01 -050041namespace fs = std::filesystem;
Gunnar Mills19e4ce52017-04-27 11:24:19 -050042
43struct RemovablePath
44{
45 fs::path path;
46
Lei YU0cd6d842021-12-27 11:56:02 +080047 explicit RemovablePath(const fs::path& path) : path(path)
Adriana Kobylak58aa7502020-06-08 11:12:11 -050048 {}
Gunnar Mills19e4ce52017-04-27 11:24:19 -050049 ~RemovablePath()
50 {
Lei YU5d930282019-03-08 16:41:51 +080051 if (!path.empty())
Adriana Kobylak2b2cd9f2018-02-12 16:20:13 -060052 {
Lei YU5d930282019-03-08 16:41:51 +080053 std::error_code ec;
54 fs::remove_all(path, ec);
Adriana Kobylak2b2cd9f2018-02-12 16:20:13 -060055 }
Gunnar Mills19e4ce52017-04-27 11:24:19 -050056 }
57};
58
Lei YUf6144e92019-10-18 17:13:49 +080059namespace // anonymous
60{
61
62std::vector<std::string> getSoftwareObjects(sdbusplus::bus::bus& bus)
63{
64 std::vector<std::string> paths;
65 auto method = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
66 MAPPER_INTERFACE, "GetSubTreePaths");
67 method.append(SOFTWARE_OBJPATH);
68 method.append(0); // Depth 0 to search all
69 method.append(std::vector<std::string>({VERSION_BUSNAME}));
70 auto reply = bus.call(method);
71 reply.read(paths);
72 return paths;
73}
74
75} // namespace
76
Gunnar Mills3027bba2017-04-27 15:49:03 -050077int Manager::processImage(const std::string& tarFilePath)
Gunnar Millse91d3212017-04-19 15:42:47 -050078{
Gunnar Mills19e4ce52017-04-27 11:24:19 -050079 if (!fs::is_regular_file(tarFilePath))
80 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -050081 error("Tarball {PATH} does not exist", "PATH", tarFilePath);
Gunnar Millsb30cadd2017-10-06 16:07:32 -050082 report<ManifestFileFailure>(ManifestFail::PATH(tarFilePath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -050083 return -1;
Gunnar Mills19e4ce52017-04-27 11:24:19 -050084 }
85 RemovablePath tarPathRemove(tarFilePath);
86 fs::path tmpDirPath(std::string{IMG_UPLOAD_DIR});
87 tmpDirPath /= "imageXXXXXX";
Lei YU5d930282019-03-08 16:41:51 +080088 auto tmpDir = tmpDirPath.string();
Gunnar Mills19e4ce52017-04-27 11:24:19 -050089
Lei YU5d930282019-03-08 16:41:51 +080090 // Create a tmp dir to extract tarball.
91 if (!mkdtemp(tmpDir.data()))
Gunnar Mills19e4ce52017-04-27 11:24:19 -050092 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -050093 error("Error ({ERRNO}) occurred during mkdtemp", "ERRNO", errno);
Gunnar Millsb30cadd2017-10-06 16:07:32 -050094 report<InternalFailure>(InternalFail::FAIL("mkdtemp"));
Gunnar Mills19e4ce52017-04-27 11:24:19 -050095 return -1;
96 }
97
Lei YU5d930282019-03-08 16:41:51 +080098 tmpDirPath = tmpDir;
99 RemovablePath tmpDirToRemove(tmpDirPath);
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500100 fs::path manifestPath = tmpDirPath;
101 manifestPath /= MANIFEST_FILE_NAME;
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500102
Lei YU5d930282019-03-08 16:41:51 +0800103 // Untar tarball into the tmp dir
104 auto rc = unTar(tarFilePath, tmpDirPath.string());
105 if (rc < 0)
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500106 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500107 error("Error ({RC}) occurred during untar", "RC", rc);
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500108 return -1;
109 }
110
111 // Verify the manifest file
112 if (!fs::is_regular_file(manifestPath))
113 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500114 error("No manifest file {PATH}", "PATH", tarFilePath);
Adriana Kobylak596466b2018-02-13 14:48:53 -0600115 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 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500123 error("Unable to read version from manifest file {PATH}", "PATH",
124 tarFilePath);
Adriana Kobylak596466b2018-02-13 14:48:53 -0600125 report<ManifestFileFailure>(ManifestFail::PATH(tarFilePath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500126 return -1;
127 }
128
Vijay Khemkab7c062e2019-09-18 17:15:57 -0700129 // Get running machine name
130 std::string currMachine = Version::getBMCMachine(OS_RELEASE_FILE);
131 if (currMachine.empty())
132 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500133 auto path = OS_RELEASE_FILE;
134 error("Failed to read machine name from osRelease: {PATH}", "PATH",
135 path);
Adriana Kobylak447d0da2021-03-15 13:40:52 -0500136 report<ImageFailure>(ImageFail::FAIL("Failed to read machine name"),
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500137 ImageFail::PATH(path));
Vijay Khemkab7c062e2019-09-18 17:15:57 -0700138 return -1;
139 }
140
141 // Get machine name for image to be upgraded
142 std::string machineStr =
143 Version::getValue(manifestPath.string(), "MachineName");
144 if (!machineStr.empty())
145 {
146 if (machineStr != currMachine)
147 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500148 error(
149 "BMC upgrade: Machine name doesn't match: {CURRENT_MACHINE} vs {NEW_MACHINE}",
150 "CURRENT_MACHINE", currMachine, "NEW_MACHINE", machineStr);
Adriana Kobylak447d0da2021-03-15 13:40:52 -0500151 report<ImageFailure>(
152 ImageFail::FAIL("Machine name does not match"),
153 ImageFail::PATH(manifestPath.string().c_str()));
Vijay Khemkab7c062e2019-09-18 17:15:57 -0700154 return -1;
155 }
156 }
157 else
158 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500159 warning("No machine name in Manifest file");
Adriana Kobylak447d0da2021-03-15 13:40:52 -0500160 report<ImageFailure>(
161 ImageFail::FAIL("MANIFEST is missing machine name"),
162 ImageFail::PATH(manifestPath.string().c_str()));
Vijay Khemkab7c062e2019-09-18 17:15:57 -0700163 }
164
Gunnar Mills3027bba2017-04-27 15:49:03 -0500165 // Get purpose
166 auto purposeString = Version::getValue(manifestPath.string(), "purpose");
167 if (purposeString.empty())
168 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500169 error("Unable to read purpose from manifest file {PATH}", "PATH",
170 tarFilePath);
Adriana Kobylak596466b2018-02-13 14:48:53 -0600171 report<ManifestFileFailure>(ManifestFail::PATH(tarFilePath.c_str()));
Gunnar Mills3027bba2017-04-27 15:49:03 -0500172 return -1;
173 }
174
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500175 auto convertedPurpose =
176 sdbusplus::message::convert_from_string<Version::VersionPurpose>(
177 purposeString);
178
179 if (!convertedPurpose)
Gunnar Millsb30cadd2017-10-06 16:07:32 -0500180 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500181 error(
182 "Failed to convert manifest purpose ({PURPOSE}) to enum; setting to Unknown.",
183 "PURPOSE", purposeString);
Gunnar Millsb30cadd2017-10-06 16:07:32 -0500184 }
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500185 auto purpose = convertedPurpose.value_or(Version::VersionPurpose::Unknown);
Gunnar Mills3027bba2017-04-27 15:49:03 -0500186
Chanh Nguyen1fd6ddd2021-01-06 11:09:09 +0700187 // Get ExtendedVersion
188 std::string extendedVersion =
189 Version::getValue(manifestPath.string(), "ExtendedVersion");
190
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500191 // Compute id
Adriana Kobylak59b640b2022-01-21 19:45:22 +0000192 auto salt = std::to_string(randomGen());
193 auto id = Version::getId(version + salt);
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500194
195 fs::path imageDirPath = std::string{IMG_UPLOAD_DIR};
196 imageDirPath /= id;
197
Adriana Kobylak2285fe02018-02-27 15:36:59 -0600198 auto objPath = std::string{SOFTWARE_OBJPATH} + '/' + id;
Gunnar Mills3027bba2017-04-27 15:49:03 -0500199
Lei YUf6144e92019-10-18 17:13:49 +0800200 // This service only manages the uploaded versions, and there could be
201 // active versions on D-Bus that is not managed by this service.
202 // So check D-Bus if there is an existing version.
203 auto allSoftwareObjs = getSoftwareObjects(bus);
204 auto it =
205 std::find(allSoftwareObjs.begin(), allSoftwareObjs.end(), objPath);
206 if (versions.find(id) == versions.end() && it == allSoftwareObjs.end())
Saqib Khand377ba12017-10-16 14:32:30 -0500207 {
selvaganapathim3ea1e872021-09-09 22:55:30 +0530208 // Rename the temp dir to image dir
209 fs::rename(tmpDirPath, imageDirPath);
210 // Clear the path, so it does not attemp to remove a non-existing path
211 tmpDirToRemove.path.clear();
212
Lei YUf6144e92019-10-18 17:13:49 +0800213 // Create Version object
Saqib Khanee13e832017-10-23 12:53:11 -0500214 auto versionPtr = std::make_unique<Version>(
Chanh Nguyen1fd6ddd2021-01-06 11:09:09 +0700215 bus, objPath, version, purpose, extendedVersion,
216 imageDirPath.string(),
Adriana Kobylak59b640b2022-01-21 19:45:22 +0000217 std::bind(&Manager::erase, this, std::placeholders::_1), id);
Saqib Khanee13e832017-10-23 12:53:11 -0500218 versionPtr->deleteObject =
Adriana Kobylak2285fe02018-02-27 15:36:59 -0600219 std::make_unique<phosphor::software::manager::Delete>(bus, objPath,
220 *versionPtr);
Saqib Khanee13e832017-10-23 12:53:11 -0500221 versions.insert(std::make_pair(id, std::move(versionPtr)));
Saqib Khand377ba12017-10-16 14:32:30 -0500222 }
223 else
224 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500225 info("Software Object with the same version ({VERSION}) already exists",
226 "VERSION", id);
Saqib Khand377ba12017-10-16 14:32:30 -0500227 }
Gunnar Millse91d3212017-04-19 15:42:47 -0500228 return 0;
229}
230
Leonel Gonzalez50d559c2017-07-07 14:38:25 -0500231void Manager::erase(std::string entryId)
232{
233 auto it = versions.find(entryId);
234 if (it == versions.end())
235 {
236 return;
237 }
Eddie James6d873712017-09-01 11:29:07 -0500238
239 if (it->second->isFunctional())
240 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500241 error(
242 "Version {VERSION} is currently running on the BMC; unable to remove",
243 "VERSION", entryId);
Eddie James6d873712017-09-01 11:29:07 -0500244 return;
245 }
246
Leonel Gonzalez50d559c2017-07-07 14:38:25 -0500247 // Delete image dir
248 fs::path imageDirPath = (*(it->second)).path();
249 if (fs::exists(imageDirPath))
250 {
251 fs::remove_all(imageDirPath);
252 }
253 this->versions.erase(entryId);
254}
255
Gunnar Mills3027bba2017-04-27 15:49:03 -0500256int Manager::unTar(const std::string& tarFilePath,
257 const std::string& extractDirPath)
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500258{
259 if (tarFilePath.empty())
260 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500261 error("TarFilePath is empty");
Gunnar Millsb30cadd2017-10-06 16:07:32 -0500262 report<UnTarFailure>(UnTarFail::PATH(tarFilePath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500263 return -1;
264 }
265 if (extractDirPath.empty())
266 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500267 error("ExtractDirPath is empty");
Adriana Kobylak596466b2018-02-13 14:48:53 -0600268 report<UnTarFailure>(UnTarFail::PATH(extractDirPath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500269 return -1;
270 }
271
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500272 info("Untaring {PATH} to {EXTRACTIONDIR}", "PATH", tarFilePath,
273 "EXTRACTIONDIR", extractDirPath);
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500274 int status = 0;
275 pid_t pid = fork();
276
277 if (pid == 0)
278 {
279 // child process
Adriana Kobylak2285fe02018-02-27 15:36:59 -0600280 execl("/bin/tar", "tar", "-xf", tarFilePath.c_str(), "-C",
281 extractDirPath.c_str(), (char*)0);
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500282 // execl only returns on fail
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500283 error("Failed to execute untar on {PATH}", "PATH", tarFilePath);
Gunnar Millsb30cadd2017-10-06 16:07:32 -0500284 report<UnTarFailure>(UnTarFail::PATH(tarFilePath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500285 return -1;
286 }
287 else if (pid > 0)
288 {
289 waitpid(pid, &status, 0);
Adriana Kobylak596466b2018-02-13 14:48:53 -0600290 if (WEXITSTATUS(status))
291 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500292 error("Failed ({STATUS}) to untar file {PATH}", "STATUS", status,
293 "PATH", tarFilePath);
Adriana Kobylak596466b2018-02-13 14:48:53 -0600294 report<UnTarFailure>(UnTarFail::PATH(tarFilePath.c_str()));
295 return -1;
296 }
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500297 }
298 else
299 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500300 error("fork() failed: {ERRNO}", "ERRNO", errno);
Gunnar Millsb30cadd2017-10-06 16:07:32 -0500301 report<UnTarFailure>(UnTarFail::PATH(tarFilePath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500302 return -1;
303 }
304
305 return 0;
306}
Gunnar Mills3027bba2017-04-27 15:49:03 -0500307
Gunnar Millse91d3212017-04-19 15:42:47 -0500308} // namespace manager
309} // namespace software
Gunnar Millsfa34e022018-09-04 10:05:45 -0500310} // namespace phosphor