blob: ac222d040b815cf7761ce3db041fa34d95308741 [file] [log] [blame] [edit]
/*
* 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