xref: /openbmc/estoraged/src/main.cpp (revision da5aa614090fbd22399db85ec8866353c4c22d9c)
1 
2 #include "estoraged.hpp"
3 #include "getConfig.hpp"
4 #include "util.hpp"
5 
6 #include <boost/asio/deadline_timer.hpp>
7 #include <boost/asio/io_context.hpp>
8 #include <boost/asio/post.hpp>
9 #include <boost/container/flat_map.hpp>
10 #include <boost/container/throw_exception.hpp>
11 #include <phosphor-logging/lg2.hpp>
12 #include <sdbusplus/asio/connection.hpp>
13 #include <sdbusplus/asio/object_server.hpp>
14 #include <sdbusplus/bus.hpp>
15 #include <sdbusplus/bus/match.hpp>
16 #include <util.hpp>
17 
18 #include <cstdlib>
19 #include <filesystem>
20 #include <iostream>
21 #include <memory>
22 #include <optional>
23 #include <string>
24 
25 /*
26  * Get the configuration objects from Entity Manager and create new D-Bus
27  * objects for each one. This function can be called multiple times, in case
28  * new configuration objects show up later.
29  *
30  * Note: Currently, eStoraged can only support 1 eMMC device.
31  * Additional changes will be needed to support more than 1 eMMC, or to support
32  * more types of storage devices.
33  */
createStorageObjects(sdbusplus::asio::object_server & objectServer,boost::container::flat_map<std::string,std::unique_ptr<estoraged::EStoraged>> & storageObjects,std::shared_ptr<sdbusplus::asio::connection> & dbusConnection)34 void createStorageObjects(
35     sdbusplus::asio::object_server& objectServer,
36     boost::container::flat_map<
37         std::string, std::unique_ptr<estoraged::EStoraged>>& storageObjects,
38     std::shared_ptr<sdbusplus::asio::connection>& dbusConnection)
39 {
40     auto getter = std::make_shared<estoraged::GetStorageConfiguration>(
41         dbusConnection,
42         [&objectServer, &storageObjects](
43             const estoraged::ManagedStorageType& storageConfigurations) {
44             size_t numConfigObj = storageConfigurations.size();
45             if (numConfigObj > 1)
46             {
47                 lg2::error(
48                     "eStoraged can only manage 1 eMMC device; found {NUM}",
49                     "NUM", numConfigObj, "REDFISH_MESSAGE_ID",
50                     std::string("OpenBMC.0.1.CreateStorageObjectsFail"));
51                 return;
52             }
53 
54             for (const std::pair<sdbusplus::message::object_path,
55                                  estoraged::StorageData>& storage :
56                  storageConfigurations)
57             {
58                 const std::string& path = storage.first.str;
59 
60                 if (storageObjects.find(path) != storageObjects.end())
61                 {
62                     /*
63                      * We've already created this object, or at least
64                      * attempted to.
65                      */
66                     continue;
67                 }
68 
69                 /* Get the properties from the config object. */
70                 const estoraged::StorageData& data = storage.second;
71 
72                 /* Look for the device file. */
73                 const std::filesystem::path blockDevDir{"/sys/block"};
74                 auto deviceInfo =
75                     estoraged::util::findDevice(data, blockDevDir);
76                 if (!deviceInfo)
77                 {
78                     lg2::error(
79                         "Device not found for path {PATH}", "PATH", path,
80                         "REDFISH_MESSAGE_ID",
81                         std::string("OpenBMC.0.1.CreateStorageObjectsFail"));
82                     /*
83                      * Set a NULL pointer as a placeholder, so that we don't
84                      * try and fail again later.
85                      */
86                     storageObjects[path] = nullptr;
87                     continue;
88                 }
89 
90                 std::filesystem::path deviceFile =
91                     std::move(deviceInfo->deviceFile);
92                 std::filesystem::path sysfsDir =
93                     std::move(deviceInfo->sysfsDir);
94                 std::string luksName = std::move(deviceInfo->luksName);
95                 std::string locationCode = std::move(deviceInfo->locationCode);
96                 uint64_t eraseMaxGeometry = deviceInfo->eraseMaxGeometry;
97                 uint64_t eraseMinGeometry = deviceInfo->eraseMinGeometry;
98 
99                 uint64_t size =
100                     estoraged::util::findSizeOfBlockDevice(deviceFile);
101 
102                 uint8_t lifeleft =
103                     estoraged::util::findPredictedMediaLifeLeftPercent(
104                         sysfsDir);
105                 std::string partNumber =
106                     estoraged::util::getPartNumber(sysfsDir);
107                 std::string serialNumber =
108                     estoraged::util::getSerialNumber(sysfsDir);
109                 const std::string& driveType = deviceInfo->driveType;
110                 const std::string& driveProtocol = deviceInfo->driveProtocol;
111                 /* Create the storage object. */
112                 std::unique_ptr<stdplus::Fd> fd;
113                 try
114                 {
115                     fd = std::make_unique<stdplus::ManagedFd>(stdplus::fd::open(
116                         deviceFile.c_str(),
117                         stdplus::fd::OpenFlags(
118                             stdplus::fd::OpenAccess::ReadWrite)));
119                 }
120                 catch (const std::system_error& e)
121                 {
122                     lg2::error("Failed to open {PATH}: {ERROR}", "PATH",
123                                deviceFile, "ERROR", e.what());
124                 }
125 
126                 storageObjects[path] = std::make_unique<estoraged::EStoraged>(
127                     std::move(fd), objectServer, path, deviceFile, luksName,
128                     size, lifeleft, partNumber, serialNumber, locationCode,
129                     eraseMaxGeometry, eraseMinGeometry, driveType,
130                     driveProtocol);
131                 lg2::info("Created eStoraged object for path {PATH}", "PATH",
132                           path, "REDFISH_MESSAGE_ID",
133                           std::string("OpenBMC.0.1.CreateStorageObjects"));
134             }
135         });
136     getter->getConfiguration();
137 }
138 
main(void)139 int main(void)
140 {
141     try
142     {
143         // setup connection to dbus
144         boost::asio::io_context io;
145         auto conn = std::make_shared<sdbusplus::asio::connection>(io);
146         // request D-Bus server name.
147         conn->request_name("xyz.openbmc_project.eStoraged");
148         sdbusplus::asio::object_server server(conn);
149         boost::container::flat_map<std::string,
150                                    std::unique_ptr<estoraged::EStoraged>>
151             storageObjects;
152 
153         boost::asio::post(io, [&]() {
154             createStorageObjects(server, storageObjects, conn);
155         });
156 
157         /*
158          * Set up an event handler to process any new configuration objects
159          * that show up later.
160          */
161         boost::asio::deadline_timer filterTimer(io);
162         std::function<void(sdbusplus::message_t&)> eventHandler =
163             [&](sdbusplus::message_t& message) {
164                 if (message.is_method_error())
165                 {
166                     lg2::error("eventHandler callback method error");
167                     return;
168                 }
169                 /*
170                  * This implicitly cancels the timer, if it's already pending.
171                  * If there's a burst of events within a short period, we want
172                  * to handle them all at once. So, we will wait this long for no
173                  * more events to occur, before processing them.
174                  */
175                 filterTimer.expires_from_now(boost::posix_time::seconds(1));
176 
177                 filterTimer.async_wait(
178                     [&](const boost::system::error_code& ec) {
179                         if (ec == boost::asio::error::operation_aborted)
180                         {
181                             /* we were canceled */
182                             return;
183                         }
184                         if (ec)
185                         {
186                             lg2::error("timer error");
187                             return;
188                         }
189                         createStorageObjects(server, storageObjects, conn);
190                     });
191             };
192 
193         auto match = std::make_unique<sdbusplus::bus::match_t>(
194             static_cast<sdbusplus::bus_t&>(*conn),
195             "type='signal',member='PropertiesChanged',path_namespace='" +
196                 std::string("/xyz/openbmc_project/inventory") +
197                 "',arg0namespace='" + estoraged::emmcConfigInterface + "'",
198             eventHandler);
199 
200         lg2::info("Storage management service is running", "REDFISH_MESSAGE_ID",
201                   std::string("OpenBMC.1.0.ServiceStarted"));
202 
203         io.run();
204         return 0;
205     }
206     catch (const std::exception& e)
207     {
208         lg2::error(e.what(), "REDFISH_MESSAGE_ID",
209                    std::string("OpenBMC.1.0.ServiceException"));
210 
211         return 2;
212     }
213     return 1;
214 }
215