1 #include "command_table.hpp"
2 
3 #include "main.hpp"
4 #include "message_handler.hpp"
5 #include "message_parsers.hpp"
6 #include "sessions_manager.hpp"
7 
8 #include <iomanip>
9 #include <main.hpp>
10 #include <phosphor-logging/elog-errors.hpp>
11 #include <phosphor-logging/log.hpp>
12 #include <user_channel/user_layer.hpp>
13 #include <xyz/openbmc_project/Common/error.hpp>
14 
15 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
16 using namespace phosphor::logging;
17 
18 namespace ipmi
19 {
20 using Value = sdbusplus::message::variant<bool, uint8_t, int16_t, uint16_t,
21                                           int32_t, uint32_t, int64_t, uint64_t,
22                                           double, std::string>;
23 
24 } // namespace ipmi
25 
26 namespace command
27 {
28 
29 void Table::registerCommand(CommandID inCommand, std::unique_ptr<Entry>&& entry)
30 {
31     auto& command = commandTable[inCommand.command];
32 
33     if (command)
34     {
35         log<level::DEBUG>(
36             "Already Registered",
37             phosphor::logging::entry("SKIPPED_ENTRY=0x%x", inCommand.command));
38         return;
39     }
40 
41     command = std::move(entry);
42 }
43 
44 void Table::executeCommand(uint32_t inCommand,
45                            std::vector<uint8_t>& commandData,
46                            std::shared_ptr<message::Handler> handler)
47 {
48     using namespace std::chrono_literals;
49 
50     auto iterator = commandTable.find(inCommand);
51 
52     if (iterator == commandTable.end())
53     {
54         CommandID command(inCommand);
55 
56         auto bus = getSdBus();
57         // forward the request onto the main ipmi queue
58         using IpmiDbusRspType = std::tuple<uint8_t, uint8_t, uint8_t, uint8_t,
59                                            std::vector<uint8_t>>;
60         uint8_t lun = command.lun();
61         uint8_t netFn = command.netFn();
62         uint8_t cmd = command.cmd();
63         std::shared_ptr<session::Session> session =
64             std::get<session::Manager&>(singletonPool)
65                 .getSession(handler->sessionID);
66         std::map<std::string, ipmi::Value> options = {
67             {"userId", ipmi::Value(static_cast<int>(
68                            ipmi::ipmiUserGetUserId(session->userName)))},
69             {"privilege",
70              ipmi::Value(static_cast<int>(session->currentPrivilege()))},
71             {"currentSessionId",
72              ipmi::Value(static_cast<uint32_t>(session->getBMCSessionID()))},
73         };
74         bus->async_method_call(
75             [handler, this](const boost::system::error_code& ec,
76                             const IpmiDbusRspType& response) {
77                 if (!ec)
78                 {
79                     const uint8_t& cc = std::get<3>(response);
80                     const std::vector<uint8_t>& responseData =
81                         std::get<4>(response);
82                     std::vector<uint8_t> payload;
83                     payload.reserve(1 + responseData.size());
84                     payload.push_back(cc);
85                     payload.insert(payload.end(), responseData.begin(),
86                                    responseData.end());
87                     handler->outPayload = std::move(payload);
88                 }
89                 else
90                 {
91                     std::vector<uint8_t> payload;
92                     payload.push_back(IPMI_CC_UNSPECIFIED_ERROR);
93                     handler->outPayload = std::move(payload);
94                 }
95             },
96             "xyz.openbmc_project.Ipmi.Host", "/xyz/openbmc_project/Ipmi",
97             "xyz.openbmc_project.Ipmi.Server", "execute", netFn, lun, cmd,
98             commandData, options);
99     }
100     else
101     {
102         auto start = std::chrono::steady_clock::now();
103 
104         handler->outPayload =
105             iterator->second->executeCommand(commandData, handler);
106 
107         auto end = std::chrono::steady_clock::now();
108 
109         auto elapsedSeconds =
110             std::chrono::duration_cast<std::chrono::seconds>(end - start);
111 
112         // If command time execution time exceeds 2 seconds, log a time
113         // exceeded message
114         if (elapsedSeconds > 2s)
115         {
116             log<level::ERR>("IPMI command timed out",
117                             entry("DELAY=%d", elapsedSeconds.count()));
118         }
119     }
120 }
121 
122 std::vector<uint8_t>
123     NetIpmidEntry::executeCommand(std::vector<uint8_t>& commandData,
124                                   std::shared_ptr<message::Handler> handler)
125 {
126     std::vector<uint8_t> errResponse;
127 
128     // Check if the command qualifies to be run prior to establishing a session
129     if (!sessionless && (handler->sessionID == session::sessionZero))
130     {
131         errResponse.resize(1);
132         errResponse[0] = IPMI_CC_INSUFFICIENT_PRIVILEGE;
133         log<level::INFO>("Table: Insufficient privilege for command",
134                          entry("LUN=%x", command.lun()),
135                          entry("NETFN=%x", command.netFn()),
136                          entry("CMD=%x", command.cmd()));
137         return errResponse;
138     }
139 
140     return functor(commandData, *handler);
141 }
142 
143 } // namespace command
144