blob: 8ff0a5c817f1cdedf8f050854bf5d75529b3db8d [file] [log] [blame]
Gunnar Millse91d3212017-04-19 15:42:47 -05001#include <string>
Gunnar Mills19e4ce52017-04-27 11:24:19 -05002#include <experimental/filesystem>
3#include <stdlib.h>
4#include <cstring>
5#include <stdio.h>
6#include <unistd.h>
Gunnar Mills3027bba2017-04-27 15:49:03 -05007#include <algorithm>
Gunnar Mills19e4ce52017-04-27 11:24:19 -05008#include <sys/wait.h>
9#include <sys/stat.h>
10#include <phosphor-logging/log.hpp>
Gunnar Mills93c12d32017-05-10 13:11:53 -050011#include <phosphor-logging/elog.hpp>
12#include <elog-errors.hpp>
13#include <xyz/openbmc_project/Software/Version/error.hpp>
Gunnar Mills19e4ce52017-04-27 11:24:19 -050014#include "config.h"
15#include "version.hpp"
Gunnar Mills3027bba2017-04-27 15:49:03 -050016#include "watch.hpp"
Gunnar Millse91d3212017-04-19 15:42:47 -050017#include "image_manager.hpp"
18
19namespace phosphor
20{
21namespace software
22{
23namespace manager
24{
25
Gunnar Mills19e4ce52017-04-27 11:24:19 -050026using namespace phosphor::logging;
Gunnar Mills93c12d32017-05-10 13:11:53 -050027using namespace sdbusplus::xyz::openbmc_project::Software::Version::Error;
Gunnar Millsb30cadd2017-10-06 16:07:32 -050028namespace Software = phosphor::logging::xyz::openbmc_project::Software;
29using ManifestFail = Software::Version::ManifestFileFailure;
30using UnTarFail = Software::Version::UnTarFailure;
31using InternalFail= Software::Version::InternalFailure;
Gunnar Mills19e4ce52017-04-27 11:24:19 -050032namespace fs = std::experimental::filesystem;
33
34struct RemovablePath
35{
36 fs::path path;
37
38 RemovablePath(const fs::path& path) : path(path) {}
39 ~RemovablePath()
40 {
Adriana Kobylak2b2cd9f2018-02-12 16:20:13 -060041 if (fs::exists(path))
42 {
43 fs::remove_all(path);
44 }
45 else
46 {
47 // Path should exist
48 log<level::ERR>("Error removable path does not exist",
49 entry("PATH=%s", path.c_str()));
50 }
Gunnar Mills19e4ce52017-04-27 11:24:19 -050051 }
52};
53
Gunnar Mills3027bba2017-04-27 15:49:03 -050054int Manager::processImage(const std::string& tarFilePath)
Gunnar Millse91d3212017-04-19 15:42:47 -050055{
Gunnar Mills19e4ce52017-04-27 11:24:19 -050056 if (!fs::is_regular_file(tarFilePath))
57 {
58 log<level::ERR>("Error tarball does not exist",
Adriana Kobylak596466b2018-02-13 14:48:53 -060059 entry("FILENAME=%s", tarFilePath.c_str()));
Gunnar Millsb30cadd2017-10-06 16:07:32 -050060 report<ManifestFileFailure>(ManifestFail::PATH(tarFilePath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -050061 return -1;
62
63 }
64 RemovablePath tarPathRemove(tarFilePath);
65 fs::path tmpDirPath(std::string{IMG_UPLOAD_DIR});
66 tmpDirPath /= "imageXXXXXX";
67
68 // Need tmp dir to write MANIFEST file to.
69 if (!mkdtemp(const_cast<char*>(tmpDirPath.c_str())))
70 {
Gunnar Mills601bbf02017-10-25 13:46:11 -050071 log<level::ERR>("Error occurred during mkdtemp",
Gunnar Mills19e4ce52017-04-27 11:24:19 -050072 entry("ERRNO=%d", errno));
Gunnar Millsb30cadd2017-10-06 16:07:32 -050073 report<InternalFailure>(InternalFail::FAIL("mkdtemp"));
Gunnar Mills19e4ce52017-04-27 11:24:19 -050074 return -1;
75 }
76
77 RemovablePath tmpDirRemove(tmpDirPath);
78 fs::path manifestPath = tmpDirPath;
79 manifestPath /= MANIFEST_FILE_NAME;
80 int status = 0;
81 pid_t pid = fork();
82
83 // Get the MANIFEST FILE
84 if (pid == 0)
85 {
86 // child process
87 execl("/bin/tar", "tar", "-xf", tarFilePath.c_str(), MANIFEST_FILE_NAME,
88 "-C", tmpDirPath.c_str(), (char*)0);
89 // execl only returns on fail
Adriana Kobylak596466b2018-02-13 14:48:53 -060090 log<level::ERR>("Failed to execute extract manifest",
91 entry("FILENAME=%s", tarFilePath.c_str()));
92 report<ManifestFileFailure>(ManifestFail::PATH(tarFilePath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -050093 return -1;
94 }
95 else if (pid > 0)
96 {
97 waitpid(pid, &status, 0);
Adriana Kobylak596466b2018-02-13 14:48:53 -060098 if (WEXITSTATUS(status))
99 {
100 log<level::ERR>("Failed to extract manifest",
101 entry("FILENAME=%s", tarFilePath.c_str()));
102 report<UnTarFailure>(UnTarFail::PATH(tarFilePath.c_str()));
103 return -1;
104 }
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500105 }
106 else
107 {
108 log<level::ERR>("fork() failed.");
Gunnar Millsb30cadd2017-10-06 16:07:32 -0500109 report<InternalFailure>(InternalFail::FAIL("fork"));
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500110 return -1;
111 }
112
113 // Verify the manifest file
114 if (!fs::is_regular_file(manifestPath))
115 {
Adriana Kobylak596466b2018-02-13 14:48:53 -0600116 log<level::ERR>("Error No manifest file",
117 entry("FILENAME=%s", tarFilePath.c_str()));
118 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 {
126 log<level::ERR>("Error unable to read version from manifest file");
Adriana Kobylak596466b2018-02-13 14:48:53 -0600127 report<ManifestFileFailure>(ManifestFail::PATH(tarFilePath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500128 return -1;
129 }
130
Gunnar Mills3027bba2017-04-27 15:49:03 -0500131 // Get purpose
132 auto purposeString = Version::getValue(manifestPath.string(), "purpose");
133 if (purposeString.empty())
134 {
135 log<level::ERR>("Error unable to read purpose from manifest file");
Adriana Kobylak596466b2018-02-13 14:48:53 -0600136 report<ManifestFileFailure>(ManifestFail::PATH(tarFilePath.c_str()));
Gunnar Mills3027bba2017-04-27 15:49:03 -0500137 return -1;
138 }
139
Gunnar Mills3027bba2017-04-27 15:49:03 -0500140 auto purpose = Version::VersionPurpose::Unknown;
Gunnar Millsb30cadd2017-10-06 16:07:32 -0500141 try
142 {
Leonel Gonzalez9a8d14c2017-06-22 11:42:24 -0500143 purpose = Version::convertVersionPurposeFromString(purposeString);
Gunnar Millsb30cadd2017-10-06 16:07:32 -0500144 }
145 catch (const sdbusplus::exception::InvalidEnumString& e)
146 {
147 log<level::ERR>("Error: Failed to convert manifest purpose to enum." \
148 " Setting to Unknown.");
Gunnar Mills3027bba2017-04-27 15:49:03 -0500149 }
150
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500151 // Compute id
152 auto id = Version::getId(version);
153
154 fs::path imageDirPath = std::string{IMG_UPLOAD_DIR};
155 imageDirPath /= id;
156
Gunnar Millsf576bca2017-05-18 13:40:39 -0500157 if (fs::exists(imageDirPath))
158 {
159 fs::remove_all(imageDirPath);
160 }
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500161 if (mkdir(imageDirPath.c_str(), S_IRWXU) != 0)
162 {
Gunnar Mills601bbf02017-10-25 13:46:11 -0500163 log<level::ERR>("Error occurred during mkdir",
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500164 entry("ERRNO=%d", errno));
Gunnar Millsb30cadd2017-10-06 16:07:32 -0500165 report<InternalFailure>(InternalFail::FAIL("mkdir"));
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500166 return -1;
167 }
168
169 // Untar tarball
Gunnar Mills9ec18ef2017-05-24 11:10:46 -0500170 auto rc = unTar(tarFilePath, imageDirPath.string());
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500171 if (rc < 0)
172 {
Gunnar Mills601bbf02017-10-25 13:46:11 -0500173 log<level::ERR>("Error occurred during untar");
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500174 return -1;
175 }
176
Gunnar Mills3027bba2017-04-27 15:49:03 -0500177 // Create Version object
178 auto objPath = std::string{SOFTWARE_OBJPATH} + '/' + id;
179
Saqib Khand377ba12017-10-16 14:32:30 -0500180 if (versions.find(id) == versions.end())
181 {
Saqib Khanee13e832017-10-23 12:53:11 -0500182 auto versionPtr = std::make_unique<Version>(
183 bus,
184 objPath,
185 version,
186 purpose,
187 imageDirPath.string(),
188 std::bind(&Manager::erase,
189 this,
190 std::placeholders::_1));
191 versionPtr->deleteObject =
192 std::make_unique<phosphor::software::manager::Delete>(
193 bus, objPath, *versionPtr);
194 versions.insert(std::make_pair(id, std::move(versionPtr)));
Saqib Khand377ba12017-10-16 14:32:30 -0500195 }
196 else
197 {
198 log<level::INFO>("Software Object with the same version already exists",
Adriana Kobylak596466b2018-02-13 14:48:53 -0600199 entry("VERSION_ID=%s", id.c_str()));
Saqib Khand377ba12017-10-16 14:32:30 -0500200 }
Gunnar Millse91d3212017-04-19 15:42:47 -0500201 return 0;
202}
203
Leonel Gonzalez50d559c2017-07-07 14:38:25 -0500204void Manager::erase(std::string entryId)
205{
206 auto it = versions.find(entryId);
207 if (it == versions.end())
208 {
209 return;
210 }
Eddie James6d873712017-09-01 11:29:07 -0500211
212 if (it->second->isFunctional())
213 {
214 log<level::ERR>(("Error: Version " + entryId + \
215 " is currently running on the BMC." \
216 " Unable to remove.").c_str());
217 return;
218 }
219
Leonel Gonzalez50d559c2017-07-07 14:38:25 -0500220 // Delete image dir
221 fs::path imageDirPath = (*(it->second)).path();
222 if (fs::exists(imageDirPath))
223 {
224 fs::remove_all(imageDirPath);
225 }
226 this->versions.erase(entryId);
227}
228
Gunnar Mills3027bba2017-04-27 15:49:03 -0500229int Manager::unTar(const std::string& tarFilePath,
230 const std::string& extractDirPath)
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500231{
232 if (tarFilePath.empty())
233 {
234 log<level::ERR>("Error TarFilePath is empty");
Gunnar Millsb30cadd2017-10-06 16:07:32 -0500235 report<UnTarFailure>(UnTarFail::PATH(tarFilePath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500236 return -1;
237 }
238 if (extractDirPath.empty())
239 {
240 log<level::ERR>("Error ExtractDirPath is empty");
Adriana Kobylak596466b2018-02-13 14:48:53 -0600241 report<UnTarFailure>(UnTarFail::PATH(extractDirPath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500242 return -1;
243 }
244
245 log<level::INFO>("Untaring",
Adriana Kobylak596466b2018-02-13 14:48:53 -0600246 entry("FILENAME=%s", tarFilePath.c_str()),
247 entry("EXTRACTIONDIR=%s", extractDirPath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500248 int status = 0;
249 pid_t pid = fork();
250
251 if (pid == 0)
252 {
253 // child process
254 execl("/bin/tar", "tar", "-xf", tarFilePath.c_str(),
255 "-C", extractDirPath.c_str(), (char*)0);
256 // execl only returns on fail
Adriana Kobylak596466b2018-02-13 14:48:53 -0600257 log<level::ERR>("Failed to execute untar file",
258 entry("FILENAME=%s", tarFilePath.c_str()));
Gunnar Millsb30cadd2017-10-06 16:07:32 -0500259 report<UnTarFailure>(UnTarFail::PATH(tarFilePath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500260 return -1;
261 }
262 else if (pid > 0)
263 {
264 waitpid(pid, &status, 0);
Adriana Kobylak596466b2018-02-13 14:48:53 -0600265 if (WEXITSTATUS(status))
266 {
267 log<level::ERR>("Failed to untar file",
268 entry("FILENAME=%s", tarFilePath.c_str()));
269 report<UnTarFailure>(UnTarFail::PATH(tarFilePath.c_str()));
270 return -1;
271 }
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500272 }
273 else
274 {
275 log<level::ERR>("fork() failed.");
Gunnar Millsb30cadd2017-10-06 16:07:32 -0500276 report<UnTarFailure>(UnTarFail::PATH(tarFilePath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500277 return -1;
278 }
279
280 return 0;
281}
Gunnar Mills3027bba2017-04-27 15:49:03 -0500282
Gunnar Millse91d3212017-04-19 15:42:47 -0500283} // namespace manager
284} // namespace software
285} // namepsace phosphor