xref: /openbmc/estoraged/src/main.cpp (revision 8d5a3a07)
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 <string>
23 
24 /*
25  * Get the configuration objects from Entity Manager and create new D-Bus
26  * objects for each one. This function can be called multiple times, in case
27  * new configuration objects show up later.
28  *
29  * Note: Currently, eStoraged can only support 1 eMMC device.
30  * Additional changes will be needed to support more than 1 eMMC, or to support
31  * more types of storage devices.
32  */
33 void createStorageObjects(
34     boost::asio::io_context& io, sdbusplus::asio::object_server& objectServer,
35     boost::container::flat_map<
36         std::string, std::unique_ptr<estoraged::EStoraged>>& storageObjects,
37     std::shared_ptr<sdbusplus::asio::connection>& dbusConnection)
38 {
39     auto getter = std::make_shared<estoraged::GetStorageConfiguration>(
40         dbusConnection,
41         [&io, &objectServer, &storageObjects](
42             const estoraged::ManagedStorageType& storageConfigurations) {
43             size_t numConfigObj = storageConfigurations.size();
44             if (numConfigObj > 1)
45             {
46                 lg2::error(
47                     "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                 std::filesystem::path deviceFile, sysfsDir;
74                 std::string luksName;
75                 bool found = estoraged::util::findDevice(
76                     data, blockDevDir, deviceFile, sysfsDir, luksName);
77                 if (!found)
78                 {
79                     lg2::error(
80                         "Device not found for path {PATH}", "PATH", path,
81                         "REDFISH_MESSAGE_ID",
82                         std::string("OpenBMC.0.1.CreateStorageObjectsFail"));
83                     /*
84                      * Set a NULL pointer as a placeholder, so that we don't
85                      * try and fail again later.
86                      */
87                     storageObjects[path] = nullptr;
88                     continue;
89                 }
90 
91                 uint64_t size =
92                     estoraged::util::findSizeOfBlockDevice(deviceFile);
93 
94                 uint8_t lifeleft =
95                     estoraged::util::findPredictedMediaLifeLeftPercent(
96                         sysfsDir);
97                 std::string partNumber =
98                     estoraged::util::getPartNumber(sysfsDir);
99                 std::string serialNumber =
100                     estoraged::util::getSerialNumber(sysfsDir);
101                 /* Create the storage object. */
102                 storageObjects[path] = std::make_unique<estoraged::EStoraged>(
103                     objectServer, path, deviceFile, luksName, size, lifeleft,
104                     partNumber, serialNumber);
105                 lg2::info("Created eStoraged object for path {PATH}", "PATH",
106                           path, "REDFISH_MESSAGE_ID",
107                           std::string("OpenBMC.0.1.CreateStorageObjects"));
108             }
109         });
110     getter->getConfiguration();
111 }
112 
113 int main(void)
114 {
115     try
116     {
117         // setup connection to dbus
118         boost::asio::io_context io;
119         auto conn = std::make_shared<sdbusplus::asio::connection>(io);
120         // request D-Bus server name.
121         conn->request_name("xyz.openbmc_project.eStoraged");
122         sdbusplus::asio::object_server server(conn);
123         boost::container::flat_map<std::string,
124                                    std::unique_ptr<estoraged::EStoraged>>
125             storageObjects;
126 
127         boost::asio::post(io, [&]() {
128             createStorageObjects(io, server, storageObjects, conn);
129         });
130 
131         /*
132          * Set up an event handler to process any new configuration objects
133          * that show up later.
134          */
135         boost::asio::deadline_timer filterTimer(io);
136         std::function<void(sdbusplus::message_t&)> eventHandler =
137             [&](sdbusplus::message_t& message) {
138                 if (message.is_method_error())
139                 {
140                     lg2::error("eventHandler callback method error");
141                     return;
142                 }
143                 /*
144                  * This implicitly cancels the timer, if it's already pending.
145                  * If there's a burst of events within a short period, we want
146                  * to handle them all at once. So, we will wait this long for no
147                  * more events to occur, before processing them.
148                  */
149                 filterTimer.expires_from_now(boost::posix_time::seconds(1));
150 
151                 filterTimer.async_wait(
152                     [&](const boost::system::error_code& ec) {
153                         if (ec == boost::asio::error::operation_aborted)
154                         {
155                             /* we were canceled */
156                             return;
157                         }
158                         if (ec)
159                         {
160                             lg2::error("timer error");
161                             return;
162                         }
163                         createStorageObjects(io, server, storageObjects, conn);
164                     });
165             };
166 
167         auto match = std::make_unique<sdbusplus::bus::match_t>(
168             static_cast<sdbusplus::bus_t&>(*conn),
169             "type='signal',member='PropertiesChanged',path_namespace='" +
170                 std::string("/xyz/openbmc_project/inventory") +
171                 "',arg0namespace='" + estoraged::emmcConfigInterface + "'",
172             eventHandler);
173 
174         lg2::info("Storage management service is running", "REDFISH_MESSAGE_ID",
175                   std::string("OpenBMC.1.0.ServiceStarted"));
176 
177         io.run();
178         return 0;
179     }
180     catch (const std::exception& e)
181     {
182         lg2::error(e.what(), "REDFISH_MESSAGE_ID",
183                    std::string("OpenBMC.1.0.ServiceException"));
184 
185         return 2;
186     }
187     return 1;
188 }
189