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