blob: 8e32e3fb3303b6f79a2d2bbbfed30a7f2d0b4696 [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 }
Pavithra Barithaya48de55f2024-06-22 05:30:44 -050057
58 RemovablePath(const RemovablePath& other) = delete;
59 RemovablePath& operator=(const RemovablePath& other) = delete;
60 RemovablePath(RemovablePath&&) = delete;
61 RemovablePath& operator=(RemovablePath&&) = delete;
Gunnar Mills19e4ce52017-04-27 11:24:19 -050062};
63
Lei YUf6144e92019-10-18 17:13:49 +080064namespace // anonymous
65{
66
Patrick Williamsbf2bb2b2022-07-22 19:26:52 -050067std::vector<std::string> getSoftwareObjects(sdbusplus::bus_t& bus)
Lei YUf6144e92019-10-18 17:13:49 +080068{
69 std::vector<std::string> paths;
70 auto method = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
71 MAPPER_INTERFACE, "GetSubTreePaths");
72 method.append(SOFTWARE_OBJPATH);
73 method.append(0); // Depth 0 to search all
74 method.append(std::vector<std::string>({VERSION_BUSNAME}));
75 auto reply = bus.call(method);
76 reply.read(paths);
77 return paths;
78}
79
80} // namespace
81
Gunnar Mills3027bba2017-04-27 15:49:03 -050082int Manager::processImage(const std::string& tarFilePath)
Gunnar Millse91d3212017-04-19 15:42:47 -050083{
George Liu44b9fef2023-02-07 14:31:32 +080084 std::error_code ec;
85 if (!fs::is_regular_file(tarFilePath, ec))
Gunnar Mills19e4ce52017-04-27 11:24:19 -050086 {
George Liu44b9fef2023-02-07 14:31:32 +080087 error("Tarball {PATH} does not exist: {ERROR_MSG}", "PATH", tarFilePath,
88 "ERROR_MSG", ec.message());
Gunnar Millsb30cadd2017-10-06 16:07:32 -050089 report<ManifestFileFailure>(ManifestFail::PATH(tarFilePath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -050090 return -1;
Gunnar Mills19e4ce52017-04-27 11:24:19 -050091 }
92 RemovablePath tarPathRemove(tarFilePath);
93 fs::path tmpDirPath(std::string{IMG_UPLOAD_DIR});
94 tmpDirPath /= "imageXXXXXX";
Lei YU5d930282019-03-08 16:41:51 +080095 auto tmpDir = tmpDirPath.string();
Gunnar Mills19e4ce52017-04-27 11:24:19 -050096
Lei YU5d930282019-03-08 16:41:51 +080097 // Create a tmp dir to extract tarball.
98 if (!mkdtemp(tmpDir.data()))
Gunnar Mills19e4ce52017-04-27 11:24:19 -050099 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500100 error("Error ({ERRNO}) occurred during mkdtemp", "ERRNO", errno);
Gunnar Millsb30cadd2017-10-06 16:07:32 -0500101 report<InternalFailure>(InternalFail::FAIL("mkdtemp"));
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500102 return -1;
103 }
104
Lei YU5d930282019-03-08 16:41:51 +0800105 tmpDirPath = tmpDir;
106 RemovablePath tmpDirToRemove(tmpDirPath);
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500107 fs::path manifestPath = tmpDirPath;
108 manifestPath /= MANIFEST_FILE_NAME;
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500109
Lei YU5d930282019-03-08 16:41:51 +0800110 // Untar tarball into the tmp dir
111 auto rc = unTar(tarFilePath, tmpDirPath.string());
112 if (rc < 0)
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500113 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500114 error("Error ({RC}) occurred during untar", "RC", rc);
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500115 return -1;
116 }
117
118 // Verify the manifest file
George Liu44b9fef2023-02-07 14:31:32 +0800119 if (!fs::is_regular_file(manifestPath, ec))
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500120 {
George Liu44b9fef2023-02-07 14:31:32 +0800121 error("No manifest file {PATH}: {ERROR_MSG}", "PATH", tarFilePath,
122 "ERROR_MSG", ec.message());
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
127 // Get version
128 auto version = Version::getValue(manifestPath.string(), "version");
129 if (version.empty())
130 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500131 error("Unable to read version from manifest file {PATH}", "PATH",
132 tarFilePath);
Adriana Kobylak596466b2018-02-13 14:48:53 -0600133 report<ManifestFileFailure>(ManifestFail::PATH(tarFilePath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500134 return -1;
135 }
136
Vijay Khemkab7c062e2019-09-18 17:15:57 -0700137 // Get running machine name
138 std::string currMachine = Version::getBMCMachine(OS_RELEASE_FILE);
139 if (currMachine.empty())
140 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500141 auto path = OS_RELEASE_FILE;
142 error("Failed to read machine name from osRelease: {PATH}", "PATH",
143 path);
Adriana Kobylak447d0da2021-03-15 13:40:52 -0500144 report<ImageFailure>(ImageFail::FAIL("Failed to read machine name"),
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500145 ImageFail::PATH(path));
Vijay Khemkab7c062e2019-09-18 17:15:57 -0700146 return -1;
147 }
148
149 // Get machine name for image to be upgraded
Patrick Williamsd5e8e732023-05-10 07:50:18 -0500150 std::string machineStr = Version::getValue(manifestPath.string(),
151 "MachineName");
Vijay Khemkab7c062e2019-09-18 17:15:57 -0700152 if (!machineStr.empty())
153 {
154 if (machineStr != currMachine)
155 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500156 error(
157 "BMC upgrade: Machine name doesn't match: {CURRENT_MACHINE} vs {NEW_MACHINE}",
158 "CURRENT_MACHINE", currMachine, "NEW_MACHINE", machineStr);
Adriana Kobylak447d0da2021-03-15 13:40:52 -0500159 report<ImageFailure>(
160 ImageFail::FAIL("Machine name does not match"),
161 ImageFail::PATH(manifestPath.string().c_str()));
Vijay Khemkab7c062e2019-09-18 17:15:57 -0700162 return -1;
163 }
164 }
165 else
166 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500167 warning("No machine name in Manifest file");
Adriana Kobylak447d0da2021-03-15 13:40:52 -0500168 report<ImageFailure>(
169 ImageFail::FAIL("MANIFEST is missing machine name"),
170 ImageFail::PATH(manifestPath.string().c_str()));
Vijay Khemkab7c062e2019-09-18 17:15:57 -0700171 }
172
Gunnar Mills3027bba2017-04-27 15:49:03 -0500173 // Get purpose
174 auto purposeString = Version::getValue(manifestPath.string(), "purpose");
175 if (purposeString.empty())
176 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500177 error("Unable to read purpose from manifest file {PATH}", "PATH",
178 tarFilePath);
Adriana Kobylak596466b2018-02-13 14:48:53 -0600179 report<ManifestFileFailure>(ManifestFail::PATH(tarFilePath.c_str()));
Gunnar Mills3027bba2017-04-27 15:49:03 -0500180 return -1;
181 }
182
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500183 auto convertedPurpose =
184 sdbusplus::message::convert_from_string<Version::VersionPurpose>(
185 purposeString);
186
187 if (!convertedPurpose)
Gunnar Millsb30cadd2017-10-06 16:07:32 -0500188 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500189 error(
190 "Failed to convert manifest purpose ({PURPOSE}) to enum; setting to Unknown.",
191 "PURPOSE", purposeString);
Gunnar Millsb30cadd2017-10-06 16:07:32 -0500192 }
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500193 auto purpose = convertedPurpose.value_or(Version::VersionPurpose::Unknown);
Gunnar Mills3027bba2017-04-27 15:49:03 -0500194
Chanh Nguyen1fd6ddd2021-01-06 11:09:09 +0700195 // Get ExtendedVersion
Patrick Williamsd5e8e732023-05-10 07:50:18 -0500196 std::string extendedVersion = Version::getValue(manifestPath.string(),
197 "ExtendedVersion");
Chanh Nguyen1fd6ddd2021-01-06 11:09:09 +0700198
Justin Ledford054bb0b2022-03-15 15:46:58 -0700199 // Get CompatibleNames
200 std::vector<std::string> compatibleNames =
201 Version::getRepeatedValues(manifestPath.string(), "CompatibleName");
202
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500203 // Compute id
Adriana Kobylak59b640b2022-01-21 19:45:22 +0000204 auto salt = std::to_string(randomGen());
205 auto id = Version::getId(version + salt);
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500206
207 fs::path imageDirPath = std::string{IMG_UPLOAD_DIR};
208 imageDirPath /= id;
209
Adriana Kobylak2285fe02018-02-27 15:36:59 -0600210 auto objPath = std::string{SOFTWARE_OBJPATH} + '/' + id;
Gunnar Mills3027bba2017-04-27 15:49:03 -0500211
Lei YUf6144e92019-10-18 17:13:49 +0800212 // This service only manages the uploaded versions, and there could be
213 // active versions on D-Bus that is not managed by this service.
214 // So check D-Bus if there is an existing version.
215 auto allSoftwareObjs = getSoftwareObjects(bus);
Patrick Williamsd5e8e732023-05-10 07:50:18 -0500216 auto it = std::find(allSoftwareObjs.begin(), allSoftwareObjs.end(),
217 objPath);
Lei YUf6144e92019-10-18 17:13:49 +0800218 if (versions.find(id) == versions.end() && it == allSoftwareObjs.end())
Saqib Khand377ba12017-10-16 14:32:30 -0500219 {
selvaganapathim3ea1e872021-09-09 22:55:30 +0530220 // Rename the temp dir to image dir
George Liu44b9fef2023-02-07 14:31:32 +0800221 fs::rename(tmpDirPath, imageDirPath, ec);
Manojkiran Eda19dd56b2024-06-17 14:29:52 +0530222 // Clear the path, so it does not attempt to remove a non-existing path
selvaganapathim3ea1e872021-09-09 22:55:30 +0530223 tmpDirToRemove.path.clear();
224
Lei YUf6144e92019-10-18 17:13:49 +0800225 // Create Version object
Saqib Khanee13e832017-10-23 12:53:11 -0500226 auto versionPtr = std::make_unique<Version>(
Chanh Nguyen1fd6ddd2021-01-06 11:09:09 +0700227 bus, objPath, version, purpose, extendedVersion,
Justin Ledford054bb0b2022-03-15 15:46:58 -0700228 imageDirPath.string(), compatibleNames,
Adriana Kobylak59b640b2022-01-21 19:45:22 +0000229 std::bind(&Manager::erase, this, std::placeholders::_1), id);
Saqib Khanee13e832017-10-23 12:53:11 -0500230 versionPtr->deleteObject =
Adriana Kobylak2285fe02018-02-27 15:36:59 -0600231 std::make_unique<phosphor::software::manager::Delete>(bus, objPath,
232 *versionPtr);
Saqib Khanee13e832017-10-23 12:53:11 -0500233 versions.insert(std::make_pair(id, std::move(versionPtr)));
Saqib Khand377ba12017-10-16 14:32:30 -0500234 }
235 else
236 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500237 info("Software Object with the same version ({VERSION}) already exists",
238 "VERSION", id);
Saqib Khand377ba12017-10-16 14:32:30 -0500239 }
Gunnar Millse91d3212017-04-19 15:42:47 -0500240 return 0;
241}
242
Leonel Gonzalez50d559c2017-07-07 14:38:25 -0500243void Manager::erase(std::string entryId)
244{
245 auto it = versions.find(entryId);
246 if (it == versions.end())
247 {
248 return;
249 }
Eddie James6d873712017-09-01 11:29:07 -0500250
Leonel Gonzalez50d559c2017-07-07 14:38:25 -0500251 // Delete image dir
252 fs::path imageDirPath = (*(it->second)).path();
George Liu44b9fef2023-02-07 14:31:32 +0800253 std::error_code ec;
254 if (fs::exists(imageDirPath, ec))
Leonel Gonzalez50d559c2017-07-07 14:38:25 -0500255 {
George Liu44b9fef2023-02-07 14:31:32 +0800256 fs::remove_all(imageDirPath, ec);
Leonel Gonzalez50d559c2017-07-07 14:38:25 -0500257 }
258 this->versions.erase(entryId);
259}
260
Gunnar Mills3027bba2017-04-27 15:49:03 -0500261int Manager::unTar(const std::string& tarFilePath,
262 const std::string& extractDirPath)
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500263{
264 if (tarFilePath.empty())
265 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500266 error("TarFilePath is empty");
Gunnar Millsb30cadd2017-10-06 16:07:32 -0500267 report<UnTarFailure>(UnTarFail::PATH(tarFilePath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500268 return -1;
269 }
270 if (extractDirPath.empty())
271 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500272 error("ExtractDirPath is empty");
Adriana Kobylak596466b2018-02-13 14:48:53 -0600273 report<UnTarFailure>(UnTarFail::PATH(extractDirPath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500274 return -1;
275 }
276
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500277 info("Untaring {PATH} to {EXTRACTIONDIR}", "PATH", tarFilePath,
278 "EXTRACTIONDIR", extractDirPath);
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500279 int status = 0;
280 pid_t pid = fork();
281
282 if (pid == 0)
283 {
284 // child process
Adriana Kobylak2285fe02018-02-27 15:36:59 -0600285 execl("/bin/tar", "tar", "-xf", tarFilePath.c_str(), "-C",
286 extractDirPath.c_str(), (char*)0);
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500287 // execl only returns on fail
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500288 error("Failed to execute untar on {PATH}", "PATH", tarFilePath);
Gunnar Millsb30cadd2017-10-06 16:07:32 -0500289 report<UnTarFailure>(UnTarFail::PATH(tarFilePath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500290 return -1;
291 }
292 else if (pid > 0)
293 {
294 waitpid(pid, &status, 0);
Adriana Kobylak596466b2018-02-13 14:48:53 -0600295 if (WEXITSTATUS(status))
296 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500297 error("Failed ({STATUS}) to untar file {PATH}", "STATUS", status,
298 "PATH", tarFilePath);
Adriana Kobylak596466b2018-02-13 14:48:53 -0600299 report<UnTarFailure>(UnTarFail::PATH(tarFilePath.c_str()));
300 return -1;
301 }
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500302 }
303 else
304 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500305 error("fork() failed: {ERRNO}", "ERRNO", errno);
Gunnar Millsb30cadd2017-10-06 16:07:32 -0500306 report<UnTarFailure>(UnTarFail::PATH(tarFilePath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500307 return -1;
308 }
309
310 return 0;
311}
Gunnar Mills3027bba2017-04-27 15:49:03 -0500312
Gunnar Millse91d3212017-04-19 15:42:47 -0500313} // namespace manager
314} // namespace software
Gunnar Millsfa34e022018-09-04 10:05:45 -0500315} // namespace phosphor