blob: 385fee469422c10ab095440bbf1d3c74e7d88882 [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
Patrick Williams098a6772023-09-01 14:55:30 -050014#include <phosphor-logging/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>
George Liu44b9fef2023-02-07 14:31:32 +080025#include <system_error>
Adriana Kobylak58aa7502020-06-08 11:12:11 -050026
Gunnar Millse91d3212017-04-19 15:42:47 -050027namespace phosphor
28{
29namespace software
30{
31namespace manager
32{
33
Patrick Williamsc9bb6422021-08-27 06:18:35 -050034PHOSPHOR_LOG2_USING;
Gunnar Mills19e4ce52017-04-27 11:24:19 -050035using namespace phosphor::logging;
Adriana Kobylakce82de52024-01-16 13:56:38 -060036using namespace sdbusplus::error::xyz::openbmc_project::software::image;
37namespace Software = phosphor::logging::xyz::openbmc_project::software;
38using ManifestFail = Software::image::ManifestFileFailure;
39using UnTarFail = Software::image::UnTarFailure;
40using InternalFail = Software::image::InternalFailure;
41using ImageFail = Software::image::ImageFailure;
Adriana Kobylakc98d9122020-05-05 10:36:01 -050042namespace fs = std::filesystem;
Gunnar Mills19e4ce52017-04-27 11:24:19 -050043
44struct RemovablePath
45{
46 fs::path path;
47
Patrick Williamsd5e8e732023-05-10 07:50:18 -050048 explicit RemovablePath(const fs::path& path) : path(path) {}
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
Patrick Williamsbf2bb2b2022-07-22 19:26:52 -050062std::vector<std::string> getSoftwareObjects(sdbusplus::bus_t& bus)
Lei YUf6144e92019-10-18 17:13:49 +080063{
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{
George Liu44b9fef2023-02-07 14:31:32 +080079 std::error_code ec;
80 if (!fs::is_regular_file(tarFilePath, ec))
Gunnar Mills19e4ce52017-04-27 11:24:19 -050081 {
George Liu44b9fef2023-02-07 14:31:32 +080082 error("Tarball {PATH} does not exist: {ERROR_MSG}", "PATH", tarFilePath,
83 "ERROR_MSG", ec.message());
Gunnar Millsb30cadd2017-10-06 16:07:32 -050084 report<ManifestFileFailure>(ManifestFail::PATH(tarFilePath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -050085 return -1;
Gunnar Mills19e4ce52017-04-27 11:24:19 -050086 }
87 RemovablePath tarPathRemove(tarFilePath);
88 fs::path tmpDirPath(std::string{IMG_UPLOAD_DIR});
89 tmpDirPath /= "imageXXXXXX";
Lei YU5d930282019-03-08 16:41:51 +080090 auto tmpDir = tmpDirPath.string();
Gunnar Mills19e4ce52017-04-27 11:24:19 -050091
Lei YU5d930282019-03-08 16:41:51 +080092 // Create a tmp dir to extract tarball.
93 if (!mkdtemp(tmpDir.data()))
Gunnar Mills19e4ce52017-04-27 11:24:19 -050094 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -050095 error("Error ({ERRNO}) occurred during mkdtemp", "ERRNO", errno);
Gunnar Millsb30cadd2017-10-06 16:07:32 -050096 report<InternalFailure>(InternalFail::FAIL("mkdtemp"));
Gunnar Mills19e4ce52017-04-27 11:24:19 -050097 return -1;
98 }
99
Lei YU5d930282019-03-08 16:41:51 +0800100 tmpDirPath = tmpDir;
101 RemovablePath tmpDirToRemove(tmpDirPath);
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500102 fs::path manifestPath = tmpDirPath;
103 manifestPath /= MANIFEST_FILE_NAME;
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500104
Lei YU5d930282019-03-08 16:41:51 +0800105 // Untar tarball into the tmp dir
106 auto rc = unTar(tarFilePath, tmpDirPath.string());
107 if (rc < 0)
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500108 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500109 error("Error ({RC}) occurred during untar", "RC", rc);
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500110 return -1;
111 }
112
113 // Verify the manifest file
George Liu44b9fef2023-02-07 14:31:32 +0800114 if (!fs::is_regular_file(manifestPath, ec))
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500115 {
George Liu44b9fef2023-02-07 14:31:32 +0800116 error("No manifest file {PATH}: {ERROR_MSG}", "PATH", tarFilePath,
117 "ERROR_MSG", ec.message());
Adriana Kobylak596466b2018-02-13 14:48:53 -0600118 report<ManifestFileFailure>(ManifestFail::PATH(tarFilePath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500119 return -1;
120 }
121
122 // Get version
123 auto version = Version::getValue(manifestPath.string(), "version");
124 if (version.empty())
125 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500126 error("Unable to read version from manifest file {PATH}", "PATH",
127 tarFilePath);
Adriana Kobylak596466b2018-02-13 14:48:53 -0600128 report<ManifestFileFailure>(ManifestFail::PATH(tarFilePath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500129 return -1;
130 }
131
Vijay Khemkab7c062e2019-09-18 17:15:57 -0700132 // Get running machine name
133 std::string currMachine = Version::getBMCMachine(OS_RELEASE_FILE);
134 if (currMachine.empty())
135 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500136 auto path = OS_RELEASE_FILE;
137 error("Failed to read machine name from osRelease: {PATH}", "PATH",
138 path);
Adriana Kobylak447d0da2021-03-15 13:40:52 -0500139 report<ImageFailure>(ImageFail::FAIL("Failed to read machine name"),
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500140 ImageFail::PATH(path));
Vijay Khemkab7c062e2019-09-18 17:15:57 -0700141 return -1;
142 }
143
144 // Get machine name for image to be upgraded
Patrick Williamsd5e8e732023-05-10 07:50:18 -0500145 std::string machineStr = Version::getValue(manifestPath.string(),
146 "MachineName");
Vijay Khemkab7c062e2019-09-18 17:15:57 -0700147 if (!machineStr.empty())
148 {
149 if (machineStr != currMachine)
150 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500151 error(
152 "BMC upgrade: Machine name doesn't match: {CURRENT_MACHINE} vs {NEW_MACHINE}",
153 "CURRENT_MACHINE", currMachine, "NEW_MACHINE", machineStr);
Adriana Kobylak447d0da2021-03-15 13:40:52 -0500154 report<ImageFailure>(
155 ImageFail::FAIL("Machine name does not match"),
156 ImageFail::PATH(manifestPath.string().c_str()));
Vijay Khemkab7c062e2019-09-18 17:15:57 -0700157 return -1;
158 }
159 }
160 else
161 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500162 warning("No machine name in Manifest file");
Adriana Kobylak447d0da2021-03-15 13:40:52 -0500163 report<ImageFailure>(
164 ImageFail::FAIL("MANIFEST is missing machine name"),
165 ImageFail::PATH(manifestPath.string().c_str()));
Vijay Khemkab7c062e2019-09-18 17:15:57 -0700166 }
167
Gunnar Mills3027bba2017-04-27 15:49:03 -0500168 // Get purpose
169 auto purposeString = Version::getValue(manifestPath.string(), "purpose");
170 if (purposeString.empty())
171 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500172 error("Unable to read purpose from manifest file {PATH}", "PATH",
173 tarFilePath);
Adriana Kobylak596466b2018-02-13 14:48:53 -0600174 report<ManifestFileFailure>(ManifestFail::PATH(tarFilePath.c_str()));
Gunnar Mills3027bba2017-04-27 15:49:03 -0500175 return -1;
176 }
177
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500178 auto convertedPurpose =
179 sdbusplus::message::convert_from_string<Version::VersionPurpose>(
180 purposeString);
181
182 if (!convertedPurpose)
Gunnar Millsb30cadd2017-10-06 16:07:32 -0500183 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500184 error(
185 "Failed to convert manifest purpose ({PURPOSE}) to enum; setting to Unknown.",
186 "PURPOSE", purposeString);
Gunnar Millsb30cadd2017-10-06 16:07:32 -0500187 }
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500188 auto purpose = convertedPurpose.value_or(Version::VersionPurpose::Unknown);
Gunnar Mills3027bba2017-04-27 15:49:03 -0500189
Chanh Nguyen1fd6ddd2021-01-06 11:09:09 +0700190 // Get ExtendedVersion
Patrick Williamsd5e8e732023-05-10 07:50:18 -0500191 std::string extendedVersion = Version::getValue(manifestPath.string(),
192 "ExtendedVersion");
Chanh Nguyen1fd6ddd2021-01-06 11:09:09 +0700193
Justin Ledford054bb0b2022-03-15 15:46:58 -0700194 // Get CompatibleNames
195 std::vector<std::string> compatibleNames =
196 Version::getRepeatedValues(manifestPath.string(), "CompatibleName");
197
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500198 // Compute id
Adriana Kobylak59b640b2022-01-21 19:45:22 +0000199 auto salt = std::to_string(randomGen());
200 auto id = Version::getId(version + salt);
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500201
202 fs::path imageDirPath = std::string{IMG_UPLOAD_DIR};
203 imageDirPath /= id;
204
Adriana Kobylak2285fe02018-02-27 15:36:59 -0600205 auto objPath = std::string{SOFTWARE_OBJPATH} + '/' + id;
Gunnar Mills3027bba2017-04-27 15:49:03 -0500206
Lei YUf6144e92019-10-18 17:13:49 +0800207 // This service only manages the uploaded versions, and there could be
208 // active versions on D-Bus that is not managed by this service.
209 // So check D-Bus if there is an existing version.
210 auto allSoftwareObjs = getSoftwareObjects(bus);
Patrick Williamsd5e8e732023-05-10 07:50:18 -0500211 auto it = std::find(allSoftwareObjs.begin(), allSoftwareObjs.end(),
212 objPath);
Lei YUf6144e92019-10-18 17:13:49 +0800213 if (versions.find(id) == versions.end() && it == allSoftwareObjs.end())
Saqib Khand377ba12017-10-16 14:32:30 -0500214 {
selvaganapathim3ea1e872021-09-09 22:55:30 +0530215 // Rename the temp dir to image dir
George Liu44b9fef2023-02-07 14:31:32 +0800216 fs::rename(tmpDirPath, imageDirPath, ec);
Manojkiran Eda19dd56b2024-06-17 14:29:52 +0530217 // Clear the path, so it does not attempt to remove a non-existing path
selvaganapathim3ea1e872021-09-09 22:55:30 +0530218 tmpDirToRemove.path.clear();
219
Lei YUf6144e92019-10-18 17:13:49 +0800220 // Create Version object
Saqib Khanee13e832017-10-23 12:53:11 -0500221 auto versionPtr = std::make_unique<Version>(
Chanh Nguyen1fd6ddd2021-01-06 11:09:09 +0700222 bus, objPath, version, purpose, extendedVersion,
Justin Ledford054bb0b2022-03-15 15:46:58 -0700223 imageDirPath.string(), compatibleNames,
Adriana Kobylak59b640b2022-01-21 19:45:22 +0000224 std::bind(&Manager::erase, this, std::placeholders::_1), id);
Saqib Khanee13e832017-10-23 12:53:11 -0500225 versionPtr->deleteObject =
Adriana Kobylak2285fe02018-02-27 15:36:59 -0600226 std::make_unique<phosphor::software::manager::Delete>(bus, objPath,
227 *versionPtr);
Saqib Khanee13e832017-10-23 12:53:11 -0500228 versions.insert(std::make_pair(id, std::move(versionPtr)));
Saqib Khand377ba12017-10-16 14:32:30 -0500229 }
230 else
231 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500232 info("Software Object with the same version ({VERSION}) already exists",
233 "VERSION", id);
Saqib Khand377ba12017-10-16 14:32:30 -0500234 }
Gunnar Millse91d3212017-04-19 15:42:47 -0500235 return 0;
236}
237
Leonel Gonzalez50d559c2017-07-07 14:38:25 -0500238void Manager::erase(std::string entryId)
239{
240 auto it = versions.find(entryId);
241 if (it == versions.end())
242 {
243 return;
244 }
Eddie James6d873712017-09-01 11:29:07 -0500245
Leonel Gonzalez50d559c2017-07-07 14:38:25 -0500246 // Delete image dir
247 fs::path imageDirPath = (*(it->second)).path();
George Liu44b9fef2023-02-07 14:31:32 +0800248 std::error_code ec;
249 if (fs::exists(imageDirPath, ec))
Leonel Gonzalez50d559c2017-07-07 14:38:25 -0500250 {
George Liu44b9fef2023-02-07 14:31:32 +0800251 fs::remove_all(imageDirPath, ec);
Leonel Gonzalez50d559c2017-07-07 14:38:25 -0500252 }
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