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