blob: b02abd303588943a3995a4f12ec183ff96f362cd [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
Justin Ledford054bb0b2022-03-15 15:46:58 -0700191 // Get CompatibleNames
192 std::vector<std::string> compatibleNames =
193 Version::getRepeatedValues(manifestPath.string(), "CompatibleName");
194
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500195 // Compute id
Adriana Kobylak59b640b2022-01-21 19:45:22 +0000196 auto salt = std::to_string(randomGen());
197 auto id = Version::getId(version + salt);
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500198
199 fs::path imageDirPath = std::string{IMG_UPLOAD_DIR};
200 imageDirPath /= id;
201
Adriana Kobylak2285fe02018-02-27 15:36:59 -0600202 auto objPath = std::string{SOFTWARE_OBJPATH} + '/' + id;
Gunnar Mills3027bba2017-04-27 15:49:03 -0500203
Lei YUf6144e92019-10-18 17:13:49 +0800204 // This service only manages the uploaded versions, and there could be
205 // active versions on D-Bus that is not managed by this service.
206 // So check D-Bus if there is an existing version.
207 auto allSoftwareObjs = getSoftwareObjects(bus);
208 auto it =
209 std::find(allSoftwareObjs.begin(), allSoftwareObjs.end(), objPath);
210 if (versions.find(id) == versions.end() && it == allSoftwareObjs.end())
Saqib Khand377ba12017-10-16 14:32:30 -0500211 {
selvaganapathim3ea1e872021-09-09 22:55:30 +0530212 // Rename the temp dir to image dir
213 fs::rename(tmpDirPath, imageDirPath);
214 // Clear the path, so it does not attemp to remove a non-existing path
215 tmpDirToRemove.path.clear();
216
Lei YUf6144e92019-10-18 17:13:49 +0800217 // Create Version object
Saqib Khanee13e832017-10-23 12:53:11 -0500218 auto versionPtr = std::make_unique<Version>(
Chanh Nguyen1fd6ddd2021-01-06 11:09:09 +0700219 bus, objPath, version, purpose, extendedVersion,
Justin Ledford054bb0b2022-03-15 15:46:58 -0700220 imageDirPath.string(), compatibleNames,
Adriana Kobylak59b640b2022-01-21 19:45:22 +0000221 std::bind(&Manager::erase, this, std::placeholders::_1), id);
Saqib Khanee13e832017-10-23 12:53:11 -0500222 versionPtr->deleteObject =
Adriana Kobylak2285fe02018-02-27 15:36:59 -0600223 std::make_unique<phosphor::software::manager::Delete>(bus, objPath,
224 *versionPtr);
Saqib Khanee13e832017-10-23 12:53:11 -0500225 versions.insert(std::make_pair(id, std::move(versionPtr)));
Saqib Khand377ba12017-10-16 14:32:30 -0500226 }
227 else
228 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500229 info("Software Object with the same version ({VERSION}) already exists",
230 "VERSION", id);
Saqib Khand377ba12017-10-16 14:32:30 -0500231 }
Gunnar Millse91d3212017-04-19 15:42:47 -0500232 return 0;
233}
234
Leonel Gonzalez50d559c2017-07-07 14:38:25 -0500235void Manager::erase(std::string entryId)
236{
237 auto it = versions.find(entryId);
238 if (it == versions.end())
239 {
240 return;
241 }
Eddie James6d873712017-09-01 11:29:07 -0500242
Leonel Gonzalez50d559c2017-07-07 14:38:25 -0500243 // Delete image dir
244 fs::path imageDirPath = (*(it->second)).path();
245 if (fs::exists(imageDirPath))
246 {
247 fs::remove_all(imageDirPath);
248 }
249 this->versions.erase(entryId);
250}
251
Gunnar Mills3027bba2017-04-27 15:49:03 -0500252int Manager::unTar(const std::string& tarFilePath,
253 const std::string& extractDirPath)
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500254{
255 if (tarFilePath.empty())
256 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500257 error("TarFilePath is empty");
Gunnar Millsb30cadd2017-10-06 16:07:32 -0500258 report<UnTarFailure>(UnTarFail::PATH(tarFilePath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500259 return -1;
260 }
261 if (extractDirPath.empty())
262 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500263 error("ExtractDirPath is empty");
Adriana Kobylak596466b2018-02-13 14:48:53 -0600264 report<UnTarFailure>(UnTarFail::PATH(extractDirPath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500265 return -1;
266 }
267
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500268 info("Untaring {PATH} to {EXTRACTIONDIR}", "PATH", tarFilePath,
269 "EXTRACTIONDIR", extractDirPath);
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500270 int status = 0;
271 pid_t pid = fork();
272
273 if (pid == 0)
274 {
275 // child process
Adriana Kobylak2285fe02018-02-27 15:36:59 -0600276 execl("/bin/tar", "tar", "-xf", tarFilePath.c_str(), "-C",
277 extractDirPath.c_str(), (char*)0);
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500278 // execl only returns on fail
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500279 error("Failed to execute untar on {PATH}", "PATH", tarFilePath);
Gunnar Millsb30cadd2017-10-06 16:07:32 -0500280 report<UnTarFailure>(UnTarFail::PATH(tarFilePath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500281 return -1;
282 }
283 else if (pid > 0)
284 {
285 waitpid(pid, &status, 0);
Adriana Kobylak596466b2018-02-13 14:48:53 -0600286 if (WEXITSTATUS(status))
287 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500288 error("Failed ({STATUS}) to untar file {PATH}", "STATUS", status,
289 "PATH", tarFilePath);
Adriana Kobylak596466b2018-02-13 14:48:53 -0600290 report<UnTarFailure>(UnTarFail::PATH(tarFilePath.c_str()));
291 return -1;
292 }
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500293 }
294 else
295 {
Patrick Williamsc9bb6422021-08-27 06:18:35 -0500296 error("fork() failed: {ERRNO}", "ERRNO", errno);
Gunnar Millsb30cadd2017-10-06 16:07:32 -0500297 report<UnTarFailure>(UnTarFail::PATH(tarFilePath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500298 return -1;
299 }
300
301 return 0;
302}
Gunnar Mills3027bba2017-04-27 15:49:03 -0500303
Gunnar Millse91d3212017-04-19 15:42:47 -0500304} // namespace manager
305} // namespace software
Gunnar Millsfa34e022018-09-04 10:05:45 -0500306} // namespace phosphor