blob: 43edded0dca28752b51b33a875ba070c22015ae0 [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 {
41 fs::remove_all(path);
42 }
43};
44
Gunnar Mills3027bba2017-04-27 15:49:03 -050045int Manager::processImage(const std::string& tarFilePath)
Gunnar Millse91d3212017-04-19 15:42:47 -050046{
Gunnar Mills19e4ce52017-04-27 11:24:19 -050047 if (!fs::is_regular_file(tarFilePath))
48 {
49 log<level::ERR>("Error tarball does not exist",
50 entry("FILENAME=%s", tarFilePath));
Gunnar Millsb30cadd2017-10-06 16:07:32 -050051 report<ManifestFileFailure>(ManifestFail::PATH(tarFilePath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -050052 return -1;
53
54 }
55 RemovablePath tarPathRemove(tarFilePath);
56 fs::path tmpDirPath(std::string{IMG_UPLOAD_DIR});
57 tmpDirPath /= "imageXXXXXX";
58
59 // Need tmp dir to write MANIFEST file to.
60 if (!mkdtemp(const_cast<char*>(tmpDirPath.c_str())))
61 {
Gunnar Mills601bbf02017-10-25 13:46:11 -050062 log<level::ERR>("Error occurred during mkdtemp",
Gunnar Mills19e4ce52017-04-27 11:24:19 -050063 entry("ERRNO=%d", errno));
Gunnar Millsb30cadd2017-10-06 16:07:32 -050064 report<InternalFailure>(InternalFail::FAIL("mkdtemp"));
Gunnar Mills19e4ce52017-04-27 11:24:19 -050065 return -1;
66 }
67
68 RemovablePath tmpDirRemove(tmpDirPath);
69 fs::path manifestPath = tmpDirPath;
70 manifestPath /= MANIFEST_FILE_NAME;
71 int status = 0;
72 pid_t pid = fork();
73
74 // Get the MANIFEST FILE
75 if (pid == 0)
76 {
77 // child process
78 execl("/bin/tar", "tar", "-xf", tarFilePath.c_str(), MANIFEST_FILE_NAME,
79 "-C", tmpDirPath.c_str(), (char*)0);
80 // execl only returns on fail
81 log<level::ERR>("Failed to untar file",
82 entry("FILENAME=%s", tarFilePath));
Gunnar Millsb30cadd2017-10-06 16:07:32 -050083 report<ManifestFileFailure>(ManifestFail::PATH(manifestPath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -050084 return -1;
85 }
86 else if (pid > 0)
87 {
88 waitpid(pid, &status, 0);
89 }
90 else
91 {
92 log<level::ERR>("fork() failed.");
Gunnar Millsb30cadd2017-10-06 16:07:32 -050093 report<InternalFailure>(InternalFail::FAIL("fork"));
Gunnar Mills19e4ce52017-04-27 11:24:19 -050094 return -1;
95 }
96
97 // Verify the manifest file
98 if (!fs::is_regular_file(manifestPath))
99 {
100 log<level::ERR>("Error No manifest file");
Gunnar Millsb30cadd2017-10-06 16:07:32 -0500101 report<ManifestFileFailure>(ManifestFail::PATH(manifestPath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500102 return -1;
103 }
104
105 // Get version
106 auto version = Version::getValue(manifestPath.string(), "version");
107 if (version.empty())
108 {
109 log<level::ERR>("Error unable to read version from manifest file");
Gunnar Millsb30cadd2017-10-06 16:07:32 -0500110 report<ManifestFileFailure>(ManifestFail::PATH(manifestPath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500111 return -1;
112 }
113
Gunnar Mills3027bba2017-04-27 15:49:03 -0500114 // Get purpose
115 auto purposeString = Version::getValue(manifestPath.string(), "purpose");
116 if (purposeString.empty())
117 {
118 log<level::ERR>("Error unable to read purpose from manifest file");
Gunnar Millsb30cadd2017-10-06 16:07:32 -0500119 report<ManifestFileFailure>(ManifestFail::PATH(manifestPath.c_str()));
Gunnar Mills3027bba2017-04-27 15:49:03 -0500120 return -1;
121 }
122
Gunnar Mills3027bba2017-04-27 15:49:03 -0500123 auto purpose = Version::VersionPurpose::Unknown;
Gunnar Millsb30cadd2017-10-06 16:07:32 -0500124 try
125 {
Leonel Gonzalez9a8d14c2017-06-22 11:42:24 -0500126 purpose = Version::convertVersionPurposeFromString(purposeString);
Gunnar Millsb30cadd2017-10-06 16:07:32 -0500127 }
128 catch (const sdbusplus::exception::InvalidEnumString& e)
129 {
130 log<level::ERR>("Error: Failed to convert manifest purpose to enum." \
131 " Setting to Unknown.");
Gunnar Mills3027bba2017-04-27 15:49:03 -0500132 }
133
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500134 // Compute id
135 auto id = Version::getId(version);
136
137 fs::path imageDirPath = std::string{IMG_UPLOAD_DIR};
138 imageDirPath /= id;
139
Gunnar Millsf576bca2017-05-18 13:40:39 -0500140 if (fs::exists(imageDirPath))
141 {
142 fs::remove_all(imageDirPath);
143 }
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500144 if (mkdir(imageDirPath.c_str(), S_IRWXU) != 0)
145 {
Gunnar Mills601bbf02017-10-25 13:46:11 -0500146 log<level::ERR>("Error occurred during mkdir",
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500147 entry("ERRNO=%d", errno));
Gunnar Millsb30cadd2017-10-06 16:07:32 -0500148 report<InternalFailure>(InternalFail::FAIL("mkdir"));
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500149 return -1;
150 }
151
152 // Untar tarball
Gunnar Mills9ec18ef2017-05-24 11:10:46 -0500153 auto rc = unTar(tarFilePath, imageDirPath.string());
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500154 if (rc < 0)
155 {
Gunnar Mills601bbf02017-10-25 13:46:11 -0500156 log<level::ERR>("Error occurred during untar");
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500157 return -1;
158 }
159
Gunnar Mills3027bba2017-04-27 15:49:03 -0500160 // Create Version object
161 auto objPath = std::string{SOFTWARE_OBJPATH} + '/' + id;
162
Saqib Khand377ba12017-10-16 14:32:30 -0500163 if (versions.find(id) == versions.end())
164 {
165 this->versions.insert(std::make_pair(
166 id,
167 std::make_unique<Version>(
168 this->bus,
169 objPath,
170 version,
171 purpose,
172 imageDirPath.string())));
173 }
174 else
175 {
176 log<level::INFO>("Software Object with the same version already exists",
177 entry("VERSION_ID=%s", id));
178 }
Gunnar Mills3027bba2017-04-27 15:49:03 -0500179
Gunnar Millse91d3212017-04-19 15:42:47 -0500180 return 0;
181}
182
Leonel Gonzalez50d559c2017-07-07 14:38:25 -0500183void Manager::erase(std::string entryId)
184{
185 auto it = versions.find(entryId);
186 if (it == versions.end())
187 {
188 return;
189 }
Eddie James6d873712017-09-01 11:29:07 -0500190
191 if (it->second->isFunctional())
192 {
193 log<level::ERR>(("Error: Version " + entryId + \
194 " is currently running on the BMC." \
195 " Unable to remove.").c_str());
196 return;
197 }
198
Leonel Gonzalez50d559c2017-07-07 14:38:25 -0500199 // Delete image dir
200 fs::path imageDirPath = (*(it->second)).path();
201 if (fs::exists(imageDirPath))
202 {
203 fs::remove_all(imageDirPath);
204 }
205 this->versions.erase(entryId);
206}
207
Eddie James9440f492017-08-30 11:34:16 -0500208void Manager::removeVersion(sdbusplus::message::message& msg)
209{
210 namespace mesg = sdbusplus::message;
211
212 mesg::object_path objPath;
213
214 msg.read(objPath);
215 std::string path(std::move(objPath));
216
217 // Version id is the last item in the path
218 auto pos = path.rfind("/");
219 if (pos == std::string::npos)
220 {
221 log<level::INFO>("No version id found in object path",
Gunnar Millsb30cadd2017-10-06 16:07:32 -0500222 entry("OBJPATH=%s", path));
Eddie James9440f492017-08-30 11:34:16 -0500223 return;
224 }
225
226 auto versionId = path.substr(pos + 1);
227
228 erase(versionId);
229}
230
Gunnar Mills3027bba2017-04-27 15:49:03 -0500231int Manager::unTar(const std::string& tarFilePath,
232 const std::string& extractDirPath)
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500233{
234 if (tarFilePath.empty())
235 {
236 log<level::ERR>("Error TarFilePath is empty");
Gunnar Millsb30cadd2017-10-06 16:07:32 -0500237 report<UnTarFailure>(UnTarFail::PATH(tarFilePath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500238 return -1;
239 }
240 if (extractDirPath.empty())
241 {
242 log<level::ERR>("Error ExtractDirPath is empty");
Gunnar Millsb30cadd2017-10-06 16:07:32 -0500243 report<UnTarFailure>(UnTarFail::PATH(tarFilePath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500244 return -1;
245 }
246
247 log<level::INFO>("Untaring",
248 entry("FILENAME=%s", tarFilePath),
249 entry("EXTRACTIONDIR=%s", extractDirPath));
250 int status = 0;
251 pid_t pid = fork();
252
253 if (pid == 0)
254 {
255 // child process
256 execl("/bin/tar", "tar", "-xf", tarFilePath.c_str(),
257 "-C", extractDirPath.c_str(), (char*)0);
258 // execl only returns on fail
259 log<level::ERR>("Failed to untar file",
260 entry("FILENAME=%s", tarFilePath));
Gunnar Millsb30cadd2017-10-06 16:07:32 -0500261 report<UnTarFailure>(UnTarFail::PATH(tarFilePath.c_str()));
Gunnar Mills19e4ce52017-04-27 11:24:19 -0500262 return -1;
263 }
264 else if (pid > 0)
265 {
266 waitpid(pid, &status, 0);
267 }
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