xref: /openbmc/phosphor-bmc-code-mgmt/bmc/image_manager.cpp (revision cab87e9cdeeb3e166d6d577511f6be4dc7721aca)
1*cab87e9cSJagpal Singh Gill #include "config.h"
2*cab87e9cSJagpal Singh Gill 
3*cab87e9cSJagpal Singh Gill #include "image_manager.hpp"
4*cab87e9cSJagpal Singh Gill 
5*cab87e9cSJagpal Singh Gill #include "version.hpp"
6*cab87e9cSJagpal Singh Gill #include "watch.hpp"
7*cab87e9cSJagpal Singh Gill 
8*cab87e9cSJagpal Singh Gill #include <stdio.h>
9*cab87e9cSJagpal Singh Gill #include <stdlib.h>
10*cab87e9cSJagpal Singh Gill #include <sys/stat.h>
11*cab87e9cSJagpal Singh Gill #include <sys/wait.h>
12*cab87e9cSJagpal Singh Gill #include <unistd.h>
13*cab87e9cSJagpal Singh Gill 
14*cab87e9cSJagpal Singh Gill #include <phosphor-logging/elog-errors.hpp>
15*cab87e9cSJagpal Singh Gill #include <phosphor-logging/elog.hpp>
16*cab87e9cSJagpal Singh Gill #include <phosphor-logging/lg2.hpp>
17*cab87e9cSJagpal Singh Gill #include <xyz/openbmc_project/Software/Image/error.hpp>
18*cab87e9cSJagpal Singh Gill 
19*cab87e9cSJagpal Singh Gill #include <algorithm>
20*cab87e9cSJagpal Singh Gill #include <cstring>
21*cab87e9cSJagpal Singh Gill #include <ctime>
22*cab87e9cSJagpal Singh Gill #include <filesystem>
23*cab87e9cSJagpal Singh Gill #include <random>
24*cab87e9cSJagpal Singh Gill #include <string>
25*cab87e9cSJagpal Singh Gill #include <system_error>
26*cab87e9cSJagpal Singh Gill 
27*cab87e9cSJagpal Singh Gill namespace phosphor
28*cab87e9cSJagpal Singh Gill {
29*cab87e9cSJagpal Singh Gill namespace software
30*cab87e9cSJagpal Singh Gill {
31*cab87e9cSJagpal Singh Gill namespace manager
32*cab87e9cSJagpal Singh Gill {
33*cab87e9cSJagpal Singh Gill 
34*cab87e9cSJagpal Singh Gill PHOSPHOR_LOG2_USING;
35*cab87e9cSJagpal Singh Gill using namespace phosphor::logging;
36*cab87e9cSJagpal Singh Gill using namespace sdbusplus::error::xyz::openbmc_project::software::image;
37*cab87e9cSJagpal Singh Gill namespace Software = phosphor::logging::xyz::openbmc_project::software;
38*cab87e9cSJagpal Singh Gill using ManifestFail = Software::image::ManifestFileFailure;
39*cab87e9cSJagpal Singh Gill using UnTarFail = Software::image::UnTarFailure;
40*cab87e9cSJagpal Singh Gill using InternalFail = Software::image::InternalFailure;
41*cab87e9cSJagpal Singh Gill using ImageFail = Software::image::ImageFailure;
42*cab87e9cSJagpal Singh Gill namespace fs = std::filesystem;
43*cab87e9cSJagpal Singh Gill 
44*cab87e9cSJagpal Singh Gill struct RemovablePath
45*cab87e9cSJagpal Singh Gill {
46*cab87e9cSJagpal Singh Gill     fs::path path;
47*cab87e9cSJagpal Singh Gill 
RemovablePathphosphor::software::manager::RemovablePath48*cab87e9cSJagpal Singh Gill     explicit RemovablePath(const fs::path& path) : path(path) {}
~RemovablePathphosphor::software::manager::RemovablePath49*cab87e9cSJagpal Singh Gill     ~RemovablePath()
50*cab87e9cSJagpal Singh Gill     {
51*cab87e9cSJagpal Singh Gill         if (!path.empty())
52*cab87e9cSJagpal Singh Gill         {
53*cab87e9cSJagpal Singh Gill             std::error_code ec;
54*cab87e9cSJagpal Singh Gill             fs::remove_all(path, ec);
55*cab87e9cSJagpal Singh Gill         }
56*cab87e9cSJagpal Singh Gill     }
57*cab87e9cSJagpal Singh Gill 
58*cab87e9cSJagpal Singh Gill     RemovablePath(const RemovablePath& other) = delete;
59*cab87e9cSJagpal Singh Gill     RemovablePath& operator=(const RemovablePath& other) = delete;
60*cab87e9cSJagpal Singh Gill     RemovablePath(RemovablePath&&) = delete;
61*cab87e9cSJagpal Singh Gill     RemovablePath& operator=(RemovablePath&&) = delete;
62*cab87e9cSJagpal Singh Gill };
63*cab87e9cSJagpal Singh Gill 
64*cab87e9cSJagpal Singh Gill namespace // anonymous
65*cab87e9cSJagpal Singh Gill {
66*cab87e9cSJagpal Singh Gill 
getSoftwareObjects(sdbusplus::bus_t & bus)67*cab87e9cSJagpal Singh Gill std::vector<std::string> getSoftwareObjects(sdbusplus::bus_t& bus)
68*cab87e9cSJagpal Singh Gill {
69*cab87e9cSJagpal Singh Gill     std::vector<std::string> paths;
70*cab87e9cSJagpal Singh Gill     auto method = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
71*cab87e9cSJagpal Singh Gill                                       MAPPER_INTERFACE, "GetSubTreePaths");
72*cab87e9cSJagpal Singh Gill     method.append(SOFTWARE_OBJPATH);
73*cab87e9cSJagpal Singh Gill     method.append(0); // Depth 0 to search all
74*cab87e9cSJagpal Singh Gill     method.append(std::vector<std::string>({VERSION_BUSNAME}));
75*cab87e9cSJagpal Singh Gill     auto reply = bus.call(method);
76*cab87e9cSJagpal Singh Gill     reply.read(paths);
77*cab87e9cSJagpal Singh Gill     return paths;
78*cab87e9cSJagpal Singh Gill }
79*cab87e9cSJagpal Singh Gill 
80*cab87e9cSJagpal Singh Gill } // namespace
81*cab87e9cSJagpal Singh Gill 
processImage(const std::string & tarFilePath)82*cab87e9cSJagpal Singh Gill int Manager::processImage(const std::string& tarFilePath)
83*cab87e9cSJagpal Singh Gill {
84*cab87e9cSJagpal Singh Gill     std::error_code ec;
85*cab87e9cSJagpal Singh Gill     if (!fs::is_regular_file(tarFilePath, ec))
86*cab87e9cSJagpal Singh Gill     {
87*cab87e9cSJagpal Singh Gill         error("Tarball {PATH} does not exist: {ERROR_MSG}", "PATH", tarFilePath,
88*cab87e9cSJagpal Singh Gill               "ERROR_MSG", ec.message());
89*cab87e9cSJagpal Singh Gill         report<ManifestFileFailure>(ManifestFail::PATH(tarFilePath.c_str()));
90*cab87e9cSJagpal Singh Gill         return -1;
91*cab87e9cSJagpal Singh Gill     }
92*cab87e9cSJagpal Singh Gill     RemovablePath tarPathRemove(tarFilePath);
93*cab87e9cSJagpal Singh Gill     fs::path tmpDirPath(std::string{IMG_UPLOAD_DIR});
94*cab87e9cSJagpal Singh Gill     tmpDirPath /= "imageXXXXXX";
95*cab87e9cSJagpal Singh Gill     auto tmpDir = tmpDirPath.string();
96*cab87e9cSJagpal Singh Gill 
97*cab87e9cSJagpal Singh Gill     // Create a tmp dir to extract tarball.
98*cab87e9cSJagpal Singh Gill     if (!mkdtemp(tmpDir.data()))
99*cab87e9cSJagpal Singh Gill     {
100*cab87e9cSJagpal Singh Gill         error("Error ({ERRNO}) occurred during mkdtemp", "ERRNO", errno);
101*cab87e9cSJagpal Singh Gill         report<InternalFailure>(InternalFail::FAIL("mkdtemp"));
102*cab87e9cSJagpal Singh Gill         return -1;
103*cab87e9cSJagpal Singh Gill     }
104*cab87e9cSJagpal Singh Gill 
105*cab87e9cSJagpal Singh Gill     tmpDirPath = tmpDir;
106*cab87e9cSJagpal Singh Gill     RemovablePath tmpDirToRemove(tmpDirPath);
107*cab87e9cSJagpal Singh Gill     fs::path manifestPath = tmpDirPath;
108*cab87e9cSJagpal Singh Gill     manifestPath /= MANIFEST_FILE_NAME;
109*cab87e9cSJagpal Singh Gill 
110*cab87e9cSJagpal Singh Gill     // Untar tarball into the tmp dir
111*cab87e9cSJagpal Singh Gill     auto rc = unTar(tarFilePath, tmpDirPath.string());
112*cab87e9cSJagpal Singh Gill     if (rc < 0)
113*cab87e9cSJagpal Singh Gill     {
114*cab87e9cSJagpal Singh Gill         error("Error ({RC}) occurred during untar", "RC", rc);
115*cab87e9cSJagpal Singh Gill         return -1;
116*cab87e9cSJagpal Singh Gill     }
117*cab87e9cSJagpal Singh Gill 
118*cab87e9cSJagpal Singh Gill     // Verify the manifest file
119*cab87e9cSJagpal Singh Gill     if (!fs::is_regular_file(manifestPath, ec))
120*cab87e9cSJagpal Singh Gill     {
121*cab87e9cSJagpal Singh Gill         error("No manifest file {PATH}: {ERROR_MSG}", "PATH", tarFilePath,
122*cab87e9cSJagpal Singh Gill               "ERROR_MSG", ec.message());
123*cab87e9cSJagpal Singh Gill         report<ManifestFileFailure>(ManifestFail::PATH(tarFilePath.c_str()));
124*cab87e9cSJagpal Singh Gill         return -1;
125*cab87e9cSJagpal Singh Gill     }
126*cab87e9cSJagpal Singh Gill 
127*cab87e9cSJagpal Singh Gill     // Get version
128*cab87e9cSJagpal Singh Gill     auto version = Version::getValue(manifestPath.string(), "version");
129*cab87e9cSJagpal Singh Gill     if (version.empty())
130*cab87e9cSJagpal Singh Gill     {
131*cab87e9cSJagpal Singh Gill         error("Unable to read version from manifest file {PATH}", "PATH",
132*cab87e9cSJagpal Singh Gill               tarFilePath);
133*cab87e9cSJagpal Singh Gill         report<ManifestFileFailure>(ManifestFail::PATH(tarFilePath.c_str()));
134*cab87e9cSJagpal Singh Gill         return -1;
135*cab87e9cSJagpal Singh Gill     }
136*cab87e9cSJagpal Singh Gill 
137*cab87e9cSJagpal Singh Gill     // Get running machine name
138*cab87e9cSJagpal Singh Gill     std::string currMachine = Version::getBMCMachine(OS_RELEASE_FILE);
139*cab87e9cSJagpal Singh Gill     if (currMachine.empty())
140*cab87e9cSJagpal Singh Gill     {
141*cab87e9cSJagpal Singh Gill         auto path = OS_RELEASE_FILE;
142*cab87e9cSJagpal Singh Gill         error("Failed to read machine name from osRelease: {PATH}", "PATH",
143*cab87e9cSJagpal Singh Gill               path);
144*cab87e9cSJagpal Singh Gill         report<ImageFailure>(ImageFail::FAIL("Failed to read machine name"),
145*cab87e9cSJagpal Singh Gill                              ImageFail::PATH(path));
146*cab87e9cSJagpal Singh Gill         return -1;
147*cab87e9cSJagpal Singh Gill     }
148*cab87e9cSJagpal Singh Gill 
149*cab87e9cSJagpal Singh Gill     // Get machine name for image to be upgraded
150*cab87e9cSJagpal Singh Gill     std::string machineStr =
151*cab87e9cSJagpal Singh Gill         Version::getValue(manifestPath.string(), "MachineName");
152*cab87e9cSJagpal Singh Gill     if (!machineStr.empty())
153*cab87e9cSJagpal Singh Gill     {
154*cab87e9cSJagpal Singh Gill         if (machineStr != currMachine)
155*cab87e9cSJagpal Singh Gill         {
156*cab87e9cSJagpal Singh Gill             error(
157*cab87e9cSJagpal Singh Gill                 "BMC upgrade: Machine name doesn't match: {CURRENT_MACHINE} vs {NEW_MACHINE}",
158*cab87e9cSJagpal Singh Gill                 "CURRENT_MACHINE", currMachine, "NEW_MACHINE", machineStr);
159*cab87e9cSJagpal Singh Gill             report<ImageFailure>(
160*cab87e9cSJagpal Singh Gill                 ImageFail::FAIL("Machine name does not match"),
161*cab87e9cSJagpal Singh Gill                 ImageFail::PATH(manifestPath.string().c_str()));
162*cab87e9cSJagpal Singh Gill             return -1;
163*cab87e9cSJagpal Singh Gill         }
164*cab87e9cSJagpal Singh Gill     }
165*cab87e9cSJagpal Singh Gill     else
166*cab87e9cSJagpal Singh Gill     {
167*cab87e9cSJagpal Singh Gill         warning("No machine name in Manifest file");
168*cab87e9cSJagpal Singh Gill         report<ImageFailure>(
169*cab87e9cSJagpal Singh Gill             ImageFail::FAIL("MANIFEST is missing machine name"),
170*cab87e9cSJagpal Singh Gill             ImageFail::PATH(manifestPath.string().c_str()));
171*cab87e9cSJagpal Singh Gill     }
172*cab87e9cSJagpal Singh Gill 
173*cab87e9cSJagpal Singh Gill     // Get purpose
174*cab87e9cSJagpal Singh Gill     auto purposeString = Version::getValue(manifestPath.string(), "purpose");
175*cab87e9cSJagpal Singh Gill     if (purposeString.empty())
176*cab87e9cSJagpal Singh Gill     {
177*cab87e9cSJagpal Singh Gill         error("Unable to read purpose from manifest file {PATH}", "PATH",
178*cab87e9cSJagpal Singh Gill               tarFilePath);
179*cab87e9cSJagpal Singh Gill         report<ManifestFileFailure>(ManifestFail::PATH(tarFilePath.c_str()));
180*cab87e9cSJagpal Singh Gill         return -1;
181*cab87e9cSJagpal Singh Gill     }
182*cab87e9cSJagpal Singh Gill 
183*cab87e9cSJagpal Singh Gill     auto convertedPurpose =
184*cab87e9cSJagpal Singh Gill         sdbusplus::message::convert_from_string<Version::VersionPurpose>(
185*cab87e9cSJagpal Singh Gill             purposeString);
186*cab87e9cSJagpal Singh Gill 
187*cab87e9cSJagpal Singh Gill     if (!convertedPurpose)
188*cab87e9cSJagpal Singh Gill     {
189*cab87e9cSJagpal Singh Gill         error(
190*cab87e9cSJagpal Singh Gill             "Failed to convert manifest purpose ({PURPOSE}) to enum; setting to Unknown.",
191*cab87e9cSJagpal Singh Gill             "PURPOSE", purposeString);
192*cab87e9cSJagpal Singh Gill     }
193*cab87e9cSJagpal Singh Gill     auto purpose = convertedPurpose.value_or(Version::VersionPurpose::Unknown);
194*cab87e9cSJagpal Singh Gill 
195*cab87e9cSJagpal Singh Gill     // Get ExtendedVersion
196*cab87e9cSJagpal Singh Gill     std::string extendedVersion =
197*cab87e9cSJagpal Singh Gill         Version::getValue(manifestPath.string(), "ExtendedVersion");
198*cab87e9cSJagpal Singh Gill 
199*cab87e9cSJagpal Singh Gill     // Get CompatibleNames
200*cab87e9cSJagpal Singh Gill     std::vector<std::string> compatibleNames =
201*cab87e9cSJagpal Singh Gill         Version::getRepeatedValues(manifestPath.string(), "CompatibleName");
202*cab87e9cSJagpal Singh Gill 
203*cab87e9cSJagpal Singh Gill     // Compute id
204*cab87e9cSJagpal Singh Gill     auto salt = std::to_string(randomGen());
205*cab87e9cSJagpal Singh Gill     auto id = Version::getId(version + salt);
206*cab87e9cSJagpal Singh Gill 
207*cab87e9cSJagpal Singh Gill     fs::path imageDirPath = std::string{IMG_UPLOAD_DIR};
208*cab87e9cSJagpal Singh Gill     imageDirPath /= id;
209*cab87e9cSJagpal Singh Gill 
210*cab87e9cSJagpal Singh Gill     auto objPath = std::string{SOFTWARE_OBJPATH} + '/' + id;
211*cab87e9cSJagpal Singh Gill 
212*cab87e9cSJagpal Singh Gill     // This service only manages the uploaded versions, and there could be
213*cab87e9cSJagpal Singh Gill     // active versions on D-Bus that is not managed by this service.
214*cab87e9cSJagpal Singh Gill     // So check D-Bus if there is an existing version.
215*cab87e9cSJagpal Singh Gill     auto allSoftwareObjs = getSoftwareObjects(bus);
216*cab87e9cSJagpal Singh Gill     auto it =
217*cab87e9cSJagpal Singh Gill         std::find(allSoftwareObjs.begin(), allSoftwareObjs.end(), objPath);
218*cab87e9cSJagpal Singh Gill     if (versions.find(id) == versions.end() && it == allSoftwareObjs.end())
219*cab87e9cSJagpal Singh Gill     {
220*cab87e9cSJagpal Singh Gill         // Rename the temp dir to image dir
221*cab87e9cSJagpal Singh Gill         fs::rename(tmpDirPath, imageDirPath, ec);
222*cab87e9cSJagpal Singh Gill         // Clear the path, so it does not attempt to remove a non-existing path
223*cab87e9cSJagpal Singh Gill         tmpDirToRemove.path.clear();
224*cab87e9cSJagpal Singh Gill 
225*cab87e9cSJagpal Singh Gill         // Create Version object
226*cab87e9cSJagpal Singh Gill         auto versionPtr = std::make_unique<Version>(
227*cab87e9cSJagpal Singh Gill             bus, objPath, version, purpose, extendedVersion,
228*cab87e9cSJagpal Singh Gill             imageDirPath.string(), compatibleNames,
229*cab87e9cSJagpal Singh Gill             std::bind(&Manager::erase, this, std::placeholders::_1), id);
230*cab87e9cSJagpal Singh Gill         versionPtr->deleteObject =
231*cab87e9cSJagpal Singh Gill             std::make_unique<phosphor::software::manager::Delete>(
232*cab87e9cSJagpal Singh Gill                 bus, objPath, *versionPtr);
233*cab87e9cSJagpal Singh Gill         versions.insert(std::make_pair(id, std::move(versionPtr)));
234*cab87e9cSJagpal Singh Gill     }
235*cab87e9cSJagpal Singh Gill     else
236*cab87e9cSJagpal Singh Gill     {
237*cab87e9cSJagpal Singh Gill         info("Software Object with the same version ({VERSION}) already exists",
238*cab87e9cSJagpal Singh Gill              "VERSION", id);
239*cab87e9cSJagpal Singh Gill     }
240*cab87e9cSJagpal Singh Gill     return 0;
241*cab87e9cSJagpal Singh Gill }
242*cab87e9cSJagpal Singh Gill 
erase(const std::string & entryId)243*cab87e9cSJagpal Singh Gill void Manager::erase(const std::string& entryId)
244*cab87e9cSJagpal Singh Gill {
245*cab87e9cSJagpal Singh Gill     auto it = versions.find(entryId);
246*cab87e9cSJagpal Singh Gill     if (it == versions.end())
247*cab87e9cSJagpal Singh Gill     {
248*cab87e9cSJagpal Singh Gill         return;
249*cab87e9cSJagpal Singh Gill     }
250*cab87e9cSJagpal Singh Gill 
251*cab87e9cSJagpal Singh Gill     // Delete image dir
252*cab87e9cSJagpal Singh Gill     fs::path imageDirPath = (*(it->second)).path();
253*cab87e9cSJagpal Singh Gill     std::error_code ec;
254*cab87e9cSJagpal Singh Gill     if (fs::exists(imageDirPath, ec))
255*cab87e9cSJagpal Singh Gill     {
256*cab87e9cSJagpal Singh Gill         fs::remove_all(imageDirPath, ec);
257*cab87e9cSJagpal Singh Gill     }
258*cab87e9cSJagpal Singh Gill     this->versions.erase(entryId);
259*cab87e9cSJagpal Singh Gill }
260*cab87e9cSJagpal Singh Gill 
unTar(const std::string & tarFilePath,const std::string & extractDirPath)261*cab87e9cSJagpal Singh Gill int Manager::unTar(const std::string& tarFilePath,
262*cab87e9cSJagpal Singh Gill                    const std::string& extractDirPath)
263*cab87e9cSJagpal Singh Gill {
264*cab87e9cSJagpal Singh Gill     if (tarFilePath.empty())
265*cab87e9cSJagpal Singh Gill     {
266*cab87e9cSJagpal Singh Gill         error("TarFilePath is empty");
267*cab87e9cSJagpal Singh Gill         report<UnTarFailure>(UnTarFail::PATH(tarFilePath.c_str()));
268*cab87e9cSJagpal Singh Gill         return -1;
269*cab87e9cSJagpal Singh Gill     }
270*cab87e9cSJagpal Singh Gill     if (extractDirPath.empty())
271*cab87e9cSJagpal Singh Gill     {
272*cab87e9cSJagpal Singh Gill         error("ExtractDirPath is empty");
273*cab87e9cSJagpal Singh Gill         report<UnTarFailure>(UnTarFail::PATH(extractDirPath.c_str()));
274*cab87e9cSJagpal Singh Gill         return -1;
275*cab87e9cSJagpal Singh Gill     }
276*cab87e9cSJagpal Singh Gill 
277*cab87e9cSJagpal Singh Gill     info("Untaring {PATH} to {EXTRACTIONDIR}", "PATH", tarFilePath,
278*cab87e9cSJagpal Singh Gill          "EXTRACTIONDIR", extractDirPath);
279*cab87e9cSJagpal Singh Gill     int status = 0;
280*cab87e9cSJagpal Singh Gill     pid_t pid = fork();
281*cab87e9cSJagpal Singh Gill 
282*cab87e9cSJagpal Singh Gill     if (pid == 0)
283*cab87e9cSJagpal Singh Gill     {
284*cab87e9cSJagpal Singh Gill         // child process
285*cab87e9cSJagpal Singh Gill         execl("/bin/tar", "tar", "-xf", tarFilePath.c_str(), "-C",
286*cab87e9cSJagpal Singh Gill               extractDirPath.c_str(), (char*)0);
287*cab87e9cSJagpal Singh Gill         // execl only returns on fail
288*cab87e9cSJagpal Singh Gill         error("Failed to execute untar on {PATH}", "PATH", tarFilePath);
289*cab87e9cSJagpal Singh Gill         report<UnTarFailure>(UnTarFail::PATH(tarFilePath.c_str()));
290*cab87e9cSJagpal Singh Gill         return -1;
291*cab87e9cSJagpal Singh Gill     }
292*cab87e9cSJagpal Singh Gill     else if (pid > 0)
293*cab87e9cSJagpal Singh Gill     {
294*cab87e9cSJagpal Singh Gill         waitpid(pid, &status, 0);
295*cab87e9cSJagpal Singh Gill         if (WEXITSTATUS(status))
296*cab87e9cSJagpal Singh Gill         {
297*cab87e9cSJagpal Singh Gill             error("Failed ({STATUS}) to untar file {PATH}", "STATUS", status,
298*cab87e9cSJagpal Singh Gill                   "PATH", tarFilePath);
299*cab87e9cSJagpal Singh Gill             report<UnTarFailure>(UnTarFail::PATH(tarFilePath.c_str()));
300*cab87e9cSJagpal Singh Gill             return -1;
301*cab87e9cSJagpal Singh Gill         }
302*cab87e9cSJagpal Singh Gill     }
303*cab87e9cSJagpal Singh Gill     else
304*cab87e9cSJagpal Singh Gill     {
305*cab87e9cSJagpal Singh Gill         error("fork() failed: {ERRNO}", "ERRNO", errno);
306*cab87e9cSJagpal Singh Gill         report<UnTarFailure>(UnTarFail::PATH(tarFilePath.c_str()));
307*cab87e9cSJagpal Singh Gill         return -1;
308*cab87e9cSJagpal Singh Gill     }
309*cab87e9cSJagpal Singh Gill 
310*cab87e9cSJagpal Singh Gill     return 0;
311*cab87e9cSJagpal Singh Gill }
312*cab87e9cSJagpal Singh Gill 
313*cab87e9cSJagpal Singh Gill } // namespace manager
314*cab87e9cSJagpal Singh Gill } // namespace software
315*cab87e9cSJagpal Singh Gill } // namespace phosphor
316