blob: 5b2ff49950fc22e3906338c57fb012079f8e484c [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
14#include <algorithm>
15#include <cstring>
16#include <elog-errors.hpp>
17#include <experimental/filesystem>
18#include <phosphor-logging/elog.hpp>
19#include <phosphor-logging/log.hpp>
20#include <string>
Adriana Kobylak43699ca2018-10-17 14:56:29 -050021#include <xyz/openbmc_project/Software/Image/error.hpp>
Gunnar Millse91d3212017-04-19 15:42:47 -050022
23namespace phosphor
24{
25namespace software
26{
27namespace manager
28{
29
Gunnar Mills19e4ce52017-04-27 11:24:19 -050030using namespace phosphor::logging;
Adriana Kobylak43699ca2018-10-17 14:56:29 -050031using namespace sdbusplus::xyz::openbmc_project::Software::Image::Error;
Gunnar Millsb30cadd2017-10-06 16:07:32 -050032namespace Software = phosphor::logging::xyz::openbmc_project::Software;
Adriana Kobylak43699ca2018-10-17 14:56:29 -050033using ManifestFail = Software::Image::ManifestFileFailure;
34using UnTarFail = Software::Image::UnTarFailure;
35using InternalFail = Software::Image::InternalFailure;
Gunnar Mills19e4ce52017-04-27 11:24:19 -050036namespace fs = std::experimental::filesystem;
37
38struct RemovablePath
39{
40 fs::path path;
41
Adriana Kobylak2285fe02018-02-27 15:36:59 -060042 RemovablePath(const fs::path& path) : path(path)
43 {
44 }
Gunnar Mills19e4ce52017-04-27 11:24:19 -050045 ~RemovablePath()
46 {
Lei YU5d930282019-03-08 16:41:51 +080047 if (!path.empty())
Adriana Kobylak2b2cd9f2018-02-12 16:20:13 -060048 {
Lei YU5d930282019-03-08 16:41:51 +080049 std::error_code ec;
50 fs::remove_all(path, ec);
Adriana Kobylak2b2cd9f2018-02-12 16:20:13 -060051 }
Gunnar Mills19e4ce52017-04-27 11:24:19 -050052 }
53};
54
Gunnar Mills3027bba2017-04-27 15:49:03 -050055int Manager::processImage(const std::string& tarFilePath)
Gunnar Millse91d3212017-04-19 15:42:47 -050056{
Gunnar Mills19e4ce52017-04-27 11:24:19 -050057 if (!fs::is_regular_file(tarFilePath))
58 {
59 log<level::ERR>("Error tarball does not exist",
Adriana Kobylak596466b2018-02-13 14:48:53 -060060 entry("FILENAME=%s", tarFilePath.c_str()));
Gunnar Millsb30cadd2017-10-06 16:07:32 -050061 report<ManifestFileFailure>(ManifestFail::PATH(tarFilePath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -050062 return -1;
Gunnar Mills19e4ce52017-04-27 11:24:19 -050063 }
64 RemovablePath tarPathRemove(tarFilePath);
65 fs::path tmpDirPath(std::string{IMG_UPLOAD_DIR});
66 tmpDirPath /= "imageXXXXXX";
Lei YU5d930282019-03-08 16:41:51 +080067 auto tmpDir = tmpDirPath.string();
Gunnar Mills19e4ce52017-04-27 11:24:19 -050068
Lei YU5d930282019-03-08 16:41:51 +080069 // Create a tmp dir to extract tarball.
70 if (!mkdtemp(tmpDir.data()))
Gunnar Mills19e4ce52017-04-27 11:24:19 -050071 {
Gunnar Mills601bbf02017-10-25 13:46:11 -050072 log<level::ERR>("Error occurred during mkdtemp",
Gunnar Mills19e4ce52017-04-27 11:24:19 -050073 entry("ERRNO=%d", errno));
Gunnar Millsb30cadd2017-10-06 16:07:32 -050074 report<InternalFailure>(InternalFail::FAIL("mkdtemp"));
Gunnar Mills19e4ce52017-04-27 11:24:19 -050075 return -1;
76 }
77
Lei YU5d930282019-03-08 16:41:51 +080078 tmpDirPath = tmpDir;
79 RemovablePath tmpDirToRemove(tmpDirPath);
Gunnar Mills19e4ce52017-04-27 11:24:19 -050080 fs::path manifestPath = tmpDirPath;
81 manifestPath /= MANIFEST_FILE_NAME;
Gunnar Mills19e4ce52017-04-27 11:24:19 -050082
Lei YU5d930282019-03-08 16:41:51 +080083 // Untar tarball into the tmp dir
84 auto rc = unTar(tarFilePath, tmpDirPath.string());
85 if (rc < 0)
Gunnar Mills19e4ce52017-04-27 11:24:19 -050086 {
Lei YU5d930282019-03-08 16:41:51 +080087 log<level::ERR>("Error occurred during untar");
Gunnar Mills19e4ce52017-04-27 11:24:19 -050088 return -1;
89 }
90
91 // Verify the manifest file
92 if (!fs::is_regular_file(manifestPath))
93 {
Adriana Kobylak596466b2018-02-13 14:48:53 -060094 log<level::ERR>("Error No manifest file",
95 entry("FILENAME=%s", tarFilePath.c_str()));
96 report<ManifestFileFailure>(ManifestFail::PATH(tarFilePath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -050097 return -1;
98 }
99
100 // Get version
101 auto version = Version::getValue(manifestPath.string(), "version");
102 if (version.empty())
103 {
104 log<level::ERR>("Error unable to read version from manifest file");
Adriana Kobylak596466b2018-02-13 14:48:53 -0600105 report<ManifestFileFailure>(ManifestFail::PATH(tarFilePath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500106 return -1;
107 }
108
Gunnar Mills3027bba2017-04-27 15:49:03 -0500109 // Get purpose
110 auto purposeString = Version::getValue(manifestPath.string(), "purpose");
111 if (purposeString.empty())
112 {
113 log<level::ERR>("Error unable to read purpose from manifest file");
Adriana Kobylak596466b2018-02-13 14:48:53 -0600114 report<ManifestFileFailure>(ManifestFail::PATH(tarFilePath.c_str()));
Gunnar Mills3027bba2017-04-27 15:49:03 -0500115 return -1;
116 }
117
Gunnar Mills3027bba2017-04-27 15:49:03 -0500118 auto purpose = Version::VersionPurpose::Unknown;
Gunnar Millsb30cadd2017-10-06 16:07:32 -0500119 try
120 {
Leonel Gonzalez9a8d14c2017-06-22 11:42:24 -0500121 purpose = Version::convertVersionPurposeFromString(purposeString);
Gunnar Millsb30cadd2017-10-06 16:07:32 -0500122 }
123 catch (const sdbusplus::exception::InvalidEnumString& e)
124 {
Adriana Kobylak2285fe02018-02-27 15:36:59 -0600125 log<level::ERR>("Error: Failed to convert manifest purpose to enum."
Gunnar Millsb30cadd2017-10-06 16:07:32 -0500126 " Setting to Unknown.");
Gunnar Mills3027bba2017-04-27 15:49:03 -0500127 }
128
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500129 // Compute id
130 auto id = Version::getId(version);
131
132 fs::path imageDirPath = std::string{IMG_UPLOAD_DIR};
133 imageDirPath /= id;
134
Gunnar Millsf576bca2017-05-18 13:40:39 -0500135 if (fs::exists(imageDirPath))
136 {
137 fs::remove_all(imageDirPath);
138 }
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500139
Lei YU5d930282019-03-08 16:41:51 +0800140 // Rename the temp dir to image dir
141 fs::rename(tmpDirPath, imageDirPath);
142
143 // Clear the path, so it does not attemp to remove a non-existing path
144 tmpDirToRemove.path.clear();
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500145
Gunnar Mills3027bba2017-04-27 15:49:03 -0500146 // Create Version object
Adriana Kobylak2285fe02018-02-27 15:36:59 -0600147 auto objPath = std::string{SOFTWARE_OBJPATH} + '/' + id;
Gunnar Mills3027bba2017-04-27 15:49:03 -0500148
Saqib Khand377ba12017-10-16 14:32:30 -0500149 if (versions.find(id) == versions.end())
150 {
Saqib Khanee13e832017-10-23 12:53:11 -0500151 auto versionPtr = std::make_unique<Version>(
Adriana Kobylak2285fe02018-02-27 15:36:59 -0600152 bus, objPath, version, purpose, imageDirPath.string(),
153 std::bind(&Manager::erase, this, std::placeholders::_1));
Saqib Khanee13e832017-10-23 12:53:11 -0500154 versionPtr->deleteObject =
Adriana Kobylak2285fe02018-02-27 15:36:59 -0600155 std::make_unique<phosphor::software::manager::Delete>(bus, objPath,
156 *versionPtr);
Saqib Khanee13e832017-10-23 12:53:11 -0500157 versions.insert(std::make_pair(id, std::move(versionPtr)));
Saqib Khand377ba12017-10-16 14:32:30 -0500158 }
159 else
160 {
161 log<level::INFO>("Software Object with the same version already exists",
Adriana Kobylak596466b2018-02-13 14:48:53 -0600162 entry("VERSION_ID=%s", id.c_str()));
Saqib Khand377ba12017-10-16 14:32:30 -0500163 }
Gunnar Millse91d3212017-04-19 15:42:47 -0500164 return 0;
165}
166
Leonel Gonzalez50d559c2017-07-07 14:38:25 -0500167void Manager::erase(std::string entryId)
168{
169 auto it = versions.find(entryId);
170 if (it == versions.end())
171 {
172 return;
173 }
Eddie James6d873712017-09-01 11:29:07 -0500174
175 if (it->second->isFunctional())
176 {
Adriana Kobylak2285fe02018-02-27 15:36:59 -0600177 log<level::ERR>(("Error: Version " + entryId +
178 " is currently running on the BMC."
179 " Unable to remove.")
180 .c_str());
Eddie James6d873712017-09-01 11:29:07 -0500181 return;
182 }
183
Leonel Gonzalez50d559c2017-07-07 14:38:25 -0500184 // Delete image dir
185 fs::path imageDirPath = (*(it->second)).path();
186 if (fs::exists(imageDirPath))
187 {
188 fs::remove_all(imageDirPath);
189 }
190 this->versions.erase(entryId);
191}
192
Gunnar Mills3027bba2017-04-27 15:49:03 -0500193int Manager::unTar(const std::string& tarFilePath,
194 const std::string& extractDirPath)
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500195{
196 if (tarFilePath.empty())
197 {
198 log<level::ERR>("Error TarFilePath is empty");
Gunnar Millsb30cadd2017-10-06 16:07:32 -0500199 report<UnTarFailure>(UnTarFail::PATH(tarFilePath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500200 return -1;
201 }
202 if (extractDirPath.empty())
203 {
204 log<level::ERR>("Error ExtractDirPath is empty");
Adriana Kobylak596466b2018-02-13 14:48:53 -0600205 report<UnTarFailure>(UnTarFail::PATH(extractDirPath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500206 return -1;
207 }
208
Adriana Kobylak2285fe02018-02-27 15:36:59 -0600209 log<level::INFO>("Untaring", entry("FILENAME=%s", tarFilePath.c_str()),
Adriana Kobylak596466b2018-02-13 14:48:53 -0600210 entry("EXTRACTIONDIR=%s", extractDirPath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500211 int status = 0;
212 pid_t pid = fork();
213
214 if (pid == 0)
215 {
216 // child process
Adriana Kobylak2285fe02018-02-27 15:36:59 -0600217 execl("/bin/tar", "tar", "-xf", tarFilePath.c_str(), "-C",
218 extractDirPath.c_str(), (char*)0);
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500219 // execl only returns on fail
Adriana Kobylak596466b2018-02-13 14:48:53 -0600220 log<level::ERR>("Failed to execute untar file",
221 entry("FILENAME=%s", tarFilePath.c_str()));
Gunnar Millsb30cadd2017-10-06 16:07:32 -0500222 report<UnTarFailure>(UnTarFail::PATH(tarFilePath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500223 return -1;
224 }
225 else if (pid > 0)
226 {
227 waitpid(pid, &status, 0);
Adriana Kobylak596466b2018-02-13 14:48:53 -0600228 if (WEXITSTATUS(status))
229 {
230 log<level::ERR>("Failed to untar file",
231 entry("FILENAME=%s", tarFilePath.c_str()));
232 report<UnTarFailure>(UnTarFail::PATH(tarFilePath.c_str()));
233 return -1;
234 }
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500235 }
236 else
237 {
238 log<level::ERR>("fork() failed.");
Gunnar Millsb30cadd2017-10-06 16:07:32 -0500239 report<UnTarFailure>(UnTarFail::PATH(tarFilePath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500240 return -1;
241 }
242
243 return 0;
244}
Gunnar Mills3027bba2017-04-27 15:49:03 -0500245
Gunnar Millse91d3212017-04-19 15:42:47 -0500246} // namespace manager
247} // namespace software
Gunnar Millsfa34e022018-09-04 10:05:45 -0500248} // namespace phosphor