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