1 #include "sol_manager.hpp"
2
3 #include "main.hpp"
4 #include "sol_context.hpp"
5
6 #include <sys/socket.h>
7 #include <sys/un.h>
8
9 #include <boost/asio/basic_stream_socket.hpp>
10 #include <boost/asio/io_context.hpp>
11 #include <boost/asio/local/stream_protocol.hpp>
12 #include <boost/asio/write.hpp>
13 #include <ipmid/utils.hpp>
14 #include <phosphor-logging/lg2.hpp>
15 #include <sdbusplus/message/types.hpp>
16
17 #include <chrono>
18 #include <cmath>
19
20 constexpr const char* solInterface = "xyz.openbmc_project.Ipmi.SOL";
21 constexpr const char* solPath = "/xyz/openbmc_project/ipmi/sol/";
22 constexpr const char* PROP_INTF = "org.freedesktop.DBus.Properties";
23
24 namespace sol
25 {
26
27 std::unique_ptr<sdbusplus::bus::match_t> matchPtrSOL(nullptr);
28 std::unique_ptr<sdbusplus::bus::match_t> solConfPropertiesSignal(nullptr);
29
initConsoleSocket()30 void Manager::initConsoleSocket()
31 {
32 // explicit length constructor for NUL-prefixed abstract path
33 std::string path(CONSOLE_SOCKET_PATH, CONSOLE_SOCKET_PATH_LEN);
34 boost::asio::local::stream_protocol::endpoint ep(path);
35 consoleSocket =
36 std::make_unique<boost::asio::local::stream_protocol::socket>(*io);
37 consoleSocket->connect(ep);
38 }
39
consoleInputHandler()40 void Manager::consoleInputHandler()
41 {
42 boost::system::error_code ec;
43 boost::asio::socket_base::bytes_readable cmd(true);
44 consoleSocket->io_control(cmd, ec);
45 size_t readSize;
46 if (!ec)
47 {
48 readSize = cmd.get();
49 }
50 else
51 {
52 lg2::error(
53 "Reading ready count from host console socket failed: {ERROR}",
54 "ERROR", ec.value());
55 return;
56 }
57 std::vector<uint8_t> buffer(readSize);
58 ec.clear();
59 size_t readDataLen =
60 consoleSocket->read_some(boost::asio::buffer(buffer), ec);
61 if (ec)
62 {
63 lg2::error("Reading from host console socket failed: {ERROR}", "ERROR",
64 ec.value());
65 return;
66 }
67
68 // Update the Console buffer with data read from the socket
69 buffer.resize(readDataLen);
70 dataBuffer.write(buffer);
71 }
72
writeConsoleSocket(const std::vector<uint8_t> & input,bool breakFlag) const73 int Manager::writeConsoleSocket(const std::vector<uint8_t>& input,
74 bool breakFlag) const
75 {
76 boost::system::error_code ec;
77 if (breakFlag)
78 {
79 consoleSocket->send(boost::asio::buffer(input), MSG_OOB, ec);
80 }
81 else
82 {
83 consoleSocket->send(boost::asio::buffer(input), 0, ec);
84 }
85
86 return ec.value();
87 }
88
startHostConsole()89 void Manager::startHostConsole()
90 {
91 if (!consoleSocket)
92 {
93 initConsoleSocket();
94 }
95
96 // Register callback to close SOL session for disable SSH SOL
97 if (matchPtrSOL == nullptr)
98 {
99 registerSOLServiceChangeCallback();
100 }
101
102 consoleSocket->async_wait(boost::asio::socket_base::wait_read,
103 [this](const boost::system::error_code& ec) {
104 if (!ec)
105 {
106 consoleInputHandler();
107 startHostConsole();
108 }
109 });
110 } // namespace sol
111
stopHostConsole()112 void Manager::stopHostConsole()
113 {
114 if (consoleSocket)
115 {
116 consoleSocket->cancel();
117 consoleSocket.reset();
118 }
119 }
120
updateSOLParameter(uint8_t channelNum)121 void Manager::updateSOLParameter(uint8_t channelNum)
122 {
123 std::variant<uint8_t, bool> value;
124 sdbusplus::bus_t dbus(ipmid_get_sd_bus_connection());
125 static std::string solService{};
126 ipmi::PropertyMap properties;
127 std::string ethdevice = ipmi::getChannelName(channelNum);
128 std::string solPathWitheEthName = solPath + ethdevice;
129 if (solService.empty())
130 {
131 try
132 {
133 solService =
134 ipmi::getService(dbus, solInterface, solPathWitheEthName);
135 }
136 catch (const std::runtime_error& e)
137 {
138 solService.clear();
139 lg2::error("Get SOL service failed: {ERROR}", "ERROR", e);
140 return;
141 }
142 }
143 try
144 {
145 properties = ipmi::getAllDbusProperties(
146 dbus, solService, solPathWitheEthName, solInterface);
147 }
148 catch (const std::runtime_error& e)
149 {
150 lg2::error("Setting sol parameter: {ERROR}", "ERROR", e);
151 return;
152 }
153
154 progress = std::get<uint8_t>(properties["Progress"]);
155
156 enable = std::get<bool>(properties["Enable"]);
157
158 forceEncrypt = std::get<bool>(properties["ForceEncryption"]);
159
160 forceAuth = std::get<bool>(properties["ForceAuthentication"]);
161
162 solMinPrivilege = static_cast<session::Privilege>(
163 std::get<uint8_t>(properties["Privilege"]));
164
165 accumulateInterval =
166 std::get<uint8_t>((properties["AccumulateIntervalMS"])) *
167 sol::accIntervalFactor * 1ms;
168
169 sendThreshold = std::get<uint8_t>(properties["Threshold"]);
170
171 retryCount = std::get<uint8_t>(properties["RetryCount"]);
172
173 retryInterval = std::get<uint8_t>(properties["RetryIntervalMS"]) *
174 sol::retryIntervalFactor * 1ms;
175
176 return;
177 }
178
startPayloadInstance(uint8_t payloadInstance,session::SessionID sessionID)179 void Manager::startPayloadInstance(uint8_t payloadInstance,
180 session::SessionID sessionID)
181 {
182 if (payloadMap.empty())
183 {
184 try
185 {
186 startHostConsole();
187 }
188 catch (const std::exception& e)
189 {
190 lg2::error(
191 "Encountered exception when starting host console. Hence stopping host console: {ERROR}",
192 "ERROR", e);
193 stopHostConsole();
194 throw;
195 }
196 }
197
198 // Create the SOL Context data for payload instance
199 std::shared_ptr<Context> context = Context::makeContext(
200 io, retryCount, sendThreshold, payloadInstance, sessionID);
201
202 payloadMap.emplace(payloadInstance, std::move(context));
203 }
204
stopPayloadInstance(uint8_t payloadInstance)205 void Manager::stopPayloadInstance(uint8_t payloadInstance)
206 {
207 auto iter = payloadMap.find(payloadInstance);
208 if (iter == payloadMap.end())
209 {
210 throw std::runtime_error("SOL Payload instance not found ");
211 }
212
213 payloadMap.erase(iter);
214
215 if (payloadMap.empty())
216 {
217 stopHostConsole();
218
219 dataBuffer.erase(dataBuffer.size());
220 }
221 }
222
stopAllPayloadInstance()223 void Manager::stopAllPayloadInstance()
224 {
225 // Erase all payload instance
226 payloadMap.erase(payloadMap.begin(), payloadMap.end());
227
228 stopHostConsole();
229
230 dataBuffer.erase(dataBuffer.size());
231 }
232
registerSOLServiceChangeCallback()233 void registerSOLServiceChangeCallback()
234 {
235 using namespace sdbusplus::bus::match::rules;
236 sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()};
237 try
238 {
239 auto servicePath = ipmi::getDbusObject(
240 bus, "xyz.openbmc_project.Control.Service.Attributes",
241 "/xyz/openbmc_project/control/service", "_6fbmc_2dconsole");
242
243 if (!std::empty(servicePath.first))
244 {
245 matchPtrSOL = std::make_unique<sdbusplus::bus::match_t>(
246 bus,
247 path_namespace(servicePath.first) +
248 "arg0namespace='xyz.openbmc_project.Control.Service."
249 "Attributes'"
250 ", " +
251 type::signal() + member("PropertiesChanged") +
252 interface("org.freedesktop.DBus.Properties"),
253 [](sdbusplus::message_t& msg) {
254 std::string intfName;
255 std::map<std::string, std::variant<bool>> properties;
256 msg.read(intfName, properties);
257
258 const auto it = properties.find("Enabled");
259 if (it != properties.end())
260 {
261 const bool* state = std::get_if<bool>(&it->second);
262
263 if (state != nullptr && *state == false)
264 {
265 // Stop all the payload session.
266 sol::Manager::get().stopAllPayloadInstance();
267 }
268 }
269 });
270 }
271 }
272 catch (const sdbusplus::exception_t& e)
273 {
274 lg2::error(
275 "Failed to get service path in registerSOLServiceChangeCallback: {ERROR}",
276 "ERROR", e);
277 }
278 }
279
procSolConfChange(sdbusplus::message_t & msg)280 void procSolConfChange(sdbusplus::message_t& msg)
281 {
282 using SolConfVariant = std::variant<bool, uint8_t>;
283 using SolConfProperties =
284 std::vector<std::pair<std::string, SolConfVariant>>;
285
286 std::string iface;
287 SolConfProperties properties;
288
289 try
290 {
291 msg.read(iface, properties);
292 }
293 catch (const std::exception& e)
294 {
295 lg2::error("procSolConfChange get properties FAIL: {ERROR}", "ERROR",
296 e);
297 return;
298 }
299
300 for (const auto& prop : properties)
301 {
302 if (prop.first == "Progress")
303 {
304 sol::Manager::get().progress = std::get<uint8_t>(prop.second);
305 }
306 else if (prop.first == "Enable")
307 {
308 sol::Manager::get().enable = std::get<bool>(prop.second);
309 }
310 else if (prop.first == "ForceEncryption")
311 {
312 sol::Manager::get().forceEncrypt = std::get<bool>(prop.second);
313 }
314 else if (prop.first == "ForceAuthentication")
315 {
316 sol::Manager::get().forceAuth = std::get<bool>(prop.second);
317 }
318 else if (prop.first == "Privilege")
319 {
320 sol::Manager::get().solMinPrivilege =
321 static_cast<session::Privilege>(std::get<uint8_t>(prop.second));
322 }
323 else if (prop.first == "AccumulateIntervalMS")
324 {
325 sol::Manager::get().accumulateInterval =
326 std::get<uint8_t>(prop.second) * sol::accIntervalFactor * 1ms;
327 }
328 else if (prop.first == "Threshold")
329 {
330 sol::Manager::get().sendThreshold = std::get<uint8_t>(prop.second);
331 }
332 else if (prop.first == "RetryCount")
333 {
334 sol::Manager::get().retryCount = std::get<uint8_t>(prop.second);
335 }
336 else if (prop.first == "RetryIntervalMS")
337 {
338 sol::Manager::get().retryInterval =
339 std::get<uint8_t>(prop.second) * sol::retryIntervalFactor * 1ms;
340 }
341 }
342 }
343
registerSolConfChangeCallbackHandler(std::string channel)344 void registerSolConfChangeCallbackHandler(std::string channel)
345 {
346 if (solConfPropertiesSignal == nullptr)
347 {
348 using namespace sdbusplus::bus::match::rules;
349 sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()};
350 try
351 {
352 auto servicePath = solPath + channel;
353
354 solConfPropertiesSignal = std::make_unique<sdbusplus::bus::match_t>(
355 bus, propertiesChangedNamespace(servicePath, solInterface),
356 procSolConfChange);
357 }
358 catch (const sdbusplus::exception_t& e)
359 {
360 lg2::error(
361 "Failed to get service path in registerSolConfChangeCallbackHandler, channel: {CHANNEL}, error: {ERROR}",
362 "CHANNEL", channel, "ERROR", e);
363 }
364 }
365 return;
366 }
367
368 } // namespace sol
369