blob: 7c198666ec039f912dd983f5151ae583eddcdea4 [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;
Adriana Kobylak2285fe02018-02-27 15:36:59 -060031using 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
Adriana Kobylak2285fe02018-02-27 15:36:59 -060038 RemovablePath(const fs::path& path) : path(path)
39 {
40 }
Gunnar Mills19e4ce52017-04-27 11:24:19 -050041 ~RemovablePath()
42 {
Adriana Kobylak2b2cd9f2018-02-12 16:20:13 -060043 if (fs::exists(path))
44 {
45 fs::remove_all(path);
46 }
47 else
48 {
49 // Path should exist
50 log<level::ERR>("Error removable path does not exist",
51 entry("PATH=%s", path.c_str()));
52 }
Gunnar Mills19e4ce52017-04-27 11:24:19 -050053 }
54};
55
Gunnar Mills3027bba2017-04-27 15:49:03 -050056int Manager::processImage(const std::string& tarFilePath)
Gunnar Millse91d3212017-04-19 15:42:47 -050057{
Gunnar Mills19e4ce52017-04-27 11:24:19 -050058 if (!fs::is_regular_file(tarFilePath))
59 {
60 log<level::ERR>("Error tarball does not exist",
Adriana Kobylak596466b2018-02-13 14:48:53 -060061 entry("FILENAME=%s", tarFilePath.c_str()));
Gunnar Millsb30cadd2017-10-06 16:07:32 -050062 report<ManifestFileFailure>(ManifestFail::PATH(tarFilePath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -050063 return -1;
Gunnar Mills19e4ce52017-04-27 11:24:19 -050064 }
65 RemovablePath tarPathRemove(tarFilePath);
66 fs::path tmpDirPath(std::string{IMG_UPLOAD_DIR});
67 tmpDirPath /= "imageXXXXXX";
68
69 // Need tmp dir to write MANIFEST file to.
70 if (!mkdtemp(const_cast<char*>(tmpDirPath.c_str())))
71 {
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
78 RemovablePath tmpDirRemove(tmpDirPath);
79 fs::path manifestPath = tmpDirPath;
80 manifestPath /= MANIFEST_FILE_NAME;
81 int status = 0;
82 pid_t pid = fork();
83
84 // Get the MANIFEST FILE
85 if (pid == 0)
86 {
87 // child process
88 execl("/bin/tar", "tar", "-xf", tarFilePath.c_str(), MANIFEST_FILE_NAME,
89 "-C", tmpDirPath.c_str(), (char*)0);
90 // execl only returns on fail
Adriana Kobylak596466b2018-02-13 14:48:53 -060091 log<level::ERR>("Failed to execute extract manifest",
92 entry("FILENAME=%s", tarFilePath.c_str()));
93 report<ManifestFileFailure>(ManifestFail::PATH(tarFilePath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -050094 return -1;
95 }
96 else if (pid > 0)
97 {
98 waitpid(pid, &status, 0);
Adriana Kobylak596466b2018-02-13 14:48:53 -060099 if (WEXITSTATUS(status))
100 {
101 log<level::ERR>("Failed to extract manifest",
102 entry("FILENAME=%s", tarFilePath.c_str()));
103 report<UnTarFailure>(UnTarFail::PATH(tarFilePath.c_str()));
104 return -1;
105 }
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500106 }
107 else
108 {
109 log<level::ERR>("fork() failed.");
Gunnar Millsb30cadd2017-10-06 16:07:32 -0500110 report<InternalFailure>(InternalFail::FAIL("fork"));
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500111 return -1;
112 }
113
114 // Verify the manifest file
115 if (!fs::is_regular_file(manifestPath))
116 {
Adriana Kobylak596466b2018-02-13 14:48:53 -0600117 log<level::ERR>("Error No manifest file",
118 entry("FILENAME=%s", tarFilePath.c_str()));
119 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 {
127 log<level::ERR>("Error unable to read version from manifest file");
Adriana Kobylak596466b2018-02-13 14:48:53 -0600128 report<ManifestFileFailure>(ManifestFail::PATH(tarFilePath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500129 return -1;
130 }
131
Gunnar Mills3027bba2017-04-27 15:49:03 -0500132 // Get purpose
133 auto purposeString = Version::getValue(manifestPath.string(), "purpose");
134 if (purposeString.empty())
135 {
136 log<level::ERR>("Error unable to read purpose from manifest file");
Adriana Kobylak596466b2018-02-13 14:48:53 -0600137 report<ManifestFileFailure>(ManifestFail::PATH(tarFilePath.c_str()));
Gunnar Mills3027bba2017-04-27 15:49:03 -0500138 return -1;
139 }
140
Gunnar Mills3027bba2017-04-27 15:49:03 -0500141 auto purpose = Version::VersionPurpose::Unknown;
Gunnar Millsb30cadd2017-10-06 16:07:32 -0500142 try
143 {
Leonel Gonzalez9a8d14c2017-06-22 11:42:24 -0500144 purpose = Version::convertVersionPurposeFromString(purposeString);
Gunnar Millsb30cadd2017-10-06 16:07:32 -0500145 }
146 catch (const sdbusplus::exception::InvalidEnumString& e)
147 {
Adriana Kobylak2285fe02018-02-27 15:36:59 -0600148 log<level::ERR>("Error: Failed to convert manifest purpose to enum."
Gunnar Millsb30cadd2017-10-06 16:07:32 -0500149 " Setting to Unknown.");
Gunnar Mills3027bba2017-04-27 15:49:03 -0500150 }
151
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500152 // Compute id
153 auto id = Version::getId(version);
154
155 fs::path imageDirPath = std::string{IMG_UPLOAD_DIR};
156 imageDirPath /= id;
157
Gunnar Millsf576bca2017-05-18 13:40:39 -0500158 if (fs::exists(imageDirPath))
159 {
160 fs::remove_all(imageDirPath);
161 }
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500162 if (mkdir(imageDirPath.c_str(), S_IRWXU) != 0)
163 {
Gunnar Mills601bbf02017-10-25 13:46:11 -0500164 log<level::ERR>("Error occurred during mkdir",
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500165 entry("ERRNO=%d", errno));
Gunnar Millsb30cadd2017-10-06 16:07:32 -0500166 report<InternalFailure>(InternalFail::FAIL("mkdir"));
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500167 return -1;
168 }
169
170 // Untar tarball
Gunnar Mills9ec18ef2017-05-24 11:10:46 -0500171 auto rc = unTar(tarFilePath, imageDirPath.string());
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500172 if (rc < 0)
173 {
Gunnar Mills601bbf02017-10-25 13:46:11 -0500174 log<level::ERR>("Error occurred during untar");
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500175 return -1;
176 }
177
Gunnar Mills3027bba2017-04-27 15:49:03 -0500178 // Create Version object
Adriana Kobylak2285fe02018-02-27 15:36:59 -0600179 auto objPath = std::string{SOFTWARE_OBJPATH} + '/' + id;
Gunnar Mills3027bba2017-04-27 15:49:03 -0500180
Saqib Khand377ba12017-10-16 14:32:30 -0500181 if (versions.find(id) == versions.end())
182 {
Saqib Khanee13e832017-10-23 12:53:11 -0500183 auto versionPtr = std::make_unique<Version>(
Adriana Kobylak2285fe02018-02-27 15:36:59 -0600184 bus, objPath, version, purpose, imageDirPath.string(),
185 std::bind(&Manager::erase, this, std::placeholders::_1));
Saqib Khanee13e832017-10-23 12:53:11 -0500186 versionPtr->deleteObject =
Adriana Kobylak2285fe02018-02-27 15:36:59 -0600187 std::make_unique<phosphor::software::manager::Delete>(bus, objPath,
188 *versionPtr);
Saqib Khanee13e832017-10-23 12:53:11 -0500189 versions.insert(std::make_pair(id, std::move(versionPtr)));
Saqib Khand377ba12017-10-16 14:32:30 -0500190 }
191 else
192 {
193 log<level::INFO>("Software Object with the same version already exists",
Adriana Kobylak596466b2018-02-13 14:48:53 -0600194 entry("VERSION_ID=%s", id.c_str()));
Saqib Khand377ba12017-10-16 14:32:30 -0500195 }
Gunnar Millse91d3212017-04-19 15:42:47 -0500196 return 0;
197}
198
Leonel Gonzalez50d559c2017-07-07 14:38:25 -0500199void Manager::erase(std::string entryId)
200{
201 auto it = versions.find(entryId);
202 if (it == versions.end())
203 {
204 return;
205 }
Eddie James6d873712017-09-01 11:29:07 -0500206
207 if (it->second->isFunctional())
208 {
Adriana Kobylak2285fe02018-02-27 15:36:59 -0600209 log<level::ERR>(("Error: Version " + entryId +
210 " is currently running on the BMC."
211 " Unable to remove.")
212 .c_str());
Eddie James6d873712017-09-01 11:29:07 -0500213 return;
214 }
215
Leonel Gonzalez50d559c2017-07-07 14:38:25 -0500216 // Delete image dir
217 fs::path imageDirPath = (*(it->second)).path();
218 if (fs::exists(imageDirPath))
219 {
220 fs::remove_all(imageDirPath);
221 }
222 this->versions.erase(entryId);
223}
224
Gunnar Mills3027bba2017-04-27 15:49:03 -0500225int Manager::unTar(const std::string& tarFilePath,
226 const std::string& extractDirPath)
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500227{
228 if (tarFilePath.empty())
229 {
230 log<level::ERR>("Error TarFilePath is empty");
Gunnar Millsb30cadd2017-10-06 16:07:32 -0500231 report<UnTarFailure>(UnTarFail::PATH(tarFilePath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500232 return -1;
233 }
234 if (extractDirPath.empty())
235 {
236 log<level::ERR>("Error ExtractDirPath is empty");
Adriana Kobylak596466b2018-02-13 14:48:53 -0600237 report<UnTarFailure>(UnTarFail::PATH(extractDirPath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500238 return -1;
239 }
240
Adriana Kobylak2285fe02018-02-27 15:36:59 -0600241 log<level::INFO>("Untaring", entry("FILENAME=%s", tarFilePath.c_str()),
Adriana Kobylak596466b2018-02-13 14:48:53 -0600242 entry("EXTRACTIONDIR=%s", extractDirPath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500243 int status = 0;
244 pid_t pid = fork();
245
246 if (pid == 0)
247 {
248 // child process
Adriana Kobylak2285fe02018-02-27 15:36:59 -0600249 execl("/bin/tar", "tar", "-xf", tarFilePath.c_str(), "-C",
250 extractDirPath.c_str(), (char*)0);
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500251 // execl only returns on fail
Adriana Kobylak596466b2018-02-13 14:48:53 -0600252 log<level::ERR>("Failed to execute untar file",
253 entry("FILENAME=%s", tarFilePath.c_str()));
Gunnar Millsb30cadd2017-10-06 16:07:32 -0500254 report<UnTarFailure>(UnTarFail::PATH(tarFilePath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500255 return -1;
256 }
257 else if (pid > 0)
258 {
259 waitpid(pid, &status, 0);
Adriana Kobylak596466b2018-02-13 14:48:53 -0600260 if (WEXITSTATUS(status))
261 {
262 log<level::ERR>("Failed to untar file",
263 entry("FILENAME=%s", tarFilePath.c_str()));
264 report<UnTarFailure>(UnTarFail::PATH(tarFilePath.c_str()));
265 return -1;
266 }
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500267 }
268 else
269 {
270 log<level::ERR>("fork() failed.");
Gunnar Millsb30cadd2017-10-06 16:07:32 -0500271 report<UnTarFailure>(UnTarFail::PATH(tarFilePath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500272 return -1;
273 }
274
275 return 0;
276}
Gunnar Mills3027bba2017-04-27 15:49:03 -0500277
Gunnar Millse91d3212017-04-19 15:42:47 -0500278} // namespace manager
279} // namespace software
280} // namepsace phosphor