ncsi: return interface info from Interface::getInfo()
Rather than expecting the ncsi_util callback to print the interface
info, return the interface info, and print that.
This allows for a specific output formatting function in
ncsi_netlink_main.c, rather than printing directly from the libnl
callback. We reformat for a more structured display of the
package/channel layouts.
We use std::optional for the return value here; it would be nice to use
std::expected instead, and get a full error code, but that's not quite
working with clang-18 at present. Even if we have an error code though,
we're just going to return EXIT_FAILURE anyway.
Tested: Invoked on a simlated dual-channel NCSI package:
# ncsi-netlink -x 2 --info
<7> Get Info , PACKAGE : 0xffffffffffffffff, INTERFACE: 0x7e8bf9bc
<7> Package id : 0
<7> package is forced
<7> Channel id : 0
<7> version 1.2 (p0c00)
<7> link state 0x40022f
<7> Channel id : 1
<7> version 1.2 (p0c01)
<7> link state 0x40022f
Change-Id: Idb62cc6695da67f4415ed9b0e7950c506018d630
Signed-off-by: Jeremy Kerr <jk@codeconstruct.com.au>
diff --git a/src/ncsi_netlink_main.cpp b/src/ncsi_netlink_main.cpp
index 04e0cbc..d5c6fc5 100644
--- a/src/ncsi_netlink_main.cpp
+++ b/src/ncsi_netlink_main.cpp
@@ -28,6 +28,50 @@
exit(EXIT_FAILURE);
}
+static void printInfo(phosphor::network::ncsi::InterfaceInfo& info)
+{
+ using namespace phosphor::network::ncsi;
+
+ for (PackageInfo& pkg : info.packages)
+ {
+ lg2::debug("Package id : {ID}", "ID", pkg.id);
+ if (pkg.forced)
+ {
+ lg2::debug(" package is forced");
+ }
+ for (ChannelInfo& chan : pkg.channels)
+ {
+ lg2::debug(" Channel id : {ID}", "ID", chan.id);
+ if (chan.forced)
+ {
+ lg2::debug(" channel is forced");
+ }
+ if (chan.active)
+ {
+ lg2::debug(" channel is active");
+ }
+
+ lg2::debug(" version {MAJOR}.{MINOR} ({STR})", "MAJOR",
+ chan.version_major, "MINOR", chan.version_minor, "STR",
+ chan.version);
+
+ lg2::debug(" link state {LINK}", "LINK", lg2::hex,
+ chan.link_state);
+
+ auto& vlans = chan.vlan_ids;
+
+ if (!vlans.empty())
+ {
+ lg2::debug(" Actve VLAN IDs:");
+ for (uint16_t vlan : vlans)
+ {
+ lg2::debug(" VID: {VLAN_ID}", "VLAN_ID", vlan);
+ }
+ }
+ }
+ }
+}
+
int main(int argc, char** argv)
{
using namespace phosphor::network;
@@ -153,7 +197,12 @@
}
else if ((options)["info"] == "true")
{
- return interface.getInfo(packageInt);
+ auto info = interface.getInfo(packageInt);
+ if (!info)
+ {
+ return EXIT_FAILURE;
+ }
+ printInfo(*info);
}
else if ((options)["clear"] == "true")
{
diff --git a/src/ncsi_util.cpp b/src/ncsi_util.cpp
index c755d33..8c628aa 100644
--- a/src/ncsi_util.cpp
+++ b/src/ncsi_util.cpp
@@ -78,7 +78,19 @@
using nlMsgPtr = std::unique_ptr<nl_msg, decltype(&::nlmsg_free)>;
using nlSocketPtr = std::unique_ptr<nl_sock, decltype(&::nl_socket_free)>;
-CallBack infoCallBack = [](struct nl_msg* msg, void*) {
+struct infoCallBackContext
+{
+ InterfaceInfo* info;
+};
+
+CallBack infoCallBack = [](struct nl_msg* msg, void* arg) {
+ if (arg == nullptr)
+ {
+ lg2::error("Internal error: invalid info callback context");
+ return -1;
+ }
+
+ struct infoCallBackContext* info = (struct infoCallBackContext*)arg;
using namespace phosphor::network::ncsi;
auto nlh = nlmsg_hdr(msg);
@@ -125,11 +137,12 @@
return -1;
}
+ PackageInfo pkg;
+
if (packagetb[NCSI_PKG_ATTR_ID])
{
auto attrID = nla_get_u32(packagetb[NCSI_PKG_ATTR_ID]);
- lg2::debug("Package has id : {ATTR_ID}", "ATTR_ID", lg2::hex,
- attrID);
+ pkg.id = attrID;
}
else
{
@@ -138,7 +151,7 @@
if (packagetb[NCSI_PKG_ATTR_FORCED])
{
- lg2::debug("This package is forced");
+ pkg.forced = true;
}
auto channelListTarget = static_cast<nlattr*>(
@@ -153,75 +166,59 @@
if (ret < 0)
{
lg2::error("Failed to parse channel nested");
- return -1;
+ continue;
}
+ ChannelInfo chan;
+
if (channeltb[NCSI_CHANNEL_ATTR_ID])
{
- auto channel = nla_get_u32(channeltb[NCSI_CHANNEL_ATTR_ID]);
- if (channeltb[NCSI_CHANNEL_ATTR_ACTIVE])
- {
- lg2::debug("Channel Active : {CHANNEL}", "CHANNEL",
- lg2::hex, channel);
- }
- else
- {
- lg2::debug("Channel Not Active : {CHANNEL}", "CHANNEL",
- lg2::hex, channel);
- }
-
- if (channeltb[NCSI_CHANNEL_ATTR_FORCED])
- {
- lg2::debug("Channel is forced");
- }
+ chan.id = nla_get_u32(channeltb[NCSI_CHANNEL_ATTR_ID]);
+ chan.active = !!channeltb[NCSI_CHANNEL_ATTR_ACTIVE];
+ chan.forced = !!channeltb[NCSI_CHANNEL_ATTR_FORCED];
}
else
{
lg2::debug("Channel with no ID");
+ continue;
}
if (channeltb[NCSI_CHANNEL_ATTR_VERSION_MAJOR])
{
- auto major =
+ chan.version_major =
nla_get_u32(channeltb[NCSI_CHANNEL_ATTR_VERSION_MAJOR]);
- lg2::debug("Channel Major Version : {CHANNEL_MAJOR_VERSION}",
- "CHANNEL_MAJOR_VERSION", lg2::hex, major);
}
if (channeltb[NCSI_CHANNEL_ATTR_VERSION_MINOR])
{
- auto minor =
+ chan.version_minor =
nla_get_u32(channeltb[NCSI_CHANNEL_ATTR_VERSION_MINOR]);
- lg2::debug("Channel Minor Version : {CHANNEL_MINOR_VERSION}",
- "CHANNEL_MINOR_VERSION", lg2::hex, minor);
}
if (channeltb[NCSI_CHANNEL_ATTR_VERSION_STR])
{
- auto str =
+ chan.version =
nla_get_string(channeltb[NCSI_CHANNEL_ATTR_VERSION_STR]);
- lg2::debug("Channel Version Str : {CHANNEL_VERSION_STR}",
- "CHANNEL_VERSION_STR", str);
}
if (channeltb[NCSI_CHANNEL_ATTR_LINK_STATE])
{
- auto link =
+ chan.link_state =
nla_get_u32(channeltb[NCSI_CHANNEL_ATTR_LINK_STATE]);
- lg2::debug("Channel Link State : {LINK_STATE}", "LINK_STATE",
- lg2::hex, link);
}
if (channeltb[NCSI_CHANNEL_ATTR_VLAN_LIST])
{
- lg2::debug("Active Vlan ids");
auto vids = channeltb[NCSI_CHANNEL_ATTR_VLAN_LIST];
auto vid = static_cast<nlattr*>(nla_data(vids));
auto len = nla_len(vids);
while (nla_ok(vid, len))
{
auto id = nla_get_u16(vid);
- lg2::debug("VID : {VLAN_ID}", "VLAN_ID", id);
+ chan.vlan_ids.push_back(id);
vid = nla_next(vid, &len);
}
}
+ pkg.channels.push_back(chan);
}
+
+ info->info->packages.push_back(pkg);
}
return static_cast<int>(NL_STOP);
};
@@ -263,7 +260,7 @@
int applyCmd(Interface& interface, const Command& cmd,
int package = DEFAULT_VALUE, int channel = DEFAULT_VALUE,
- int flags = NONE, CallBack function = nullptr)
+ int flags = NONE, CallBack function = nullptr, void* arg = nullptr)
{
nlSocketPtr socket(nl_socket_alloc(), &::nl_socket_free);
if (socket == nullptr)
@@ -388,7 +385,7 @@
// Add a callback function to the socket
enum nl_cb_kind cb_kind = function ? NL_CB_CUSTOM : NL_CB_DEFAULT;
- nl_socket_modify_cb(socket.get(), NL_CB_VALID, cb_kind, function, nullptr);
+ nl_socket_modify_cb(socket.get(), NL_CB_VALID, cb_kind, function, arg);
ret = nl_send_auto(socket.get(), msg.get());
if (ret < 0)
@@ -451,22 +448,28 @@
*this, internal::Command(ncsi_nl_commands::NCSI_CMD_CLEAR_INTERFACE));
}
-int Interface::getInfo(int package)
+std::optional<InterfaceInfo> Interface::getInfo(int package)
{
+ int rc, flags = package == DEFAULT_VALUE ? NLM_F_DUMP : NONE;
+ InterfaceInfo info;
+
lg2::debug("Get Info , PACKAGE : {PACKAGE}, INTERFACE: {INTERFACE}",
"PACKAGE", lg2::hex, package, "INTERFACE", this);
- if (package == DEFAULT_VALUE)
+
+ struct internal::infoCallBackContext ctx = {
+ .info = &info,
+ };
+
+ rc = internal::applyCmd(
+ *this, internal::Command(ncsi_nl_commands::NCSI_CMD_PKG_INFO), package,
+ DEFAULT_VALUE, flags, internal::infoCallBack, &ctx);
+
+ if (rc < 0)
{
- return internal::applyCmd(
- *this, internal::Command(ncsi_nl_commands::NCSI_CMD_PKG_INFO),
- package, DEFAULT_VALUE, NLM_F_DUMP, internal::infoCallBack);
+ return {};
}
- else
- {
- return internal::applyCmd(*this, ncsi_nl_commands::NCSI_CMD_PKG_INFO,
- package, DEFAULT_VALUE, NONE,
- internal::infoCallBack);
- }
+
+ return info;
}
int Interface::setPackageMask(unsigned int mask)
diff --git a/src/ncsi_util.hpp b/src/ncsi_util.hpp
index aa2051c..452c916 100644
--- a/src/ncsi_util.hpp
+++ b/src/ncsi_util.hpp
@@ -1,7 +1,11 @@
#pragma once
+#include <stdint.h>
+
+#include <optional>
#include <span>
#include <string>
+#include <vector>
namespace phosphor
{
@@ -13,6 +17,29 @@
constexpr auto DEFAULT_VALUE = -1;
constexpr auto NONE = 0;
+struct ChannelInfo
+{
+ uint32_t id;
+ bool active;
+ bool forced;
+ uint32_t version_major, version_minor;
+ std::string version;
+ uint32_t link_state;
+ std::vector<uint16_t> vlan_ids;
+};
+
+struct PackageInfo
+{
+ uint32_t id;
+ bool forced;
+ std::vector<ChannelInfo> channels;
+};
+
+struct InterfaceInfo
+{
+ std::vector<PackageInfo> packages;
+};
+
struct Interface
{
/* @brief This function will ask underlying NCSI driver
@@ -50,11 +77,13 @@
/* @brief This function is used to dump all the info
* of the package and the channels underlying
- * the package.
- * @param[in] package - NCSI Package.
- * @returns 0 on success and negative value for failure.
+ * the package, or all packages if DEFAULT_VALUE
+ * is passed
+ * @param[in] package - NCSI Package
+ * @returns an InterfaceInfo with package data the specified pacakge,
+ * or all packages if none is specified.
*/
- int getInfo(int package);
+ std::optional<InterfaceInfo> getInfo(int package);
/* @brief This function assigns a mask controlling responses to AEN from a
* package.