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