1 #include "command_table.hpp"
2 
3 #include "message_handler.hpp"
4 #include "message_parsers.hpp"
5 #include "sessions_manager.hpp"
6 #include "xyz/openbmc_project/Common/error.hpp"
7 
8 #include <iomanip>
9 #include <iostream>
10 #include <phosphor-logging/elog-errors.hpp>
11 #include <phosphor-logging/log.hpp>
12 
13 using namespace phosphor::logging;
14 
15 namespace command
16 {
17 
18 void Table::registerCommand(CommandID inCommand, std::unique_ptr<Entry>&& entry)
19 {
20     auto& command = commandTable[inCommand.command];
21 
22     if (command)
23     {
24         log<level::DEBUG>(
25             "Already Registered",
26             phosphor::logging::entry("SKIPPED_ENTRY=0x%x",
27                                      uint32_t(inCommand.command)));
28         return;
29     }
30 
31     command = std::move(entry);
32 }
33 
34 std::vector<uint8_t> Table::executeCommand(uint32_t inCommand,
35                                            std::vector<uint8_t>& commandData,
36                                            const message::Handler& handler)
37 {
38     using namespace std::chrono_literals;
39 
40     std::vector<uint8_t> response;
41 
42     auto iterator = commandTable.find(inCommand);
43 
44     if (iterator == commandTable.end())
45     {
46         response.resize(1);
47         response[0] = IPMI_CC_INVALID;
48     }
49     else
50     {
51         auto start = std::chrono::steady_clock::now();
52 
53         response = iterator->second->executeCommand(commandData, handler);
54 
55         auto end = std::chrono::steady_clock::now();
56 
57         auto elapsedSeconds =
58             std::chrono::duration_cast<std::chrono::seconds>(end - start);
59 
60         // If command time execution time exceeds 2 seconds, log a time
61         // exceeded message
62         if (elapsedSeconds > 2s)
63         {
64             std::cerr << "E> IPMI command timed out:Elapsed time = "
65                       << elapsedSeconds.count() << "s"
66                       << "\n";
67         }
68     }
69     return response;
70 }
71 
72 std::vector<uint8_t>
73     NetIpmidEntry::executeCommand(std::vector<uint8_t>& commandData,
74                                   const message::Handler& handler)
75 {
76     std::vector<uint8_t> errResponse;
77 
78     // Check if the command qualifies to be run prior to establishing a session
79     if (!sessionless && (handler.sessionID == session::SESSION_ZERO))
80     {
81         errResponse.resize(1);
82         errResponse[0] = IPMI_CC_INSUFFICIENT_PRIVILEGE;
83         std::cerr << "E> Table::Not enough privileges for command 0x"
84                   << std::hex << command.command << "\n";
85         return errResponse;
86     }
87 
88     return functor(commandData, handler);
89 }
90 
91 std::vector<uint8_t>
92     ProviderIpmidEntry::executeCommand(std::vector<uint8_t>& commandData,
93                                        const message::Handler& handler)
94 {
95     std::vector<uint8_t> response(message::parser::MAX_PAYLOAD_SIZE - 1);
96     size_t respSize = commandData.size();
97     ipmi_ret_t ipmiRC = IPMI_CC_UNSPECIFIED_ERROR;
98     try
99     {
100         ipmiRC = functor(0, 0, reinterpret_cast<void*>(commandData.data()),
101                          reinterpret_cast<void*>(response.data() + 1),
102                          &respSize, NULL);
103     }
104     // IPMI command handlers can throw unhandled exceptions, catch those
105     // and return sane error code.
106     catch (const std::exception& e)
107     {
108         std::cerr << "E> Unspecified error for command 0x" << std::hex
109                   << command.command << " - " << e.what() << "\n";
110         respSize = 0;
111         // fall through
112     }
113     /*
114      * respSize gets you the size of the response data for the IPMI command. The
115      * first byte in a response to the IPMI command is the Completion Code.
116      * So we are inserting completion code as the first byte and incrementing
117      * the response payload size by the size of the completion code.
118      */
119     response[0] = ipmiRC;
120     response.resize(respSize + sizeof(ipmi_ret_t));
121 
122     return response;
123 }
124 
125 } // namespace command
126