blob: f9988715e63ce3ade1419614894d4cb918a4a81b [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>
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 Kobylak43699ca2018-10-17 14:56:29 -050036using namespace sdbusplus::xyz::openbmc_project::Software::Image::Error;
Gunnar Millsb30cadd2017-10-06 16:07:32 -050037namespace Software = phosphor::logging::xyz::openbmc_project::Software;
Adriana Kobylak43699ca2018-10-17 14:56:29 -050038using ManifestFail = Software::Image::ManifestFileFailure;
39using UnTarFail = Software::Image::UnTarFailure;
40using InternalFail = Software::Image::InternalFailure;
Adriana Kobylak447d0da2021-03-15 13:40:52 -050041using 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
Lei YU0cd6d842021-12-27 11:56:02 +080048 explicit RemovablePath(const fs::path& path) : path(path)
Adriana Kobylak58aa7502020-06-08 11:12:11 -050049 {}
Gunnar Mills19e4ce52017-04-27 11:24:19 -050050 ~RemovablePath()
51 {
Lei YU5d930282019-03-08 16:41:51 +080052 if (!path.empty())
Adriana Kobylak2b2cd9f2018-02-12 16:20:13 -060053 {
Lei YU5d930282019-03-08 16:41:51 +080054 std::error_code ec;
55 fs::remove_all(path, ec);
Adriana Kobylak2b2cd9f2018-02-12 16:20:13 -060056 }
Gunnar Mills19e4ce52017-04-27 11:24:19 -050057 }
58};
59
Lei YUf6144e92019-10-18 17:13:49 +080060namespace // anonymous
61{
62
Patrick Williamsbf2bb2b2022-07-22 19:26:52 -050063std::vector<std::string> getSoftwareObjects(sdbusplus::bus_t& bus)
Lei YUf6144e92019-10-18 17:13:49 +080064{
65 std::vector<std::string> paths;
66 auto method = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
67 MAPPER_INTERFACE, "GetSubTreePaths");
68 method.append(SOFTWARE_OBJPATH);
69 method.append(0); // Depth 0 to search all
70 method.append(std::vector<std::string>({VERSION_BUSNAME}));
71 auto reply = bus.call(method);
72 reply.read(paths);
73 return paths;
74}
75
76} // namespace
77
Gunnar Mills3027bba2017-04-27 15:49:03 -050078int Manager::processImage(const std::string& tarFilePath)
Gunnar Millse91d3212017-04-19 15:42:47 -050079{
George Liu44b9fef2023-02-07 14:31:32 +080080 std::error_code ec;
81 if (!fs::is_regular_file(tarFilePath, ec))
Gunnar Mills19e4ce52017-04-27 11:24:19 -050082 {
George Liu44b9fef2023-02-07 14:31:32 +080083 error("Tarball {PATH} does not exist: {ERROR_MSG}", "PATH", tarFilePath,
84 "ERROR_MSG", ec.message());
Gunnar Millsb30cadd2017-10-06 16:07:32 -050085 report<ManifestFileFailure>(ManifestFail::PATH(tarFilePath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -050086 return -1;
Gunnar Mills19e4ce52017-04-27 11:24:19 -050087 }
88 RemovablePath tarPathRemove(tarFilePath);
89 fs::path tmpDirPath(std::string{IMG_UPLOAD_DIR});
90 tmpDirPath /= "imageXXXXXX";
Lei YU5d930282019-03-08 16:41:51 +080091 auto tmpDir = tmpDirPath.string();
Gunnar Mills19e4ce52017-04-27 11:24:19 -050092
Lei YU5d930282019-03-08 16:41:51 +080093 // Create a tmp dir to extract tarball.
94 if (!mkdtemp(tmpDir.data()))
Gunnar Mills19e4ce52017-04-27 11:24:19 -050095 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -050096 error("Error ({ERRNO}) occurred during mkdtemp", "ERRNO", errno);
Gunnar Millsb30cadd2017-10-06 16:07:32 -050097 report<InternalFailure>(InternalFail::FAIL("mkdtemp"));
Gunnar Mills19e4ce52017-04-27 11:24:19 -050098 return -1;
99 }
100
Lei YU5d930282019-03-08 16:41:51 +0800101 tmpDirPath = tmpDir;
102 RemovablePath tmpDirToRemove(tmpDirPath);
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500103 fs::path manifestPath = tmpDirPath;
104 manifestPath /= MANIFEST_FILE_NAME;
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500105
Lei YU5d930282019-03-08 16:41:51 +0800106 // Untar tarball into the tmp dir
107 auto rc = unTar(tarFilePath, tmpDirPath.string());
108 if (rc < 0)
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500109 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500110 error("Error ({RC}) occurred during untar", "RC", rc);
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500111 return -1;
112 }
113
114 // Verify the manifest file
George Liu44b9fef2023-02-07 14:31:32 +0800115 if (!fs::is_regular_file(manifestPath, ec))
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500116 {
George Liu44b9fef2023-02-07 14:31:32 +0800117 error("No manifest file {PATH}: {ERROR_MSG}", "PATH", tarFilePath,
118 "ERROR_MSG", ec.message());
Adriana Kobylak596466b2018-02-13 14:48:53 -0600119 report<ManifestFileFailure>(ManifestFail::PATH(tarFilePath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500120 return -1;
121 }
122
123 // Get version
124 auto version = Version::getValue(manifestPath.string(), "version");
125 if (version.empty())
126 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500127 error("Unable to read version from manifest file {PATH}", "PATH",
128 tarFilePath);
Adriana Kobylak596466b2018-02-13 14:48:53 -0600129 report<ManifestFileFailure>(ManifestFail::PATH(tarFilePath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500130 return -1;
131 }
132
Vijay Khemkab7c062e2019-09-18 17:15:57 -0700133 // Get running machine name
134 std::string currMachine = Version::getBMCMachine(OS_RELEASE_FILE);
135 if (currMachine.empty())
136 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500137 auto path = OS_RELEASE_FILE;
138 error("Failed to read machine name from osRelease: {PATH}", "PATH",
139 path);
Adriana Kobylak447d0da2021-03-15 13:40:52 -0500140 report<ImageFailure>(ImageFail::FAIL("Failed to read machine name"),
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500141 ImageFail::PATH(path));
Vijay Khemkab7c062e2019-09-18 17:15:57 -0700142 return -1;
143 }
144
145 // Get machine name for image to be upgraded
146 std::string machineStr =
147 Version::getValue(manifestPath.string(), "MachineName");
148 if (!machineStr.empty())
149 {
150 if (machineStr != currMachine)
151 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500152 error(
153 "BMC upgrade: Machine name doesn't match: {CURRENT_MACHINE} vs {NEW_MACHINE}",
154 "CURRENT_MACHINE", currMachine, "NEW_MACHINE", machineStr);
Adriana Kobylak447d0da2021-03-15 13:40:52 -0500155 report<ImageFailure>(
156 ImageFail::FAIL("Machine name does not match"),
157 ImageFail::PATH(manifestPath.string().c_str()));
Vijay Khemkab7c062e2019-09-18 17:15:57 -0700158 return -1;
159 }
160 }
161 else
162 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500163 warning("No machine name in Manifest file");
Adriana Kobylak447d0da2021-03-15 13:40:52 -0500164 report<ImageFailure>(
165 ImageFail::FAIL("MANIFEST is missing machine name"),
166 ImageFail::PATH(manifestPath.string().c_str()));
Vijay Khemkab7c062e2019-09-18 17:15:57 -0700167 }
168
Gunnar Mills3027bba2017-04-27 15:49:03 -0500169 // Get purpose
170 auto purposeString = Version::getValue(manifestPath.string(), "purpose");
171 if (purposeString.empty())
172 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500173 error("Unable to read purpose from manifest file {PATH}", "PATH",
174 tarFilePath);
Adriana Kobylak596466b2018-02-13 14:48:53 -0600175 report<ManifestFileFailure>(ManifestFail::PATH(tarFilePath.c_str()));
Gunnar Mills3027bba2017-04-27 15:49:03 -0500176 return -1;
177 }
178
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500179 auto convertedPurpose =
180 sdbusplus::message::convert_from_string<Version::VersionPurpose>(
181 purposeString);
182
183 if (!convertedPurpose)
Gunnar Millsb30cadd2017-10-06 16:07:32 -0500184 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500185 error(
186 "Failed to convert manifest purpose ({PURPOSE}) to enum; setting to Unknown.",
187 "PURPOSE", purposeString);
Gunnar Millsb30cadd2017-10-06 16:07:32 -0500188 }
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500189 auto purpose = convertedPurpose.value_or(Version::VersionPurpose::Unknown);
Gunnar Mills3027bba2017-04-27 15:49:03 -0500190
Chanh Nguyen1fd6ddd2021-01-06 11:09:09 +0700191 // Get ExtendedVersion
192 std::string extendedVersion =
193 Version::getValue(manifestPath.string(), "ExtendedVersion");
194
Justin Ledford054bb0b2022-03-15 15:46:58 -0700195 // Get CompatibleNames
196 std::vector<std::string> compatibleNames =
197 Version::getRepeatedValues(manifestPath.string(), "CompatibleName");
198
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500199 // Compute id
Adriana Kobylak59b640b2022-01-21 19:45:22 +0000200 auto salt = std::to_string(randomGen());
201 auto id = Version::getId(version + salt);
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500202
203 fs::path imageDirPath = std::string{IMG_UPLOAD_DIR};
204 imageDirPath /= id;
205
Adriana Kobylak2285fe02018-02-27 15:36:59 -0600206 auto objPath = std::string{SOFTWARE_OBJPATH} + '/' + id;
Gunnar Mills3027bba2017-04-27 15:49:03 -0500207
Lei YUf6144e92019-10-18 17:13:49 +0800208 // This service only manages the uploaded versions, and there could be
209 // active versions on D-Bus that is not managed by this service.
210 // So check D-Bus if there is an existing version.
211 auto allSoftwareObjs = getSoftwareObjects(bus);
212 auto it =
213 std::find(allSoftwareObjs.begin(), allSoftwareObjs.end(), objPath);
214 if (versions.find(id) == versions.end() && it == allSoftwareObjs.end())
Saqib Khand377ba12017-10-16 14:32:30 -0500215 {
selvaganapathim3ea1e872021-09-09 22:55:30 +0530216 // Rename the temp dir to image dir
George Liu44b9fef2023-02-07 14:31:32 +0800217 fs::rename(tmpDirPath, imageDirPath, ec);
selvaganapathim3ea1e872021-09-09 22:55:30 +0530218 // Clear the path, so it does not attemp to remove a non-existing path
219 tmpDirToRemove.path.clear();
220
Lei YUf6144e92019-10-18 17:13:49 +0800221 // Create Version object
Saqib Khanee13e832017-10-23 12:53:11 -0500222 auto versionPtr = std::make_unique<Version>(
Chanh Nguyen1fd6ddd2021-01-06 11:09:09 +0700223 bus, objPath, version, purpose, extendedVersion,
Justin Ledford054bb0b2022-03-15 15:46:58 -0700224 imageDirPath.string(), compatibleNames,
Adriana Kobylak59b640b2022-01-21 19:45:22 +0000225 std::bind(&Manager::erase, this, std::placeholders::_1), id);
Saqib Khanee13e832017-10-23 12:53:11 -0500226 versionPtr->deleteObject =
Adriana Kobylak2285fe02018-02-27 15:36:59 -0600227 std::make_unique<phosphor::software::manager::Delete>(bus, objPath,
228 *versionPtr);
Saqib Khanee13e832017-10-23 12:53:11 -0500229 versions.insert(std::make_pair(id, std::move(versionPtr)));
Saqib Khand377ba12017-10-16 14:32:30 -0500230 }
231 else
232 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500233 info("Software Object with the same version ({VERSION}) already exists",
234 "VERSION", id);
Saqib Khand377ba12017-10-16 14:32:30 -0500235 }
Gunnar Millse91d3212017-04-19 15:42:47 -0500236 return 0;
237}
238
Leonel Gonzalez50d559c2017-07-07 14:38:25 -0500239void Manager::erase(std::string entryId)
240{
241 auto it = versions.find(entryId);
242 if (it == versions.end())
243 {
244 return;
245 }
Eddie James6d873712017-09-01 11:29:07 -0500246
Leonel Gonzalez50d559c2017-07-07 14:38:25 -0500247 // Delete image dir
248 fs::path imageDirPath = (*(it->second)).path();
George Liu44b9fef2023-02-07 14:31:32 +0800249 std::error_code ec;
250 if (fs::exists(imageDirPath, ec))
Leonel Gonzalez50d559c2017-07-07 14:38:25 -0500251 {
George Liu44b9fef2023-02-07 14:31:32 +0800252 fs::remove_all(imageDirPath, ec);
Leonel Gonzalez50d559c2017-07-07 14:38:25 -0500253 }
254 this->versions.erase(entryId);
255}
256
Gunnar Mills3027bba2017-04-27 15:49:03 -0500257int Manager::unTar(const std::string& tarFilePath,
258 const std::string& extractDirPath)
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500259{
260 if (tarFilePath.empty())
261 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500262 error("TarFilePath is empty");
Gunnar Millsb30cadd2017-10-06 16:07:32 -0500263 report<UnTarFailure>(UnTarFail::PATH(tarFilePath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500264 return -1;
265 }
266 if (extractDirPath.empty())
267 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500268 error("ExtractDirPath is empty");
Adriana Kobylak596466b2018-02-13 14:48:53 -0600269 report<UnTarFailure>(UnTarFail::PATH(extractDirPath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500270 return -1;
271 }
272
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500273 info("Untaring {PATH} to {EXTRACTIONDIR}", "PATH", tarFilePath,
274 "EXTRACTIONDIR", extractDirPath);
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500275 int status = 0;
276 pid_t pid = fork();
277
278 if (pid == 0)
279 {
280 // child process
Adriana Kobylak2285fe02018-02-27 15:36:59 -0600281 execl("/bin/tar", "tar", "-xf", tarFilePath.c_str(), "-C",
282 extractDirPath.c_str(), (char*)0);
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500283 // execl only returns on fail
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500284 error("Failed to execute untar on {PATH}", "PATH", tarFilePath);
Gunnar Millsb30cadd2017-10-06 16:07:32 -0500285 report<UnTarFailure>(UnTarFail::PATH(tarFilePath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500286 return -1;
287 }
288 else if (pid > 0)
289 {
290 waitpid(pid, &status, 0);
Adriana Kobylak596466b2018-02-13 14:48:53 -0600291 if (WEXITSTATUS(status))
292 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500293 error("Failed ({STATUS}) to untar file {PATH}", "STATUS", status,
294 "PATH", tarFilePath);
Adriana Kobylak596466b2018-02-13 14:48:53 -0600295 report<UnTarFailure>(UnTarFail::PATH(tarFilePath.c_str()));
296 return -1;
297 }
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500298 }
299 else
300 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500301 error("fork() failed: {ERRNO}", "ERRNO", errno);
Gunnar Millsb30cadd2017-10-06 16:07:32 -0500302 report<UnTarFailure>(UnTarFail::PATH(tarFilePath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500303 return -1;
304 }
305
306 return 0;
307}
Gunnar Mills3027bba2017-04-27 15:49:03 -0500308
Gunnar Millse91d3212017-04-19 15:42:47 -0500309} // namespace manager
310} // namespace software
Gunnar Millsfa34e022018-09-04 10:05:45 -0500311} // namespace phosphor