blob: 8bae16c63c413b9206ae9a5116fe2941631420af [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>
21#include <xyz/openbmc_project/Software/Version/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;
Gunnar Mills93c12d32017-05-10 13:11:53 -050031using namespace sdbusplus::xyz::openbmc_project::Software::Version::Error;
Gunnar Millsb30cadd2017-10-06 16:07:32 -050032namespace Software = phosphor::logging::xyz::openbmc_project::Software;
33using ManifestFail = Software::Version::ManifestFileFailure;
34using UnTarFail = Software::Version::UnTarFailure;
Adriana Kobylak2285fe02018-02-27 15:36:59 -060035using InternalFail = Software::Version::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 {
Adriana Kobylak2b2cd9f2018-02-12 16:20:13 -060047 if (fs::exists(path))
48 {
49 fs::remove_all(path);
50 }
51 else
52 {
53 // Path should exist
54 log<level::ERR>("Error removable path does not exist",
55 entry("PATH=%s", path.c_str()));
56 }
Gunnar Mills19e4ce52017-04-27 11:24:19 -050057 }
58};
59
Gunnar Mills3027bba2017-04-27 15:49:03 -050060int Manager::processImage(const std::string& tarFilePath)
Gunnar Millse91d3212017-04-19 15:42:47 -050061{
Gunnar Mills19e4ce52017-04-27 11:24:19 -050062 if (!fs::is_regular_file(tarFilePath))
63 {
64 log<level::ERR>("Error tarball does not exist",
Adriana Kobylak596466b2018-02-13 14:48:53 -060065 entry("FILENAME=%s", tarFilePath.c_str()));
Gunnar Millsb30cadd2017-10-06 16:07:32 -050066 report<ManifestFileFailure>(ManifestFail::PATH(tarFilePath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -050067 return -1;
Gunnar Mills19e4ce52017-04-27 11:24:19 -050068 }
69 RemovablePath tarPathRemove(tarFilePath);
70 fs::path tmpDirPath(std::string{IMG_UPLOAD_DIR});
71 tmpDirPath /= "imageXXXXXX";
72
73 // Need tmp dir to write MANIFEST file to.
74 if (!mkdtemp(const_cast<char*>(tmpDirPath.c_str())))
75 {
Gunnar Mills601bbf02017-10-25 13:46:11 -050076 log<level::ERR>("Error occurred during mkdtemp",
Gunnar Mills19e4ce52017-04-27 11:24:19 -050077 entry("ERRNO=%d", errno));
Gunnar Millsb30cadd2017-10-06 16:07:32 -050078 report<InternalFailure>(InternalFail::FAIL("mkdtemp"));
Gunnar Mills19e4ce52017-04-27 11:24:19 -050079 return -1;
80 }
81
82 RemovablePath tmpDirRemove(tmpDirPath);
83 fs::path manifestPath = tmpDirPath;
84 manifestPath /= MANIFEST_FILE_NAME;
85 int status = 0;
86 pid_t pid = fork();
87
88 // Get the MANIFEST FILE
89 if (pid == 0)
90 {
91 // child process
92 execl("/bin/tar", "tar", "-xf", tarFilePath.c_str(), MANIFEST_FILE_NAME,
93 "-C", tmpDirPath.c_str(), (char*)0);
94 // execl only returns on fail
Adriana Kobylak596466b2018-02-13 14:48:53 -060095 log<level::ERR>("Failed to execute extract manifest",
96 entry("FILENAME=%s", tarFilePath.c_str()));
97 report<ManifestFileFailure>(ManifestFail::PATH(tarFilePath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -050098 return -1;
99 }
100 else if (pid > 0)
101 {
102 waitpid(pid, &status, 0);
Adriana Kobylak596466b2018-02-13 14:48:53 -0600103 if (WEXITSTATUS(status))
104 {
105 log<level::ERR>("Failed to extract manifest",
106 entry("FILENAME=%s", tarFilePath.c_str()));
107 report<UnTarFailure>(UnTarFail::PATH(tarFilePath.c_str()));
108 return -1;
109 }
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500110 }
111 else
112 {
113 log<level::ERR>("fork() failed.");
Gunnar Millsb30cadd2017-10-06 16:07:32 -0500114 report<InternalFailure>(InternalFail::FAIL("fork"));
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500115 return -1;
116 }
117
118 // Verify the manifest file
119 if (!fs::is_regular_file(manifestPath))
120 {
Adriana Kobylak596466b2018-02-13 14:48:53 -0600121 log<level::ERR>("Error No manifest file",
122 entry("FILENAME=%s", tarFilePath.c_str()));
123 report<ManifestFileFailure>(ManifestFail::PATH(tarFilePath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500124 return -1;
125 }
126
127 // Get version
128 auto version = Version::getValue(manifestPath.string(), "version");
129 if (version.empty())
130 {
131 log<level::ERR>("Error unable to read version from manifest file");
Adriana Kobylak596466b2018-02-13 14:48:53 -0600132 report<ManifestFileFailure>(ManifestFail::PATH(tarFilePath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500133 return -1;
134 }
135
Gunnar Mills3027bba2017-04-27 15:49:03 -0500136 // Get purpose
137 auto purposeString = Version::getValue(manifestPath.string(), "purpose");
138 if (purposeString.empty())
139 {
140 log<level::ERR>("Error unable to read purpose from manifest file");
Adriana Kobylak596466b2018-02-13 14:48:53 -0600141 report<ManifestFileFailure>(ManifestFail::PATH(tarFilePath.c_str()));
Gunnar Mills3027bba2017-04-27 15:49:03 -0500142 return -1;
143 }
144
Gunnar Mills3027bba2017-04-27 15:49:03 -0500145 auto purpose = Version::VersionPurpose::Unknown;
Gunnar Millsb30cadd2017-10-06 16:07:32 -0500146 try
147 {
Leonel Gonzalez9a8d14c2017-06-22 11:42:24 -0500148 purpose = Version::convertVersionPurposeFromString(purposeString);
Gunnar Millsb30cadd2017-10-06 16:07:32 -0500149 }
150 catch (const sdbusplus::exception::InvalidEnumString& e)
151 {
Adriana Kobylak2285fe02018-02-27 15:36:59 -0600152 log<level::ERR>("Error: Failed to convert manifest purpose to enum."
Gunnar Millsb30cadd2017-10-06 16:07:32 -0500153 " Setting to Unknown.");
Gunnar Mills3027bba2017-04-27 15:49:03 -0500154 }
155
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500156 // Compute id
157 auto id = Version::getId(version);
158
159 fs::path imageDirPath = std::string{IMG_UPLOAD_DIR};
160 imageDirPath /= id;
161
Gunnar Millsf576bca2017-05-18 13:40:39 -0500162 if (fs::exists(imageDirPath))
163 {
164 fs::remove_all(imageDirPath);
165 }
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500166 if (mkdir(imageDirPath.c_str(), S_IRWXU) != 0)
167 {
Gunnar Mills601bbf02017-10-25 13:46:11 -0500168 log<level::ERR>("Error occurred during mkdir",
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500169 entry("ERRNO=%d", errno));
Gunnar Millsb30cadd2017-10-06 16:07:32 -0500170 report<InternalFailure>(InternalFail::FAIL("mkdir"));
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500171 return -1;
172 }
173
174 // Untar tarball
Gunnar Mills9ec18ef2017-05-24 11:10:46 -0500175 auto rc = unTar(tarFilePath, imageDirPath.string());
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500176 if (rc < 0)
177 {
Gunnar Mills601bbf02017-10-25 13:46:11 -0500178 log<level::ERR>("Error occurred during untar");
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500179 return -1;
180 }
181
Gunnar Mills3027bba2017-04-27 15:49:03 -0500182 // Create Version object
Adriana Kobylak2285fe02018-02-27 15:36:59 -0600183 auto objPath = std::string{SOFTWARE_OBJPATH} + '/' + id;
Gunnar Mills3027bba2017-04-27 15:49:03 -0500184
Saqib Khand377ba12017-10-16 14:32:30 -0500185 if (versions.find(id) == versions.end())
186 {
Saqib Khanee13e832017-10-23 12:53:11 -0500187 auto versionPtr = std::make_unique<Version>(
Adriana Kobylak2285fe02018-02-27 15:36:59 -0600188 bus, objPath, version, purpose, imageDirPath.string(),
189 std::bind(&Manager::erase, this, std::placeholders::_1));
Saqib Khanee13e832017-10-23 12:53:11 -0500190 versionPtr->deleteObject =
Adriana Kobylak2285fe02018-02-27 15:36:59 -0600191 std::make_unique<phosphor::software::manager::Delete>(bus, objPath,
192 *versionPtr);
Saqib Khanee13e832017-10-23 12:53:11 -0500193 versions.insert(std::make_pair(id, std::move(versionPtr)));
Saqib Khand377ba12017-10-16 14:32:30 -0500194 }
195 else
196 {
197 log<level::INFO>("Software Object with the same version already exists",
Adriana Kobylak596466b2018-02-13 14:48:53 -0600198 entry("VERSION_ID=%s", id.c_str()));
Saqib Khand377ba12017-10-16 14:32:30 -0500199 }
Gunnar Millse91d3212017-04-19 15:42:47 -0500200 return 0;
201}
202
Leonel Gonzalez50d559c2017-07-07 14:38:25 -0500203void Manager::erase(std::string entryId)
204{
205 auto it = versions.find(entryId);
206 if (it == versions.end())
207 {
208 return;
209 }
Eddie James6d873712017-09-01 11:29:07 -0500210
211 if (it->second->isFunctional())
212 {
Adriana Kobylak2285fe02018-02-27 15:36:59 -0600213 log<level::ERR>(("Error: Version " + entryId +
214 " is currently running on the BMC."
215 " Unable to remove.")
216 .c_str());
Eddie James6d873712017-09-01 11:29:07 -0500217 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
Adriana Kobylak2285fe02018-02-27 15:36:59 -0600245 log<level::INFO>("Untaring", entry("FILENAME=%s", tarFilePath.c_str()),
Adriana Kobylak596466b2018-02-13 14:48:53 -0600246 entry("EXTRACTIONDIR=%s", extractDirPath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500247 int status = 0;
248 pid_t pid = fork();
249
250 if (pid == 0)
251 {
252 // child process
Adriana Kobylak2285fe02018-02-27 15:36:59 -0600253 execl("/bin/tar", "tar", "-xf", tarFilePath.c_str(), "-C",
254 extractDirPath.c_str(), (char*)0);
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500255 // execl only returns on fail
Adriana Kobylak596466b2018-02-13 14:48:53 -0600256 log<level::ERR>("Failed to execute untar file",
257 entry("FILENAME=%s", tarFilePath.c_str()));
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 else if (pid > 0)
262 {
263 waitpid(pid, &status, 0);
Adriana Kobylak596466b2018-02-13 14:48:53 -0600264 if (WEXITSTATUS(status))
265 {
266 log<level::ERR>("Failed to untar file",
267 entry("FILENAME=%s", tarFilePath.c_str()));
268 report<UnTarFailure>(UnTarFail::PATH(tarFilePath.c_str()));
269 return -1;
270 }
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500271 }
272 else
273 {
274 log<level::ERR>("fork() failed.");
Gunnar Millsb30cadd2017-10-06 16:07:32 -0500275 report<UnTarFailure>(UnTarFail::PATH(tarFilePath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500276 return -1;
277 }
278
279 return 0;
280}
Gunnar Mills3027bba2017-04-27 15:49:03 -0500281
Gunnar Millse91d3212017-04-19 15:42:47 -0500282} // namespace manager
283} // namespace software
Gunnar Millsfa34e022018-09-04 10:05:45 -0500284} // namespace phosphor