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