mirror of
				https://git.proxmox.com/git/mirror_frr
				synced 2025-11-04 06:38:04 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			985 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			985 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
						|
/*
 | 
						|
 * MGMTD Backend Client Connection Adapter
 | 
						|
 *
 | 
						|
 * Copyright (C) 2021  Vmware, Inc.
 | 
						|
 *		       Pushpasis Sarkar <spushpasis@vmware.com>
 | 
						|
 * Copyright (c) 2023, LabN Consulting, L.L.C.
 | 
						|
 */
 | 
						|
 | 
						|
#include <zebra.h>
 | 
						|
#include "frrevent.h"
 | 
						|
#include "sockopt.h"
 | 
						|
#include "network.h"
 | 
						|
#include "libfrr.h"
 | 
						|
#include "mgmt_msg.h"
 | 
						|
#include "mgmt_pb.h"
 | 
						|
#include "mgmtd/mgmt.h"
 | 
						|
#include "mgmtd/mgmt_memory.h"
 | 
						|
#include "mgmt_be_client.h"
 | 
						|
#include "mgmtd/mgmt_be_adapter.h"
 | 
						|
 | 
						|
#define MGMTD_BE_ADAPTER_DBG(fmt, ...)                                         \
 | 
						|
	DEBUGD(&mgmt_debug_be, "BE-ADAPTER: %s: " fmt, __func__, ##__VA_ARGS__)
 | 
						|
#define MGMTD_BE_ADAPTER_ERR(fmt, ...)                                         \
 | 
						|
	zlog_err("BE-ADAPTER: %s: ERROR: " fmt, __func__, ##__VA_ARGS__)
 | 
						|
 | 
						|
#define FOREACH_ADAPTER_IN_LIST(adapter)                                       \
 | 
						|
	frr_each_safe (mgmt_be_adapters, &mgmt_be_adapters, (adapter))
 | 
						|
 | 
						|
/*
 | 
						|
 * Static mapping of YANG XPath regular expressions and
 | 
						|
 * the corresponding interested backend clients.
 | 
						|
 * NOTE: Thiis is a static mapping defined by all MGMTD
 | 
						|
 * backend client modules (for now, till we develop a
 | 
						|
 * more dynamic way of creating and updating this map).
 | 
						|
 * A running map is created by MGMTD in run-time to
 | 
						|
 * handle real-time mapping of YANG xpaths to one or
 | 
						|
 * more interested backend client adapters.
 | 
						|
 *
 | 
						|
 * Please see xpath_map_reg[] in lib/mgmt_be_client.c
 | 
						|
 * for the actual map
 | 
						|
 */
 | 
						|
struct mgmt_be_xpath_map_init {
 | 
						|
	const char *xpath_regexp;
 | 
						|
	uint subscr_info[MGMTD_BE_CLIENT_ID_MAX];
 | 
						|
};
 | 
						|
 | 
						|
struct mgmt_be_xpath_map {
 | 
						|
	char *xpath_regexp;
 | 
						|
	uint subscr_info[MGMTD_BE_CLIENT_ID_MAX];
 | 
						|
};
 | 
						|
 | 
						|
struct mgmt_be_client_xpath {
 | 
						|
	const char *xpath;
 | 
						|
	uint subscribed;
 | 
						|
};
 | 
						|
 | 
						|
struct mgmt_be_client_xpath_map {
 | 
						|
	struct mgmt_be_client_xpath *xpaths;
 | 
						|
	uint nxpaths;
 | 
						|
};
 | 
						|
 | 
						|
struct mgmt_be_get_adapter_config_params {
 | 
						|
	struct mgmt_be_client_adapter *adapter;
 | 
						|
	struct nb_config_cbs *cfg_chgs;
 | 
						|
	uint32_t seq;
 | 
						|
};
 | 
						|
 | 
						|
/*
 | 
						|
 * Static mapping of YANG XPath regular expressions and
 | 
						|
 * the corresponding interested backend clients.
 | 
						|
 * NOTE: Thiis is a static mapping defined by all MGMTD
 | 
						|
 * backend client modules (for now, till we develop a
 | 
						|
 * more dynamic way of creating and updating this map).
 | 
						|
 * A running map is created by MGMTD in run-time to
 | 
						|
 * handle real-time mapping of YANG xpaths to one or
 | 
						|
 * more interested backend client adapters.
 | 
						|
 */
 | 
						|
static const struct mgmt_be_xpath_map_init mgmt_xpath_map_init[] = {
 | 
						|
	{
 | 
						|
		.xpath_regexp = "/frr-vrf:lib/*",
 | 
						|
		.subscr_info =
 | 
						|
			{
 | 
						|
#if HAVE_STATICD
 | 
						|
				[MGMTD_BE_CLIENT_ID_STATICD] =
 | 
						|
					MGMT_SUBSCR_VALIDATE_CFG |
 | 
						|
					MGMT_SUBSCR_NOTIFY_CFG,
 | 
						|
#endif
 | 
						|
			},
 | 
						|
	},
 | 
						|
	{
 | 
						|
		.xpath_regexp = "/frr-interface:lib/*",
 | 
						|
		.subscr_info =
 | 
						|
			{
 | 
						|
#if HAVE_STATICD
 | 
						|
				[MGMTD_BE_CLIENT_ID_STATICD] =
 | 
						|
					MGMT_SUBSCR_VALIDATE_CFG |
 | 
						|
					MGMT_SUBSCR_NOTIFY_CFG,
 | 
						|
#endif
 | 
						|
			},
 | 
						|
	},
 | 
						|
 | 
						|
	{
 | 
						|
		.xpath_regexp =
 | 
						|
			"/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/*",
 | 
						|
		.subscr_info =
 | 
						|
			{
 | 
						|
#if HAVE_STATICD
 | 
						|
				[MGMTD_BE_CLIENT_ID_STATICD] =
 | 
						|
					MGMT_SUBSCR_VALIDATE_CFG |
 | 
						|
					MGMT_SUBSCR_NOTIFY_CFG,
 | 
						|
#endif
 | 
						|
			},
 | 
						|
	},
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 * Each client gets their own map, but also union all the strings into the
 | 
						|
 * above map as well.
 | 
						|
 */
 | 
						|
#if HAVE_STATICD
 | 
						|
static struct mgmt_be_client_xpath staticd_xpaths[] = {
 | 
						|
	{
 | 
						|
		.xpath = "/frr-vrf:lib/*",
 | 
						|
		.subscribed = MGMT_SUBSCR_VALIDATE_CFG | MGMT_SUBSCR_NOTIFY_CFG,
 | 
						|
	},
 | 
						|
	{
 | 
						|
		.xpath = "/frr-interface:lib/*",
 | 
						|
		.subscribed = MGMT_SUBSCR_VALIDATE_CFG | MGMT_SUBSCR_NOTIFY_CFG,
 | 
						|
	},
 | 
						|
	{
 | 
						|
		.xpath =
 | 
						|
			"/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/*",
 | 
						|
		.subscribed = MGMT_SUBSCR_VALIDATE_CFG | MGMT_SUBSCR_NOTIFY_CFG,
 | 
						|
	},
 | 
						|
};
 | 
						|
#endif
 | 
						|
 | 
						|
static struct mgmt_be_client_xpath_map
 | 
						|
	mgmt_client_xpaths[MGMTD_BE_CLIENT_ID_MAX] = {
 | 
						|
#ifdef HAVE_STATICD
 | 
						|
		[MGMTD_BE_CLIENT_ID_STATICD] = {staticd_xpaths,
 | 
						|
						array_size(staticd_xpaths)},
 | 
						|
#endif
 | 
						|
};
 | 
						|
 | 
						|
#define MGMTD_BE_MAX_NUM_XPATH_MAP 256
 | 
						|
 | 
						|
/* We would like to have a better ADT than one with O(n)
 | 
						|
   comparisons */
 | 
						|
static struct mgmt_be_xpath_map *mgmt_xpath_map;
 | 
						|
static uint mgmt_num_xpath_maps;
 | 
						|
 | 
						|
static struct event_loop *mgmt_loop;
 | 
						|
static struct msg_server mgmt_be_server = {.fd = -1};
 | 
						|
 | 
						|
static struct mgmt_be_adapters_head mgmt_be_adapters;
 | 
						|
 | 
						|
static struct mgmt_be_client_adapter
 | 
						|
	*mgmt_be_adapters_by_id[MGMTD_BE_CLIENT_ID_MAX];
 | 
						|
 | 
						|
/* Forward declarations */
 | 
						|
static void
 | 
						|
mgmt_be_adapter_sched_init_event(struct mgmt_be_client_adapter *adapter);
 | 
						|
 | 
						|
static uint mgmt_be_get_subscr_for_xpath_and_client(
 | 
						|
	const char *xpath, enum mgmt_be_client_id client_id, uint subscr_mask);
 | 
						|
 | 
						|
static struct mgmt_be_client_adapter *
 | 
						|
mgmt_be_find_adapter_by_fd(int conn_fd)
 | 
						|
{
 | 
						|
	struct mgmt_be_client_adapter *adapter;
 | 
						|
 | 
						|
	FOREACH_ADAPTER_IN_LIST (adapter) {
 | 
						|
		if (adapter->conn->fd == conn_fd)
 | 
						|
			return adapter;
 | 
						|
	}
 | 
						|
 | 
						|
	return NULL;
 | 
						|
}
 | 
						|
 | 
						|
static struct mgmt_be_client_adapter *
 | 
						|
mgmt_be_find_adapter_by_name(const char *name)
 | 
						|
{
 | 
						|
	struct mgmt_be_client_adapter *adapter;
 | 
						|
 | 
						|
	FOREACH_ADAPTER_IN_LIST (adapter) {
 | 
						|
		if (!strncmp(adapter->name, name, sizeof(adapter->name)))
 | 
						|
			return adapter;
 | 
						|
	}
 | 
						|
 | 
						|
	return NULL;
 | 
						|
}
 | 
						|
 | 
						|
static void mgmt_be_xpath_map_init(void)
 | 
						|
{
 | 
						|
	uint i;
 | 
						|
 | 
						|
	MGMTD_BE_ADAPTER_DBG("Init XPath Maps");
 | 
						|
 | 
						|
	mgmt_num_xpath_maps = array_size(mgmt_xpath_map_init);
 | 
						|
	mgmt_xpath_map =
 | 
						|
		calloc(1, sizeof(*mgmt_xpath_map) * mgmt_num_xpath_maps);
 | 
						|
	for (i = 0; i < mgmt_num_xpath_maps; i++) {
 | 
						|
		MGMTD_BE_ADAPTER_DBG(" - XPATH: '%s'",
 | 
						|
				     mgmt_xpath_map_init[i].xpath_regexp);
 | 
						|
		mgmt_xpath_map[i].xpath_regexp = XSTRDUP(
 | 
						|
			MTYPE_MGMTD_XPATH, mgmt_xpath_map_init[i].xpath_regexp);
 | 
						|
		memcpy(mgmt_xpath_map[i].subscr_info,
 | 
						|
		       mgmt_xpath_map_init[i].subscr_info,
 | 
						|
		       sizeof(mgmt_xpath_map_init[i].subscr_info));
 | 
						|
	}
 | 
						|
	MGMTD_BE_ADAPTER_DBG("Total XPath Maps: %u", mgmt_num_xpath_maps);
 | 
						|
}
 | 
						|
 | 
						|
static void mgmt_be_xpath_map_cleanup(void)
 | 
						|
{
 | 
						|
	uint i;
 | 
						|
 | 
						|
	for (i = 0; i < mgmt_num_xpath_maps; i++)
 | 
						|
		XFREE(MTYPE_MGMTD_XPATH, mgmt_xpath_map[i].xpath_regexp);
 | 
						|
	free(mgmt_xpath_map);
 | 
						|
}
 | 
						|
 | 
						|
static int mgmt_be_eval_regexp_match(const char *xpath_regexp,
 | 
						|
				     const char *xpath)
 | 
						|
{
 | 
						|
	int match_len = 0, re_indx = 0, xp_indx = 0;
 | 
						|
	int rexp_len, xpath_len;
 | 
						|
	bool match = true, re_wild = false, xp_wild = false;
 | 
						|
	bool delim = false, enter_wild_match = false;
 | 
						|
	char wild_delim = 0;
 | 
						|
 | 
						|
	rexp_len = strlen(xpath_regexp);
 | 
						|
	xpath_len = strlen(xpath);
 | 
						|
 | 
						|
	/*
 | 
						|
	 * Remove the trailing wildcard from the regexp and Xpath.
 | 
						|
	 */
 | 
						|
	if (rexp_len && xpath_regexp[rexp_len-1] == '*')
 | 
						|
		rexp_len--;
 | 
						|
	if (xpath_len && xpath[xpath_len-1] == '*')
 | 
						|
		xpath_len--;
 | 
						|
 | 
						|
	if (!rexp_len || !xpath_len)
 | 
						|
		return 0;
 | 
						|
 | 
						|
	for (re_indx = 0, xp_indx = 0;
 | 
						|
	     match && re_indx < rexp_len && xp_indx < xpath_len;) {
 | 
						|
		match = (xpath_regexp[re_indx] == xpath[xp_indx]);
 | 
						|
 | 
						|
		/*
 | 
						|
		 * Check if we need to enter wildcard matching.
 | 
						|
		 */
 | 
						|
		if (!enter_wild_match && !match &&
 | 
						|
			(xpath_regexp[re_indx] == '*'
 | 
						|
			 || xpath[xp_indx] == '*')) {
 | 
						|
			/*
 | 
						|
			 * Found wildcard
 | 
						|
			 */
 | 
						|
			enter_wild_match =
 | 
						|
				(xpath_regexp[re_indx-1] == '/'
 | 
						|
				 || xpath_regexp[re_indx-1] == '\''
 | 
						|
				 || xpath[xp_indx-1] == '/'
 | 
						|
				 || xpath[xp_indx-1] == '\'');
 | 
						|
			if (enter_wild_match) {
 | 
						|
				if (xpath_regexp[re_indx] == '*') {
 | 
						|
					/*
 | 
						|
					 * Begin RE wildcard match.
 | 
						|
					 */
 | 
						|
					re_wild = true;
 | 
						|
					wild_delim = xpath_regexp[re_indx-1];
 | 
						|
				} else if (xpath[xp_indx] == '*') {
 | 
						|
					/*
 | 
						|
					 * Begin XP wildcard match.
 | 
						|
					 */
 | 
						|
					xp_wild = true;
 | 
						|
					wild_delim = xpath[xp_indx-1];
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		/*
 | 
						|
		 * Check if we need to exit wildcard matching.
 | 
						|
		 */
 | 
						|
		if (enter_wild_match) {
 | 
						|
			if (re_wild && xpath[xp_indx] == wild_delim) {
 | 
						|
				/*
 | 
						|
				 * End RE wildcard matching.
 | 
						|
				 */
 | 
						|
				re_wild = false;
 | 
						|
				if (re_indx < rexp_len-1)
 | 
						|
					re_indx++;
 | 
						|
				enter_wild_match = false;
 | 
						|
			} else if (xp_wild
 | 
						|
				   && xpath_regexp[re_indx] == wild_delim) {
 | 
						|
				/*
 | 
						|
				 * End XP wildcard matching.
 | 
						|
				 */
 | 
						|
				xp_wild = false;
 | 
						|
				if (xp_indx < xpath_len-1)
 | 
						|
					xp_indx++;
 | 
						|
				enter_wild_match = false;
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		match = (xp_wild || re_wild
 | 
						|
			 || xpath_regexp[re_indx] == xpath[xp_indx]);
 | 
						|
 | 
						|
		/*
 | 
						|
		 * Check if we found a delimiter in both the Xpaths
 | 
						|
		 */
 | 
						|
		if ((xpath_regexp[re_indx] == '/'
 | 
						|
			&& xpath[xp_indx] == '/')
 | 
						|
			|| (xpath_regexp[re_indx] == ']'
 | 
						|
				&& xpath[xp_indx] == ']')
 | 
						|
			|| (xpath_regexp[re_indx] == '['
 | 
						|
				&& xpath[xp_indx] == '[')) {
 | 
						|
			/*
 | 
						|
			 * Increment the match count if we have a
 | 
						|
			 * new delimiter.
 | 
						|
			 */
 | 
						|
			if (match && re_indx && xp_indx && !delim)
 | 
						|
				match_len++;
 | 
						|
			delim = true;
 | 
						|
		} else {
 | 
						|
			delim = false;
 | 
						|
		}
 | 
						|
 | 
						|
		/*
 | 
						|
		 * Proceed to the next character in the RE/XP string as
 | 
						|
		 * necessary.
 | 
						|
		 */
 | 
						|
		if (!re_wild)
 | 
						|
			re_indx++;
 | 
						|
		if (!xp_wild)
 | 
						|
			xp_indx++;
 | 
						|
	}
 | 
						|
 | 
						|
	/*
 | 
						|
	 * If we finished matching and the last token was a full match
 | 
						|
	 * increment the match count appropriately.
 | 
						|
	 */
 | 
						|
	if (match && !delim &&
 | 
						|
		(xpath_regexp[re_indx] == '/'
 | 
						|
		 || xpath_regexp[re_indx] == ']'))
 | 
						|
		match_len++;
 | 
						|
 | 
						|
	return match_len;
 | 
						|
}
 | 
						|
 | 
						|
static void mgmt_be_adapter_delete(struct mgmt_be_client_adapter *adapter)
 | 
						|
{
 | 
						|
	MGMTD_BE_ADAPTER_DBG("deleting client adapter '%s'", adapter->name);
 | 
						|
 | 
						|
	/*
 | 
						|
	 * Notify about disconnect for appropriate cleanup
 | 
						|
	 */
 | 
						|
	mgmt_txn_notify_be_adapter_conn(adapter, false);
 | 
						|
	if (adapter->id < MGMTD_BE_CLIENT_ID_MAX) {
 | 
						|
		mgmt_be_adapters_by_id[adapter->id] = NULL;
 | 
						|
		adapter->id = MGMTD_BE_CLIENT_ID_MAX;
 | 
						|
	}
 | 
						|
 | 
						|
	assert(adapter->refcount == 1);
 | 
						|
	mgmt_be_adapter_unlock(&adapter);
 | 
						|
}
 | 
						|
 | 
						|
static int mgmt_be_adapter_notify_disconnect(struct msg_conn *conn)
 | 
						|
{
 | 
						|
	struct mgmt_be_client_adapter *adapter = conn->user;
 | 
						|
 | 
						|
	MGMTD_BE_ADAPTER_DBG("notify disconnect for client adapter '%s'",
 | 
						|
			     adapter->name);
 | 
						|
 | 
						|
	mgmt_be_adapter_delete(adapter);
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
mgmt_be_adapter_cleanup_old_conn(struct mgmt_be_client_adapter *adapter)
 | 
						|
{
 | 
						|
	struct mgmt_be_client_adapter *old;
 | 
						|
 | 
						|
	FOREACH_ADAPTER_IN_LIST (old) {
 | 
						|
		if (old != adapter &&
 | 
						|
		    !strncmp(adapter->name, old->name, sizeof(adapter->name))) {
 | 
						|
			/*
 | 
						|
			 * We have a Zombie lingering around
 | 
						|
			 */
 | 
						|
			MGMTD_BE_ADAPTER_DBG(
 | 
						|
				"Client '%s' (FD:%d) seems to have reconnected. Removing old connection (FD:%d)!",
 | 
						|
				adapter->name, adapter->conn->fd,
 | 
						|
				old->conn->fd);
 | 
						|
			/* this will/should delete old */
 | 
						|
			msg_conn_disconnect(old->conn, false);
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static int mgmt_be_adapter_send_msg(struct mgmt_be_client_adapter *adapter,
 | 
						|
				    Mgmtd__BeMessage *be_msg)
 | 
						|
{
 | 
						|
	return msg_conn_send_msg(
 | 
						|
		adapter->conn, MGMT_MSG_VERSION_PROTOBUF, be_msg,
 | 
						|
		mgmtd__be_message__get_packed_size(be_msg),
 | 
						|
		(size_t(*)(void *, void *))mgmtd__be_message__pack, false);
 | 
						|
}
 | 
						|
 | 
						|
static int mgmt_be_send_subscr_reply(struct mgmt_be_client_adapter *adapter,
 | 
						|
				     bool success)
 | 
						|
{
 | 
						|
	Mgmtd__BeMessage be_msg;
 | 
						|
	Mgmtd__BeSubscribeReply reply;
 | 
						|
 | 
						|
	mgmtd__be_subscribe_reply__init(&reply);
 | 
						|
	reply.success = success;
 | 
						|
 | 
						|
	mgmtd__be_message__init(&be_msg);
 | 
						|
	be_msg.message_case = MGMTD__BE_MESSAGE__MESSAGE_SUBSCR_REPLY;
 | 
						|
	be_msg.subscr_reply = &reply;
 | 
						|
 | 
						|
	MGMTD_FE_CLIENT_DBG("Sending SUBSCR_REPLY client: %s sucess: %u",
 | 
						|
			    adapter->name, success);
 | 
						|
 | 
						|
	return mgmt_be_adapter_send_msg(adapter, &be_msg);
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
mgmt_be_adapter_handle_msg(struct mgmt_be_client_adapter *adapter,
 | 
						|
			      Mgmtd__BeMessage *be_msg)
 | 
						|
{
 | 
						|
	/*
 | 
						|
	 * protobuf-c adds a max size enum with an internal, and changing by
 | 
						|
	 * version, name; cast to an int to avoid unhandled enum warnings
 | 
						|
	 */
 | 
						|
	switch ((int)be_msg->message_case) {
 | 
						|
	case MGMTD__BE_MESSAGE__MESSAGE_SUBSCR_REQ:
 | 
						|
		MGMTD_BE_ADAPTER_DBG(
 | 
						|
			"Got SUBSCR_REQ from '%s' to %sregister %zu xpaths",
 | 
						|
			be_msg->subscr_req->client_name,
 | 
						|
			!be_msg->subscr_req->subscribe_xpaths &&
 | 
						|
					be_msg->subscr_req->n_xpath_reg
 | 
						|
				? "de"
 | 
						|
				: "",
 | 
						|
			be_msg->subscr_req->n_xpath_reg);
 | 
						|
 | 
						|
		if (strlen(be_msg->subscr_req->client_name)) {
 | 
						|
			strlcpy(adapter->name, be_msg->subscr_req->client_name,
 | 
						|
				sizeof(adapter->name));
 | 
						|
			adapter->id = mgmt_be_client_name2id(adapter->name);
 | 
						|
			if (adapter->id >= MGMTD_BE_CLIENT_ID_MAX) {
 | 
						|
				MGMTD_BE_ADAPTER_ERR(
 | 
						|
					"Unable to resolve adapter '%s' to a valid ID. Disconnecting!",
 | 
						|
					adapter->name);
 | 
						|
				/* this will/should delete old */
 | 
						|
				msg_conn_disconnect(adapter->conn, false);
 | 
						|
				zlog_err("XXX different from original code");
 | 
						|
				break;
 | 
						|
			}
 | 
						|
			mgmt_be_adapters_by_id[adapter->id] = adapter;
 | 
						|
			mgmt_be_adapter_cleanup_old_conn(adapter);
 | 
						|
 | 
						|
			/* schedule INIT sequence now that it is registered */
 | 
						|
			mgmt_be_adapter_sched_init_event(adapter);
 | 
						|
		}
 | 
						|
 | 
						|
		if (be_msg->subscr_req->n_xpath_reg)
 | 
						|
			/* we aren't handling dynamic xpaths yet */
 | 
						|
			mgmt_be_send_subscr_reply(adapter, false);
 | 
						|
		else
 | 
						|
			mgmt_be_send_subscr_reply(adapter, true);
 | 
						|
		break;
 | 
						|
	case MGMTD__BE_MESSAGE__MESSAGE_TXN_REPLY:
 | 
						|
		MGMTD_BE_ADAPTER_DBG(
 | 
						|
			"Got %s TXN_REPLY from '%s' txn-id %" PRIx64
 | 
						|
			" with '%s'",
 | 
						|
			be_msg->txn_reply->create ? "Create" : "Delete",
 | 
						|
			adapter->name, be_msg->txn_reply->txn_id,
 | 
						|
			be_msg->txn_reply->success ? "success" : "failure");
 | 
						|
		/*
 | 
						|
		 * Forward the TXN_REPLY to txn module.
 | 
						|
		 */
 | 
						|
		mgmt_txn_notify_be_txn_reply(
 | 
						|
			be_msg->txn_reply->txn_id,
 | 
						|
			be_msg->txn_reply->create,
 | 
						|
			be_msg->txn_reply->success, adapter);
 | 
						|
		break;
 | 
						|
	case MGMTD__BE_MESSAGE__MESSAGE_CFG_DATA_REPLY:
 | 
						|
		MGMTD_BE_ADAPTER_DBG(
 | 
						|
			"Got CFGDATA_REPLY from '%s' txn-id %" PRIx64
 | 
						|
			" batch-id %" PRIu64 " err:'%s'",
 | 
						|
			adapter->name, be_msg->cfg_data_reply->txn_id,
 | 
						|
			be_msg->cfg_data_reply->batch_id,
 | 
						|
			be_msg->cfg_data_reply->error_if_any
 | 
						|
				? be_msg->cfg_data_reply->error_if_any
 | 
						|
				: "None");
 | 
						|
		/*
 | 
						|
		 * Forward the CGFData-create reply to txn module.
 | 
						|
		 */
 | 
						|
		mgmt_txn_notify_be_cfgdata_reply(
 | 
						|
			be_msg->cfg_data_reply->txn_id,
 | 
						|
			be_msg->cfg_data_reply->batch_id,
 | 
						|
			be_msg->cfg_data_reply->success,
 | 
						|
			be_msg->cfg_data_reply->error_if_any, adapter);
 | 
						|
		break;
 | 
						|
	case MGMTD__BE_MESSAGE__MESSAGE_CFG_APPLY_REPLY:
 | 
						|
		MGMTD_BE_ADAPTER_DBG(
 | 
						|
			"Got %s CFG_APPLY_REPLY from '%s' txn-id %" PRIx64
 | 
						|
			" for %zu batches id %" PRIu64 "-%" PRIu64 " err:'%s'",
 | 
						|
			be_msg->cfg_apply_reply->success ? "successful"
 | 
						|
							 : "failed",
 | 
						|
			adapter->name, be_msg->cfg_apply_reply->txn_id,
 | 
						|
			be_msg->cfg_apply_reply->n_batch_ids,
 | 
						|
			be_msg->cfg_apply_reply->batch_ids[0],
 | 
						|
			be_msg->cfg_apply_reply->batch_ids
 | 
						|
				[be_msg->cfg_apply_reply->n_batch_ids - 1],
 | 
						|
			be_msg->cfg_apply_reply->error_if_any
 | 
						|
				? be_msg->cfg_apply_reply->error_if_any
 | 
						|
				: "None");
 | 
						|
		/*
 | 
						|
		 * Forward the CGFData-apply reply to txn module.
 | 
						|
		 */
 | 
						|
		mgmt_txn_notify_be_cfg_apply_reply(
 | 
						|
			be_msg->cfg_apply_reply->txn_id,
 | 
						|
			be_msg->cfg_apply_reply->success,
 | 
						|
			(uint64_t *)be_msg->cfg_apply_reply->batch_ids,
 | 
						|
			be_msg->cfg_apply_reply->n_batch_ids,
 | 
						|
			be_msg->cfg_apply_reply->error_if_any, adapter);
 | 
						|
		break;
 | 
						|
	case MGMTD__BE_MESSAGE__MESSAGE_GET_REPLY:
 | 
						|
	case MGMTD__BE_MESSAGE__MESSAGE_CFG_CMD_REPLY:
 | 
						|
	case MGMTD__BE_MESSAGE__MESSAGE_SHOW_CMD_REPLY:
 | 
						|
	case MGMTD__BE_MESSAGE__MESSAGE_NOTIFY_DATA:
 | 
						|
		/*
 | 
						|
		 * TODO: Add handling code in future.
 | 
						|
		 */
 | 
						|
		break;
 | 
						|
	/*
 | 
						|
	 * NOTE: The following messages are always sent from MGMTD to
 | 
						|
	 * Backend clients only and/or need not be handled on MGMTd.
 | 
						|
	 */
 | 
						|
	case MGMTD__BE_MESSAGE__MESSAGE_SUBSCR_REPLY:
 | 
						|
	case MGMTD__BE_MESSAGE__MESSAGE_GET_REQ:
 | 
						|
	case MGMTD__BE_MESSAGE__MESSAGE_TXN_REQ:
 | 
						|
	case MGMTD__BE_MESSAGE__MESSAGE_CFG_DATA_REQ:
 | 
						|
	case MGMTD__BE_MESSAGE__MESSAGE_CFG_APPLY_REQ:
 | 
						|
	case MGMTD__BE_MESSAGE__MESSAGE_CFG_CMD_REQ:
 | 
						|
	case MGMTD__BE_MESSAGE__MESSAGE_SHOW_CMD_REQ:
 | 
						|
	case MGMTD__BE_MESSAGE__MESSAGE__NOT_SET:
 | 
						|
	default:
 | 
						|
		/*
 | 
						|
		 * A 'default' case is being added contrary to the
 | 
						|
		 * FRR code guidelines to take care of build
 | 
						|
		 * failures on certain build systems (courtesy of
 | 
						|
		 * the proto-c package).
 | 
						|
		 */
 | 
						|
		break;
 | 
						|
	}
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
int mgmt_be_send_txn_req(struct mgmt_be_client_adapter *adapter,
 | 
						|
			 uint64_t txn_id, bool create)
 | 
						|
{
 | 
						|
	Mgmtd__BeMessage be_msg;
 | 
						|
	Mgmtd__BeTxnReq txn_req;
 | 
						|
 | 
						|
	mgmtd__be_txn_req__init(&txn_req);
 | 
						|
	txn_req.create = create;
 | 
						|
	txn_req.txn_id = txn_id;
 | 
						|
 | 
						|
	mgmtd__be_message__init(&be_msg);
 | 
						|
	be_msg.message_case = MGMTD__BE_MESSAGE__MESSAGE_TXN_REQ;
 | 
						|
	be_msg.txn_req = &txn_req;
 | 
						|
 | 
						|
	MGMTD_BE_ADAPTER_DBG("Sending TXN_REQ to '%s' txn-id: %" PRIu64,
 | 
						|
			     adapter->name, txn_id);
 | 
						|
 | 
						|
	return mgmt_be_adapter_send_msg(adapter, &be_msg);
 | 
						|
}
 | 
						|
 | 
						|
int mgmt_be_send_cfgdata_req(struct mgmt_be_client_adapter *adapter,
 | 
						|
			     uint64_t txn_id, uint64_t batch_id,
 | 
						|
			     Mgmtd__YangCfgDataReq **cfgdata_reqs,
 | 
						|
			     size_t num_reqs, bool end_of_data)
 | 
						|
{
 | 
						|
	Mgmtd__BeMessage be_msg;
 | 
						|
	Mgmtd__BeCfgDataCreateReq cfgdata_req;
 | 
						|
 | 
						|
	mgmtd__be_cfg_data_create_req__init(&cfgdata_req);
 | 
						|
	cfgdata_req.batch_id = batch_id;
 | 
						|
	cfgdata_req.txn_id = txn_id;
 | 
						|
	cfgdata_req.data_req = cfgdata_reqs;
 | 
						|
	cfgdata_req.n_data_req = num_reqs;
 | 
						|
	cfgdata_req.end_of_data = end_of_data;
 | 
						|
 | 
						|
	mgmtd__be_message__init(&be_msg);
 | 
						|
	be_msg.message_case = MGMTD__BE_MESSAGE__MESSAGE_CFG_DATA_REQ;
 | 
						|
	be_msg.cfg_data_req = &cfgdata_req;
 | 
						|
 | 
						|
	MGMTD_BE_ADAPTER_DBG(
 | 
						|
		"Sending CFGDATA_CREATE_REQ to '%s' txn-id: %" PRIu64
 | 
						|
		" batch-id: %" PRIu64,
 | 
						|
		adapter->name, txn_id, batch_id);
 | 
						|
 | 
						|
	return mgmt_be_adapter_send_msg(adapter, &be_msg);
 | 
						|
}
 | 
						|
 | 
						|
int mgmt_be_send_cfgapply_req(struct mgmt_be_client_adapter *adapter,
 | 
						|
			      uint64_t txn_id)
 | 
						|
{
 | 
						|
	Mgmtd__BeMessage be_msg;
 | 
						|
	Mgmtd__BeCfgDataApplyReq apply_req;
 | 
						|
 | 
						|
	mgmtd__be_cfg_data_apply_req__init(&apply_req);
 | 
						|
	apply_req.txn_id = txn_id;
 | 
						|
 | 
						|
	mgmtd__be_message__init(&be_msg);
 | 
						|
	be_msg.message_case = MGMTD__BE_MESSAGE__MESSAGE_CFG_APPLY_REQ;
 | 
						|
	be_msg.cfg_apply_req = &apply_req;
 | 
						|
 | 
						|
	MGMTD_BE_ADAPTER_DBG("Sending CFG_APPLY_REQ to '%s' txn-id: %" PRIu64,
 | 
						|
			     adapter->name, txn_id);
 | 
						|
 | 
						|
	return mgmt_be_adapter_send_msg(adapter, &be_msg);
 | 
						|
}
 | 
						|
 | 
						|
static void mgmt_be_adapter_process_msg(uint8_t version, uint8_t *data,
 | 
						|
					size_t len, struct msg_conn *conn)
 | 
						|
{
 | 
						|
	struct mgmt_be_client_adapter *adapter = conn->user;
 | 
						|
	Mgmtd__BeMessage *be_msg = mgmtd__be_message__unpack(NULL, len, data);
 | 
						|
 | 
						|
	if (!be_msg) {
 | 
						|
		MGMTD_BE_ADAPTER_DBG(
 | 
						|
			"Failed to decode %zu bytes for adapter: %s", len,
 | 
						|
			adapter->name);
 | 
						|
		return;
 | 
						|
	}
 | 
						|
	MGMTD_BE_ADAPTER_DBG("Decoded %zu bytes of message: %u for adapter: %s",
 | 
						|
			     len, be_msg->message_case, adapter->name);
 | 
						|
	(void)mgmt_be_adapter_handle_msg(adapter, be_msg);
 | 
						|
	mgmtd__be_message__free_unpacked(be_msg, NULL);
 | 
						|
}
 | 
						|
 | 
						|
static void mgmt_be_iter_and_get_cfg(const char *xpath, struct lyd_node *node,
 | 
						|
				     struct nb_node *nb_node, void *ctx)
 | 
						|
{
 | 
						|
	struct mgmt_be_get_adapter_config_params *parms = ctx;
 | 
						|
	struct mgmt_be_client_adapter *adapter = parms->adapter;
 | 
						|
	uint subscr;
 | 
						|
 | 
						|
	subscr = mgmt_be_get_subscr_for_xpath_and_client(
 | 
						|
		xpath, adapter->id, MGMT_SUBSCR_NOTIFY_CFG);
 | 
						|
	if (subscr)
 | 
						|
		nb_config_diff_created(node, &parms->seq, parms->cfg_chgs);
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Initialize a BE client over a new connection
 | 
						|
 */
 | 
						|
static void mgmt_be_adapter_conn_init(struct event *thread)
 | 
						|
{
 | 
						|
	struct mgmt_be_client_adapter *adapter;
 | 
						|
 | 
						|
	adapter = (struct mgmt_be_client_adapter *)EVENT_ARG(thread);
 | 
						|
	assert(adapter && adapter->conn->fd >= 0);
 | 
						|
 | 
						|
	/*
 | 
						|
	 * Check first if the current session can run a CONFIG
 | 
						|
	 * transaction or not. Reschedule if a CONFIG transaction
 | 
						|
	 * from another session is already in progress.
 | 
						|
	 */
 | 
						|
	if (mgmt_config_txn_in_progress() != MGMTD_SESSION_ID_NONE) {
 | 
						|
		zlog_err("XXX txn in progress, retry init");
 | 
						|
		mgmt_be_adapter_sched_init_event(adapter);
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	/*
 | 
						|
	 * Notify TXN module to create a CONFIG transaction and
 | 
						|
	 * download the CONFIGs identified for this new client.
 | 
						|
	 * If the TXN module fails to initiate the CONFIG transaction
 | 
						|
	 * disconnect from the client forcing a reconnect later.
 | 
						|
	 * That should also take care of destroying the adapter.
 | 
						|
	 */
 | 
						|
	if (mgmt_txn_notify_be_adapter_conn(adapter, true) != 0) {
 | 
						|
		zlog_err("XXX notify be adapter conn fail");
 | 
						|
		msg_conn_disconnect(adapter->conn, false);
 | 
						|
		adapter = NULL;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Schedule the initialization of the BE client connection.
 | 
						|
 */
 | 
						|
static void
 | 
						|
mgmt_be_adapter_sched_init_event(struct mgmt_be_client_adapter *adapter)
 | 
						|
{
 | 
						|
	event_add_timer_msec(mgmt_loop, mgmt_be_adapter_conn_init, adapter,
 | 
						|
			     MGMTD_BE_CONN_INIT_DELAY_MSEC,
 | 
						|
			     &adapter->conn_init_ev);
 | 
						|
}
 | 
						|
 | 
						|
void mgmt_be_adapter_lock(struct mgmt_be_client_adapter *adapter)
 | 
						|
{
 | 
						|
	adapter->refcount++;
 | 
						|
}
 | 
						|
 | 
						|
extern void mgmt_be_adapter_unlock(struct mgmt_be_client_adapter **adapter)
 | 
						|
{
 | 
						|
	struct mgmt_be_client_adapter *a = *adapter;
 | 
						|
	assert(a && a->refcount);
 | 
						|
 | 
						|
	if (!--a->refcount) {
 | 
						|
		mgmt_be_adapters_del(&mgmt_be_adapters, a);
 | 
						|
		EVENT_OFF(a->conn_init_ev);
 | 
						|
		msg_server_conn_delete(a->conn);
 | 
						|
		XFREE(MTYPE_MGMTD_BE_ADPATER, a);
 | 
						|
	}
 | 
						|
 | 
						|
	*adapter = NULL;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Initialize the BE adapter module
 | 
						|
 */
 | 
						|
void mgmt_be_adapter_init(struct event_loop *tm)
 | 
						|
{
 | 
						|
	assert(!mgmt_loop);
 | 
						|
	mgmt_loop = tm;
 | 
						|
 | 
						|
	mgmt_be_adapters_init(&mgmt_be_adapters);
 | 
						|
	mgmt_be_xpath_map_init();
 | 
						|
 | 
						|
	if (msg_server_init(&mgmt_be_server, MGMTD_BE_SERVER_PATH, tm,
 | 
						|
			    mgmt_be_create_adapter, "backend",
 | 
						|
			    &mgmt_debug_be)) {
 | 
						|
		zlog_err("cannot initialize backend server");
 | 
						|
		exit(1);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Destroy the BE adapter module
 | 
						|
 */
 | 
						|
void mgmt_be_adapter_destroy(void)
 | 
						|
{
 | 
						|
	struct mgmt_be_client_adapter *adapter;
 | 
						|
 | 
						|
	msg_server_cleanup(&mgmt_be_server);
 | 
						|
	FOREACH_ADAPTER_IN_LIST (adapter) {
 | 
						|
		mgmt_be_adapter_delete(adapter);
 | 
						|
	}
 | 
						|
	mgmt_be_xpath_map_cleanup();
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * The server accepted a new connection
 | 
						|
 */
 | 
						|
struct msg_conn *mgmt_be_create_adapter(int conn_fd, union sockunion *from)
 | 
						|
{
 | 
						|
	struct mgmt_be_client_adapter *adapter = NULL;
 | 
						|
 | 
						|
	assert(!mgmt_be_find_adapter_by_fd(conn_fd));
 | 
						|
 | 
						|
	adapter = XCALLOC(MTYPE_MGMTD_BE_ADPATER,
 | 
						|
			  sizeof(struct mgmt_be_client_adapter));
 | 
						|
	adapter->id = MGMTD_BE_CLIENT_ID_MAX;
 | 
						|
	snprintf(adapter->name, sizeof(adapter->name), "Unknown-FD-%d",
 | 
						|
		 conn_fd);
 | 
						|
 | 
						|
	mgmt_be_adapter_lock(adapter);
 | 
						|
	mgmt_be_adapters_add_tail(&mgmt_be_adapters, adapter);
 | 
						|
	RB_INIT(nb_config_cbs, &adapter->cfg_chgs);
 | 
						|
 | 
						|
	adapter->conn = msg_server_conn_create(
 | 
						|
		mgmt_loop, conn_fd, mgmt_be_adapter_notify_disconnect,
 | 
						|
		mgmt_be_adapter_process_msg, MGMTD_BE_MAX_NUM_MSG_PROC,
 | 
						|
		MGMTD_BE_MAX_NUM_MSG_WRITE, MGMTD_BE_MSG_MAX_LEN, adapter,
 | 
						|
		"BE-adapter");
 | 
						|
 | 
						|
	MGMTD_BE_ADAPTER_DBG("Added new MGMTD Backend adapter '%s'",
 | 
						|
			     adapter->name);
 | 
						|
 | 
						|
	return adapter->conn;
 | 
						|
}
 | 
						|
 | 
						|
struct mgmt_be_client_adapter *
 | 
						|
mgmt_be_get_adapter_by_id(enum mgmt_be_client_id id)
 | 
						|
{
 | 
						|
	return (id < MGMTD_BE_CLIENT_ID_MAX ? mgmt_be_adapters_by_id[id]
 | 
						|
					       : NULL);
 | 
						|
}
 | 
						|
 | 
						|
struct mgmt_be_client_adapter *
 | 
						|
mgmt_be_get_adapter_by_name(const char *name)
 | 
						|
{
 | 
						|
	return mgmt_be_find_adapter_by_name(name);
 | 
						|
}
 | 
						|
 | 
						|
int mgmt_be_get_adapter_config(struct mgmt_be_client_adapter *adapter,
 | 
						|
			       struct nb_config_cbs **cfg_chgs)
 | 
						|
{
 | 
						|
	struct mgmt_be_get_adapter_config_params parms;
 | 
						|
	struct nb_config *cfg_root = mgmt_ds_get_nb_config(mm->running_ds);
 | 
						|
 | 
						|
	assert(cfg_chgs);
 | 
						|
 | 
						|
	/*
 | 
						|
	 * TODO: we should consider making this an assertable condition and
 | 
						|
	 * guaranteeing it be true when this function is called. B/c what is
 | 
						|
	 * going to happen if there are some changes being sent, and we don't
 | 
						|
	 * gather a new snapshot, what new changes that came after the previous
 | 
						|
	 * snapshot will then be lost?
 | 
						|
	 */
 | 
						|
	if (RB_EMPTY(nb_config_cbs, &adapter->cfg_chgs)) {
 | 
						|
		parms.adapter = adapter;
 | 
						|
		parms.cfg_chgs = &adapter->cfg_chgs;
 | 
						|
		parms.seq = 0;
 | 
						|
 | 
						|
		mgmt_ds_iter_data(MGMTD_DS_RUNNING, cfg_root, "",
 | 
						|
				  mgmt_be_iter_and_get_cfg, (void *)&parms);
 | 
						|
	}
 | 
						|
 | 
						|
	*cfg_chgs = &adapter->cfg_chgs;
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
void mgmt_be_get_subscr_info_for_xpath(
 | 
						|
	const char *xpath, struct mgmt_be_client_subscr_info *subscr_info)
 | 
						|
{
 | 
						|
	enum mgmt_be_client_id id;
 | 
						|
	uint i;
 | 
						|
 | 
						|
	memset(subscr_info, 0, sizeof(*subscr_info));
 | 
						|
 | 
						|
	MGMTD_BE_ADAPTER_DBG("XPATH: '%s'", xpath);
 | 
						|
	for (i = 0; i < mgmt_num_xpath_maps; i++) {
 | 
						|
		if (!mgmt_be_eval_regexp_match(mgmt_xpath_map[i].xpath_regexp,
 | 
						|
					       xpath))
 | 
						|
			continue;
 | 
						|
		FOREACH_MGMTD_BE_CLIENT_ID (id) {
 | 
						|
			subscr_info->xpath_subscr[id] |=
 | 
						|
				mgmt_xpath_map[i].subscr_info[id];
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if (DEBUG_MODE_CHECK(&mgmt_debug_be, DEBUG_MODE_ALL)) {
 | 
						|
		FOREACH_MGMTD_BE_CLIENT_ID (id) {
 | 
						|
			if (!subscr_info->xpath_subscr[id])
 | 
						|
				continue;
 | 
						|
			MGMTD_BE_ADAPTER_DBG("Cient: %s: subscribed: 0x%x",
 | 
						|
					     mgmt_be_client_id2name(id),
 | 
						|
					     subscr_info->xpath_subscr[id]);
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Return the subscription info bits for a given `xpath` for a given
 | 
						|
 * `client_id`.
 | 
						|
 *
 | 
						|
 * Args:
 | 
						|
 *     xpath - the xpath to check for subscription information.
 | 
						|
 *     client_id - the BE client being checked for.
 | 
						|
 *     subscr_mask - The subscr bits the caller is interested in seeing
 | 
						|
 * if set.
 | 
						|
 *
 | 
						|
 * Returns:
 | 
						|
 *     The subscription info bits.
 | 
						|
 */
 | 
						|
static uint mgmt_be_get_subscr_for_xpath_and_client(
 | 
						|
	const char *xpath, enum mgmt_be_client_id client_id, uint subscr_mask)
 | 
						|
{
 | 
						|
	struct mgmt_be_client_xpath_map *map;
 | 
						|
	uint subscr = 0;
 | 
						|
	uint i;
 | 
						|
 | 
						|
	assert(client_id < MGMTD_BE_CLIENT_ID_MAX);
 | 
						|
 | 
						|
	MGMTD_BE_ADAPTER_DBG("Checking client: %s for xpath: '%s'",
 | 
						|
			     mgmt_be_client_id2name(client_id), xpath);
 | 
						|
 | 
						|
	map = &mgmt_client_xpaths[client_id];
 | 
						|
	for (i = 0; i < map->nxpaths; i++) {
 | 
						|
		if (!mgmt_be_eval_regexp_match(map->xpaths[i].xpath, xpath))
 | 
						|
			continue;
 | 
						|
		MGMTD_BE_ADAPTER_DBG("xpath: %s: matched: %s",
 | 
						|
				     map->xpaths[i].xpath, xpath);
 | 
						|
		subscr |= map->xpaths[i].subscribed;
 | 
						|
		if ((subscr & subscr_mask) == subscr_mask)
 | 
						|
			break;
 | 
						|
	}
 | 
						|
	MGMTD_BE_ADAPTER_DBG("client: %s: subscribed: 0x%x",
 | 
						|
			     mgmt_be_client_id2name(client_id), subscr);
 | 
						|
	return subscr;
 | 
						|
}
 | 
						|
 | 
						|
void mgmt_be_adapter_status_write(struct vty *vty)
 | 
						|
{
 | 
						|
	struct mgmt_be_client_adapter *adapter;
 | 
						|
 | 
						|
	vty_out(vty, "MGMTD Backend Adapters\n");
 | 
						|
 | 
						|
	FOREACH_ADAPTER_IN_LIST (adapter) {
 | 
						|
		vty_out(vty, "  Client: \t\t\t%s\n", adapter->name);
 | 
						|
		vty_out(vty, "    Conn-FD: \t\t\t%d\n", adapter->conn->fd);
 | 
						|
		vty_out(vty, "    Client-Id: \t\t\t%d\n", adapter->id);
 | 
						|
		vty_out(vty, "    Ref-Count: \t\t\t%u\n", adapter->refcount);
 | 
						|
		vty_out(vty, "    Msg-Recvd: \t\t\t%" PRIu64 "\n",
 | 
						|
			adapter->conn->mstate.nrxm);
 | 
						|
		vty_out(vty, "    Bytes-Recvd: \t\t%" PRIu64 "\n",
 | 
						|
			adapter->conn->mstate.nrxb);
 | 
						|
		vty_out(vty, "    Msg-Sent: \t\t\t%" PRIu64 "\n",
 | 
						|
			adapter->conn->mstate.ntxm);
 | 
						|
		vty_out(vty, "    Bytes-Sent: \t\t%" PRIu64 "\n",
 | 
						|
			adapter->conn->mstate.ntxb);
 | 
						|
	}
 | 
						|
	vty_out(vty, "  Total: %d\n",
 | 
						|
		(int)mgmt_be_adapters_count(&mgmt_be_adapters));
 | 
						|
}
 | 
						|
 | 
						|
void mgmt_be_xpath_register_write(struct vty *vty)
 | 
						|
{
 | 
						|
	uint indx;
 | 
						|
	enum mgmt_be_client_id id;
 | 
						|
	struct mgmt_be_client_adapter *adapter;
 | 
						|
	uint info;
 | 
						|
 | 
						|
	vty_out(vty, "MGMTD Backend XPath Registry\n");
 | 
						|
 | 
						|
	for (indx = 0; indx < mgmt_num_xpath_maps; indx++) {
 | 
						|
		vty_out(vty, " - XPATH: '%s'\n",
 | 
						|
			mgmt_xpath_map[indx].xpath_regexp);
 | 
						|
		FOREACH_MGMTD_BE_CLIENT_ID (id) {
 | 
						|
			info = mgmt_xpath_map[indx].subscr_info[id];
 | 
						|
			if (!info)
 | 
						|
				continue;
 | 
						|
			vty_out(vty,
 | 
						|
				"   -- Client: '%s'\tValidate:%d, Notify:%d, Own:%d\n",
 | 
						|
				mgmt_be_client_id2name(id),
 | 
						|
				(info & MGMT_SUBSCR_VALIDATE_CFG) != 0,
 | 
						|
				(info & MGMT_SUBSCR_NOTIFY_CFG) != 0,
 | 
						|
				(info & MGMT_SUBSCR_OPER_OWN) != 0);
 | 
						|
			adapter = mgmt_be_get_adapter_by_id(id);
 | 
						|
			if (adapter)
 | 
						|
				vty_out(vty, "     -- Adapter: %p\n", adapter);
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	vty_out(vty, "Total XPath Registries: %u\n", mgmt_num_xpath_maps);
 | 
						|
}
 | 
						|
 | 
						|
void mgmt_be_xpath_subscr_info_write(struct vty *vty, const char *xpath)
 | 
						|
{
 | 
						|
	struct mgmt_be_client_subscr_info subscr;
 | 
						|
	enum mgmt_be_client_id id;
 | 
						|
	struct mgmt_be_client_adapter *adapter;
 | 
						|
	uint info;
 | 
						|
 | 
						|
	mgmt_be_get_subscr_info_for_xpath(xpath, &subscr);
 | 
						|
 | 
						|
	vty_out(vty, "XPath: '%s'\n", xpath);
 | 
						|
	FOREACH_MGMTD_BE_CLIENT_ID (id) {
 | 
						|
		info = subscr.xpath_subscr[id];
 | 
						|
		if (!info)
 | 
						|
			continue;
 | 
						|
		vty_out(vty,
 | 
						|
			"  -- Client: '%s'\tValidate:%d, Notify:%d, Own:%d\n",
 | 
						|
			mgmt_be_client_id2name(id),
 | 
						|
			(info & MGMT_SUBSCR_VALIDATE_CFG) != 0,
 | 
						|
			(info & MGMT_SUBSCR_NOTIFY_CFG) != 0,
 | 
						|
			(info & MGMT_SUBSCR_OPER_OWN) != 0);
 | 
						|
		adapter = mgmt_be_get_adapter_by_id(id);
 | 
						|
		if (adapter)
 | 
						|
			vty_out(vty, "    -- Adapter: %p\n", adapter);
 | 
						|
	}
 | 
						|
}
 |