/* *** defines ****************************************************************/
#ifndef __stringify_1
-#define __stringify_1( x ) #x
+# define __stringify_1(x) # x
#endif
#ifndef __stringify
-#define __stringify( x ) __stringify_1( x )
+# define __stringify(x) __stringify_1(x)
#endif
-#define CHECK_LLDP_GENERIC( failed, __call, ... ) \
- do \
- { \
- if ( failed( __call ) ) \
- { \
- __VA_ARGS__; \
- } \
- } while ( 0 )
-
-#define FAILED_NULL( p ) ( ( p ) == nullptr )
-#define CHECK_LLDP_P( __call, conn ) \
- CHECK_LLDP_GENERIC( \
- FAILED_NULL, __call, const auto _rc_{ lldpctl_last_error( conn ) }; if ( LLDPCTL_NO_ERROR != _rc_ ) { \
- throw std::system_error( std::error_code( _rc_, LldpErrCategory() ), "'" __stringify(__call) "' failed" ); \
- } )
-
-#define FAILED_NEGATIVE( v ) ( ( v ) < 0 )
-#define CHECK_LLDP_N( __call, conn ) \
- CHECK_LLDP_GENERIC( \
- FAILED_NEGATIVE, __call, const auto _rc_{ lldpctl_last_error( conn ) }; \
- throw std::system_error( std::error_code( _rc_, LldpErrCategory() ), "'" __stringify(__call) "' failed" ); )
-
-#define CHECK_LLDP_N2( pre, __call, conn ) \
- CHECK_LLDP_GENERIC( \
- FAILED_NEGATIVE, __call, if ( pre ) { \
- const auto _rc_{ lldpctl_last_error( conn ) }; \
- throw std::system_error( std::error_code( _rc_, LldpErrCategory() ), "'" __stringify(__call) "' failed" ); \
- } )
+#define CHECK_LLDP_GENERIC(failed, __call, ...) \
+ do { \
+ if (failed(__call)) { \
+ __VA_ARGS__; \
+ } \
+ } while (0)
+
+#define FAILED_NULL(p) ((p) == nullptr)
+#define CHECK_LLDP_P(__call, conn) \
+ CHECK_LLDP_GENERIC( \
+ FAILED_NULL, __call, const auto _rc_ { lldpctl_last_error(conn) }; \
+ if (LLDPCTL_NO_ERROR != _rc_) { \
+ throw std::system_error(std::error_code(_rc_, LldpErrCategory()), \
+ "'" __stringify(__call) "' failed"); \
+ })
+
+#define FAILED_NEGATIVE(v) ((v) < 0)
+#define CHECK_LLDP_N(__call, conn) \
+ CHECK_LLDP_GENERIC(FAILED_NEGATIVE, __call, \
+ const auto _rc_ { lldpctl_last_error(conn) }; \
+ throw std::system_error(std::error_code(_rc_, LldpErrCategory()), \
+ "'" __stringify(__call) "' failed");)
+
+#define CHECK_LLDP_N2(pre, __call, conn) \
+ CHECK_LLDP_GENERIC( \
+ FAILED_NEGATIVE, __call, if (pre) { \
+ const auto _rc_ { lldpctl_last_error(conn) }; \
+ throw std::system_error(std::error_code(_rc_, LldpErrCategory()), \
+ "'" __stringify(__call) "' failed"); \
+ })
/* *** type declarations ******************************************************/
/* *** exported interfaces ****************************************************/
-namespace lldpcli
-{
-namespace literals
-{
+namespace lldpcli {
+namespace literals {
/**
* @brief Operator to define std::byte literals.
*
* Example: auto byte{ 0x01_b };
*/
-consteval std::byte operator"" _b( unsigned long long int value )
+consteval std::byte operator"" _b(unsigned long long int value)
{
- return static_cast<std::byte>( value );
+ return static_cast<std::byte>(value);
}
} // namespace literals
/**
* @brief LLDP error category.
*/
-class LldpErrCategory : public std::error_category
-{
-public:
- const char *name() const noexcept override
- {
- return "lldpctl";
- }
-
- std::string message( int ev ) const override
- {
- return ::lldpctl_strerror( static_cast<lldpctl_error_t>( ev ) );
- }
+class LldpErrCategory : public std::error_category {
+ public:
+ const char *name() const noexcept override { return "lldpctl"; }
+
+ std::string message(int ev) const override
+ {
+ return ::lldpctl_strerror(static_cast<lldpctl_error_t>(ev));
+ }
};
/**
* @brief Wrapper class for @p lldpctl_atom_t with automatic lifetime management.
*/
-class LldpAtom
-{
-public:
- using vector = std::vector<std::byte>;
- using span = std::span<const std::byte>;
-
- /**
- * @brief Construct a new Lldp Atom.
- *
- * @param atom The atom provided by the library.
- * @param inc_ref_cnt If @p true increment the atom's reference count as ownership is not implicitly transferred from the caller.
- * @param conn The connection the atom belongs to. Used to extend the connection lifetime as the atom can't live without it.
- * May be @p nullptr in which case the connection's lifetime can't be extended by the atom object (e.g. as the connection is owned by the library).
- * @param parent The optional parent atom. Used to extend the parent atom's lifetime as the child atom can't live without it.
- */
- explicit LldpAtom(
- lldpctl_atom_t *atom, bool inc_ref_cnt, const std::shared_ptr<lldpctl_conn_t> &conn,
- std::unique_ptr<LldpAtom> parent = nullptr )
- : atom_( atom )
- , conn_( conn )
- , parent_( std::move( parent ) )
- {
- if ( inc_ref_cnt )
- {
- ::lldpctl_atom_inc_ref( atom_ );
- }
- }
-
- ~LldpAtom()
- {
- if ( atom_ )
- {
- ::lldpctl_atom_dec_ref( atom_ );
- }
- }
-
- LldpAtom( const LldpAtom &other ) noexcept
- : atom_( other.atom_ )
- , conn_( other.conn_ )
- , parent_( other.parent_ ? std::make_unique<LldpAtom>( *other.parent_ ) : nullptr )
- {
- ::lldpctl_atom_inc_ref( atom_ );
- }
-
- LldpAtom &operator=( const LldpAtom &other )
- {
- if ( this != &other )
- {
- atom_ = other.atom_;
- conn_ = other.conn_;
- parent_ = other.parent_ ? std::make_unique<LldpAtom>( *other.parent_ ) : nullptr;
- ::lldpctl_atom_inc_ref( atom_ );
- }
-
- return *this;
- }
-
- LldpAtom( LldpAtom &&other ) noexcept
- : atom_( other.atom_ )
- , conn_( other.conn_ )
- , parent_( std::move( other.parent_ ) )
- {
- other.atom_ = nullptr;
- other.conn_ = nullptr;
- other.parent_ = nullptr;
- }
-
- LldpAtom &operator=( LldpAtom &&other ) noexcept
- {
- if ( this != &other )
- {
- atom_ = other.atom_;
- conn_ = other.conn_;
- parent_ = std::move( other.parent_ );
- other.atom_ = nullptr;
- other.conn_ = nullptr;
- other.parent_ = nullptr;
- }
-
- return *this;
- }
-
- LldpAtom GetPort() const
- {
- lldpctl_atom_t *atom;
- CHECK_LLDP_P( atom = ::lldpctl_get_port( atom_ ), conn_.get() );
- return LldpAtom{ atom, false, conn_ };
- }
-
- std::optional<LldpAtom> GetAtom( lldpctl_key_t key ) const
- {
- auto atom{ ::lldpctl_atom_get( atom_, key ) };
- return atom ? std::make_optional<LldpAtom>( atom, false, conn_ ) : std::nullopt;
- }
-
- LldpAtom CreateAtom() const
- {
- lldpctl_atom_t *atom;
- CHECK_LLDP_P( atom = ::lldpctl_atom_create( atom_ ), conn_.get() );
- /* Store the parent atom to increase its reference count so that it remains living as long as the child lives. */
- return LldpAtom{ atom, false, conn_, std::make_unique<LldpAtom>( *this ) };
- }
-
- void SetAtom( lldpctl_key_t key, const LldpAtom &child )
- {
- CHECK_LLDP_P( ::lldpctl_atom_set( atom_, key, child.atom_ ), conn_.get() );
- }
-
- std::list<LldpAtom> GetAtomList( lldpctl_key_t key ) const
- {
- lldpctl_atom_t *it;
- CHECK_LLDP_P( it = ::lldpctl_atom_get( atom_, key ), conn_.get() );
-
- std::list<LldpAtom> list;
- lldpctl_atom_t *atom;
- lldpctl_atom_foreach( it, atom )
- {
- list.emplace_back( atom, true, conn_ );
- }
-
- return list;
- }
-
- template<typename T>
- auto GetValue( lldpctl_key_t key ) const
- {
- if constexpr ( std::is_same_v<T, std::string> || std::is_same_v<T, std::string_view> )
- {
- const auto str{ ::lldpctl_atom_get_str( atom_, key ) };
- return str ? std::make_optional<T>( str ) : std::nullopt;
- }
- else if constexpr ( std::is_same_v<T, int> )
- {
- const auto value{ ::lldpctl_atom_get_int( atom_, key ) };
- return lldpctl_last_error( lldpctl_atom_get_connection( atom_ ) ) == LLDPCTL_NO_ERROR
- ? std::make_optional<T>( value )
- : std::nullopt;
- }
- else if constexpr ( std::is_same_v<T, vector> || std::is_same_v<T, span> )
- {
- size_t length{ 0 };
- const auto buffer{ ::lldpctl_atom_get_buffer( atom_, key, &length ) };
-
- if ( buffer )
- {
- auto it{ reinterpret_cast<const std::byte *>( buffer ) };
- return T{ it, it + length };
- }
- else
- {
- return T{};
- }
- }
- else
- {
- static_assert( always_false_<T>::value, "Unsupported type" );
- }
- }
-
- template<typename T>
- void SetValue( lldpctl_key_t key, const T &data )
- {
- if constexpr (
- std::is_same_v<T, std::optional<std::string>> || std::is_same_v<T, std::optional<std::string_view>> )
- {
- CHECK_LLDP_P(
- ::lldpctl_atom_set_str( atom_, key, data.has_value() ? data->data() : nullptr ), conn_.get() );
- }
- else if constexpr ( std::is_same_v<T, int> )
- {
- CHECK_LLDP_P( ::lldpctl_atom_set_int( atom_, key, data ), conn_.get() );
- }
- else if constexpr ( std::is_same_v<T, vector> || std::is_same_v<T, span> )
- {
- CHECK_LLDP_P(
- ::lldpctl_atom_set_buffer( atom_, key, reinterpret_cast<const uint8_t *>( data.data() ), data.size() ),
- conn_.get() );
- }
- else
- {
- static_assert( always_false_<T>::value, "Unsupported type" );
- }
- }
-
-private:
- template<typename>
- struct always_false_ : std::false_type
- {
- };
-
- lldpctl_atom_t *atom_;
- std::shared_ptr<lldpctl_conn_t> conn_;
- std::unique_ptr<LldpAtom> parent_;
+class LldpAtom {
+ public:
+ using vector = std::vector<std::byte>;
+ using span = std::span<const std::byte>;
+
+ /**
+ * @brief Construct a new Lldp Atom.
+ *
+ * @param atom The atom provided by the library.
+ * @param inc_ref_cnt If @p true increment the atom's reference count as
+ * ownership is not implicitly transferred from the caller.
+ * @param conn The connection the atom belongs to. Used to extend the
+ * connection lifetime as the atom can't live without it. May be @p nullptr in
+ * which case the connection's lifetime can't be extended by the atom object
+ * (e.g. as the connection is owned by the library).
+ * @param parent The optional parent atom. Used to extend the parent
+ * atom's lifetime as the child atom can't live without it.
+ */
+ explicit LldpAtom(lldpctl_atom_t *atom, bool inc_ref_cnt,
+ const std::shared_ptr<lldpctl_conn_t> &conn,
+ std::unique_ptr<LldpAtom> parent = nullptr)
+ : atom_(atom)
+ , conn_(conn)
+ , parent_(std::move(parent))
+ {
+ if (inc_ref_cnt) {
+ ::lldpctl_atom_inc_ref(atom_);
+ }
+ }
+
+ ~LldpAtom()
+ {
+ if (atom_) {
+ ::lldpctl_atom_dec_ref(atom_);
+ }
+ }
+
+ LldpAtom(const LldpAtom &other) noexcept
+ : atom_(other.atom_)
+ , conn_(other.conn_)
+ , parent_(
+ other.parent_ ? std::make_unique<LldpAtom>(*other.parent_) : nullptr)
+ {
+ ::lldpctl_atom_inc_ref(atom_);
+ }
+
+ LldpAtom &operator=(const LldpAtom &other)
+ {
+ if (this != &other) {
+ atom_ = other.atom_;
+ conn_ = other.conn_;
+ parent_ = other.parent_ ?
+ std::make_unique<LldpAtom>(*other.parent_) :
+ nullptr;
+ ::lldpctl_atom_inc_ref(atom_);
+ }
+
+ return *this;
+ }
+
+ LldpAtom(LldpAtom &&other) noexcept
+ : atom_(other.atom_)
+ , conn_(other.conn_)
+ , parent_(std::move(other.parent_))
+ {
+ other.atom_ = nullptr;
+ other.conn_ = nullptr;
+ other.parent_ = nullptr;
+ }
+
+ LldpAtom &operator=(LldpAtom &&other) noexcept
+ {
+ if (this != &other) {
+ atom_ = other.atom_;
+ conn_ = other.conn_;
+ parent_ = std::move(other.parent_);
+ other.atom_ = nullptr;
+ other.conn_ = nullptr;
+ other.parent_ = nullptr;
+ }
+
+ return *this;
+ }
+
+ LldpAtom GetPort() const
+ {
+ lldpctl_atom_t *atom;
+ CHECK_LLDP_P(atom = ::lldpctl_get_port(atom_), conn_.get());
+ return LldpAtom { atom, false, conn_ };
+ }
+
+ std::optional<LldpAtom> GetAtom(lldpctl_key_t key) const
+ {
+ auto atom { ::lldpctl_atom_get(atom_, key) };
+ return atom ? std::make_optional<LldpAtom>(atom, false, conn_) :
+ std::nullopt;
+ }
+
+ LldpAtom CreateAtom() const
+ {
+ lldpctl_atom_t *atom;
+ CHECK_LLDP_P(atom = ::lldpctl_atom_create(atom_), conn_.get());
+ /* Store the parent atom to increase its reference count so that it
+ * remains living as long as the child lives. */
+ return LldpAtom { atom, false, conn_,
+ std::make_unique<LldpAtom>(*this) };
+ }
+
+ void SetAtom(lldpctl_key_t key, const LldpAtom &child)
+ {
+ CHECK_LLDP_P(::lldpctl_atom_set(atom_, key, child.atom_), conn_.get());
+ }
+
+ std::list<LldpAtom> GetAtomList(lldpctl_key_t key) const
+ {
+ lldpctl_atom_t *it;
+ CHECK_LLDP_P(it = ::lldpctl_atom_get(atom_, key), conn_.get());
+
+ std::list<LldpAtom> list;
+ lldpctl_atom_t *atom;
+ lldpctl_atom_foreach(it, atom)
+ {
+ list.emplace_back(atom, true, conn_);
+ }
+
+ return list;
+ }
+
+ template <typename T> auto GetValue(lldpctl_key_t key) const
+ {
+ if constexpr (std::is_same_v<T, std::string> ||
+ std::is_same_v<T, std::string_view>) {
+ const auto str { ::lldpctl_atom_get_str(atom_, key) };
+ return str ? std::make_optional<T>(str) : std::nullopt;
+ } else if constexpr (std::is_same_v<T, int>) {
+ const auto value { ::lldpctl_atom_get_int(atom_, key) };
+ return lldpctl_last_error(lldpctl_atom_get_connection(atom_)) ==
+ LLDPCTL_NO_ERROR ?
+ std::make_optional<T>(value) :
+ std::nullopt;
+ } else if constexpr (std::is_same_v<T, vector> ||
+ std::is_same_v<T, span>) {
+ size_t length { 0 };
+ const auto buffer { ::lldpctl_atom_get_buffer(atom_, key,
+ &length) };
+
+ if (buffer) {
+ auto it { reinterpret_cast<const std::byte *>(buffer) };
+ return T { it, it + length };
+ } else {
+ return T {};
+ }
+ } else {
+ static_assert(always_false_<T>::value, "Unsupported type");
+ }
+ }
+
+ template <typename T> void SetValue(lldpctl_key_t key, const T &data)
+ {
+ if constexpr (std::is_same_v<T, std::optional<std::string>> ||
+ std::is_same_v<T, std::optional<std::string_view>>) {
+ CHECK_LLDP_P(::lldpctl_atom_set_str(atom_, key,
+ data.has_value() ? data->data() : nullptr),
+ conn_.get());
+ } else if constexpr (std::is_same_v<T, int>) {
+ CHECK_LLDP_P(::lldpctl_atom_set_int(atom_, key, data),
+ conn_.get());
+ } else if constexpr (std::is_same_v<T, vector> ||
+ std::is_same_v<T, span>) {
+ CHECK_LLDP_P(::lldpctl_atom_set_buffer(atom_, key,
+ reinterpret_cast<const uint8_t *>(data.data()),
+ data.size()),
+ conn_.get());
+ } else {
+ static_assert(always_false_<T>::value, "Unsupported type");
+ }
+ }
+
+ private:
+ template <typename> struct always_false_ : std::false_type {
+ };
+
+ lldpctl_atom_t *atom_;
+ std::shared_ptr<lldpctl_conn_t> conn_;
+ std::unique_ptr<LldpAtom> parent_;
};
/**
* @brief Wrapper class for @p lldpctl_conn_t with automatic lifetime management.
*/
-class LldpCtl
-{
-public:
- explicit LldpCtl()
- {
- if ( !conn_ )
- {
- throw std::system_error(
- std::error_code( LLDPCTL_ERR_NOMEM, LldpErrCategory() ), "Could not create lldpctl connection." );
- }
- }
-
- ~LldpCtl() = default;
-
- LldpCtl( const LldpCtl &other ) = default;
-
- LldpCtl &operator=( const LldpCtl &other )
- {
- if ( this != &other )
- {
- conn_ = other.conn_;
- }
-
- return *this;
- }
-
- LldpCtl( LldpCtl &&other ) noexcept
- : conn_( other.conn_ )
- {
- other.conn_ = nullptr;
- }
-
- LldpCtl &operator=( LldpCtl &&other ) noexcept
- {
- if ( this != &other )
- {
- conn_ = other.conn_;
- other.conn_ = nullptr;
- }
-
- return *this;
- }
-
- LldpAtom GetConfiguration() const
- {
- lldpctl_atom_t *atom;
- CHECK_LLDP_P( atom = ::lldpctl_get_configuration( conn_.get() ), conn_.get() );
- return LldpAtom{ atom, false, conn_ };
- }
-
- std::list<LldpAtom> GetInterfaces() const
- {
- const auto &it{ ::lldpctl_get_interfaces( conn_.get() ) };
-
- std::list<LldpAtom> list;
- lldpctl_atom_t *atom;
- lldpctl_atom_foreach( it, atom )
- {
- list.emplace_back( atom, true, conn_ );
- }
-
- return list;
- }
-
- std::optional<LldpAtom> GetInterface( std::string_view if_name ) const
- {
- for ( const auto &interface : GetInterfaces() )
- {
- if ( interface.GetValue<std::string_view>( lldpctl_k_interface_name ) == if_name )
- {
- return interface;
- }
- }
-
- return std::nullopt;
- }
-
- LldpAtom GetLocalChassis() const
- {
- lldpctl_atom_t *atom;
- CHECK_LLDP_P( atom = ::lldpctl_get_local_chassis( conn_.get() ), conn_.get() );
- return LldpAtom{ atom, false, conn_ };
- }
-
- LldpAtom GetDefaultPort() const
- {
- lldpctl_atom_t *atom;
- CHECK_LLDP_P( atom = ::lldpctl_get_default_port( conn_.get() ), conn_.get() );
- return LldpAtom{ atom, false, conn_ };
- }
-
- static std::string_view get_default_transport() noexcept
- {
- return ::lldpctl_get_default_transport();
- }
-
- static std::map<std::string, int, std::less<>> KeyGetMap( lldpctl_key_t key ) noexcept
- {
- std::map<std::string, int, std::less<>> map;
-
- lldpctl_map_t *entry{ ::lldpctl_key_get_map( key ) };
- while ( entry->string )
- {
- map.try_emplace( entry->string, entry->value );
- ++entry;
- }
-
- return map;
- }
-
-private:
- std::shared_ptr<lldpctl_conn_t> conn_{ ::lldpctl_new( nullptr, nullptr, this ), &::lldpctl_release };
+class LldpCtl {
+ public:
+ explicit LldpCtl()
+ {
+ if (!conn_) {
+ throw std::system_error(std::error_code(LLDPCTL_ERR_NOMEM,
+ LldpErrCategory()),
+ "Could not create lldpctl connection.");
+ }
+ }
+
+ ~LldpCtl() = default;
+
+ LldpCtl(const LldpCtl &other) = default;
+
+ LldpCtl &operator=(const LldpCtl &other)
+ {
+ if (this != &other) {
+ conn_ = other.conn_;
+ }
+
+ return *this;
+ }
+
+ LldpCtl(LldpCtl &&other) noexcept
+ : conn_(other.conn_)
+ {
+ other.conn_ = nullptr;
+ }
+
+ LldpCtl &operator=(LldpCtl &&other) noexcept
+ {
+ if (this != &other) {
+ conn_ = other.conn_;
+ other.conn_ = nullptr;
+ }
+
+ return *this;
+ }
+
+ LldpAtom GetConfiguration() const
+ {
+ lldpctl_atom_t *atom;
+ CHECK_LLDP_P(atom = ::lldpctl_get_configuration(conn_.get()),
+ conn_.get());
+ return LldpAtom { atom, false, conn_ };
+ }
+
+ std::list<LldpAtom> GetInterfaces() const
+ {
+ const auto &it { ::lldpctl_get_interfaces(conn_.get()) };
+
+ std::list<LldpAtom> list;
+ lldpctl_atom_t *atom;
+ lldpctl_atom_foreach(it, atom)
+ {
+ list.emplace_back(atom, true, conn_);
+ }
+
+ return list;
+ }
+
+ std::optional<LldpAtom> GetInterface(std::string_view if_name) const
+ {
+ for (const auto &interface : GetInterfaces()) {
+ if (interface.GetValue<std::string_view>(
+ lldpctl_k_interface_name) == if_name) {
+ return interface;
+ }
+ }
+
+ return std::nullopt;
+ }
+
+ LldpAtom GetLocalChassis() const
+ {
+ lldpctl_atom_t *atom;
+ CHECK_LLDP_P(atom = ::lldpctl_get_local_chassis(conn_.get()),
+ conn_.get());
+ return LldpAtom { atom, false, conn_ };
+ }
+
+ LldpAtom GetDefaultPort() const
+ {
+ lldpctl_atom_t *atom;
+ CHECK_LLDP_P(atom = ::lldpctl_get_default_port(conn_.get()),
+ conn_.get());
+ return LldpAtom { atom, false, conn_ };
+ }
+
+ static std::string_view get_default_transport() noexcept
+ {
+ return ::lldpctl_get_default_transport();
+ }
+
+ static std::map<std::string, int, std::less<>> KeyGetMap(
+ lldpctl_key_t key) noexcept
+ {
+ std::map<std::string, int, std::less<>> map;
+
+ lldpctl_map_t *entry { ::lldpctl_key_get_map(key) };
+ while (entry->string) {
+ map.try_emplace(entry->string, entry->value);
+ ++entry;
+ }
+
+ return map;
+ }
+
+ private:
+ std::shared_ptr<lldpctl_conn_t> conn_ { ::lldpctl_new(nullptr, nullptr, this),
+ &::lldpctl_release };
};
/**
* @brief Wrapper for change callback registration.
*
- * @tparam X Context pointer type for the general optional callback passed to the constructor.
- * @tparam Y Context pointer type for the interface specific callbacks passed to @ref RegisterInterfaceCallback.
+ * @tparam X Context pointer type for the general optional callback passed to the
+ * constructor.
+ * @tparam Y Context pointer type for the interface specific callbacks passed to @ref
+ * RegisterInterfaceCallback.
*/
-template<typename X = void, typename Y = void>
-class LldpWatch
-{
-public:
- template<typename C>
- using ChangeCallback = const std::function<void(
- std::string_view if_name, lldpctl_change_t change, const LldpAtom interface, const LldpAtom neighbor, C *ctx )>;
-
- /**
- * @brief Construct a new Lldp Watch object.
- *
- * @param callback Optional callback to trigger on remote changes.
- * Additionally, interface specific callbacks can be registered using @ref RegisterInterfaceCallback.
- * @param ctx Optional context passed to @p callback.
- */
- explicit LldpWatch( const std::optional<ChangeCallback<X>> &callback = std::nullopt, const X *ctx = nullptr )
- : general_callback_(
- callback.has_value() ? std::make_optional( std::make_pair( *callback, const_cast<X *>( ctx ) ) )
- : std::nullopt )
- {
- if ( !conn_ )
- {
- throw std::system_error(
- std::error_code( LLDPCTL_ERR_NOMEM, LldpErrCategory() ), "Could not create lldpctl connection." );
- }
-
- CHECK_LLDP_N(
- ::lldpctl_watch_callback2( conn_, &LldpWatch<X, Y>::WatchCallback, static_cast<void *>( this ) ), conn_ );
-
- thread_ = std::jthread{ [this]( std::stop_token stop )
- {
- while ( !stop.stop_requested() )
- {
- CHECK_LLDP_N2( !stop.stop_requested(), ::lldpctl_watch( conn_ ), conn_ );
- }
- } };
- }
-
- ~LldpWatch()
- {
- if ( conn_ )
- {
- thread_.request_stop();
- ::lldpctl_watch_sync_unblock( conn_ );
- thread_.join();
- ::lldpctl_release( conn_ );
- }
- }
-
- LldpWatch( const LldpWatch & ) = delete;
- LldpWatch &operator=( const LldpWatch & ) = delete;
- LldpWatch( LldpWatch &&other ) = delete;
- LldpWatch &operator=( LldpWatch && ) = delete;
-
- /**
- * @brief Register an interface specific callback on remote changes.
- *
- * @param if_name The local interface to monitor.
- * @param callback Callback to trigger on remote changes.
- * @param ctx Optional context passed to @p callback.
- * @param trigger_init It @p true then @p callback is invoked during registration for all existing neighbors.
- */
- void RegisterInterfaceCallback(
- const std::string &if_name, ChangeCallback<Y> callback, const Y *ctx, bool trigger_init = false )
- {
- const auto interface{ LldpCtl().GetInterface( if_name ) };
- if ( !interface.has_value() )
- {
- throw std::system_error(
- std::error_code( LLDPCTL_ERR_NOT_EXIST, LldpErrCategory() ),
- "Couldn't find interface '" + if_name + "'" );
- }
-
- std::scoped_lock lock{ mutex_ };
-
- /**
- * Note:
- * There's a race one way or the other - we decided to accept the one that the neighbor
- * changes between reading it and registering the callback.
- */
- if ( trigger_init )
- {
- for ( const auto &neighbor : interface->GetPort().GetAtomList( lldpctl_k_port_neighbors ) )
- {
- callback( if_name, lldpctl_change_t::lldpctl_c_added, *interface, neighbor, const_cast<Y *>( ctx ) );
- }
- }
-
- interface_callbacks_.try_emplace( if_name, std::make_pair( callback, const_cast<Y *>( ctx ) ) );
- }
-
-private:
- static void WatchCallback( lldpctl_change_t change, lldpctl_atom_t *interface, lldpctl_atom_t *neighbor, void *p )
- {
- /* These LldpAtoms don't extend the lifetime of the underlying connection as it's owned by the library. */
- LldpAtom interface_atom{ interface, true, nullptr };
- LldpAtom neighbor_atom{ neighbor, true, nullptr };
-
- const auto if_name{ *interface_atom.GetValue<std::string_view>( lldpctl_k_interface_name ) };
-
- auto self{ static_cast<LldpWatch<X, Y> *>( p ) };
-
- std::scoped_lock lock{ self->mutex_ };
-
- if ( self->general_callback_.has_value() )
- {
- auto [callback, ctx]{ self->general_callback_.value() };
- callback( if_name, change, interface_atom, neighbor_atom, ctx );
- }
-
- if ( auto it{ self->interface_callbacks_.find( if_name ) }; it != self->interface_callbacks_.end() )
- {
- auto [callback, ctx]{ it->second };
- callback( if_name, change, interface_atom, neighbor_atom, ctx );
- }
- }
-
- lldpctl_conn_t *conn_{ ::lldpctl_new( nullptr, nullptr, this ) };
- std::jthread thread_;
- std::mutex mutex_;
- const std::optional<std::pair<ChangeCallback<X>, X *>> general_callback_;
- std::map<std::string, std::pair<ChangeCallback<Y>, Y *>, std::less<>> interface_callbacks_;
+template <typename X = void, typename Y = void> class LldpWatch {
+ public:
+ template <typename C>
+ using ChangeCallback =
+ const std::function<void(std::string_view if_name, lldpctl_change_t change,
+ const LldpAtom interface, const LldpAtom neighbor, C *ctx)>;
+
+ /**
+ * @brief Construct a new Lldp Watch object.
+ *
+ * @param callback Optional callback to trigger on remote changes.
+ * Additionally, interface specific callbacks can be registered
+ * using @ref RegisterInterfaceCallback.
+ * @param ctx Optional context passed to @p callback.
+ */
+ explicit LldpWatch(
+ const std::optional<ChangeCallback<X>> &callback = std::nullopt,
+ const X *ctx = nullptr)
+ : general_callback_(callback.has_value() ?
+ std::make_optional(
+ std::make_pair(*callback, const_cast<X *>(ctx))) :
+ std::nullopt)
+ {
+ if (!conn_) {
+ throw std::system_error(std::error_code(LLDPCTL_ERR_NOMEM,
+ LldpErrCategory()),
+ "Could not create lldpctl connection.");
+ }
+
+ CHECK_LLDP_N(::lldpctl_watch_callback2(conn_,
+ &LldpWatch<X, Y>::WatchCallback,
+ static_cast<void *>(this)),
+ conn_);
+
+ thread_ = std::jthread { [this](std::stop_token stop) {
+ while (!stop.stop_requested()) {
+ CHECK_LLDP_N2(!stop.stop_requested(),
+ ::lldpctl_watch(conn_), conn_);
+ }
+ } };
+ }
+
+ ~LldpWatch()
+ {
+ if (conn_) {
+ thread_.request_stop();
+ ::lldpctl_watch_sync_unblock(conn_);
+ thread_.join();
+ ::lldpctl_release(conn_);
+ }
+ }
+
+ LldpWatch(const LldpWatch &) = delete;
+ LldpWatch &operator=(const LldpWatch &) = delete;
+ LldpWatch(LldpWatch &&other) = delete;
+ LldpWatch &operator=(LldpWatch &&) = delete;
+
+ /**
+ * @brief Register an interface specific callback on remote changes.
+ *
+ * @param if_name The local interface to monitor.
+ * @param callback Callback to trigger on remote changes.
+ * @param ctx Optional context passed to @p callback.
+ * @param trigger_init It @p true then @p callback is invoked during
+ * registration for all existing neighbors.
+ */
+ void RegisterInterfaceCallback(const std::string &if_name,
+ ChangeCallback<Y> callback, const Y *ctx, bool trigger_init = false)
+ {
+ const auto interface {
+ LldpCtl().GetInterface(if_name)
+ };
+ if (!interface.has_value()) {
+ throw std::system_error(std::error_code(LLDPCTL_ERR_NOT_EXIST,
+ LldpErrCategory()),
+ "Couldn't find interface '" + if_name + "'");
+ }
+
+ std::scoped_lock lock { mutex_ };
+
+ /**
+ * Note:
+ * There's a race one way or the other - we decided to accept the one
+ * that the neighbor changes between reading it and registering the
+ * callback.
+ */
+ if (trigger_init) {
+ for (const auto &neighbor : interface->GetPort().GetAtomList(
+ lldpctl_k_port_neighbors)) {
+ callback(if_name, lldpctl_change_t::lldpctl_c_added,
+ *interface, neighbor, const_cast<Y *>(ctx));
+ }
+ }
+
+ interface_callbacks_.try_emplace(if_name,
+ std::make_pair(callback, const_cast<Y *>(ctx)));
+ }
+
+ private:
+ static void WatchCallback(lldpctl_change_t change, lldpctl_atom_t *interface,
+ lldpctl_atom_t *neighbor, void *p)
+ {
+ /* These LldpAtoms don't extend the lifetime of the underlying
+ * connection as it's owned by the library. */
+ LldpAtom interface_atom { interface, true, nullptr };
+ LldpAtom neighbor_atom { neighbor, true, nullptr };
+
+ const auto if_name { *interface_atom.GetValue<std::string_view>(
+ lldpctl_k_interface_name) };
+
+ auto self { static_cast<LldpWatch<X, Y> *>(p) };
+
+ std::scoped_lock lock { self->mutex_ };
+
+ if (self->general_callback_.has_value()) {
+ auto [callback, ctx] { self->general_callback_.value() };
+ callback(if_name, change, interface_atom, neighbor_atom, ctx);
+ }
+
+ if (auto it { self->interface_callbacks_.find(if_name) };
+ it != self->interface_callbacks_.end()) {
+ auto [callback, ctx] { it->second };
+ callback(if_name, change, interface_atom, neighbor_atom, ctx);
+ }
+ }
+
+ lldpctl_conn_t *conn_ { ::lldpctl_new(nullptr, nullptr, this) };
+ std::jthread thread_;
+ std::mutex mutex_;
+ const std::optional<std::pair<ChangeCallback<X>, X *>> general_callback_;
+ std::map<std::string, std::pair<ChangeCallback<Y>, Y *>, std::less<>>
+ interface_callbacks_;
};
-} // namespace lldpcli
\ No newline at end of file
+} // namespace lldpcli