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