xref: /openbmc/estoraged/src/main.cpp (revision d7be42bd69a80a2cdca17a8ccbc9c0a696b5d95c)
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  */
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("eStoraged can only manage 1 eMMC device; found {NUM}",
48                        "NUM", numConfigObj, "REDFISH_MESSAGE_ID",
49                        std::string("OpenBMC.0.1.CreateStorageObjectsFail"));
50             return;
51         }
52 
53         for (const std::pair<sdbusplus::message::object_path,
54                              estoraged::StorageData>& storage :
55              storageConfigurations)
56         {
57             const std::string& path = storage.first.str;
58 
59             if (storageObjects.find(path) != storageObjects.end())
60             {
61                 /*
62                  * We've already created this object, or at least
63                  * attempted to.
64                  */
65                 continue;
66             }
67 
68             /* Get the properties from the config object. */
69             const estoraged::StorageData& data = storage.second;
70 
71             /* Look for the device file. */
72             const std::filesystem::path blockDevDir{"/sys/block"};
73             auto deviceInfo = estoraged::util::findDevice(data, blockDevDir);
74             if (!deviceInfo)
75             {
76                 lg2::error("Device not found for path {PATH}", "PATH", path,
77                            "REDFISH_MESSAGE_ID",
78                            std::string("OpenBMC.0.1.CreateStorageObjectsFail"));
79                 /*
80                  * Set a NULL pointer as a placeholder, so that we don't
81                  * try and fail again later.
82                  */
83                 storageObjects[path] = nullptr;
84                 continue;
85             }
86 
87             std::filesystem::path deviceFile =
88                 std::move(deviceInfo->deviceFile);
89             std::filesystem::path sysfsDir = std::move(deviceInfo->sysfsDir);
90             std::string luksName = std::move(deviceInfo->luksName);
91             std::string locationCode = std::move(deviceInfo->locationCode);
92             uint64_t eraseMaxGeometry = deviceInfo->eraseMaxGeometry;
93             uint64_t eraseMinGeometry = deviceInfo->eraseMinGeometry;
94 
95             uint64_t size = estoraged::util::findSizeOfBlockDevice(deviceFile);
96 
97             uint8_t lifeleft =
98                 estoraged::util::findPredictedMediaLifeLeftPercent(sysfsDir);
99             std::string partNumber = estoraged::util::getPartNumber(sysfsDir);
100             std::string serialNumber =
101                 estoraged::util::getSerialNumber(sysfsDir);
102             std::string driveType = deviceInfo->driveType;
103             /* Create the storage object. */
104             storageObjects[path] = std::make_unique<estoraged::EStoraged>(
105                 objectServer, path, deviceFile, luksName, size, lifeleft,
106                 partNumber, serialNumber, locationCode, eraseMaxGeometry,
107                 eraseMinGeometry, driveType);
108             lg2::info("Created eStoraged object for path {PATH}", "PATH", path,
109                       "REDFISH_MESSAGE_ID",
110                       std::string("OpenBMC.0.1.CreateStorageObjects"));
111         }
112     });
113     getter->getConfiguration();
114 }
115 
116 int main(void)
117 {
118     try
119     {
120         // setup connection to dbus
121         boost::asio::io_context io;
122         auto conn = std::make_shared<sdbusplus::asio::connection>(io);
123         // request D-Bus server name.
124         conn->request_name("xyz.openbmc_project.eStoraged");
125         sdbusplus::asio::object_server server(conn);
126         boost::container::flat_map<std::string,
127                                    std::unique_ptr<estoraged::EStoraged>>
128             storageObjects;
129 
130         boost::asio::post(
131             io, [&]() { createStorageObjects(server, storageObjects, conn); });
132 
133         /*
134          * Set up an event handler to process any new configuration objects
135          * that show up later.
136          */
137         boost::asio::deadline_timer filterTimer(io);
138         std::function<void(sdbusplus::message_t&)> eventHandler =
139             [&](sdbusplus::message_t& message) {
140             if (message.is_method_error())
141             {
142                 lg2::error("eventHandler callback method error");
143                 return;
144             }
145             /*
146              * This implicitly cancels the timer, if it's already pending.
147              * If there's a burst of events within a short period, we want
148              * to handle them all at once. So, we will wait this long for no
149              * more events to occur, before processing them.
150              */
151             filterTimer.expires_from_now(boost::posix_time::seconds(1));
152 
153             filterTimer.async_wait([&](const boost::system::error_code& ec) {
154                 if (ec == boost::asio::error::operation_aborted)
155                 {
156                     /* we were canceled */
157                     return;
158                 }
159                 if (ec)
160                 {
161                     lg2::error("timer error");
162                     return;
163                 }
164                 createStorageObjects(server, storageObjects, conn);
165             });
166         };
167 
168         auto match = std::make_unique<sdbusplus::bus::match_t>(
169             static_cast<sdbusplus::bus_t&>(*conn),
170             "type='signal',member='PropertiesChanged',path_namespace='" +
171                 std::string("/xyz/openbmc_project/inventory") +
172                 "',arg0namespace='" + estoraged::emmcConfigInterface + "'",
173             eventHandler);
174 
175         lg2::info("Storage management service is running", "REDFISH_MESSAGE_ID",
176                   std::string("OpenBMC.1.0.ServiceStarted"));
177 
178         io.run();
179         return 0;
180     }
181     catch (const std::exception& e)
182     {
183         lg2::error(e.what(), "REDFISH_MESSAGE_ID",
184                    std::string("OpenBMC.1.0.ServiceException"));
185 
186         return 2;
187     }
188     return 1;
189 }
190