| /* |
| * SPDX-FileCopyrightText: Copyright (c) 2023-2024 NVIDIA CORPORATION & |
| * AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #pragma once |
| |
| #include "utils.hpp" |
| |
| #include <err.h> |
| #include <sys/socket.h> |
| #include <sys/un.h> |
| #include <unistd.h> |
| |
| #include <CLI/CLI.hpp> |
| #include <nlohmann/json.hpp> |
| |
| #include <cstring> |
| #include <deque> |
| #include <iomanip> |
| #include <iostream> |
| #include <sstream> |
| #include <utility> |
| |
| namespace nsmtool |
| { |
| |
| namespace helper |
| { |
| |
| constexpr uint8_t NSM_ENTITY_ID = 8; |
| using ordered_json = nlohmann::ordered_json; |
| |
| /** @brief print the input message if verbose is enabled |
| * |
| * @param[in] verbose - verbosity flag - true/false |
| * @param[in] msg - message to print |
| * @param[in] data - data to print |
| * |
| * @return - None |
| */ |
| |
| template <class T> |
| void Logger(bool verbose, const char* msg, const T& data) |
| { |
| if (verbose) |
| { |
| std::stringstream s; |
| s << data; |
| std::cout << msg << s.str() << std::endl; |
| } |
| } |
| |
| /** @brief Convert byte input into a hexadecimal string. |
| * |
| * @param[in] data - binary data to be converted |
| * @param[in] len - length of data to be converted |
| * |
| * @return - Hexadecimal string representation of the input data. |
| */ |
| static inline std::string bytesToHexString(const uint8_t* data, size_t len) |
| { |
| std::stringstream ss; |
| for (size_t i = 0; i < len; ++i) |
| { |
| ss << std::format("{:02x}", data[i]); |
| } |
| |
| return ss.str(); |
| } |
| |
| /** @brief Convert hexadecimal string into a byte array. |
| * |
| * @param[in] str - hexadecimal string to be converted |
| * |
| * @return - Byte array representation of the input string. |
| */ |
| static inline std::vector<uint8_t> hexStringToBytes(const std::string& str) |
| { |
| if (str.empty()) |
| { |
| return std::vector<uint8_t>(); |
| } |
| size_t start = 0; |
| if (str.length() >= 2 && str.substr(0, 2) == "0x") |
| { |
| start = 2; |
| } |
| if (str.find_first_not_of("0123456789abcdefABCDEF", start) != |
| std::string::npos) |
| { |
| std::cerr << "String contains invalid characters: " << str << std::endl; |
| return std::vector<uint8_t>(); |
| } |
| if (str.length() % 2 != 0) |
| { |
| std::cerr << "String length is not even: " << str << std::endl; |
| return std::vector<uint8_t>(); |
| } |
| std::vector<uint8_t> result; |
| for (size_t i = start; i < str.length(); i += 2) |
| { |
| try |
| { |
| result.push_back(std::stoi(str.substr(i, 2), nullptr, 16) & 0xFF); |
| } |
| catch (const std::exception&) |
| { |
| std::cerr << "Invalid hex value in string: " << str << std::endl; |
| return std::vector<uint8_t>(); |
| } |
| } |
| return result; |
| } |
| |
| /** @brief Display in JSON format. |
| * |
| * @param[in] data - data to print in json |
| * |
| * @return - None |
| */ |
| static inline void DisplayInJson(const ordered_json& data) |
| { |
| std::cout << data.dump(4, ' ', false, |
| nlohmann::json::error_handler_t::replace) |
| << std::endl; |
| } |
| |
| /** @brief MCTP socket read/recieve |
| * |
| * @param[in] requestMsg - Request message to compare against loopback |
| * message recieved from mctp socket |
| * @param[out] responseMsg - Response buffer recieved from mctp socket |
| * @param[in] verbose - verbosity flag - true/false |
| * |
| * @return - 0 on success. |
| * -1 or -errno on failure. |
| */ |
| int mctpSockSendRecv(const std::vector<uint8_t>& requestMsg, |
| std::vector<uint8_t>& responseMsg, bool verbose); |
| |
| /** @brief bitfield variable parser |
| * |
| * @param[out] res - result variable in which parsed values will be kept |
| * @param[in] key - key against which the value is to kept in ordered json |
| * @param[in] value - the bitfield array value |
| * @param[in] size - size of bitfield array |
| * |
| * @return - None |
| */ |
| void parseBitfieldVar(ordered_json& res, const std::string& key, |
| const bitfield8_t* value, uint8_t size); |
| |
| class CommandInterface |
| { |
| public: |
| explicit CommandInterface(const char* type, const char* name, |
| CLI::App* app) : |
| nsmType(type), commandName(name), mctpEid(NSM_ENTITY_ID), |
| verbose(false), instanceId(0) |
| { |
| app->add_option("-m,--mctp_eid", mctpEid, "MCTP endpoint ID"); |
| app->add_flag("-v, --verbose", verbose); |
| app->callback([&]() { exec(); }); |
| } |
| |
| virtual ~CommandInterface() = default; |
| |
| virtual std::pair<int, std::vector<uint8_t>> createRequestMsg() = 0; |
| |
| // Optional: override to create multiple request messages in one run |
| // Default implementation will call createRequestMsg() once. |
| virtual std::pair<int, std::vector<std::vector<uint8_t>>> |
| createRequestMsgs() |
| { |
| auto [rc, single] = createRequestMsg(); |
| if (rc != 0) |
| { |
| return {rc, {}}; |
| } |
| return {rc, {std::move(single)}}; |
| } |
| |
| virtual void parseResponseMsg(struct nsm_msg* responsePtr, |
| size_t payloadLength) = 0; |
| |
| virtual void exec(); |
| |
| int nsmSendRecv(std::vector<uint8_t>& requestMsg, |
| std::vector<uint8_t>& responseMsg); |
| |
| /** |
| * @brief get MCTP endpoint ID |
| * |
| * @return uint8_t - MCTP endpoint ID |
| */ |
| inline uint8_t getMCTPEID() |
| { |
| return mctpEid; |
| } |
| |
| /** |
| * @brief check if verbose mode is enabled |
| * |
| * @return bool - true if verbose mode is enabled |
| */ |
| inline bool isVerbose() const |
| { |
| return verbose; |
| } |
| |
| private: |
| /** @brief Get MCTP demux daemon socket address |
| * |
| * getMctpSockAddr does a D-Bus lookup for MCTP remote endpoint and return |
| * the unix socket info to be used for Tx/Rx |
| * |
| * @param[in] eid - Request MCTP endpoint |
| * |
| * @return On success return the type, protocol and unit socket address, on |
| * failure the address will be empty |
| */ |
| std::tuple<int, int, std::vector<uint8_t>> |
| getMctpSockInfo(uint8_t remoteEID); |
| const std::string nsmType; |
| const std::string commandName; |
| uint8_t mctpEid; |
| bool verbose; |
| |
| protected: |
| uint8_t instanceId; |
| |
| // Queue support so response handlers can schedule follow-up requests |
| void enqueueRequest(const std::vector<uint8_t>& requestMsg) |
| { |
| if (!requestMsg.empty()) |
| { |
| requestQueue.push_back(requestMsg); |
| } |
| } |
| |
| void enqueueRequests(const std::vector<std::vector<uint8_t>>& requestMsgs) |
| { |
| for (const auto& msg : requestMsgs) |
| { |
| enqueueRequest(msg); |
| } |
| } |
| |
| std::deque<std::vector<uint8_t>> requestQueue; |
| }; |
| |
| } // namespace helper |
| } // namespace nsmtool |