xref: /openbmc/openpower-vpd-parser/wait-vpd-parser/src/inventory_backup_handler.cpp (revision c53c47e512551e61ab146f533c8c46ca53888eba)
1 #include "inventory_backup_handler.hpp"
2 
3 #include "error_codes.hpp"
4 #include "utility/common_utility.hpp"
5 #include "utility/dbus_utility.hpp"
6 
checkInventoryBackupPath(uint16_t & o_errCode) const7 bool InventoryBackupHandler::checkInventoryBackupPath(
8     uint16_t& o_errCode) const noexcept
9 {
10     bool l_rc{false};
11     o_errCode = 0;
12     try
13     {
14         const auto l_systemInventoryBackupPath{
15             m_inventoryBackupPath /
16             std::filesystem::path(vpd::constants::systemInvPath)
17                 .relative_path()};
18 
19         if (std::filesystem::is_directory(l_systemInventoryBackupPath) &&
20             !std::filesystem::is_empty(l_systemInventoryBackupPath))
21         {
22             // check number of directories under /system path, as we are only
23             // interested in the FRUs VPD which is populated under respective
24             // /system/chassis* directories
25             auto l_backupDirIt = std::filesystem::directory_iterator{
26                 l_systemInventoryBackupPath};
27 
28             const auto l_numSubDirectories = std::count_if(
29                 std::filesystem::begin(l_backupDirIt),
30                 std::filesystem::end(l_backupDirIt),
31                 [](const auto& l_entry) { return l_entry.is_directory(); });
32 
33             l_rc = (l_numSubDirectories != 0);
34         }
35     }
36     catch (const std::exception& l_ex)
37     {
38         o_errCode = vpd::error_code::STANDARD_EXCEPTION;
39 
40         m_logger->logMessage("Failed to check inventory path. Error: " +
41                              std::string(l_ex.what()));
42     }
43     return l_rc;
44 }
45 
restoreInventoryBackupData(uint16_t & o_errCode) const46 bool InventoryBackupHandler::restoreInventoryBackupData(
47     uint16_t& o_errCode) const noexcept
48 {
49     bool l_rc{false};
50     o_errCode = 0;
51     try
52     {
53         if (!checkInventoryBackupPath(o_errCode))
54         {
55             if (o_errCode)
56             {
57                 throw std::runtime_error(
58                     "Failed to check if inventory backup path exists. Error: " +
59                     vpd::commonUtility::getErrCodeMsg(o_errCode));
60             }
61             return l_rc;
62         }
63 
64         const auto l_systemInventoryPrimaryPath{
65             m_inventoryPrimaryPath /
66             std::filesystem::path(vpd::constants::systemVpdInvPath)
67                 .relative_path()};
68 
69         if (std::filesystem::is_directory(l_systemInventoryPrimaryPath))
70         {
71             const auto l_systemInventoryBackupPath{
72                 m_inventoryBackupPath /
73                 std::filesystem::path(vpd::constants::systemVpdInvPath)
74                     .relative_path()};
75 
76             // copy all sub directories under /system from backup path to
77             // primary path
78             auto l_backupDirIt = std::filesystem::directory_iterator{
79                 l_systemInventoryBackupPath};
80 
81             // vector to hold a list of sub directories under /system for which
82             // restoration failed
83             using FailedPathList = std::vector<std::filesystem::path>;
84             FailedPathList l_failedPaths;
85 
86             std::for_each(std::filesystem::begin(l_backupDirIt),
87                           std::filesystem::end(l_backupDirIt),
88                           [this,
89                            l_systemInventoryPrimaryPath =
90                                std::as_const(l_systemInventoryPrimaryPath),
91                            &l_failedPaths](const auto& l_entry) {
92                               if (l_entry.is_directory())
93                               {
94                                   if (!moveFiles(l_entry.path(),
95                                                  l_systemInventoryPrimaryPath /
96                                                      l_entry.path().filename()))
97                                   {
98                                       l_failedPaths.emplace_back(
99                                           l_entry.path().filename());
100                                   }
101                               }
102                           });
103 
104             // check if there are any paths for which restoration failed, if yes
105             // log a PEL
106             if (!l_failedPaths.empty())
107             {
108                 std::string l_formattedFailedPaths{"Failed paths: "};
109 
110                 // create a string representation of the failed path list
111                 for (const auto& l_path : l_failedPaths)
112                 {
113                     l_formattedFailedPaths +=
114                         std::format("[{}],", l_path.string());
115                 }
116 
117                 m_logger->logMessage(
118                     "Failed to restore inventory data for some chassis. Check user data for details",
119                     vpd::PlaceHolder::PEL,
120                     vpd::types::PelInfoTuple{
121                         vpd::types::ErrorType::FirmwareError,
122                         vpd::types::SeverityType::Warning, 0,
123                         l_formattedFailedPaths, std::nullopt, std::nullopt,
124                         std::nullopt});
125             }
126             // We have logged a PEL for the paths(chassis) which we have failed
127             // to move. Consider restoration is successful even if have failed
128             // to move some of the paths (chassis) under /system, so that
129             // inventory manager exposes atleast some of the chassis.
130             l_rc = true;
131         }
132         else
133         {
134             o_errCode = vpd::error_code::INVALID_INVENTORY_PATH;
135         }
136     }
137     catch (const std::exception& l_ex)
138     {
139         m_logger->logMessage(
140             "Failed to restore inventory backup data from [" +
141                 m_inventoryBackupPath.string() + "] to [" +
142                 m_inventoryPrimaryPath.string() +
143                 "] Error: " + std::string(l_ex.what()),
144             vpd::PlaceHolder::PEL,
145             vpd::types::PelInfoTuple{vpd::types::ErrorType::FirmwareError,
146                                      vpd::types::SeverityType::Warning, 0,
147                                      std::nullopt, std::nullopt, std::nullopt,
148                                      std::nullopt});
149 
150         o_errCode = vpd::error_code::STANDARD_EXCEPTION;
151     }
152     return l_rc;
153 }
154 
clearInventoryBackupData(uint16_t & o_errCode) const155 bool InventoryBackupHandler::clearInventoryBackupData(
156     uint16_t& o_errCode) const noexcept
157 {
158     bool l_rc{false};
159     o_errCode = 0;
160     try
161     {
162         const auto l_systemInventoryBackupPath{
163             m_inventoryBackupPath /
164             std::filesystem::path(vpd::constants::systemVpdInvPath)
165                 .relative_path()};
166 
167         std::filesystem::remove_all(l_systemInventoryBackupPath);
168 
169         l_rc = true;
170     }
171     catch (const std::exception& l_ex)
172     {
173         m_logger->logMessage(
174             "Failed to clear inventory backup data from path [" +
175                 m_inventoryBackupPath.string() +
176                 "]. Error: " + std::string(l_ex.what()),
177             vpd::PlaceHolder::PEL,
178             vpd::types::PelInfoTuple{vpd::types::ErrorType::FirmwareError,
179                                      vpd::types::SeverityType::Warning, 0,
180                                      std::nullopt, std::nullopt, std::nullopt,
181                                      std::nullopt});
182 
183         o_errCode = vpd::error_code::STANDARD_EXCEPTION;
184     }
185     return l_rc;
186 }
187 
restartInventoryManagerService(uint16_t & o_errCode) const188 bool InventoryBackupHandler::restartInventoryManagerService(
189     uint16_t& o_errCode) const noexcept
190 {
191     bool l_rc{false};
192     o_errCode = 0;
193     try
194     {
195         // Restart inventory manager service, with a re-try limit of 3 attempts
196         constexpr auto l_numRetries{3};
197 
198         for (unsigned l_attempt = 0; l_attempt < l_numRetries; ++l_attempt)
199         {
200             if (vpd::commonUtility::restartService(
201                     m_inventoryManagerServiceName, o_errCode))
202             {
203                 // restarting inventory manager service is successful, return
204                 // success
205                 return true;
206             }
207         }
208 
209         // re-try limit crossed
210         m_logger->logMessage(
211             "Failed to restart [" + m_inventoryManagerServiceName + "] after " +
212             std::to_string(l_numRetries) + " attempts. Error: " +
213             vpd::commonUtility::getErrCodeMsg(o_errCode));
214 
215         // check inventory manager service state and set appropriate error code
216         o_errCode =
217             vpd::dbusUtility::isServiceRunning(m_inventoryManagerServiceName)
218                 ? vpd::error_code::SERVICE_RUNNING
219                 : vpd::error_code::SERVICE_NOT_RUNNING;
220     }
221     catch (const std::exception& l_ex)
222     {
223         m_logger->logMessage("Failed to restart inventory manager service " +
224                              m_inventoryManagerServiceName +
225                              ". Error: " + std::string(l_ex.what()));
226         o_errCode = vpd::error_code::STANDARD_EXCEPTION;
227     }
228     return l_rc;
229 }
230 
moveFiles(const std::filesystem::path & l_src,const std::filesystem::path & l_dest) const231 bool InventoryBackupHandler::moveFiles(
232     const std::filesystem::path& l_src,
233     const std::filesystem::path& l_dest) const noexcept
234 {
235     bool l_rc{false};
236     try
237     {
238         std::error_code l_ec;
239 
240         // try to rename first as it is more efficient
241         std::filesystem::rename(l_src, l_dest, l_ec);
242 
243         if (l_ec == std::errc::directory_not_empty)
244         {
245             // rename failed, because destination path
246             // already exists and is not empty
247             //  let's try to copy the data while overwriting
248 
249             std::filesystem::copy(
250                 l_src, l_dest,
251                 std::filesystem::copy_options::recursive |
252                     std::filesystem::copy_options::overwrite_existing);
253 
254             // remove the original after successful copy
255             if (static_cast<std::uintmax_t>(-1) ==
256                 std::filesystem::remove_all(l_src, l_ec))
257             {
258                 m_logger->logMessage(
259                     "Failed to remove file [" + l_src.string() +
260                         "]. Error: " + l_ec.message(),
261                     vpd::PlaceHolder::COLLECTION);
262             }
263         }
264         else if (l_ec)
265         {
266             // for all other errors, we need to throw so
267             // that it fails the operation
268             throw std::runtime_error(l_ec.message());
269         }
270 
271         l_rc = true;
272     }
273     catch (const std::exception& l_ex)
274     {
275         m_logger->logMessage(
276             "Failed to move files from [" + l_src.string() + "] to [" +
277                 l_dest.string() + "]. Error: " + l_ex.what(),
278             vpd::PlaceHolder::COLLECTION);
279     }
280 
281     return l_rc;
282 }
283