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 
18 namespace sol
19 {
20 
21 using namespace phosphor::logging;
22 
23 std::unique_ptr<sdbusplus::bus::match_t> matchPtrSOL(nullptr);
24 
25 void Manager::initConsoleSocket()
26 {
27     // explicit length constructor for NUL-prefixed abstract path
28     std::string path(CONSOLE_SOCKET_PATH, CONSOLE_SOCKET_PATH_LEN);
29     boost::asio::local::stream_protocol::endpoint ep(path);
30     consoleSocket =
31         std::make_unique<boost::asio::local::stream_protocol::socket>(*io);
32     consoleSocket->connect(ep);
33 }
34 
35 void Manager::consoleInputHandler()
36 {
37     boost::system::error_code ec;
38     boost::asio::socket_base::bytes_readable cmd(true);
39     consoleSocket->io_control(cmd, ec);
40     size_t readSize;
41     if (!ec)
42     {
43         readSize = cmd.get();
44     }
45     else
46     {
47         log<level::ERR>("Reading ready count from host console socket failed:",
48                         entry("EXCEPTION=%s", ec.message().c_str()));
49         return;
50     }
51     std::vector<uint8_t> buffer(readSize);
52     ec.clear();
53     size_t readDataLen =
54         consoleSocket->read_some(boost::asio::buffer(buffer), ec);
55     if (ec)
56     {
57         log<level::ERR>("Reading from host console socket failed:",
58                         entry("EXCEPTION=%s", ec.message().c_str()));
59         return;
60     }
61 
62     // Update the Console buffer with data read from the socket
63     buffer.resize(readDataLen);
64     dataBuffer.write(buffer);
65 }
66 
67 int Manager::writeConsoleSocket(const std::vector<uint8_t>& input) const
68 {
69     boost::system::error_code ec;
70     boost::asio::write(*consoleSocket, boost::asio::buffer(input), ec);
71     return ec.value();
72 }
73 
74 void Manager::startHostConsole()
75 {
76     if (!consoleSocket)
77     {
78         initConsoleSocket();
79     }
80 
81     // Register callback to close SOL session for disable SSH SOL
82     if (matchPtrSOL == nullptr)
83     {
84         registerSOLServiceChangeCallback();
85     }
86 
87     consoleSocket->async_wait(boost::asio::socket_base::wait_read,
88                               [this](const boost::system::error_code& ec) {
89                                   if (!ec)
90                                   {
91                                       consoleInputHandler();
92                                       startHostConsole();
93                                   }
94                               });
95 } // namespace sol
96 
97 void Manager::stopHostConsole()
98 {
99     if (consoleSocket)
100     {
101         consoleSocket->cancel();
102         consoleSocket.reset();
103     }
104 }
105 
106 void Manager::startPayloadInstance(uint8_t payloadInstance,
107                                    session::SessionID sessionID)
108 {
109     if (payloadMap.empty())
110     {
111         try
112         {
113             startHostConsole();
114         }
115         catch (const std::exception& e)
116         {
117             log<level::ERR>("Encountered exception when starting host console. "
118                             "Hence stopping host console.",
119                             entry("EXCEPTION=%s", e.what()));
120             stopHostConsole();
121             throw;
122         }
123     }
124 
125     // Create the SOL Context data for payload instance
126     std::shared_ptr<Context> context = Context::makeContext(
127         io, retryCount, sendThreshold, payloadInstance, sessionID);
128 
129     payloadMap.emplace(payloadInstance, std::move(context));
130 }
131 
132 void Manager::stopPayloadInstance(uint8_t payloadInstance)
133 {
134     auto iter = payloadMap.find(payloadInstance);
135     if (iter == payloadMap.end())
136     {
137         throw std::runtime_error("SOL Payload instance not found ");
138     }
139 
140     payloadMap.erase(iter);
141 
142     if (payloadMap.empty())
143     {
144         stopHostConsole();
145 
146         dataBuffer.erase(dataBuffer.size());
147     }
148 }
149 
150 void Manager::stopAllPayloadInstance()
151 {
152     // Erase all payload instance
153     payloadMap.erase(payloadMap.begin(), payloadMap.end());
154 
155     stopHostConsole();
156 
157     dataBuffer.erase(dataBuffer.size());
158 }
159 
160 void registerSOLServiceChangeCallback()
161 {
162     using namespace sdbusplus::bus::match::rules;
163     sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
164     try
165     {
166         auto servicePath = ipmi::getDbusObject(
167             bus, "xyz.openbmc_project.Control.Service.Attributes",
168             "/xyz/openbmc_project/control/service", "obmc_2dconsole");
169 
170         if (!std::empty(servicePath.first))
171         {
172             matchPtrSOL = std::make_unique<sdbusplus::bus::match_t>(
173                 bus,
174                 path_namespace(servicePath.first) +
175                     "arg0namespace='xyz.openbmc_project.Control.Service."
176                     "Attributes'"
177                     ", " +
178                     type::signal() + member("PropertiesChanged") +
179                     interface("org.freedesktop.DBus.Properties"),
180                 [](sdbusplus::message::message& msg) {
181                     std::string intfName;
182                     std::map<std::string, std::variant<bool>> properties;
183                     msg.read(intfName, properties);
184 
185                     const auto it = properties.find("Enabled");
186                     if (it != properties.end())
187                     {
188                         const bool* state = std::get_if<bool>(&it->second);
189 
190                         if (state != nullptr && *state == false)
191                         {
192                             // Stop all the payload session.
193                             std::get<sol::Manager&>(singletonPool)
194                                 .stopAllPayloadInstance();
195                         }
196                     }
197                 });
198         }
199     }
200     catch (sdbusplus::exception_t& e)
201     {
202         log<level::ERR>(
203             "Failed to get service path in registerSOLServiceChangeCallback");
204     }
205 }
206 
207 } // namespace sol
208