summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/agentx.c42
-rw-r--r--lib/bfd.c30
-rw-r--r--lib/bfd.h5
-rw-r--r--lib/clippy.c48
-rw-r--r--lib/command.c8
-rw-r--r--lib/command.h5
-rw-r--r--lib/compiler.h8
-rw-r--r--lib/cspf.c2
-rw-r--r--lib/cspf.h2
-rw-r--r--lib/defun_lex.l19
-rw-r--r--lib/distribute.c15
-rw-r--r--lib/event.c (renamed from lib/thread.c)613
-rw-r--r--lib/ferr.c4
-rw-r--r--lib/filter_cli.c8
-rw-r--r--lib/filter_nb.c75
-rw-r--r--lib/frr_pthread.c20
-rw-r--r--lib/frr_pthread.h4
-rw-r--r--lib/frr_zmq.c77
-rw-r--r--lib/frr_zmq.h53
-rw-r--r--lib/frrevent.h295
-rw-r--r--lib/frrscript.c6
-rw-r--r--lib/getopt.c1011
-rw-r--r--lib/getopt.h143
-rw-r--r--lib/getopt1.c163
-rw-r--r--lib/grammar_sandbox_main.c10
-rw-r--r--lib/hash.c10
-rw-r--r--lib/hash.h11
-rw-r--r--lib/if_rmap.c17
-rw-r--r--lib/iso.c144
-rw-r--r--lib/iso.h49
-rw-r--r--lib/ldp_sync.c4
-rw-r--r--lib/ldp_sync.h2
-rw-r--r--lib/lib_vty.c11
-rw-r--r--lib/libfrr.c42
-rw-r--r--lib/libfrr.h16
-rw-r--r--lib/libfrr_trace.h12
-rw-r--r--lib/link_state.c24
-rw-r--r--lib/link_state.h4
-rw-r--r--lib/log.c24
-rw-r--r--lib/log.h1
-rw-r--r--lib/mgmt.proto331
-rw-r--r--lib/mgmt_be_client.c1219
-rw-r--r--lib/mgmt_be_client.h260
-rw-r--r--lib/mgmt_fe_client.c1072
-rw-r--r--lib/mgmt_fe_client.h346
-rw-r--r--lib/mgmt_msg.c414
-rw-r--r--lib/mgmt_msg.h73
-rw-r--r--lib/mgmt_pb.h25
-rw-r--r--lib/northbound.c308
-rw-r--r--lib/northbound.h226
-rw-r--r--lib/northbound_cli.c123
-rw-r--r--lib/northbound_cli.h2
-rw-r--r--lib/northbound_confd.c50
-rw-r--r--lib/northbound_grpc.cpp26
-rw-r--r--lib/northbound_sysrepo.c25
-rw-r--r--lib/prefix.c2
-rw-r--r--lib/prefix.h5
-rw-r--r--lib/pullwr.c25
-rw-r--r--lib/pullwr.h10
-rw-r--r--lib/qobj.c2
-rw-r--r--lib/resolver.c60
-rw-r--r--lib/resolver.h6
-rw-r--r--lib/routemap.c87
-rw-r--r--lib/sigevent.c17
-rw-r--r--lib/sigevent.h6
-rw-r--r--lib/smux.h4
-rw-r--r--lib/spf_backoff.c46
-rw-r--r--lib/spf_backoff.h4
-rw-r--r--lib/subdir.am36
-rw-r--r--lib/systemd.c12
-rw-r--r--lib/systemd.h4
-rw-r--r--lib/thread.h291
-rw-r--r--lib/vrf.c6
-rw-r--r--lib/vty.c582
-rw-r--r--lib/vty.h55
-rw-r--r--lib/wheel.c32
-rw-r--r--lib/wheel.h6
-rw-r--r--lib/workqueue.c39
-rw-r--r--lib/workqueue.h8
-rw-r--r--lib/xref.h2
-rw-r--r--lib/yang.c7
-rw-r--r--lib/yang.h2
-rw-r--r--lib/zclient.c48
-rw-r--r--lib/zclient.h10
-rw-r--r--lib/zlog.c4
-rw-r--r--lib/zlog_5424.c26
-rw-r--r--lib/zlog_5424.h8
-rw-r--r--lib/zlog_5424_cli.c6
88 files changed, 6313 insertions, 2682 deletions
diff --git a/lib/agentx.c b/lib/agentx.c
index 2f45ae8332..45f14c2703 100644
--- a/lib/agentx.c
+++ b/lib/agentx.c
@@ -27,13 +27,13 @@ DEFINE_HOOK(agentx_enabled, (), ());
static bool agentx_enabled = false;
-static struct thread_master *agentx_tm;
-static struct thread *timeout_thr = NULL;
+static struct event_loop *agentx_tm;
+static struct event *timeout_thr = NULL;
static struct list *events = NULL;
static void agentx_events_update(void);
-static void agentx_timeout(struct thread *t)
+static void agentx_timeout(struct event *t)
{
snmp_timeout();
run_alarms();
@@ -41,18 +41,18 @@ static void agentx_timeout(struct thread *t)
agentx_events_update();
}
-static void agentx_read(struct thread *t)
+static void agentx_read(struct event *t)
{
fd_set fds;
int flags, new_flags = 0;
int nonblock = false;
- struct listnode *ln = THREAD_ARG(t);
- struct thread **thr = listgetdata(ln);
+ struct listnode *ln = EVENT_ARG(t);
+ struct event **thr = listgetdata(ln);
XFREE(MTYPE_TMP, thr);
list_delete_node(events, ln);
/* fix for non blocking socket */
- flags = fcntl(THREAD_FD(t), F_GETFL, 0);
+ flags = fcntl(EVENT_FD(t), F_GETFL, 0);
if (-1 == flags) {
flog_err(EC_LIB_SYSTEM_CALL, "Failed to get FD settings fcntl: %s(%d)",
strerror(errno), errno);
@@ -62,19 +62,19 @@ static void agentx_read(struct thread *t)
if (flags & O_NONBLOCK)
nonblock = true;
else
- new_flags = fcntl(THREAD_FD(t), F_SETFL, flags | O_NONBLOCK);
+ new_flags = fcntl(EVENT_FD(t), F_SETFL, flags | O_NONBLOCK);
if (new_flags == -1)
flog_err(EC_LIB_SYSTEM_CALL, "Failed to set snmp fd non blocking: %s(%d)",
strerror(errno), errno);
FD_ZERO(&fds);
- FD_SET(THREAD_FD(t), &fds);
+ FD_SET(EVENT_FD(t), &fds);
snmp_read(&fds);
/* Reset the flag */
if (!nonblock) {
- new_flags = fcntl(THREAD_FD(t), F_SETFL, flags);
+ new_flags = fcntl(EVENT_FD(t), F_SETFL, flags);
if (new_flags == -1)
flog_err(
@@ -94,22 +94,22 @@ static void agentx_events_update(void)
struct timeval timeout = {.tv_sec = 0, .tv_usec = 0};
fd_set fds;
struct listnode *ln;
- struct thread **thr;
+ struct event **thr;
int fd, thr_fd;
- thread_cancel(&timeout_thr);
+ event_cancel(&timeout_thr);
FD_ZERO(&fds);
snmp_select_info(&maxfd, &fds, &timeout, &block);
if (!block) {
- thread_add_timer_tv(agentx_tm, agentx_timeout, NULL, &timeout,
- &timeout_thr);
+ event_add_timer_tv(agentx_tm, agentx_timeout, NULL, &timeout,
+ &timeout_thr);
}
ln = listhead(events);
thr = ln ? listgetdata(ln) : NULL;
- thr_fd = thr ? THREAD_FD(*thr) : -1;
+ thr_fd = thr ? EVENT_FD(*thr) : -1;
/* "two-pointer" / two-list simultaneous iteration
* ln/thr/thr_fd point to the next existing event listener to hit while
@@ -119,21 +119,21 @@ static void agentx_events_update(void)
if (thr_fd == fd) {
struct listnode *nextln = listnextnode(ln);
if (!FD_ISSET(fd, &fds)) {
- thread_cancel(thr);
+ event_cancel(thr);
XFREE(MTYPE_TMP, thr);
list_delete_node(events, ln);
}
ln = nextln;
thr = ln ? listgetdata(ln) : NULL;
- thr_fd = thr ? THREAD_FD(*thr) : -1;
+ thr_fd = thr ? EVENT_FD(*thr) : -1;
}
/* need listener, but haven't hit one where it would be */
else if (FD_ISSET(fd, &fds)) {
struct listnode *newln;
- thr = XCALLOC(MTYPE_TMP, sizeof(struct thread *));
+ thr = XCALLOC(MTYPE_TMP, sizeof(struct event *));
newln = listnode_add_before(events, ln, thr);
- thread_add_read(agentx_tm, agentx_read, newln, fd, thr);
+ event_add_read(agentx_tm, agentx_read, newln, fd, thr);
}
}
@@ -142,7 +142,7 @@ static void agentx_events_update(void)
while (ln) {
struct listnode *nextln = listnextnode(ln);
thr = listgetdata(ln);
- thread_cancel(thr);
+ event_cancel(thr);
XFREE(MTYPE_TMP, thr);
list_delete_node(events, ln);
ln = nextln;
@@ -244,7 +244,7 @@ bool smux_enabled(void)
return agentx_enabled;
}
-void smux_init(struct thread_master *tm)
+void smux_init(struct event_loop *tm)
{
agentx_tm = tm;
diff --git a/lib/bfd.c b/lib/bfd.c
index 0d77c5c822..cc6d09a60f 100644
--- a/lib/bfd.c
+++ b/lib/bfd.c
@@ -10,7 +10,7 @@
#include "command.h"
#include "memory.h"
#include "prefix.h"
-#include "thread.h"
+#include "frrevent.h"
#include "stream.h"
#include "vrf.h"
#include "zclient.h"
@@ -75,7 +75,7 @@ struct bfd_session_params {
* Next event.
*
* This variable controls what action to execute when the command batch
- * finishes. Normally we'd use `thread_add_event` value, however since
+ * finishes. Normally we'd use `event_add_event` value, however since
* that function is going to be called multiple times and the value
* might be different we'll use this variable to keep track of it.
*/
@@ -87,7 +87,7 @@ struct bfd_session_params {
* configuration load or northbound batch), so we'll use this to
* install/uninstall the BFD session parameters only once.
*/
- struct thread *installev;
+ struct event *installev;
/** BFD session installation state. */
bool installed;
@@ -111,7 +111,7 @@ struct bfd_sessions_global {
struct bfd_source_list source_list;
/** Pointer to FRR's event manager. */
- struct thread_master *tm;
+ struct event_loop *tm;
/** Pointer to zebra client data structure. */
struct zclient *zc;
@@ -485,9 +485,9 @@ static bool _bfd_sess_valid(const struct bfd_session_params *bsp)
return true;
}
-static void _bfd_sess_send(struct thread *t)
+static void _bfd_sess_send(struct event *t)
{
- struct bfd_session_params *bsp = THREAD_ARG(t);
+ struct bfd_session_params *bsp = EVENT_ARG(t);
int rv;
/* Validate configuration before trying to send bogus data. */
@@ -532,16 +532,16 @@ static void _bfd_sess_send(struct thread *t)
static void _bfd_sess_remove(struct bfd_session_params *bsp)
{
+ /* Cancel any pending installation request. */
+ EVENT_OFF(bsp->installev);
+
/* Not installed, nothing to do. */
if (!bsp->installed)
return;
- /* Cancel any pending installation request. */
- THREAD_OFF(bsp->installev);
-
/* Send request to remove any session. */
bsp->lastev = BSE_UNINSTALL;
- thread_execute(bsglobal.tm, _bfd_sess_send, bsp, 0);
+ event_execute(bsglobal.tm, _bfd_sess_send, bsp, 0);
}
void bfd_sess_free(struct bfd_session_params **bsp)
@@ -733,13 +733,13 @@ void bfd_sess_set_auto_source(struct bfd_session_params *bsp, bool enable)
void bfd_sess_install(struct bfd_session_params *bsp)
{
bsp->lastev = BSE_INSTALL;
- thread_add_event(bsglobal.tm, _bfd_sess_send, bsp, 0, &bsp->installev);
+ event_add_event(bsglobal.tm, _bfd_sess_send, bsp, 0, &bsp->installev);
}
void bfd_sess_uninstall(struct bfd_session_params *bsp)
{
bsp->lastev = BSE_UNINSTALL;
- thread_add_event(bsglobal.tm, _bfd_sess_send, bsp, 0, &bsp->installev);
+ event_add_event(bsglobal.tm, _bfd_sess_send, bsp, 0, &bsp->installev);
}
enum bfd_session_state bfd_sess_status(const struct bfd_session_params *bsp)
@@ -890,11 +890,11 @@ int zclient_bfd_session_replay(ZAPI_CALLBACK_ARGS)
bsp->installed = false;
/* Cancel any pending installation request. */
- THREAD_OFF(bsp->installev);
+ EVENT_OFF(bsp->installev);
/* Ask for installation. */
bsp->lastev = BSE_INSTALL;
- thread_execute(bsglobal.tm, _bfd_sess_send, bsp, 0);
+ event_execute(bsglobal.tm, _bfd_sess_send, bsp, 0);
}
return 0;
@@ -1039,7 +1039,7 @@ static int bfd_protocol_integration_finish(void)
return 0;
}
-void bfd_protocol_integration_init(struct zclient *zc, struct thread_master *tm)
+void bfd_protocol_integration_init(struct zclient *zc, struct event_loop *tm)
{
/* Initialize data structure. */
TAILQ_INIT(&bsglobal.bsplist);
diff --git a/lib/bfd.h b/lib/bfd.h
index 66c08cde74..bfa5287340 100644
--- a/lib/bfd.h
+++ b/lib/bfd.h
@@ -348,7 +348,7 @@ void bfd_sess_show(struct vty *vty, struct json_object *json,
* Initializes the BFD integration library. This function executes the
* following actions:
*
- * - Copy the `struct thread_master` pointer to use as "thread" to execute
+ * - Copy the `struct event_loop` pointer to use as "thread" to execute
* the BFD session parameters installation.
* - Copy the `struct zclient` pointer to install its callbacks.
* - Initializes internal data structures.
@@ -356,8 +356,7 @@ void bfd_sess_show(struct vty *vty, struct json_object *json,
* \param tm normally the daemon main thread event manager.
* \param zc the zebra client of the daemon.
*/
-void bfd_protocol_integration_init(struct zclient *zc,
- struct thread_master *tm);
+void bfd_protocol_integration_init(struct zclient *zc, struct event_loop *tm);
/**
* BFD session registration arguments.
diff --git a/lib/clippy.c b/lib/clippy.c
index c3c9f5c401..d414053c55 100644
--- a/lib/clippy.c
+++ b/lib/clippy.c
@@ -14,6 +14,53 @@
#include "command_graph.h"
#include "clippy.h"
+#if PY_VERSION_HEX >= 0x03080000
+/* new python init/config API added in Python 3.8 */
+int main(int argc, char **argv)
+{
+ PyStatus status;
+ PyPreConfig preconfig[1];
+ PyConfig config[1];
+
+ PyPreConfig_InitPythonConfig(preconfig);
+ preconfig->configure_locale = 0;
+ preconfig->coerce_c_locale = 1;
+ preconfig->coerce_c_locale_warn = 0;
+ preconfig->isolated = 0;
+ preconfig->utf8_mode = 1;
+ preconfig->parse_argv = 0;
+
+ status = Py_PreInitializeFromBytesArgs(preconfig, argc, argv);
+ if (PyStatus_Exception(status))
+ Py_ExitStatusException(status);
+
+ PyConfig_InitPythonConfig(config);
+#if PY_VERSION_HEX >= 0x030b0000 /* 3.11 */
+ config->safe_path = 0;
+#endif
+
+ status = PyConfig_SetBytesArgv(config, argc, argv);
+ if (PyStatus_Exception(status))
+ Py_ExitStatusException(status);
+
+ PyConfig_SetBytesString(config, &config->program_name,
+ argc > 0 ? argv[0] : "clippy");
+ if (argc > 1)
+ PyConfig_SetBytesString(config, &config->run_filename, argv[1]);
+
+ PyImport_AppendInittab("_clippy", command_py_init);
+
+ status = Py_InitializeFromConfig(config);
+ if (PyStatus_Exception(status))
+ Py_ExitStatusException(status);
+
+ PyConfig_Clear(config);
+
+ return Py_RunMain();
+}
+
+#else /* Python < 3.8 */
+/* old python init/config API, deprecated in Python 3.11 */
#if PY_MAJOR_VERSION >= 3
#define pychar wchar_t
static wchar_t *wconv(const char *s)
@@ -89,6 +136,7 @@ int main(int argc, char **argv)
free(wargv);
return 0;
}
+#endif /* Python < 3.8 */
/* and now for the ugly part... provide simplified logging functions so we
* don't need to link libzebra (which would be a circular build dep) */
diff --git a/lib/command.c b/lib/command.c
index ee5a3889e8..97ea200ff4 100644
--- a/lib/command.c
+++ b/lib/command.c
@@ -17,7 +17,7 @@
#include "memory.h"
#include "log.h"
#include "log_vty.h"
-#include "thread.h"
+#include "frrevent.h"
#include "vector.h"
#include "linklist.h"
#include "vty.h"
@@ -2542,7 +2542,7 @@ void cmd_init(int terminal)
install_default(CONFIG_NODE);
- thread_cmd_init();
+ event_cmd_init();
workqueue_cmd_init();
hash_cmd_init();
}
@@ -2596,9 +2596,7 @@ void cmd_terminate(void)
// well
graph_delete_graph(cmd_node->cmdgraph);
vector_free(cmd_node->cmd_vector);
- hash_clean(cmd_node->cmd_hash, NULL);
- hash_free(cmd_node->cmd_hash);
- cmd_node->cmd_hash = NULL;
+ hash_clean_and_free(&cmd_node->cmd_hash, NULL);
}
vector_free(cmdvec);
diff --git a/lib/command.h b/lib/command.h
index 6538e56588..8856f9f09f 100644
--- a/lib/command.h
+++ b/lib/command.h
@@ -429,6 +429,11 @@ struct cmd_node {
#define SHARP_STR "Sharp Routing Protocol\n"
#define OSPF_GR_STR \
"OSPF non-stop forwarding (NSF) also known as OSPF Graceful Restart\n"
+#define MGMTD_STR "Management Daemon (MGMTD) information\n"
+#define MGMTD_BE_ADAPTER_STR "MGMTD Backend Adapter information\n"
+#define MGMTD_FE_ADAPTER_STR "MGMTD Frontend Adapter information\n"
+#define MGMTD_TXN_STR "MGMTD Transaction information\n"
+#define MGMTD_DS_STR "MGMTD Datastore information\n"
#define CMD_VNI_RANGE "(1-16777215)"
#define CONF_BACKUP_EXT ".sav"
diff --git a/lib/compiler.h b/lib/compiler.h
index d12e282832..29fcfbefbf 100644
--- a/lib/compiler.h
+++ b/lib/compiler.h
@@ -439,6 +439,14 @@ _Static_assert(sizeof(_uint64_t) == 8 && sizeof(_int64_t) == 8,
#pragma diag_suppress 167
#endif /* __INTELISENSE__ */
+#if defined(__GNUC__) && (__GNUC__ >= 3)
+#define likely(_x) __builtin_expect(!!(_x), 1)
+#define unlikely(_x) __builtin_expect(!!(_x), 0)
+#else
+#define likely(_x) !!(_x)
+#define unlikely(_x) !!(_x)
+#endif
+
#ifdef __cplusplus
}
#endif
diff --git a/lib/cspf.c b/lib/cspf.c
index b92c9cb395..6a0fb7f63c 100644
--- a/lib/cspf.c
+++ b/lib/cspf.c
@@ -88,7 +88,7 @@ static struct c_path *cpath_copy(struct c_path *dest, const struct c_path *src)
*
* @param path Constrained Path structure to be deleted
*/
-static void cpath_del(struct c_path *path)
+void cpath_del(struct c_path *path)
{
if (!path)
return;
diff --git a/lib/cspf.h b/lib/cspf.h
index 3eceaa04af..bba685a617 100644
--- a/lib/cspf.h
+++ b/lib/cspf.h
@@ -191,6 +191,8 @@ extern void cspf_del(struct cspf *algo);
*/
extern struct c_path *compute_p2p_path(struct cspf *algo, struct ls_ted *ted);
+extern void cpath_del(struct c_path *path);
+
#ifdef __cplusplus
}
#endif
diff --git a/lib/defun_lex.l b/lib/defun_lex.l
index 505ea59b0b..124f864166 100644
--- a/lib/defun_lex.l
+++ b/lib/defun_lex.l
@@ -53,6 +53,7 @@ int comment_link;
char string_end;
char *value;
+static const char *yyfilename;
static void extendbuf(char **what, const char *arg)
{
@@ -119,8 +120,17 @@ SPECIAL [(),]
}
}
<rstring>\\\n /* ignore */
+<rstring>\n {
+ fprintf(stderr,
+ "%s:%d: string continues past the end of the line\n",
+ yyfilename, yylineno);
+ free(value);
+ value = NULL;
+ BEGIN(INITIAL);
+ return STRING;
+ }
<rstring>\\. extend(yytext);
-<rstring>[^\\\"\']+ extend(yytext);
+<rstring>[^\\\"\'\n]+ extend(yytext);
"DEFUN" value = strdup(yytext); return DEFUNNY;
"DEFUN_NOSH" value = strdup(yytext); return DEFUNNY;
@@ -207,6 +217,10 @@ static PyObject *get_args(const char *filename, int lineno)
if (tval[0] == ')')
depth--;
}
+ if (!tval)
+ return PyErr_Format(PyExc_ValueError,
+ "%s:%d: invalid token in DEFPY parameters",
+ filename, lineno);
if (!pyArg)
pyArg = PyList_New(0);
PyList_Append(pyArg, PyUnicode_FromString(tval));
@@ -231,6 +245,7 @@ PyObject *clippy_parse(PyObject *self, PyObject *args)
int token;
yyin = fd;
value = NULL;
+ yyfilename = filename;
PyObject *pyCont = PyDict_New();
PyObject *pyObj = PyList_New(0);
@@ -248,6 +263,7 @@ PyObject *clippy_parse(PyObject *self, PyObject *args)
if (!pyArgs) {
free(tval);
Py_DECREF(pyCont);
+ yyfilename = NULL;
return NULL;
}
pyItem = PyDict_New();
@@ -276,5 +292,6 @@ PyObject *clippy_parse(PyObject *self, PyObject *args)
}
def_yylex_destroy();
fclose(fd);
+ yyfilename = NULL;
return pyCont;
}
diff --git a/lib/distribute.c b/lib/distribute.c
index 4cfdcc7840..65487676d6 100644
--- a/lib/distribute.c
+++ b/lib/distribute.c
@@ -445,14 +445,15 @@ int config_write_distribute(struct vty *vty,
void distribute_list_delete(struct distribute_ctx **ctx)
{
- if ((*ctx)->disthash) {
- hash_clean((*ctx)->disthash, (void (*)(void *))distribute_free);
+ hash_clean_and_free(&(*ctx)->disthash,
+ (void (*)(void *))distribute_free);
+
+ if (dist_ctx_list) {
+ listnode_delete(dist_ctx_list, *ctx);
+ if (list_isempty(dist_ctx_list))
+ list_delete(&dist_ctx_list);
}
- if (!dist_ctx_list)
- dist_ctx_list = list_new();
- listnode_delete(dist_ctx_list, *ctx);
- if (list_isempty(dist_ctx_list))
- list_delete(&dist_ctx_list);
+
XFREE(MTYPE_DISTRIBUTE_CTX, (*ctx));
}
diff --git a/lib/thread.c b/lib/event.c
index 8324783a7b..a8eb89f48d 100644
--- a/lib/thread.c
+++ b/lib/event.c
@@ -8,7 +8,7 @@
#include <zebra.h>
#include <sys/resource.h>
-#include "thread.h"
+#include "frrevent.h"
#include "memory.h"
#include "frrcu.h"
#include "log.h"
@@ -24,23 +24,23 @@
#include "libfrr.h"
DEFINE_MTYPE_STATIC(LIB, THREAD, "Thread");
-DEFINE_MTYPE_STATIC(LIB, THREAD_MASTER, "Thread master");
-DEFINE_MTYPE_STATIC(LIB, THREAD_POLL, "Thread Poll Info");
-DEFINE_MTYPE_STATIC(LIB, THREAD_STATS, "Thread stats");
+DEFINE_MTYPE_STATIC(LIB, EVENT_MASTER, "Thread master");
+DEFINE_MTYPE_STATIC(LIB, EVENT_POLL, "Thread Poll Info");
+DEFINE_MTYPE_STATIC(LIB, EVENT_STATS, "Thread stats");
-DECLARE_LIST(thread_list, struct thread, threaditem);
+DECLARE_LIST(event_list, struct event, eventitem);
struct cancel_req {
int flags;
- struct thread *thread;
+ struct event *thread;
void *eventobj;
- struct thread **threadref;
+ struct event **threadref;
};
/* Flags for task cancellation */
-#define THREAD_CANCEL_FLAG_READY 0x01
+#define EVENT_CANCEL_FLAG_READY 0x01
-static int thread_timer_cmp(const struct thread *a, const struct thread *b)
+static int event_timer_cmp(const struct event *a, const struct event *b)
{
if (a->u.sands.tv_sec < b->u.sands.tv_sec)
return -1;
@@ -53,7 +53,7 @@ static int thread_timer_cmp(const struct thread *a, const struct thread *b)
return 0;
}
-DECLARE_HEAP(thread_timer_list, struct thread, timeritem, thread_timer_cmp);
+DECLARE_HEAP(event_timer_list, struct event, timeritem, event_timer_cmp);
#if defined(__APPLE__)
#include <mach/mach.h>
@@ -64,7 +64,7 @@ DECLARE_HEAP(thread_timer_list, struct thread, timeritem, thread_timer_cmp);
do { \
const unsigned char wakebyte = 0x01; \
write(m->io_pipe[1], &wakebyte, 1); \
- } while (0);
+ } while (0)
/* control variable for initializer */
static pthread_once_t init_once = PTHREAD_ONCE_INIT;
@@ -73,7 +73,7 @@ pthread_key_t thread_current;
static pthread_mutex_t masters_mtx = PTHREAD_MUTEX_INITIALIZER;
static struct list *masters;
-static void thread_free(struct thread_master *master, struct thread *thread);
+static void thread_free(struct event_loop *master, struct event *thread);
#ifndef EXCLUDE_CPU_TIME
#define EXCLUDE_CPU_TIME 0
@@ -87,25 +87,26 @@ unsigned long cputime_threshold = CONSUMED_TIME_CHECK;
unsigned long walltime_threshold = CONSUMED_TIME_CHECK;
/* CLI start ---------------------------------------------------------------- */
-#include "lib/thread_clippy.c"
+#include "lib/event_clippy.c"
-static unsigned int cpu_record_hash_key(const struct cpu_thread_history *a)
+static unsigned int cpu_record_hash_key(const struct cpu_event_history *a)
{
int size = sizeof(a->func);
return jhash(&a->func, size, 0);
}
-static bool cpu_record_hash_cmp(const struct cpu_thread_history *a,
- const struct cpu_thread_history *b)
+static bool cpu_record_hash_cmp(const struct cpu_event_history *a,
+ const struct cpu_event_history *b)
{
return a->func == b->func;
}
-static void *cpu_record_hash_alloc(struct cpu_thread_history *a)
+static void *cpu_record_hash_alloc(struct cpu_event_history *a)
{
- struct cpu_thread_history *new;
- new = XCALLOC(MTYPE_THREAD_STATS, sizeof(struct cpu_thread_history));
+ struct cpu_event_history *new;
+
+ new = XCALLOC(MTYPE_EVENT_STATS, sizeof(struct cpu_event_history));
new->func = a->func;
new->funcname = a->funcname;
return new;
@@ -113,13 +114,13 @@ static void *cpu_record_hash_alloc(struct cpu_thread_history *a)
static void cpu_record_hash_free(void *a)
{
- struct cpu_thread_history *hist = a;
+ struct cpu_event_history *hist = a;
- XFREE(MTYPE_THREAD_STATS, hist);
+ XFREE(MTYPE_EVENT_STATS, hist);
}
-static void vty_out_cpu_thread_history(struct vty *vty,
- struct cpu_thread_history *a)
+static void vty_out_cpu_event_history(struct vty *vty,
+ struct cpu_event_history *a)
{
vty_out(vty,
"%5zu %10zu.%03zu %9zu %8zu %9zu %8zu %9zu %9zu %9zu %10zu",
@@ -128,21 +129,21 @@ static void vty_out_cpu_thread_history(struct vty *vty,
(a->real.total / a->total_calls), a->real.max,
a->total_cpu_warn, a->total_wall_warn, a->total_starv_warn);
vty_out(vty, " %c%c%c%c%c %s\n",
- a->types & (1 << THREAD_READ) ? 'R' : ' ',
- a->types & (1 << THREAD_WRITE) ? 'W' : ' ',
- a->types & (1 << THREAD_TIMER) ? 'T' : ' ',
- a->types & (1 << THREAD_EVENT) ? 'E' : ' ',
- a->types & (1 << THREAD_EXECUTE) ? 'X' : ' ', a->funcname);
+ a->types & (1 << EVENT_READ) ? 'R' : ' ',
+ a->types & (1 << EVENT_WRITE) ? 'W' : ' ',
+ a->types & (1 << EVENT_TIMER) ? 'T' : ' ',
+ a->types & (1 << EVENT_EVENT) ? 'E' : ' ',
+ a->types & (1 << EVENT_EXECUTE) ? 'X' : ' ', a->funcname);
}
static void cpu_record_hash_print(struct hash_bucket *bucket, void *args[])
{
- struct cpu_thread_history *totals = args[0];
- struct cpu_thread_history copy;
+ struct cpu_event_history *totals = args[0];
+ struct cpu_event_history copy;
struct vty *vty = args[1];
uint8_t *filter = args[2];
- struct cpu_thread_history *a = bucket->data;
+ struct cpu_event_history *a = bucket->data;
copy.total_active =
atomic_load_explicit(&a->total_active, memory_order_seq_cst);
@@ -167,7 +168,7 @@ static void cpu_record_hash_print(struct hash_bucket *bucket, void *args[])
if (!(copy.types & *filter))
return;
- vty_out_cpu_thread_history(vty, &copy);
+ vty_out_cpu_event_history(vty, &copy);
totals->total_active += copy.total_active;
totals->total_calls += copy.total_calls;
totals->total_cpu_warn += copy.total_cpu_warn;
@@ -183,9 +184,9 @@ static void cpu_record_hash_print(struct hash_bucket *bucket, void *args[])
static void cpu_record_print(struct vty *vty, uint8_t filter)
{
- struct cpu_thread_history tmp;
+ struct cpu_event_history tmp;
void *args[3] = {&tmp, vty, &filter};
- struct thread_master *m;
+ struct event_loop *m;
struct listnode *ln;
if (!cputime_enabled)
@@ -203,8 +204,8 @@ static void cpu_record_print(struct vty *vty, uint8_t filter)
frr_with_mutex (&masters_mtx) {
for (ALL_LIST_ELEMENTS_RO(masters, ln, m)) {
const char *name = m->name ? m->name : "main";
-
char underline[strlen(name) + 1];
+
memset(underline, '-', sizeof(underline));
underline[sizeof(underline) - 1] = '\0';
@@ -244,7 +245,7 @@ static void cpu_record_print(struct vty *vty, uint8_t filter)
vty_out(vty, " Type Thread\n");
if (tmp.total_calls > 0)
- vty_out_cpu_thread_history(vty, &tmp);
+ vty_out_cpu_event_history(vty, &tmp);
}
static void cpu_record_hash_clear(struct hash_bucket *bucket, void *args[])
@@ -252,7 +253,7 @@ static void cpu_record_hash_clear(struct hash_bucket *bucket, void *args[])
uint8_t *filter = args[0];
struct hash *cpu_record = args[1];
- struct cpu_thread_history *a = bucket->data;
+ struct cpu_event_history *a = bucket->data;
if (!(a->types & *filter))
return;
@@ -263,13 +264,14 @@ static void cpu_record_hash_clear(struct hash_bucket *bucket, void *args[])
static void cpu_record_clear(uint8_t filter)
{
uint8_t *tmp = &filter;
- struct thread_master *m;
+ struct event_loop *m;
struct listnode *ln;
frr_with_mutex (&masters_mtx) {
for (ALL_LIST_ELEMENTS_RO(masters, ln, m)) {
frr_with_mutex (&m->mtx) {
void *args[2] = {tmp, m->cpu_record};
+
hash_iterate(
m->cpu_record,
(void (*)(struct hash_bucket *,
@@ -289,23 +291,23 @@ static uint8_t parse_filter(const char *filterstr)
switch (filterstr[i]) {
case 'r':
case 'R':
- filter |= (1 << THREAD_READ);
+ filter |= (1 << EVENT_READ);
break;
case 'w':
case 'W':
- filter |= (1 << THREAD_WRITE);
+ filter |= (1 << EVENT_WRITE);
break;
case 't':
case 'T':
- filter |= (1 << THREAD_TIMER);
+ filter |= (1 << EVENT_TIMER);
break;
case 'e':
case 'E':
- filter |= (1 << THREAD_EVENT);
+ filter |= (1 << EVENT_EVENT);
break;
case 'x':
case 'X':
- filter |= (1 << THREAD_EXECUTE);
+ filter |= (1 << EVENT_EXECUTE);
break;
default:
break;
@@ -395,11 +397,11 @@ ALIAS (service_walltime_warning,
"Set up miscellaneous service\n"
"Warn for tasks exceeding total wallclock threshold\n")
-static void show_thread_poll_helper(struct vty *vty, struct thread_master *m)
+static void show_thread_poll_helper(struct vty *vty, struct event_loop *m)
{
const char *name = m->name ? m->name : "main";
char underline[strlen(name) + 1];
- struct thread *thread;
+ struct event *thread;
uint32_t i;
memset(underline, '-', sizeof(underline));
@@ -444,12 +446,11 @@ DEFUN_NOSH (show_thread_poll,
"Show poll FD's and information\n")
{
struct listnode *node;
- struct thread_master *m;
+ struct event_loop *m;
frr_with_mutex (&masters_mtx) {
- for (ALL_LIST_ELEMENTS_RO(masters, node, m)) {
+ for (ALL_LIST_ELEMENTS_RO(masters, node, m))
show_thread_poll_helper(vty, m);
- }
}
return CMD_SUCCESS;
@@ -481,11 +482,11 @@ DEFUN (clear_thread_cpu,
return CMD_SUCCESS;
}
-static void show_thread_timers_helper(struct vty *vty, struct thread_master *m)
+static void show_thread_timers_helper(struct vty *vty, struct event_loop *m)
{
const char *name = m->name ? m->name : "main";
char underline[strlen(name) + 1];
- struct thread *thread;
+ struct event *thread;
memset(underline, '-', sizeof(underline));
underline[sizeof(underline) - 1] = '\0';
@@ -493,7 +494,7 @@ static void show_thread_timers_helper(struct vty *vty, struct thread_master *m)
vty_out(vty, "\nShowing timers for %s\n", name);
vty_out(vty, "-------------------%s\n", underline);
- frr_each (thread_timer_list, &m->timer, thread) {
+ frr_each (event_timer_list, &m->timer, thread) {
vty_out(vty, " %-50s%pTH\n", thread->hist->funcname, thread);
}
}
@@ -506,7 +507,7 @@ DEFPY_NOSH (show_thread_timers,
"Show all timers and how long they have in the system\n")
{
struct listnode *node;
- struct thread_master *m;
+ struct event_loop *m;
frr_with_mutex (&masters_mtx) {
for (ALL_LIST_ELEMENTS_RO(masters, node, m))
@@ -516,7 +517,7 @@ DEFPY_NOSH (show_thread_timers,
return CMD_SUCCESS;
}
-void thread_cmd_init(void)
+void event_cmd_init(void)
{
install_element(VIEW_NODE, &show_thread_cpu_cmd);
install_element(VIEW_NODE, &show_thread_poll_cmd);
@@ -544,14 +545,14 @@ static void initializer(void)
pthread_key_create(&thread_current, NULL);
}
-struct thread_master *thread_master_create(const char *name)
+struct event_loop *event_master_create(const char *name)
{
- struct thread_master *rv;
+ struct event_loop *rv;
struct rlimit limit;
pthread_once(&init_once, &initializer);
- rv = XCALLOC(MTYPE_THREAD_MASTER, sizeof(struct thread_master));
+ rv = XCALLOC(MTYPE_EVENT_MASTER, sizeof(struct event_loop));
/* Initialize master mutex */
pthread_mutex_init(&rv->mtx, NULL);
@@ -559,7 +560,7 @@ struct thread_master *thread_master_create(const char *name)
/* Set name */
name = name ? name : "default";
- rv->name = XSTRDUP(MTYPE_THREAD_MASTER, name);
+ rv->name = XSTRDUP(MTYPE_EVENT_MASTER, name);
/* Initialize I/O task data structures */
@@ -570,13 +571,14 @@ struct thread_master *thread_master_create(const char *name)
rv->fd_limit = (int)limit.rlim_cur;
}
- rv->read = XCALLOC(MTYPE_THREAD_POLL,
- sizeof(struct thread *) * rv->fd_limit);
+ rv->read = XCALLOC(MTYPE_EVENT_POLL,
+ sizeof(struct event *) * rv->fd_limit);
- rv->write = XCALLOC(MTYPE_THREAD_POLL,
- sizeof(struct thread *) * rv->fd_limit);
+ rv->write = XCALLOC(MTYPE_EVENT_POLL,
+ sizeof(struct event *) * rv->fd_limit);
char tmhashname[strlen(name) + 32];
+
snprintf(tmhashname, sizeof(tmhashname), "%s - threadmaster event hash",
name);
rv->cpu_record = hash_create_size(
@@ -584,12 +586,12 @@ struct thread_master *thread_master_create(const char *name)
(bool (*)(const void *, const void *))cpu_record_hash_cmp,
tmhashname);
- thread_list_init(&rv->event);
- thread_list_init(&rv->ready);
- thread_list_init(&rv->unuse);
- thread_timer_list_init(&rv->timer);
+ event_list_init(&rv->event);
+ event_list_init(&rv->ready);
+ event_list_init(&rv->unuse);
+ event_timer_list_init(&rv->timer);
- /* Initialize thread_fetch() settings */
+ /* Initialize event_fetch() settings */
rv->spin = true;
rv->handle_signals = true;
@@ -607,9 +609,9 @@ struct thread_master *thread_master_create(const char *name)
/* Initialize data structures for poll() */
rv->handler.pfdsize = rv->fd_limit;
rv->handler.pfdcount = 0;
- rv->handler.pfds = XCALLOC(MTYPE_THREAD_MASTER,
+ rv->handler.pfds = XCALLOC(MTYPE_EVENT_MASTER,
sizeof(struct pollfd) * rv->handler.pfdsize);
- rv->handler.copy = XCALLOC(MTYPE_THREAD_MASTER,
+ rv->handler.copy = XCALLOC(MTYPE_EVENT_MASTER,
sizeof(struct pollfd) * rv->handler.pfdsize);
/* add to list of threadmasters */
@@ -623,32 +625,32 @@ struct thread_master *thread_master_create(const char *name)
return rv;
}
-void thread_master_set_name(struct thread_master *master, const char *name)
+void event_master_set_name(struct event_loop *master, const char *name)
{
frr_with_mutex (&master->mtx) {
- XFREE(MTYPE_THREAD_MASTER, master->name);
- master->name = XSTRDUP(MTYPE_THREAD_MASTER, name);
+ XFREE(MTYPE_EVENT_MASTER, master->name);
+ master->name = XSTRDUP(MTYPE_EVENT_MASTER, name);
}
}
-#define THREAD_UNUSED_DEPTH 10
+#define EVENT_UNUSED_DEPTH 10
/* Move thread to unuse list. */
-static void thread_add_unuse(struct thread_master *m, struct thread *thread)
+static void thread_add_unuse(struct event_loop *m, struct event *thread)
{
pthread_mutex_t mtxc = thread->mtx;
assert(m != NULL && thread != NULL);
thread->hist->total_active--;
- memset(thread, 0, sizeof(struct thread));
- thread->type = THREAD_UNUSED;
+ memset(thread, 0, sizeof(struct event));
+ thread->type = EVENT_UNUSED;
/* Restore the thread mutex context. */
thread->mtx = mtxc;
- if (thread_list_count(&m->unuse) < THREAD_UNUSED_DEPTH) {
- thread_list_add_tail(&m->unuse, thread);
+ if (event_list_count(&m->unuse) < EVENT_UNUSED_DEPTH) {
+ event_list_add_tail(&m->unuse, thread);
return;
}
@@ -656,19 +658,17 @@ static void thread_add_unuse(struct thread_master *m, struct thread *thread)
}
/* Free all unused thread. */
-static void thread_list_free(struct thread_master *m,
- struct thread_list_head *list)
+static void thread_list_free(struct event_loop *m, struct event_list_head *list)
{
- struct thread *t;
+ struct event *t;
- while ((t = thread_list_pop(list)))
+ while ((t = event_list_pop(list)))
thread_free(m, t);
}
-static void thread_array_free(struct thread_master *m,
- struct thread **thread_array)
+static void thread_array_free(struct event_loop *m, struct event **thread_array)
{
- struct thread *t;
+ struct event *t;
int index;
for (index = 0; index < m->fd_limit; ++index) {
@@ -678,41 +678,41 @@ static void thread_array_free(struct thread_master *m,
thread_free(m, t);
}
}
- XFREE(MTYPE_THREAD_POLL, thread_array);
+ XFREE(MTYPE_EVENT_POLL, thread_array);
}
/*
- * thread_master_free_unused
+ * event_master_free_unused
*
* As threads are finished with they are put on the
* unuse list for later reuse.
* If we are shutting down, Free up unused threads
* So we can see if we forget to shut anything off
*/
-void thread_master_free_unused(struct thread_master *m)
+void event_master_free_unused(struct event_loop *m)
{
frr_with_mutex (&m->mtx) {
- struct thread *t;
- while ((t = thread_list_pop(&m->unuse)))
+ struct event *t;
+
+ while ((t = event_list_pop(&m->unuse)))
thread_free(m, t);
}
}
/* Stop thread scheduler. */
-void thread_master_free(struct thread_master *m)
+void event_master_free(struct event_loop *m)
{
- struct thread *t;
+ struct event *t;
frr_with_mutex (&masters_mtx) {
listnode_delete(masters, m);
- if (masters->count == 0) {
+ if (masters->count == 0)
list_delete(&masters);
- }
}
thread_array_free(m, m->read);
thread_array_free(m, m->write);
- while ((t = thread_timer_list_pop(&m->timer)))
+ while ((t = event_timer_list_pop(&m->timer)))
thread_free(m, t);
thread_list_free(m, &m->event);
thread_list_free(m, &m->ready);
@@ -724,22 +724,20 @@ void thread_master_free(struct thread_master *m)
list_delete(&m->cancel_req);
m->cancel_req = NULL;
- hash_clean(m->cpu_record, cpu_record_hash_free);
- hash_free(m->cpu_record);
- m->cpu_record = NULL;
+ hash_clean_and_free(&m->cpu_record, cpu_record_hash_free);
- XFREE(MTYPE_THREAD_MASTER, m->name);
- XFREE(MTYPE_THREAD_MASTER, m->handler.pfds);
- XFREE(MTYPE_THREAD_MASTER, m->handler.copy);
- XFREE(MTYPE_THREAD_MASTER, m);
+ XFREE(MTYPE_EVENT_MASTER, m->name);
+ XFREE(MTYPE_EVENT_MASTER, m->handler.pfds);
+ XFREE(MTYPE_EVENT_MASTER, m->handler.copy);
+ XFREE(MTYPE_EVENT_MASTER, m);
}
/* Return remain time in milliseconds. */
-unsigned long thread_timer_remain_msec(struct thread *thread)
+unsigned long event_timer_remain_msec(struct event *thread)
{
int64_t remain;
- if (!thread_is_scheduled(thread))
+ if (!event_is_scheduled(thread))
return 0;
frr_with_mutex (&thread->mtx) {
@@ -750,14 +748,15 @@ unsigned long thread_timer_remain_msec(struct thread *thread)
}
/* Return remain time in seconds. */
-unsigned long thread_timer_remain_second(struct thread *thread)
+unsigned long event_timer_remain_second(struct event *thread)
{
- return thread_timer_remain_msec(thread) / 1000LL;
+ return event_timer_remain_msec(thread) / 1000LL;
}
-struct timeval thread_timer_remain(struct thread *thread)
+struct timeval event_timer_remain(struct event *thread)
{
struct timeval remain;
+
frr_with_mutex (&thread->mtx) {
monotime_until(&thread->u.sands, &remain);
}
@@ -782,28 +781,26 @@ static int time_hhmmss(char *buf, int buf_size, long sec)
return wr != 8;
}
-char *thread_timer_to_hhmmss(char *buf, int buf_size,
- struct thread *t_timer)
+char *event_timer_to_hhmmss(char *buf, int buf_size, struct event *t_timer)
{
- if (t_timer) {
- time_hhmmss(buf, buf_size,
- thread_timer_remain_second(t_timer));
- } else {
+ if (t_timer)
+ time_hhmmss(buf, buf_size, event_timer_remain_second(t_timer));
+ else
snprintf(buf, buf_size, "--:--:--");
- }
+
return buf;
}
/* Get new thread. */
-static struct thread *thread_get(struct thread_master *m, uint8_t type,
- void (*func)(struct thread *), void *arg,
- const struct xref_threadsched *xref)
+static struct event *thread_get(struct event_loop *m, uint8_t type,
+ void (*func)(struct event *), void *arg,
+ const struct xref_eventsched *xref)
{
- struct thread *thread = thread_list_pop(&m->unuse);
- struct cpu_thread_history tmp;
+ struct event *thread = event_list_pop(&m->unuse);
+ struct cpu_event_history tmp;
if (!thread) {
- thread = XCALLOC(MTYPE_THREAD, sizeof(struct thread));
+ thread = XCALLOC(MTYPE_THREAD, sizeof(struct event));
/* mutex only needs to be initialized at struct creation. */
pthread_mutex_init(&thread->mtx, NULL);
m->alloc++;
@@ -813,7 +810,7 @@ static struct thread *thread_get(struct thread_master *m, uint8_t type,
thread->add_type = type;
thread->master = m;
thread->arg = arg;
- thread->yield = THREAD_YIELD_TIME_SLOT; /* default */
+ thread->yield = EVENT_YIELD_TIME_SLOT; /* default */
thread->ref = NULL;
thread->ignore_timer_late = false;
@@ -842,7 +839,7 @@ static struct thread *thread_get(struct thread_master *m, uint8_t type,
return thread;
}
-static void thread_free(struct thread_master *master, struct thread *thread)
+static void thread_free(struct event_loop *master, struct event *thread)
{
/* Update statistics. */
assert(master->alloc > 0);
@@ -853,7 +850,7 @@ static void thread_free(struct thread_master *master, struct thread *thread)
XFREE(MTYPE_THREAD, thread);
}
-static int fd_poll(struct thread_master *m, const struct timeval *timer_wait,
+static int fd_poll(struct event_loop *m, const struct timeval *timer_wait,
bool *eintr_p)
{
sigset_t origsigs;
@@ -862,7 +859,7 @@ static int fd_poll(struct thread_master *m, const struct timeval *timer_wait,
/*
* If timer_wait is null here, that means poll() should block
- * indefinitely, unless the thread_master has overridden it by setting
+ * indefinitely, unless the event_master has overridden it by setting
* ->selectpoll_timeout.
*
* If the value is positive, it specifies the maximum number of
@@ -875,15 +872,17 @@ static int fd_poll(struct thread_master *m, const struct timeval *timer_wait,
/* number of file descriptors with events */
int num;
- if (timer_wait != NULL
- && m->selectpoll_timeout == 0) // use the default value
+ if (timer_wait != NULL && m->selectpoll_timeout == 0) {
+ /* use the default value */
timeout = (timer_wait->tv_sec * 1000)
+ (timer_wait->tv_usec / 1000);
- else if (m->selectpoll_timeout > 0) // use the user's timeout
+ } else if (m->selectpoll_timeout > 0) {
+ /* use the user's timeout */
timeout = m->selectpoll_timeout;
- else if (m->selectpoll_timeout
- < 0) // effect a poll (return immediately)
+ } else if (m->selectpoll_timeout < 0) {
+ /* effect a poll (return immediately) */
timeout = 0;
+ }
zlog_tls_buffer_flush();
rcu_read_unlock();
@@ -951,16 +950,15 @@ done:
}
/* Add new read thread. */
-void _thread_add_read_write(const struct xref_threadsched *xref,
- struct thread_master *m,
- void (*func)(struct thread *), void *arg, int fd,
- struct thread **t_ptr)
+void _event_add_read_write(const struct xref_eventsched *xref,
+ struct event_loop *m, void (*func)(struct event *),
+ void *arg, int fd, struct event **t_ptr)
{
- int dir = xref->thread_type;
- struct thread *thread = NULL;
- struct thread **thread_array;
+ int dir = xref->event_type;
+ struct event *thread = NULL;
+ struct event **thread_array;
- if (dir == THREAD_READ)
+ if (dir == EVENT_READ)
frrtrace(9, frr_libfrr, schedule_read, m,
xref->funcname, xref->xref.file, xref->xref.line,
t_ptr, fd, 0, arg, 0);
@@ -974,20 +972,22 @@ void _thread_add_read_write(const struct xref_threadsched *xref,
assert(!"Number of FD's open is greater than FRR currently configured to handle, aborting");
frr_with_mutex (&m->mtx) {
+ /* Thread is already scheduled; don't reschedule */
if (t_ptr && *t_ptr)
- // thread is already scheduled; don't reschedule
break;
/* default to a new pollfd */
nfds_t queuepos = m->handler.pfdcount;
- if (dir == THREAD_READ)
+ if (dir == EVENT_READ)
thread_array = m->read;
else
thread_array = m->write;
- /* if we already have a pollfd for our file descriptor, find and
- * use it */
+ /*
+ * if we already have a pollfd for our file descriptor, find and
+ * use it
+ */
for (nfds_t i = 0; i < m->handler.pfdcount; i++)
if (m->handler.pfds[i].fd == fd) {
queuepos = i;
@@ -1010,7 +1010,7 @@ void _thread_add_read_write(const struct xref_threadsched *xref,
m->handler.pfds[queuepos].fd = fd;
m->handler.pfds[queuepos].events |=
- (dir == THREAD_READ ? POLLIN : POLLOUT);
+ (dir == EVENT_READ ? POLLIN : POLLOUT);
if (queuepos == m->handler.pfdcount)
m->handler.pfdcount++;
@@ -1031,13 +1031,13 @@ void _thread_add_read_write(const struct xref_threadsched *xref,
}
}
-static void _thread_add_timer_timeval(const struct xref_threadsched *xref,
- struct thread_master *m,
- void (*func)(struct thread *), void *arg,
- struct timeval *time_relative,
- struct thread **t_ptr)
+static void _event_add_timer_timeval(const struct xref_eventsched *xref,
+ struct event_loop *m,
+ void (*func)(struct event *), void *arg,
+ struct timeval *time_relative,
+ struct event **t_ptr)
{
- struct thread *thread;
+ struct event *thread;
struct timeval t;
assert(m != NULL);
@@ -1057,11 +1057,11 @@ static void _thread_add_timer_timeval(const struct xref_threadsched *xref,
/* thread is already scheduled; don't reschedule */
return;
- thread = thread_get(m, THREAD_TIMER, func, arg, xref);
+ thread = thread_get(m, EVENT_TIMER, func, arg, xref);
frr_with_mutex (&thread->mtx) {
thread->u.sands = t;
- thread_timer_list_add(&m->timer, thread);
+ event_timer_list_add(&m->timer, thread);
if (t_ptr) {
*t_ptr = thread;
thread->ref = t_ptr;
@@ -1072,7 +1072,7 @@ static void _thread_add_timer_timeval(const struct xref_threadsched *xref,
* might change the time we'll wait for, give the pthread
* a chance to re-compute.
*/
- if (thread_timer_list_first(&m->timer) == thread)
+ if (event_timer_list_first(&m->timer) == thread)
AWAKEN(m);
}
#define ONEYEAR2SEC (60 * 60 * 24 * 365)
@@ -1085,9 +1085,9 @@ static void _thread_add_timer_timeval(const struct xref_threadsched *xref,
/* Add timer event thread. */
-void _thread_add_timer(const struct xref_threadsched *xref,
- struct thread_master *m, void (*func)(struct thread *),
- void *arg, long timer, struct thread **t_ptr)
+void _event_add_timer(const struct xref_eventsched *xref, struct event_loop *m,
+ void (*func)(struct event *), void *arg, long timer,
+ struct event **t_ptr)
{
struct timeval trel;
@@ -1096,14 +1096,13 @@ void _thread_add_timer(const struct xref_threadsched *xref,
trel.tv_sec = timer;
trel.tv_usec = 0;
- _thread_add_timer_timeval(xref, m, func, arg, &trel, t_ptr);
+ _event_add_timer_timeval(xref, m, func, arg, &trel, t_ptr);
}
/* Add timer event thread with "millisecond" resolution */
-void _thread_add_timer_msec(const struct xref_threadsched *xref,
- struct thread_master *m,
- void (*func)(struct thread *), void *arg,
- long timer, struct thread **t_ptr)
+void _event_add_timer_msec(const struct xref_eventsched *xref,
+ struct event_loop *m, void (*func)(struct event *),
+ void *arg, long timer, struct event **t_ptr)
{
struct timeval trel;
@@ -1112,24 +1111,23 @@ void _thread_add_timer_msec(const struct xref_threadsched *xref,
trel.tv_sec = timer / 1000;
trel.tv_usec = 1000 * (timer % 1000);
- _thread_add_timer_timeval(xref, m, func, arg, &trel, t_ptr);
+ _event_add_timer_timeval(xref, m, func, arg, &trel, t_ptr);
}
/* Add timer event thread with "timeval" resolution */
-void _thread_add_timer_tv(const struct xref_threadsched *xref,
- struct thread_master *m,
- void (*func)(struct thread *), void *arg,
- struct timeval *tv, struct thread **t_ptr)
+void _event_add_timer_tv(const struct xref_eventsched *xref,
+ struct event_loop *m, void (*func)(struct event *),
+ void *arg, struct timeval *tv, struct event **t_ptr)
{
- _thread_add_timer_timeval(xref, m, func, arg, tv, t_ptr);
+ _event_add_timer_timeval(xref, m, func, arg, tv, t_ptr);
}
/* Add simple event thread. */
-void _thread_add_event(const struct xref_threadsched *xref,
- struct thread_master *m, void (*func)(struct thread *),
- void *arg, int val, struct thread **t_ptr)
+void _event_add_event(const struct xref_eventsched *xref, struct event_loop *m,
+ void (*func)(struct event *), void *arg, int val,
+ struct event **t_ptr)
{
- struct thread *thread = NULL;
+ struct event *thread = NULL;
frrtrace(9, frr_libfrr, schedule_event, m,
xref->funcname, xref->xref.file, xref->xref.line,
@@ -1142,10 +1140,10 @@ void _thread_add_event(const struct xref_threadsched *xref,
/* thread is already scheduled; don't reschedule */
break;
- thread = thread_get(m, THREAD_EVENT, func, arg, xref);
+ thread = thread_get(m, EVENT_EVENT, func, arg, xref);
frr_with_mutex (&thread->mtx) {
thread->u.val = val;
- thread_list_add_tail(&m->event, thread);
+ event_list_add_tail(&m->event, thread);
}
if (t_ptr) {
@@ -1163,7 +1161,7 @@ void _thread_add_event(const struct xref_threadsched *xref,
* NOT's out the .events field of pollfd corresponding to the given file
* descriptor. The event to be NOT'd is passed in the 'state' parameter.
*
- * This needs to happen for both copies of pollfd's. See 'thread_fetch'
+ * This needs to happen for both copies of pollfd's. See 'event_fetch'
* implementation for details.
*
* @param master
@@ -1173,8 +1171,8 @@ void _thread_add_event(const struct xref_threadsched *xref,
* - POLLIN
* - POLLOUT
*/
-static void thread_cancel_rw(struct thread_master *master, int fd, short state,
- int idx_hint)
+static void event_cancel_rw(struct event_loop *master, int fd, short state,
+ int idx_hint)
{
bool found = false;
@@ -1218,8 +1216,10 @@ static void thread_cancel_rw(struct thread_master *master, int fd, short state,
master->handler.pfds[master->handler.pfdcount].events = 0;
}
- /* If we have the same pollfd in the copy, perform the same operations,
- * otherwise return. */
+ /*
+ * If we have the same pollfd in the copy, perform the same operations,
+ * otherwise return.
+ */
if (i >= master->handler.copycount)
return;
@@ -1231,7 +1231,7 @@ static void thread_cancel_rw(struct thread_master *master, int fd, short state,
* sizeof(struct pollfd));
master->handler.copycount--;
master->handler.copy[master->handler.copycount].fd = 0;
- master->handler.copy[master->handler.copycount].events = 0;
+ master->handler.copy[master->handler.copycount].events = 0;
}
}
@@ -1239,10 +1239,10 @@ static void thread_cancel_rw(struct thread_master *master, int fd, short state,
* Process task cancellation given a task argument: iterate through the
* various lists of tasks, looking for any that match the argument.
*/
-static void cancel_arg_helper(struct thread_master *master,
+static void cancel_arg_helper(struct event_loop *master,
const struct cancel_req *cr)
{
- struct thread *t;
+ struct event *t;
nfds_t i;
int fd;
struct pollfd *pfd;
@@ -1252,26 +1252,26 @@ static void cancel_arg_helper(struct thread_master *master,
return;
/* First process the ready lists. */
- frr_each_safe(thread_list, &master->event, t) {
+ frr_each_safe (event_list, &master->event, t) {
if (t->arg != cr->eventobj)
continue;
- thread_list_del(&master->event, t);
+ event_list_del(&master->event, t);
if (t->ref)
*t->ref = NULL;
thread_add_unuse(master, t);
}
- frr_each_safe(thread_list, &master->ready, t) {
+ frr_each_safe (event_list, &master->ready, t) {
if (t->arg != cr->eventobj)
continue;
- thread_list_del(&master->ready, t);
+ event_list_del(&master->ready, t);
if (t->ref)
*t->ref = NULL;
thread_add_unuse(master, t);
}
/* If requested, stop here and ignore io and timers */
- if (CHECK_FLAG(cr->flags, THREAD_CANCEL_FLAG_READY))
+ if (CHECK_FLAG(cr->flags, EVENT_CANCEL_FLAG_READY))
return;
/* Check the io tasks */
@@ -1287,7 +1287,7 @@ static void cancel_arg_helper(struct thread_master *master,
fd = pfd->fd;
/* Found a match to cancel: clean up fd arrays */
- thread_cancel_rw(master, pfd->fd, pfd->events, i);
+ event_cancel_rw(master, pfd->fd, pfd->events, i);
/* Clean up thread arrays */
master->read[fd] = NULL;
@@ -1307,14 +1307,14 @@ static void cancel_arg_helper(struct thread_master *master,
}
/* Check the timer tasks */
- t = thread_timer_list_first(&master->timer);
+ t = event_timer_list_first(&master->timer);
while (t) {
- struct thread *t_next;
+ struct event *t_next;
- t_next = thread_timer_list_next(&master->timer, t);
+ t_next = event_timer_list_next(&master->timer, t);
if (t->arg == cr->eventobj) {
- thread_timer_list_del(&master->timer, t);
+ event_timer_list_del(&master->timer, t);
if (t->ref)
*t->ref = NULL;
thread_add_unuse(master, t);
@@ -1327,16 +1327,16 @@ static void cancel_arg_helper(struct thread_master *master,
/**
* Process cancellation requests.
*
- * This may only be run from the pthread which owns the thread_master.
+ * This may only be run from the pthread which owns the event_master.
*
* @param master the thread master to process
* @REQUIRE master->mtx
*/
-static void do_thread_cancel(struct thread_master *master)
+static void do_event_cancel(struct event_loop *master)
{
- struct thread_list_head *list = NULL;
- struct thread **thread_array = NULL;
- struct thread *thread;
+ struct event_list_head *list = NULL;
+ struct event **thread_array = NULL;
+ struct event *thread;
struct cancel_req *cr;
struct listnode *ln;
@@ -1367,33 +1367,33 @@ static void do_thread_cancel(struct thread_master *master)
/* Determine the appropriate queue to cancel the thread from */
switch (thread->type) {
- case THREAD_READ:
- thread_cancel_rw(master, thread->u.fd, POLLIN, -1);
+ case EVENT_READ:
+ event_cancel_rw(master, thread->u.fd, POLLIN, -1);
thread_array = master->read;
break;
- case THREAD_WRITE:
- thread_cancel_rw(master, thread->u.fd, POLLOUT, -1);
+ case EVENT_WRITE:
+ event_cancel_rw(master, thread->u.fd, POLLOUT, -1);
thread_array = master->write;
break;
- case THREAD_TIMER:
- thread_timer_list_del(&master->timer, thread);
+ case EVENT_TIMER:
+ event_timer_list_del(&master->timer, thread);
break;
- case THREAD_EVENT:
+ case EVENT_EVENT:
list = &master->event;
break;
- case THREAD_READY:
+ case EVENT_READY:
list = &master->ready;
break;
- default:
+ case EVENT_UNUSED:
+ case EVENT_EXECUTE:
continue;
break;
}
- if (list) {
- thread_list_del(list, thread);
- } else if (thread_array) {
+ if (list)
+ event_list_del(list, thread);
+ else if (thread_array)
thread_array[thread->u.fd] = NULL;
- }
if (thread->ref)
*thread->ref = NULL;
@@ -1405,7 +1405,7 @@ static void do_thread_cancel(struct thread_master *master)
if (master->cancel_req)
list_delete_all_node(master->cancel_req);
- /* Wake up any threads which may be blocked in thread_cancel_async() */
+ /* Wake up any threads which may be blocked in event_cancel_async() */
master->canceled = true;
pthread_cond_broadcast(&master->cancel_cond);
}
@@ -1413,7 +1413,7 @@ static void do_thread_cancel(struct thread_master *master)
/*
* Helper function used for multiple flavors of arg-based cancellation.
*/
-static void cancel_event_helper(struct thread_master *m, void *arg, int flags)
+static void cancel_event_helper(struct event_loop *m, void *arg, int flags)
{
struct cancel_req *cr;
@@ -1430,7 +1430,7 @@ static void cancel_event_helper(struct thread_master *m, void *arg, int flags)
frr_with_mutex (&m->mtx) {
cr->eventobj = arg;
listnode_add(m->cancel_req, cr);
- do_thread_cancel(m);
+ do_event_cancel(m);
}
}
@@ -1439,10 +1439,10 @@ static void cancel_event_helper(struct thread_master *m, void *arg, int flags)
*
* MT-Unsafe
*
- * @param m the thread_master to cancel from
+ * @param m the event_master to cancel from
* @param arg the argument passed when creating the event
*/
-void thread_cancel_event(struct thread_master *master, void *arg)
+void event_cancel_event(struct event_loop *master, void *arg)
{
cancel_event_helper(master, arg, 0);
}
@@ -1452,14 +1452,14 @@ void thread_cancel_event(struct thread_master *master, void *arg)
*
* MT-Unsafe
*
- * @param m the thread_master to cancel from
+ * @param m the event_master to cancel from
* @param arg the argument passed when creating the event
*/
-void thread_cancel_event_ready(struct thread_master *m, void *arg)
+void event_cancel_event_ready(struct event_loop *m, void *arg)
{
/* Only cancel ready/event tasks */
- cancel_event_helper(m, arg, THREAD_CANCEL_FLAG_READY);
+ cancel_event_helper(m, arg, EVENT_CANCEL_FLAG_READY);
}
/**
@@ -1469,19 +1469,19 @@ void thread_cancel_event_ready(struct thread_master *m, void *arg)
*
* @param thread task to cancel
*/
-void thread_cancel(struct thread **thread)
+void event_cancel(struct event **thread)
{
- struct thread_master *master;
+ struct event_loop *master;
if (thread == NULL || *thread == NULL)
return;
master = (*thread)->master;
- frrtrace(9, frr_libfrr, thread_cancel, master,
- (*thread)->xref->funcname, (*thread)->xref->xref.file,
- (*thread)->xref->xref.line, NULL, (*thread)->u.fd,
- (*thread)->u.val, (*thread)->arg, (*thread)->u.sands.tv_sec);
+ frrtrace(9, frr_libfrr, event_cancel, master, (*thread)->xref->funcname,
+ (*thread)->xref->xref.file, (*thread)->xref->xref.line, NULL,
+ (*thread)->u.fd, (*thread)->u.val, (*thread)->arg,
+ (*thread)->u.sands.tv_sec);
assert(master->owner == pthread_self());
@@ -1490,7 +1490,7 @@ void thread_cancel(struct thread **thread)
XCALLOC(MTYPE_TMP, sizeof(struct cancel_req));
cr->thread = *thread;
listnode_add(master->cancel_req, cr);
- do_thread_cancel(master);
+ do_event_cancel(master);
}
*thread = NULL;
@@ -1499,7 +1499,7 @@ void thread_cancel(struct thread **thread)
/**
* Asynchronous cancellation.
*
- * Called with either a struct thread ** or void * to an event argument,
+ * Called with either a struct event ** or void * to an event argument,
* this function posts the correct cancellation request and blocks until it is
* serviced.
*
@@ -1508,7 +1508,7 @@ void thread_cancel(struct thread **thread)
* The last two parameters are mutually exclusive, i.e. if you pass one the
* other must be NULL.
*
- * When the cancellation procedure executes on the target thread_master, the
+ * When the cancellation procedure executes on the target event_master, the
* thread * provided is checked for nullity. If it is null, the thread is
* assumed to no longer exist and the cancellation request is a no-op. Thus
* users of this API must pass a back-reference when scheduling the original
@@ -1520,19 +1520,19 @@ void thread_cancel(struct thread **thread)
* @param thread pointer to thread to cancel
* @param eventobj the event
*/
-void thread_cancel_async(struct thread_master *master, struct thread **thread,
- void *eventobj)
+void event_cancel_async(struct event_loop *master, struct event **thread,
+ void *eventobj)
{
assert(!(thread && eventobj) && (thread || eventobj));
if (thread && *thread)
- frrtrace(9, frr_libfrr, thread_cancel_async, master,
+ frrtrace(9, frr_libfrr, event_cancel_async, master,
(*thread)->xref->funcname, (*thread)->xref->xref.file,
(*thread)->xref->xref.line, NULL, (*thread)->u.fd,
(*thread)->u.val, (*thread)->arg,
(*thread)->u.sands.tv_sec);
else
- frrtrace(9, frr_libfrr, thread_cancel_async, master, NULL, NULL,
+ frrtrace(9, frr_libfrr, event_cancel_async, master, NULL, NULL,
0, NULL, 0, 0, eventobj, 0);
assert(master->owner != pthread_self());
@@ -1562,30 +1562,30 @@ void thread_cancel_async(struct thread_master *master, struct thread **thread,
}
/* ------------------------------------------------------------------------- */
-static struct timeval *thread_timer_wait(struct thread_timer_list_head *timers,
+static struct timeval *thread_timer_wait(struct event_timer_list_head *timers,
struct timeval *timer_val)
{
- if (!thread_timer_list_count(timers))
+ if (!event_timer_list_count(timers))
return NULL;
- struct thread *next_timer = thread_timer_list_first(timers);
+ struct event *next_timer = event_timer_list_first(timers);
+
monotime_until(&next_timer->u.sands, timer_val);
return timer_val;
}
-static struct thread *thread_run(struct thread_master *m, struct thread *thread,
- struct thread *fetch)
+static struct event *thread_run(struct event_loop *m, struct event *thread,
+ struct event *fetch)
{
*fetch = *thread;
thread_add_unuse(m, thread);
return fetch;
}
-static int thread_process_io_helper(struct thread_master *m,
- struct thread *thread, short state,
- short actual_state, int pos)
+static int thread_process_io_helper(struct event_loop *m, struct event *thread,
+ short state, short actual_state, int pos)
{
- struct thread **thread_array;
+ struct event **thread_array;
/*
* poll() clears the .events field, but the pollfd array we
@@ -1608,14 +1608,14 @@ static int thread_process_io_helper(struct thread_master *m,
return 0;
}
- if (thread->type == THREAD_READ)
+ if (thread->type == EVENT_READ)
thread_array = m->read;
else
thread_array = m->write;
thread_array[thread->u.fd] = NULL;
- thread_list_add_tail(&m->ready, thread);
- thread->type = THREAD_READY;
+ event_list_add_tail(&m->ready, thread);
+ thread->type = EVENT_READY;
return 1;
}
@@ -1629,7 +1629,7 @@ static int thread_process_io_helper(struct thread_master *m,
* @param m the thread master
* @param num the number of active file descriptors (return value of poll())
*/
-static void thread_process_io(struct thread_master *m, unsigned int num)
+static void thread_process_io(struct event_loop *m, unsigned int num)
{
unsigned int ready = 0;
struct pollfd *pfds = m->handler.copy;
@@ -1642,10 +1642,10 @@ static void thread_process_io(struct thread_master *m, unsigned int num)
ready++;
/*
- * Unless someone has called thread_cancel from another
+ * Unless someone has called event_cancel from another
* pthread, the only thing that could have changed in
* m->handler.pfds while we were asleep is the .events
- * field in a given pollfd. Barring thread_cancel() that
+ * field in a given pollfd. Barring event_cancel() that
* value should be a superset of the values we have in our
* copy, so there's no need to update it. Similarily,
* barring deletion, the fd should still be a valid index
@@ -1663,9 +1663,10 @@ static void thread_process_io(struct thread_master *m, unsigned int num)
thread_process_io_helper(m, m->write[pfds[i].fd],
POLLOUT, pfds[i].revents, i);
- /* if one of our file descriptors is garbage, remove the same
- * from
- * both pfds + update sizes and index */
+ /*
+ * if one of our file descriptors is garbage, remove the same
+ * from both pfds + update sizes and index
+ */
if (pfds[i].revents & POLLNVAL) {
memmove(m->handler.pfds + i, m->handler.pfds + i + 1,
(m->handler.pfdcount - i - 1)
@@ -1687,15 +1688,15 @@ static void thread_process_io(struct thread_master *m, unsigned int num)
}
/* Add all timers that have popped to the ready list. */
-static unsigned int thread_process_timers(struct thread_master *m,
+static unsigned int thread_process_timers(struct event_loop *m,
struct timeval *timenow)
{
struct timeval prev = *timenow;
bool displayed = false;
- struct thread *thread;
+ struct event *thread;
unsigned int ready = 0;
- while ((thread = thread_timer_list_first(&m->timer))) {
+ while ((thread = event_timer_list_first(&m->timer))) {
if (timercmp(timenow, &thread->u.sands, <))
break;
prev = thread->u.sands;
@@ -1719,9 +1720,9 @@ static unsigned int thread_process_timers(struct thread_master *m,
}
}
- thread_timer_list_pop(&m->timer);
- thread->type = THREAD_READY;
- thread_list_add_tail(&m->ready, thread);
+ event_timer_list_pop(&m->timer);
+ thread->type = EVENT_READY;
+ event_list_add_tail(&m->ready, thread);
ready++;
}
@@ -1729,14 +1730,14 @@ static unsigned int thread_process_timers(struct thread_master *m,
}
/* process a list en masse, e.g. for event thread lists */
-static unsigned int thread_process(struct thread_list_head *list)
+static unsigned int thread_process(struct event_list_head *list)
{
- struct thread *thread;
+ struct event *thread;
unsigned int ready = 0;
- while ((thread = thread_list_pop(list))) {
- thread->type = THREAD_READY;
- thread_list_add_tail(&thread->master->ready, thread);
+ while ((thread = event_list_pop(list))) {
+ thread->type = EVENT_READY;
+ event_list_add_tail(&thread->master->ready, thread);
ready++;
}
return ready;
@@ -1744,9 +1745,9 @@ static unsigned int thread_process(struct thread_list_head *list)
/* Fetch next ready thread. */
-struct thread *thread_fetch(struct thread_master *m, struct thread *fetch)
+struct event *event_fetch(struct event_loop *m, struct event *fetch)
{
- struct thread *thread = NULL;
+ struct event *thread = NULL;
struct timeval now;
struct timeval zerotime = {0, 0};
struct timeval tv;
@@ -1762,13 +1763,13 @@ struct thread *thread_fetch(struct thread_master *m, struct thread *fetch)
pthread_mutex_lock(&m->mtx);
/* Process any pending cancellation requests */
- do_thread_cancel(m);
+ do_event_cancel(m);
/*
* Attempt to flush ready queue before going into poll().
* This is performance-critical. Think twice before modifying.
*/
- if ((thread = thread_list_pop(&m->ready))) {
+ if ((thread = event_list_pop(&m->ready))) {
fetch = thread_run(m, thread, fetch);
if (fetch->ref)
*fetch->ref = NULL;
@@ -1804,11 +1805,11 @@ struct thread *thread_fetch(struct thread_master *m, struct thread *fetch)
* In every case except the last, we need to hit poll() at least
* once per loop to avoid starvation by events
*/
- if (!thread_list_count(&m->ready))
+ if (!event_list_count(&m->ready))
tw = thread_timer_wait(&m->timer, &tv);
- if (thread_list_count(&m->ready) ||
- (tw && !timercmp(tw, &zerotime, >)))
+ if (event_list_count(&m->ready) ||
+ (tw && !timercmp(tw, &zerotime, >)))
tw = &zerotime;
if (!tw && m->handler.pfdcount == 0) { /* die */
@@ -1869,8 +1870,8 @@ static unsigned long timeval_elapsed(struct timeval a, struct timeval b)
+ (a.tv_usec - b.tv_usec));
}
-unsigned long thread_consumed_time(RUSAGE_T *now, RUSAGE_T *start,
- unsigned long *cputime)
+unsigned long event_consumed_time(RUSAGE_T *now, RUSAGE_T *start,
+ unsigned long *cputime)
{
#ifdef HAVE_CLOCK_THREAD_CPUTIME_ID
@@ -1904,19 +1905,22 @@ unsigned long thread_consumed_time(RUSAGE_T *now, RUSAGE_T *start,
return timeval_elapsed(now->real, start->real);
}
-/* We should aim to yield after yield milliseconds, which defaults
- to THREAD_YIELD_TIME_SLOT .
- Note: we are using real (wall clock) time for this calculation.
- It could be argued that CPU time may make more sense in certain
- contexts. The things to consider are whether the thread may have
- blocked (in which case wall time increases, but CPU time does not),
- or whether the system is heavily loaded with other processes competing
- for CPU time. On balance, wall clock time seems to make sense.
- Plus it has the added benefit that gettimeofday should be faster
- than calling getrusage. */
-int thread_should_yield(struct thread *thread)
+/*
+ * We should aim to yield after yield milliseconds, which defaults
+ * to EVENT_YIELD_TIME_SLOT .
+ * Note: we are using real (wall clock) time for this calculation.
+ * It could be argued that CPU time may make more sense in certain
+ * contexts. The things to consider are whether the thread may have
+ * blocked (in which case wall time increases, but CPU time does not),
+ * or whether the system is heavily loaded with other processes competing
+ * for CPU time. On balance, wall clock time seems to make sense.
+ * Plus it has the added benefit that gettimeofday should be faster
+ * than calling getrusage.
+ */
+int event_should_yield(struct event *thread)
{
int result;
+
frr_with_mutex (&thread->mtx) {
result = monotime_since(&thread->real, NULL)
> (int64_t)thread->yield;
@@ -1924,14 +1928,14 @@ int thread_should_yield(struct thread *thread)
return result;
}
-void thread_set_yield_time(struct thread *thread, unsigned long yield_time)
+void event_set_yield_time(struct event *thread, unsigned long yield_time)
{
frr_with_mutex (&thread->mtx) {
thread->yield = yield_time;
}
}
-void thread_getrusage(RUSAGE_T *r)
+void event_getrusage(RUSAGE_T *r)
{
monotime(&r->real);
if (!cputime_enabled) {
@@ -1965,7 +1969,7 @@ void thread_getrusage(RUSAGE_T *r)
* particular, the maximum real and cpu times must be monotonically increasing
* or this code is not correct.
*/
-void thread_call(struct thread *thread)
+void event_call(struct event *thread)
{
RUSAGE_T before, after;
@@ -1982,10 +1986,10 @@ void thread_call(struct thread *thread)
thread->real = before.real;
- frrtrace(9, frr_libfrr, thread_call, thread->master,
+ frrtrace(9, frr_libfrr, event_call, thread->master,
thread->xref->funcname, thread->xref->xref.file,
- thread->xref->xref.line, NULL, thread->u.fd,
- thread->u.val, thread->arg, thread->u.sands.tv_sec);
+ thread->xref->xref.line, NULL, thread->u.fd, thread->u.val,
+ thread->arg, thread->u.sands.tv_sec);
pthread_setspecific(thread_current, thread);
(*thread->func)(thread);
@@ -1997,7 +2001,7 @@ void thread_call(struct thread *thread)
unsigned long walltime, cputime;
unsigned long exp;
- walltime = thread_consumed_time(&after, &before, &cputime);
+ walltime = event_consumed_time(&after, &before, &cputime);
/* update walltime */
atomic_fetch_add_explicit(&thread->hist->real.total, walltime,
@@ -2061,26 +2065,25 @@ void thread_call(struct thread *thread)
}
/* Execute thread */
-void _thread_execute(const struct xref_threadsched *xref,
- struct thread_master *m, void (*func)(struct thread *),
- void *arg, int val)
+void _event_execute(const struct xref_eventsched *xref, struct event_loop *m,
+ void (*func)(struct event *), void *arg, int val)
{
- struct thread *thread;
+ struct event *thread;
/* Get or allocate new thread to execute. */
frr_with_mutex (&m->mtx) {
- thread = thread_get(m, THREAD_EVENT, func, arg, xref);
+ thread = thread_get(m, EVENT_EVENT, func, arg, xref);
/* Set its event value. */
frr_with_mutex (&thread->mtx) {
- thread->add_type = THREAD_EXECUTE;
+ thread->add_type = EVENT_EXECUTE;
thread->u.val = val;
thread->ref = &thread;
}
}
/* Execute thread doing all accounting. */
- thread_call(thread);
+ event_call(thread);
/* Give back or free thread. */
thread_add_unuse(m, thread);
@@ -2133,16 +2136,13 @@ void debug_signals(const sigset_t *sigs)
}
static ssize_t printfrr_thread_dbg(struct fbuf *buf, struct printfrr_eargs *ea,
- const struct thread *thread)
-{
- static const char * const types[] = {
- [THREAD_READ] = "read",
- [THREAD_WRITE] = "write",
- [THREAD_TIMER] = "timer",
- [THREAD_EVENT] = "event",
- [THREAD_READY] = "ready",
- [THREAD_UNUSED] = "unused",
- [THREAD_EXECUTE] = "exec",
+ const struct event *thread)
+{
+ static const char *const types[] = {
+ [EVENT_READ] = "read", [EVENT_WRITE] = "write",
+ [EVENT_TIMER] = "timer", [EVENT_EVENT] = "event",
+ [EVENT_READY] = "ready", [EVENT_UNUSED] = "unused",
+ [EVENT_EXECUTE] = "exec",
};
ssize_t rv = 0;
char info[16] = "";
@@ -2158,14 +2158,19 @@ static ssize_t printfrr_thread_dbg(struct fbuf *buf, struct printfrr_eargs *ea,
rv += bprintfrr(buf, " INVALID(%u)", thread->type);
switch (thread->type) {
- case THREAD_READ:
- case THREAD_WRITE:
+ case EVENT_READ:
+ case EVENT_WRITE:
snprintfrr(info, sizeof(info), "fd=%d", thread->u.fd);
break;
- case THREAD_TIMER:
+ case EVENT_TIMER:
snprintfrr(info, sizeof(info), "r=%pTVMud", &thread->u.sands);
break;
+ case EVENT_READY:
+ case EVENT_EVENT:
+ case EVENT_UNUSED:
+ case EVENT_EXECUTE:
+ break;
}
rv += bprintfrr(buf, " %-12s %s() %s from %s:%d}", info,
@@ -2178,7 +2183,7 @@ printfrr_ext_autoreg_p("TH", printfrr_thread);
static ssize_t printfrr_thread(struct fbuf *buf, struct printfrr_eargs *ea,
const void *ptr)
{
- const struct thread *thread = ptr;
+ const struct event *thread = ptr;
struct timespec remain = {};
if (ea->fmt[0] == 'D') {
diff --git a/lib/ferr.c b/lib/ferr.c
index 5998befec2..33bcb075fa 100644
--- a/lib/ferr.c
+++ b/lib/ferr.c
@@ -180,9 +180,7 @@ void log_ref_init(void)
void log_ref_fini(void)
{
frr_with_mutex (&refs_mtx) {
- hash_clean(refs, NULL);
- hash_free(refs);
- refs = NULL;
+ hash_clean_and_free(&refs, NULL);
}
}
diff --git a/lib/filter_cli.c b/lib/filter_cli.c
index 7ef0d47f67..5c3dc5e49d 100644
--- a/lib/filter_cli.c
+++ b/lib/filter_cli.c
@@ -1272,9 +1272,6 @@ DEFPY_YANG(
pda.any = true;
}
- if (plist_is_dup(vty->candidate_config->dnode, &pda))
- return CMD_SUCCESS;
-
/*
* Create the prefix-list first, so we can generate sequence if
* none given (backward compatibility).
@@ -1326,6 +1323,7 @@ DEFPY_YANG(
vty, "./ipv4-prefix-length-lesser-or-equal",
NB_OP_DESTROY, NULL);
}
+ nb_cli_enqueue_change(vty, "./any", NB_OP_DESTROY, NULL);
} else {
nb_cli_enqueue_change(vty, "./any", NB_OP_CREATE, NULL);
}
@@ -1476,9 +1474,6 @@ DEFPY_YANG(
pda.any = true;
}
- if (plist_is_dup(vty->candidate_config->dnode, &pda))
- return CMD_SUCCESS;
-
/*
* Create the prefix-list first, so we can generate sequence if
* none given (backward compatibility).
@@ -1530,6 +1525,7 @@ DEFPY_YANG(
vty, "./ipv6-prefix-length-lesser-or-equal",
NB_OP_DESTROY, NULL);
}
+ nb_cli_enqueue_change(vty, "./any", NB_OP_DESTROY, NULL);
} else {
nb_cli_enqueue_change(vty, "./any", NB_OP_CREATE, NULL);
}
diff --git a/lib/filter_nb.c b/lib/filter_nb.c
index a14f232339..9511b8f5b5 100644
--- a/lib/filter_nb.c
+++ b/lib/filter_nb.c
@@ -456,24 +456,6 @@ bool plist_is_dup(const struct lyd_node *dnode, struct plist_dup_args *pda)
return pda->pda_found;
}
-static bool plist_is_dup_nb(const struct lyd_node *dnode)
-{
- const struct lyd_node *entry_dnode =
- yang_dnode_get_parent(dnode, "entry");
- struct plist_dup_args pda = {};
-
- /* Initialize. */
- pda.pda_type = yang_dnode_get_string(entry_dnode, "../type");
- pda.pda_name = yang_dnode_get_string(entry_dnode, "../name");
- pda.pda_action = yang_dnode_get_string(entry_dnode, "action");
- pda.pda_entry_dnode = entry_dnode;
-
- plist_dnode_to_prefix(entry_dnode, &pda.any, &pda.prefix, &pda.ge,
- &pda.le);
-
- return plist_is_dup(entry_dnode, &pda);
-}
-
/*
* XPath: /frr-filter:lib/access-list
*/
@@ -1331,13 +1313,6 @@ lib_prefix_list_entry_ipv4_prefix_modify(struct nb_cb_modify_args *args)
const struct lyd_node *plist_dnode =
yang_dnode_get_parent(args->dnode, "prefix-list");
- if (plist_is_dup_nb(args->dnode)) {
- snprintf(args->errmsg, args->errmsg_len,
- "duplicated prefix list value: %s",
- yang_dnode_get_string(args->dnode, NULL));
- return NB_ERR_VALIDATION;
- }
-
return prefix_list_nb_validate_v4_af_type(
plist_dnode, args->errmsg, args->errmsg_len);
}
@@ -1366,13 +1341,6 @@ lib_prefix_list_entry_ipv6_prefix_modify(struct nb_cb_modify_args *args)
const struct lyd_node *plist_dnode =
yang_dnode_get_parent(args->dnode, "prefix-list");
- if (plist_is_dup_nb(args->dnode)) {
- snprintf(args->errmsg, args->errmsg_len,
- "duplicated prefix list value: %s",
- yang_dnode_get_string(args->dnode, NULL));
- return NB_ERR_VALIDATION;
- }
-
return prefix_list_nb_validate_v6_af_type(
plist_dnode, args->errmsg, args->errmsg_len);
}
@@ -1404,13 +1372,6 @@ static int lib_prefix_list_entry_ipv4_prefix_length_greater_or_equal_modify(
const struct lyd_node *plist_dnode =
yang_dnode_get_parent(args->dnode, "prefix-list");
- if (plist_is_dup_nb(args->dnode)) {
- snprintf(args->errmsg, args->errmsg_len,
- "duplicated prefix list value: %s",
- yang_dnode_get_string(args->dnode, NULL));
- return NB_ERR_VALIDATION;
- }
-
return prefix_list_nb_validate_v4_af_type(
plist_dnode, args->errmsg, args->errmsg_len);
}
@@ -1448,13 +1409,6 @@ static int lib_prefix_list_entry_ipv4_prefix_length_lesser_or_equal_modify(
const struct lyd_node *plist_dnode =
yang_dnode_get_parent(args->dnode, "prefix-list");
- if (plist_is_dup_nb(args->dnode)) {
- snprintf(args->errmsg, args->errmsg_len,
- "duplicated prefix list value: %s",
- yang_dnode_get_string(args->dnode, NULL));
- return NB_ERR_VALIDATION;
- }
-
return prefix_list_nb_validate_v4_af_type(
plist_dnode, args->errmsg, args->errmsg_len);
}
@@ -1492,13 +1446,6 @@ static int lib_prefix_list_entry_ipv6_prefix_length_greater_or_equal_modify(
const struct lyd_node *plist_dnode =
yang_dnode_get_parent(args->dnode, "prefix-list");
- if (plist_is_dup_nb(args->dnode)) {
- snprintf(args->errmsg, args->errmsg_len,
- "duplicated prefix list value: %s",
- yang_dnode_get_string(args->dnode, NULL));
- return NB_ERR_VALIDATION;
- }
-
return prefix_list_nb_validate_v6_af_type(
plist_dnode, args->errmsg, args->errmsg_len);
}
@@ -1536,13 +1483,6 @@ static int lib_prefix_list_entry_ipv6_prefix_length_lesser_or_equal_modify(
const struct lyd_node *plist_dnode =
yang_dnode_get_parent(args->dnode, "prefix-list");
- if (plist_is_dup_nb(args->dnode)) {
- snprintf(args->errmsg, args->errmsg_len,
- "duplicated prefix list value: %s",
- yang_dnode_get_string(args->dnode, NULL));
- return NB_ERR_VALIDATION;
- }
-
return prefix_list_nb_validate_v6_af_type(
plist_dnode, args->errmsg, args->errmsg_len);
}
@@ -1574,16 +1514,11 @@ static int lib_prefix_list_entry_any_create(struct nb_cb_create_args *args)
struct prefix_list_entry *ple;
int type;
- if (args->event == NB_EV_VALIDATE) {
- if (plist_is_dup_nb(args->dnode)) {
- snprintf(args->errmsg, args->errmsg_len,
- "duplicated prefix list value: %s",
- yang_dnode_get_string(args->dnode, NULL));
- return NB_ERR_VALIDATION;
- }
-
+ /*
+ * If we have gotten to this point, it's legal
+ */
+ if (args->event == NB_EV_VALIDATE)
return NB_OK;
- }
if (args->event != NB_EV_APPLY)
return NB_OK;
@@ -1630,7 +1565,7 @@ static int lib_prefix_list_entry_any_destroy(struct nb_cb_destroy_args *args)
/* Start prefix entry update procedure. */
prefix_list_entry_update_start(ple);
- prefix_list_entry_set_empty(ple);
+ ple->any = false;
/* Finish prefix entry update procedure. */
prefix_list_entry_update_finish(ple);
diff --git a/lib/frr_pthread.c b/lib/frr_pthread.c
index 0c617238fa..c4ead01bf6 100644
--- a/lib/frr_pthread.c
+++ b/lib/frr_pthread.c
@@ -75,7 +75,7 @@ struct frr_pthread *frr_pthread_new(const struct frr_pthread_attr *attr,
/* initialize mutex */
pthread_mutex_init(&fpt->mtx, NULL);
/* create new thread master */
- fpt->master = thread_master_create(name);
+ fpt->master = event_master_create(name);
/* set attributes */
fpt->attr = *attr;
name = (name ? name : "Anonymous thread");
@@ -101,7 +101,7 @@ struct frr_pthread *frr_pthread_new(const struct frr_pthread_attr *attr,
static void frr_pthread_destroy_nolock(struct frr_pthread *fpt)
{
- thread_master_free(fpt->master);
+ event_master_free(fpt->master);
pthread_mutex_destroy(&fpt->mtx);
pthread_mutex_destroy(fpt->running_cond_mtx);
pthread_cond_destroy(fpt->running_cond);
@@ -224,14 +224,14 @@ void frr_pthread_stop_all(void)
*/
/* dummy task for sleeper pipe */
-static void fpt_dummy(struct thread *thread)
+static void fpt_dummy(struct event *thread)
{
}
/* poison pill task to end event loop */
-static void fpt_finish(struct thread *thread)
+static void fpt_finish(struct event *thread)
{
- struct frr_pthread *fpt = THREAD_ARG(thread);
+ struct frr_pthread *fpt = EVENT_ARG(thread);
atomic_store_explicit(&fpt->running, false, memory_order_relaxed);
}
@@ -239,7 +239,7 @@ static void fpt_finish(struct thread *thread)
/* stop function, called from other threads to halt this one */
static int fpt_halt(struct frr_pthread *fpt, void **res)
{
- thread_add_event(fpt->master, &fpt_finish, fpt, 0, NULL);
+ event_add_event(fpt->master, &fpt_finish, fpt, 0, NULL);
pthread_join(fpt->thread, res);
return 0;
@@ -281,7 +281,7 @@ static void *fpt_run(void *arg)
int sleeper[2];
pipe(sleeper);
- thread_add_read(fpt->master, &fpt_dummy, NULL, sleeper[0], NULL);
+ event_add_read(fpt->master, &fpt_dummy, NULL, sleeper[0], NULL);
fpt->master->handle_signals = false;
@@ -289,11 +289,11 @@ static void *fpt_run(void *arg)
frr_pthread_notify_running(fpt);
- struct thread task;
+ struct event task;
while (atomic_load_explicit(&fpt->running, memory_order_relaxed)) {
pthread_testcancel();
- if (thread_fetch(fpt->master, &task)) {
- thread_call(&task);
+ if (event_fetch(fpt->master, &task)) {
+ event_call(&task);
}
}
diff --git a/lib/frr_pthread.h b/lib/frr_pthread.h
index b1d08717fb..f91044dfae 100644
--- a/lib/frr_pthread.h
+++ b/lib/frr_pthread.h
@@ -11,7 +11,7 @@
#include "frratomic.h"
#include "memory.h"
#include "frrcu.h"
-#include "thread.h"
+#include "frrevent.h"
#ifdef __cplusplus
extern "C" {
@@ -41,7 +41,7 @@ struct frr_pthread {
struct rcu_thread *rcu_thread;
/* thread master for this pthread's thread.c event loop */
- struct thread_master *master;
+ struct event_loop *master;
/* caller-specified data; start & stop funcs, name, id */
struct frr_pthread_attr attr;
diff --git a/lib/frr_zmq.c b/lib/frr_zmq.c
index 2673d57605..b28dd7f1bb 100644
--- a/lib/frr_zmq.c
+++ b/lib/frr_zmq.c
@@ -15,7 +15,7 @@
#include <zebra.h>
#include <zmq.h>
-#include "thread.h"
+#include "frrevent.h"
#include "memory.h"
#include "frr_zmq.h"
#include "log.h"
@@ -43,9 +43,9 @@ void frrzmq_finish(void)
}
}
-static void frrzmq_read_msg(struct thread *t)
+static void frrzmq_read_msg(struct event *t)
{
- struct frrzmq_cb **cbp = THREAD_ARG(t);
+ struct frrzmq_cb **cbp = EVENT_ARG(t);
struct frrzmq_cb *cb;
zmq_msg_t msg;
unsigned partno;
@@ -138,8 +138,8 @@ static void frrzmq_read_msg(struct thread *t)
if (read)
frrzmq_check_events(cbp, &cb->write, ZMQ_POLLOUT);
- thread_add_read(t->master, frrzmq_read_msg, cbp,
- cb->fd, &cb->read.thread);
+ event_add_read(t->master, frrzmq_read_msg, cbp, cb->fd,
+ &cb->read.thread);
return;
out_err:
@@ -149,14 +149,13 @@ out_err:
cb->read.cb_error(cb->read.arg, cb->zmqsock);
}
-int _frrzmq_thread_add_read(const struct xref_threadsched *xref,
- struct thread_master *master,
- void (*msgfunc)(void *arg, void *zmqsock),
- void (*partfunc)(void *arg, void *zmqsock,
- zmq_msg_t *msg, unsigned partnum),
- void (*errfunc)(void *arg, void *zmqsock),
- void *arg, void *zmqsock,
- struct frrzmq_cb **cbp)
+int _frrzmq_event_add_read(const struct xref_eventsched *xref,
+ struct event_loop *master,
+ void (*msgfunc)(void *arg, void *zmqsock),
+ void (*partfunc)(void *arg, void *zmqsock,
+ zmq_msg_t *msg, unsigned partnum),
+ void (*errfunc)(void *arg, void *zmqsock), void *arg,
+ void *zmqsock, struct frrzmq_cb **cbp)
{
int fd, events;
size_t len;
@@ -191,19 +190,19 @@ int _frrzmq_thread_add_read(const struct xref_threadsched *xref,
cb->in_cb = false;
if (events & ZMQ_POLLIN) {
- thread_cancel(&cb->read.thread);
+ event_cancel(&cb->read.thread);
- thread_add_event(master, frrzmq_read_msg, cbp, fd,
- &cb->read.thread);
- } else
- thread_add_read(master, frrzmq_read_msg, cbp, fd,
+ event_add_event(master, frrzmq_read_msg, cbp, fd,
&cb->read.thread);
+ } else
+ event_add_read(master, frrzmq_read_msg, cbp, fd,
+ &cb->read.thread);
return 0;
}
-static void frrzmq_write_msg(struct thread *t)
+static void frrzmq_write_msg(struct event *t)
{
- struct frrzmq_cb **cbp = THREAD_ARG(t);
+ struct frrzmq_cb **cbp = EVENT_ARG(t);
struct frrzmq_cb *cb;
unsigned char written = 0;
int ret;
@@ -247,8 +246,8 @@ static void frrzmq_write_msg(struct thread *t)
if (written)
frrzmq_check_events(cbp, &cb->read, ZMQ_POLLIN);
- thread_add_write(t->master, frrzmq_write_msg, cbp,
- cb->fd, &cb->write.thread);
+ event_add_write(t->master, frrzmq_write_msg, cbp, cb->fd,
+ &cb->write.thread);
return;
out_err:
@@ -258,11 +257,11 @@ out_err:
cb->write.cb_error(cb->write.arg, cb->zmqsock);
}
-int _frrzmq_thread_add_write(const struct xref_threadsched *xref,
- struct thread_master *master,
- void (*msgfunc)(void *arg, void *zmqsock),
- void (*errfunc)(void *arg, void *zmqsock),
- void *arg, void *zmqsock, struct frrzmq_cb **cbp)
+int _frrzmq_event_add_write(const struct xref_eventsched *xref,
+ struct event_loop *master,
+ void (*msgfunc)(void *arg, void *zmqsock),
+ void (*errfunc)(void *arg, void *zmqsock),
+ void *arg, void *zmqsock, struct frrzmq_cb **cbp)
{
int fd, events;
size_t len;
@@ -297,13 +296,13 @@ int _frrzmq_thread_add_write(const struct xref_threadsched *xref,
cb->in_cb = false;
if (events & ZMQ_POLLOUT) {
- thread_cancel(&cb->write.thread);
+ event_cancel(&cb->write.thread);
- _thread_add_event(xref, master, frrzmq_write_msg, cbp, fd,
- &cb->write.thread);
- } else
- thread_add_write(master, frrzmq_write_msg, cbp, fd,
+ _event_add_event(xref, master, frrzmq_write_msg, cbp, fd,
&cb->write.thread);
+ } else
+ event_add_write(master, frrzmq_write_msg, cbp, fd,
+ &cb->write.thread);
return 0;
}
@@ -312,7 +311,7 @@ void frrzmq_thread_cancel(struct frrzmq_cb **cb, struct cb_core *core)
if (!cb || !*cb)
return;
core->cancelled = true;
- thread_cancel(&core->thread);
+ event_cancel(&core->thread);
/* If cancelled from within a callback, don't try to free memory
* in this path.
@@ -343,15 +342,15 @@ void frrzmq_check_events(struct frrzmq_cb **cbp, struct cb_core *core,
if (zmq_getsockopt(cb->zmqsock, ZMQ_EVENTS, &events, &len))
return;
if ((events & event) && core->thread && !core->cancelled) {
- struct thread_master *tm = core->thread->master;
+ struct event_loop *tm = core->thread->master;
- thread_cancel(&core->thread);
+ event_cancel(&core->thread);
if (event == ZMQ_POLLIN)
- thread_add_event(tm, frrzmq_read_msg,
- cbp, cb->fd, &core->thread);
+ event_add_event(tm, frrzmq_read_msg, cbp, cb->fd,
+ &core->thread);
else
- thread_add_event(tm, frrzmq_write_msg,
- cbp, cb->fd, &core->thread);
+ event_add_event(tm, frrzmq_write_msg, cbp, cb->fd,
+ &core->thread);
}
}
diff --git a/lib/frr_zmq.h b/lib/frr_zmq.h
index f12291d602..73da3770f4 100644
--- a/lib/frr_zmq.h
+++ b/lib/frr_zmq.h
@@ -7,7 +7,7 @@
#ifndef _FRRZMQ_H
#define _FRRZMQ_H
-#include "thread.h"
+#include "frrevent.h"
#include <zmq.h>
#ifdef __cplusplus
@@ -26,7 +26,7 @@ extern "C" {
/* callback integration */
struct cb_core {
- struct thread *thread;
+ struct event *thread;
void *arg;
bool cancelled;
@@ -59,30 +59,29 @@ extern void frrzmq_finish(void);
#define _xref_zmq_a(type, f, d, call) \
({ \
- static const struct xref_threadsched _xref \
- __attribute__((used)) = { \
- .xref = XREF_INIT(XREFT_THREADSCHED, NULL, __func__), \
+ static const struct xref_eventsched _xref __attribute__( \
+ (used)) = { \
+ .xref = XREF_INIT(XREFT_EVENTSCHED, NULL, __func__), \
.funcname = #f, \
.dest = #d, \
- .thread_type = THREAD_ ## type, \
+ .event_type = EVENT_##type, \
}; \
XREF_LINK(_xref.xref); \
call; \
- }) \
- /* end */
+ }) /* end */
/* core event registration, one of these 2 macros should be used */
-#define frrzmq_thread_add_read_msg(m, f, e, a, z, d) \
+#define frrzmq_event_add_read_msg(m, f, e, a, z, d) \
_xref_zmq_a(READ, f, d, \
- _frrzmq_thread_add_read(&_xref, m, f, NULL, e, a, z, d))
+ _frrzmq_event_add_read(&_xref, m, f, NULL, e, a, z, d))
-#define frrzmq_thread_add_read_part(m, f, e, a, z, d) \
+#define frrzmq_event_add_read_part(m, f, e, a, z, d) \
_xref_zmq_a(READ, f, d, \
- _frrzmq_thread_add_read(&_xref, m, NULL, f, e, a, z, d))
+ _frrzmq_event_add_read(&_xref, m, NULL, f, e, a, z, d))
-#define frrzmq_thread_add_write_msg(m, f, e, a, z, d) \
+#define frrzmq_event_add_write_msg(m, f, e, a, z, d) \
_xref_zmq_a(WRITE, f, d, \
- _frrzmq_thread_add_write(&_xref, m, f, e, a, z, d))
+ _frrzmq_event_add_write(&_xref, m, f, e, a, z, d))
struct cb_core;
struct frrzmq_cb;
@@ -108,18 +107,20 @@ struct frrzmq_cb;
* may schedule the event to run as soon as libfrr is back in its main
* loop.
*/
-extern int _frrzmq_thread_add_read(
- const struct xref_threadsched *xref, struct thread_master *master,
- void (*msgfunc)(void *arg, void *zmqsock),
- void (*partfunc)(void *arg, void *zmqsock, zmq_msg_t *msg,
- unsigned partnum),
- void (*errfunc)(void *arg, void *zmqsock), void *arg, void *zmqsock,
- struct frrzmq_cb **cb);
-extern int _frrzmq_thread_add_write(
- const struct xref_threadsched *xref, struct thread_master *master,
- void (*msgfunc)(void *arg, void *zmqsock),
- void (*errfunc)(void *arg, void *zmqsock), void *arg, void *zmqsock,
- struct frrzmq_cb **cb);
+extern int
+_frrzmq_event_add_read(const struct xref_eventsched *xref,
+ struct event_loop *master,
+ void (*msgfunc)(void *arg, void *zmqsock),
+ void (*partfunc)(void *arg, void *zmqsock,
+ zmq_msg_t *msg, unsigned partnum),
+ void (*errfunc)(void *arg, void *zmqsock), void *arg,
+ void *zmqsock, struct frrzmq_cb **cb);
+extern int _frrzmq_event_add_write(const struct xref_eventsched *xref,
+ struct event_loop *master,
+ void (*msgfunc)(void *arg, void *zmqsock),
+ void (*errfunc)(void *arg, void *zmqsock),
+ void *arg, void *zmqsock,
+ struct frrzmq_cb **cb);
extern void frrzmq_thread_cancel(struct frrzmq_cb **cb, struct cb_core *core);
diff --git a/lib/frrevent.h b/lib/frrevent.h
new file mode 100644
index 0000000000..2b0c52bb51
--- /dev/null
+++ b/lib/frrevent.h
@@ -0,0 +1,295 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* Event management routine header.
+ * Copyright (C) 1998 Kunihiro Ishiguro
+ */
+
+#ifndef _ZEBRA_THREAD_H
+#define _ZEBRA_THREAD_H
+
+#include <zebra.h>
+#include <pthread.h>
+#include <poll.h>
+#include "monotime.h"
+#include "frratomic.h"
+#include "typesafe.h"
+#include "xref.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern bool cputime_enabled;
+extern unsigned long cputime_threshold;
+/* capturing wallclock time is always enabled since it is fast (reading
+ * hardware TSC w/o syscalls)
+ */
+extern unsigned long walltime_threshold;
+
+struct rusage_t {
+#ifdef HAVE_CLOCK_THREAD_CPUTIME_ID
+ struct timespec cpu;
+#else
+ struct rusage cpu;
+#endif
+ struct timeval real;
+};
+#define RUSAGE_T struct rusage_t
+
+#define GETRUSAGE(X) event_getrusage(X)
+
+PREDECL_LIST(event_list);
+PREDECL_HEAP(event_timer_list);
+
+struct fd_handler {
+ /* number of pfd that fit in the allocated space of pfds. This is a
+ * constant and is the same for both pfds and copy.
+ */
+ nfds_t pfdsize;
+
+ /* file descriptors to monitor for i/o */
+ struct pollfd *pfds;
+ /* number of pollfds stored in pfds */
+ nfds_t pfdcount;
+
+ /* chunk used for temp copy of pollfds */
+ struct pollfd *copy;
+ /* number of pollfds stored in copy */
+ nfds_t copycount;
+};
+
+struct xref_eventsched {
+ struct xref xref;
+
+ const char *funcname;
+ const char *dest;
+ uint32_t event_type;
+};
+
+/* Master of the theads. */
+struct event_loop {
+ char *name;
+
+ struct event **read;
+ struct event **write;
+ struct event_timer_list_head timer;
+ struct event_list_head event, ready, unuse;
+ struct list *cancel_req;
+ bool canceled;
+ pthread_cond_t cancel_cond;
+ struct hash *cpu_record;
+ int io_pipe[2];
+ int fd_limit;
+ struct fd_handler handler;
+ unsigned long alloc;
+ long selectpoll_timeout;
+ bool spin;
+ bool handle_signals;
+ pthread_mutex_t mtx;
+ pthread_t owner;
+
+ bool ready_run_loop;
+ RUSAGE_T last_getrusage;
+};
+
+/* Event types. */
+enum event_types {
+ EVENT_READ,
+ EVENT_WRITE,
+ EVENT_TIMER,
+ EVENT_EVENT,
+ EVENT_READY,
+ EVENT_UNUSED,
+ EVENT_EXECUTE,
+};
+
+/* Event itself. */
+struct event {
+ enum event_types type; /* event type */
+ enum event_types add_type; /* event type */
+ struct event_list_item eventitem;
+ struct event_timer_list_item timeritem;
+ struct event **ref; /* external reference (if given) */
+ struct event_loop *master; /* pointer to the struct event_loop */
+ void (*func)(struct event *e); /* event function */
+ void *arg; /* event argument */
+ union {
+ int val; /* second argument of the event. */
+ int fd; /* file descriptor in case of r/w */
+ struct timeval sands; /* rest of time sands value. */
+ } u;
+ struct timeval real;
+ struct cpu_event_history *hist; /* cache pointer to cpu_history */
+ unsigned long yield; /* yield time in microseconds */
+ const struct xref_eventsched *xref; /* origin location */
+ pthread_mutex_t mtx; /* mutex for thread.c functions */
+ bool ignore_timer_late;
+};
+
+#ifdef _FRR_ATTRIBUTE_PRINTFRR
+#pragma FRR printfrr_ext "%pTH"(struct event *)
+#endif
+
+struct cpu_event_history {
+ void (*func)(struct event *e);
+ atomic_size_t total_cpu_warn;
+ atomic_size_t total_wall_warn;
+ atomic_size_t total_starv_warn;
+ atomic_size_t total_calls;
+ atomic_size_t total_active;
+ struct time_stats {
+ atomic_size_t total, max;
+ } real;
+ struct time_stats cpu;
+ atomic_uint_fast32_t types;
+ const char *funcname;
+};
+
+/* Struct timeval's tv_usec one second value. */
+#define TIMER_SECOND_MICRO 1000000L
+
+/* Event yield time. */
+#define EVENT_YIELD_TIME_SLOT 10 * 1000L /* 10ms */
+
+#define EVENT_TIMER_STRLEN 12
+
+/* Macros. */
+#define EVENT_ARG(X) ((X)->arg)
+#define EVENT_FD(X) ((X)->u.fd)
+#define EVENT_VAL(X) ((X)->u.val)
+
+/*
+ * Please consider this macro deprecated, and do not use it in new code.
+ */
+#define EVENT_OFF(thread) \
+ do { \
+ if ((thread)) \
+ event_cancel(&(thread)); \
+ } while (0)
+
+/*
+ * Macro wrappers to generate xrefs for all thread add calls. Includes
+ * file/line/function info for debugging/tracing.
+ */
+#include "lib/xref.h"
+
+#define _xref_t_a(addfn, type, m, f, a, v, t) \
+ ({ \
+ static const struct xref_eventsched _xref __attribute__( \
+ (used)) = { \
+ .xref = XREF_INIT(XREFT_EVENTSCHED, NULL, __func__), \
+ .funcname = #f, \
+ .dest = #t, \
+ .event_type = EVENT_##type, \
+ }; \
+ XREF_LINK(_xref.xref); \
+ _event_add_##addfn(&_xref, m, f, a, v, t); \
+ }) /* end */
+
+#define event_add_read(m, f, a, v, t) _xref_t_a(read_write, READ, m, f, a, v, t)
+#define event_add_write(m, f, a, v, t) \
+ _xref_t_a(read_write, WRITE, m, f, a, v, t)
+#define event_add_timer(m, f, a, v, t) _xref_t_a(timer, TIMER, m, f, a, v, t)
+#define event_add_timer_msec(m, f, a, v, t) \
+ _xref_t_a(timer_msec, TIMER, m, f, a, v, t)
+#define event_add_timer_tv(m, f, a, v, t) \
+ _xref_t_a(timer_tv, TIMER, m, f, a, v, t)
+#define event_add_event(m, f, a, v, t) _xref_t_a(event, EVENT, m, f, a, v, t)
+
+#define event_execute(m, f, a, v) \
+ ({ \
+ static const struct xref_eventsched _xref __attribute__( \
+ (used)) = { \
+ .xref = XREF_INIT(XREFT_EVENTSCHED, NULL, __func__), \
+ .funcname = #f, \
+ .dest = NULL, \
+ .event_type = EVENT_EXECUTE, \
+ }; \
+ XREF_LINK(_xref.xref); \
+ _event_execute(&_xref, m, f, a, v); \
+ }) /* end */
+
+/* Prototypes. */
+extern struct event_loop *event_master_create(const char *name);
+void event_master_set_name(struct event_loop *master, const char *name);
+extern void event_master_free(struct event_loop *m);
+extern void event_master_free_unused(struct event_loop *m);
+
+extern void _event_add_read_write(const struct xref_eventsched *xref,
+ struct event_loop *master,
+ void (*fn)(struct event *), void *arg, int fd,
+ struct event **tref);
+
+extern void _event_add_timer(const struct xref_eventsched *xref,
+ struct event_loop *master,
+ void (*fn)(struct event *), void *arg, long t,
+ struct event **tref);
+
+extern void _event_add_timer_msec(const struct xref_eventsched *xref,
+ struct event_loop *master,
+ void (*fn)(struct event *), void *arg, long t,
+ struct event **tref);
+
+extern void _event_add_timer_tv(const struct xref_eventsched *xref,
+ struct event_loop *master,
+ void (*fn)(struct event *), void *arg,
+ struct timeval *tv, struct event **tref);
+
+extern void _event_add_event(const struct xref_eventsched *xref,
+ struct event_loop *master,
+ void (*fn)(struct event *), void *arg, int val,
+ struct event **tref);
+
+extern void _event_execute(const struct xref_eventsched *xref,
+ struct event_loop *master,
+ void (*fn)(struct event *), void *arg, int val);
+
+extern void event_cancel(struct event **event);
+extern void event_cancel_async(struct event_loop *m, struct event **eptr,
+ void *data);
+/* Cancel ready tasks with an arg matching 'arg' */
+extern void event_cancel_event_ready(struct event_loop *m, void *arg);
+/* Cancel all tasks with an arg matching 'arg', including timers and io */
+extern void event_cancel_event(struct event_loop *m, void *arg);
+extern struct event *event_fetch(struct event_loop *m, struct event *event);
+extern void event_call(struct event *event);
+extern unsigned long event_timer_remain_second(struct event *event);
+extern struct timeval event_timer_remain(struct event *event);
+extern unsigned long event_timer_remain_msec(struct event *event);
+extern int event_should_yield(struct event *event);
+/* set yield time for thread */
+extern void event_set_yield_time(struct event *event, unsigned long ytime);
+
+/* Internal libfrr exports */
+extern void event_getrusage(RUSAGE_T *r);
+extern void event_cmd_init(void);
+
+/* Returns elapsed real (wall clock) time. */
+extern unsigned long event_consumed_time(RUSAGE_T *after, RUSAGE_T *before,
+ unsigned long *cpu_time_elapsed);
+
+/* only for use in logging functions! */
+extern pthread_key_t thread_current;
+extern char *event_timer_to_hhmmss(char *buf, int buf_size,
+ struct event *t_timer);
+
+static inline bool event_is_scheduled(struct event *thread)
+{
+ if (thread)
+ return true;
+
+ return false;
+}
+
+/* Debug signal mask */
+void debug_signals(const sigset_t *sigs);
+
+static inline void event_ignore_late_timer(struct event *event)
+{
+ event->ignore_timer_late = true;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _ZEBRA_THREAD_H */
diff --git a/lib/frrscript.c b/lib/frrscript.c
index 4248a45002..1b99c7e2c6 100644
--- a/lib/frrscript.c
+++ b/lib/frrscript.c
@@ -398,8 +398,7 @@ fail:
void frrscript_delete(struct frrscript *fs)
{
- hash_clean(fs->lua_function_hash, lua_function_free);
- hash_free(fs->lua_function_hash);
+ hash_clean_and_free(&fs->lua_function_hash, lua_function_free);
XFREE(MTYPE_SCRIPT, fs->name);
XFREE(MTYPE_SCRIPT, fs);
}
@@ -417,8 +416,7 @@ void frrscript_init(const char *sd)
void frrscript_fini(void)
{
- hash_clean(codec_hash, codec_free);
- hash_free(codec_hash);
+ hash_clean_and_free(&codec_hash, codec_free);
frrscript_names_destroy();
}
diff --git a/lib/getopt.c b/lib/getopt.c
deleted file mode 100644
index 9d0a311310..0000000000
--- a/lib/getopt.c
+++ /dev/null
@@ -1,1011 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/* Getopt for GNU.
- * NOTE: getopt is now part of the C library, so if you don't know what
- * "Keep this file name-space clean" means, talk to drepper@gnu.org
- * before changing it!
- *
- * Copyright (C) 1987, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98
- * Free Software Foundation, Inc.
- *
- * NOTE: The canonical source of this file is maintained with the GNU C Library.
- * Bugs can be reported to bug-glibc@gnu.org.
- */
-
-/* This tells Alpha OSF/1 not to define a getopt prototype in <stdio.h>.
- Ditto for AIX 3.2 and <stdlib.h>. */
-#ifndef _NO_PROTO
-# define _NO_PROTO
-#endif
-
-#include <zebra.h>
-
-#if !defined __STDC__ || !__STDC__
-/* This is a separate conditional since some stdc systems
- reject `defined (const)'. */
-#ifndef const
-# define const
-#endif
-#endif
-
-#include <stdio.h>
-
-/* Comment out all this code if we are using the GNU C Library, and are not
- actually compiling the library itself. This code is part of the GNU C
- Library, but also included in many other GNU distributions. Compiling
- and linking in this code is a waste when using the GNU C library
- (especially if it is a shared library). Rather than having every GNU
- program understand `configure --with-gnu-libc' and omit the object files,
- it is simpler to just do this in the source for each such file. */
-
-#define GETOPT_INTERFACE_VERSION 2
-#if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2
-#include <gnu-versions.h>
-#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION
-# define ELIDE_CODE
-#endif
-#endif
-
-#ifndef ELIDE_CODE
-
-
-/* This needs to come after some library #include
- to get __GNU_LIBRARY__ defined. */
-#ifdef __GNU_LIBRARY__
-/* Don't include stdlib.h for non-GNU C libraries because some of them
- contain conflicting prototypes for getopt. */
-#include <stdlib.h>
-#include <unistd.h>
-#endif /* GNU C library. */
-
-#ifdef VMS
-#include <unixlib.h>
-#if HAVE_STRING_H - 0
-#include <string.h>
-#endif
-#endif
-
-#ifndef _
-/* This is for other GNU distributions with internationalized messages.
- When compiling libc, the _ macro is predefined. */
-#ifdef HAVE_LIBINTL_H
-#include <libintl.h>
-# define _(msgid) gettext (msgid)
-#else
-# define _(msgid) (msgid)
-#endif
-#endif
-
-/* This version of `getopt' appears to the caller like standard Unix `getopt'
- but it behaves differently for the user, since it allows the user
- to intersperse the options with the other arguments.
-
- As `getopt' works, it permutes the elements of ARGV so that,
- when it is done, all the options precede everything else. Thus
- all application programs are extended to handle flexible argument order.
-
- Setting the environment variable POSIXLY_CORRECT disables permutation.
- Then the behavior is completely standard.
-
- GNU application programs can use a third alternative mode in which
- they can distinguish the relative order of options and other arguments. */
-
-#include "getopt.h"
-
-/* For communication from `getopt' to the caller.
- When `getopt' finds an option that takes an argument,
- the argument value is returned here.
- Also, when `ordering' is RETURN_IN_ORDER,
- each non-option ARGV-element is returned here. */
-
-char *optarg = NULL;
-
-/* Index in ARGV of the next element to be scanned.
- This is used for communication to and from the caller
- and for communication between successive calls to `getopt'.
-
- On entry to `getopt', zero means this is the first call; initialize.
-
- When `getopt' returns -1, this is the index of the first of the
- non-option elements that the caller should itself scan.
-
- Otherwise, `optind' communicates from one call to the next
- how much of ARGV has been scanned so far. */
-
-/* 1003.2 says this must be 1 before any call. */
-int optind = 1;
-
-/* Formerly, initialization of getopt depended on optind==0, which
- causes problems with re-calling getopt as programs generally don't
- know that. */
-
-int __getopt_initialized = 0;
-
-/* The next char to be scanned in the option-element
- in which the last option character we returned was found.
- This allows us to pick up the scan where we left off.
-
- If this is zero, or a null string, it means resume the scan
- by advancing to the next ARGV-element. */
-
-static char *nextchar;
-
-/* Callers store zero here to inhibit the error message
- for unrecognized options. */
-
-int opterr = 1;
-
-/* Set to an option character which was unrecognized.
- This must be initialized on some systems to avoid linking in the
- system's own getopt implementation. */
-
-int optopt = '?';
-
-/* Describe how to deal with options that follow non-option ARGV-elements.
-
- If the caller did not specify anything,
- the default is REQUIRE_ORDER if the environment variable
- POSIXLY_CORRECT is defined, PERMUTE otherwise.
-
- REQUIRE_ORDER means don't recognize them as options;
- stop option processing when the first non-option is seen.
- This is what Unix does.
- This mode of operation is selected by either setting the environment
- variable POSIXLY_CORRECT, or using `+' as the first character
- of the list of option characters.
-
- PERMUTE is the default. We permute the contents of ARGV as we scan,
- so that eventually all the non-options are at the end. This allows options
- to be given in any order, even with programs that were not written to
- expect this.
-
- RETURN_IN_ORDER is an option available to programs that were written
- to expect options and other ARGV-elements in any order and that care about
- the ordering of the two. We describe each non-option ARGV-element
- as if it were the argument of an option with character code 1.
- Using `-' as the first character of the list of option characters
- selects this mode of operation.
-
- The special argument `--' forces an end of option-scanning regardless
- of the value of `ordering'. In the case of RETURN_IN_ORDER, only
- `--' can cause `getopt' to return -1 with `optind' != ARGC. */
-
-static enum { REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER } ordering;
-
-/* Value of POSIXLY_CORRECT environment variable. */
-static char *posixly_correct;
-
-#ifdef __GNU_LIBRARY__
-/* We want to avoid inclusion of string.h with non-GNU libraries
- because there are many ways it can cause trouble.
- On some systems, it contains special magic macros that don't work
- in GCC. */
-#include <string.h>
-# define my_index strchr
-#else
-
-#if HAVE_STRING_H
-#include <string.h>
-#else
-#include <strings.h>
-#endif
-
-/* Avoid depending on library functions or files
- whose names are inconsistent. */
-
-#ifndef getenv
-extern char *getenv(const char *);
-#endif
-
-static char *my_index(const char *str, int chr)
-{
- while (*str) {
- if (*str == chr)
- return (char *)str;
- str++;
- }
- return 0;
-}
-
-/* If using GCC, we can safely declare strlen this way.
- If not using GCC, it is ok not to declare it. */
-#ifdef __GNUC__
-/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h.
- That was relevant to code that was here before. */
-#if (!defined __STDC__ || !__STDC__) && !defined strlen
-/* gcc with -traditional declares the built-in strlen to return int,
- and has done so at least since version 2.4.5. -- rms. */
-extern int strlen(const char *);
-#endif /* not __STDC__ */
-#endif /* __GNUC__ */
-
-#endif /* not __GNU_LIBRARY__ */
-
-/* Handle permutation of arguments. */
-
-/* Describe the part of ARGV that contains non-options that have
- been skipped. `first_nonopt' is the index in ARGV of the first of them;
- `last_nonopt' is the index after the last of them. */
-
-static int first_nonopt;
-static int last_nonopt;
-
-#ifdef _LIBC
-/* Bash 2.0 gives us an environment variable containing flags
- indicating ARGV elements that should not be considered arguments. */
-
-/* Defined in getopt_init.c */
-extern char *__getopt_nonoption_flags;
-
-static int nonoption_flags_max_len;
-static int nonoption_flags_len;
-
-static int original_argc;
-static char *const *original_argv;
-
-/* Make sure the environment variable bash 2.0 puts in the environment
- is valid for the getopt call we must make sure that the ARGV passed
- to getopt is that one passed to the process. */
-static void __attribute__((unused))
-store_args_and_env(int argc, char *const *argv)
-{
- /* XXX This is no good solution. We should rather copy the args so
- that we can compare them later. But we must not use malloc(3). */
- original_argc = argc;
- original_argv = argv;
-}
-#ifdef text_set_element
-text_set_element(__libc_subinit, store_args_and_env);
-#endif /* text_set_element */
-
-#define SWAP_FLAGS(ch1, ch2) \
- if (nonoption_flags_len > 0) { \
- char __tmp = __getopt_nonoption_flags[ch1]; \
- __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2]; \
- __getopt_nonoption_flags[ch2] = __tmp; \
- }
-#else /* !_LIBC */
-# define SWAP_FLAGS(ch1, ch2)
-#endif /* _LIBC */
-
-/* Exchange two adjacent subsequences of ARGV.
- One subsequence is elements [first_nonopt,last_nonopt)
- which contains all the non-options that have been skipped so far.
- The other is elements [last_nonopt,optind), which contains all
- the options processed since those non-options were skipped.
-
- `first_nonopt' and `last_nonopt' are relocated so that they describe
- the new indices of the non-options in ARGV after they are moved. */
-
-#if defined __STDC__ && __STDC__
-static void exchange(char **);
-#endif
-
-static void exchange(argv) char **argv;
-{
- int bottom = first_nonopt;
- int middle = last_nonopt;
- int top = optind;
- char *tem;
-
-/* Exchange the shorter segment with the far end of the longer segment.
- That puts the shorter segment into the right place.
- It leaves the longer segment in the right place overall,
- but it consists of two parts that need to be swapped next. */
-
-#ifdef _LIBC
- /* First make sure the handling of the `__getopt_nonoption_flags'
- string can work normally. Our top argument must be in the range
- of the string. */
- if (nonoption_flags_len > 0 && top >= nonoption_flags_max_len) {
- /* We must extend the array. The user plays games with us and
- presents new arguments. */
- char *new_str = malloc(top + 1);
- if (new_str == NULL)
- nonoption_flags_len = nonoption_flags_max_len = 0;
- else {
- memset(__mempcpy(new_str, __getopt_nonoption_flags,
- nonoption_flags_max_len),
- '\0', top + 1 - nonoption_flags_max_len);
- nonoption_flags_max_len = top + 1;
- __getopt_nonoption_flags = new_str;
- }
- }
-#endif
-
- while (top > middle && middle > bottom) {
- if (top - middle > middle - bottom) {
- /* Bottom segment is the short one. */
- int len = middle - bottom;
- register int i;
-
- /* Swap it with the top part of the top segment. */
- for (i = 0; i < len; i++) {
- tem = argv[bottom + i];
- argv[bottom + i] =
- argv[top - (middle - bottom) + i];
- argv[top - (middle - bottom) + i] = tem;
- SWAP_FLAGS(bottom + i,
- top - (middle - bottom) + i);
- }
- /* Exclude the moved bottom segment from further
- * swapping. */
- top -= len;
- } else {
- /* Top segment is the short one. */
- int len = top - middle;
- register int i;
-
- /* Swap it with the bottom part of the bottom segment.
- */
- for (i = 0; i < len; i++) {
- tem = argv[bottom + i];
- argv[bottom + i] = argv[middle + i];
- argv[middle + i] = tem;
- SWAP_FLAGS(bottom + i, middle + i);
- }
- /* Exclude the moved top segment from further swapping.
- */
- bottom += len;
- }
- }
-
- /* Update records for the slots the non-options now occupy. */
-
- first_nonopt += (optind - last_nonopt);
- last_nonopt = optind;
-}
-
-/* Initialize the internal data when the first call is made. */
-
-#if defined __STDC__ && __STDC__
-static const char *_getopt_initialize(int, char *const *, const char *);
-#endif
-static const char *_getopt_initialize(argc, argv, optstring) int argc;
-char *const *argv;
-const char *optstring;
-{
- /* Start processing options with ARGV-element 1 (since ARGV-element 0
- is the program name); the sequence of previously skipped
- non-option ARGV-elements is empty. */
-
- first_nonopt = last_nonopt = optind;
-
- nextchar = NULL;
-
- posixly_correct = getenv("POSIXLY_CORRECT");
-
- /* Determine how to handle the ordering of options and nonoptions. */
-
- if (optstring[0] == '-') {
- ordering = RETURN_IN_ORDER;
- ++optstring;
- } else if (optstring[0] == '+') {
- ordering = REQUIRE_ORDER;
- ++optstring;
- } else if (posixly_correct != NULL)
- ordering = REQUIRE_ORDER;
- else
- ordering = PERMUTE;
-
-#ifdef _LIBC
- if (posixly_correct == NULL && argc == original_argc
- && argv == original_argv) {
- if (nonoption_flags_max_len == 0) {
- if (__getopt_nonoption_flags == NULL
- || __getopt_nonoption_flags[0] == '\0')
- nonoption_flags_max_len = -1;
- else {
- const char *orig_str = __getopt_nonoption_flags;
- int len = nonoption_flags_max_len =
- strlen(orig_str);
- if (nonoption_flags_max_len < argc)
- nonoption_flags_max_len = argc;
- __getopt_nonoption_flags =
- (char *)malloc(nonoption_flags_max_len);
- if (__getopt_nonoption_flags == NULL)
- nonoption_flags_max_len = -1;
- else
- memset(__mempcpy(
- __getopt_nonoption_flags,
- orig_str, len),
- '\0',
- nonoption_flags_max_len - len);
- }
- }
- nonoption_flags_len = nonoption_flags_max_len;
- } else
- nonoption_flags_len = 0;
-#endif
-
- return optstring;
-}
-
-/* Scan elements of ARGV (whose length is ARGC) for option characters
- given in OPTSTRING.
-
- If an element of ARGV starts with '-', and is not exactly "-" or "--",
- then it is an option element. The characters of this element
- (aside from the initial '-') are option characters. If `getopt'
- is called repeatedly, it returns successively each of the option characters
- from each of the option elements.
-
- If `getopt' finds another option character, it returns that character,
- updating `optind' and `nextchar' so that the next call to `getopt' can
- resume the scan with the following option character or ARGV-element.
-
- If there are no more option characters, `getopt' returns -1.
- Then `optind' is the index in ARGV of the first ARGV-element
- that is not an option. (The ARGV-elements have been permuted
- so that those that are not options now come last.)
-
- OPTSTRING is a string containing the legitimate option characters.
- If an option character is seen that is not listed in OPTSTRING,
- return '?' after printing an error message. If you set `opterr' to
- zero, the error message is suppressed but we still return '?'.
-
- If a char in OPTSTRING is followed by a colon, that means it wants an arg,
- so the following text in the same ARGV-element, or the text of the following
- ARGV-element, is returned in `optarg'. Two colons mean an option that
- wants an optional arg; if there is text in the current ARGV-element,
- it is returned in `optarg', otherwise `optarg' is set to zero.
-
- If OPTSTRING starts with `-' or `+', it requests different methods of
- handling the non-option ARGV-elements.
- See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above.
-
- Long-named options begin with `--' instead of `-'.
- Their names may be abbreviated as long as the abbreviation is unique
- or is an exact match for some defined option. If they have an
- argument, it follows the option name in the same ARGV-element, separated
- from the option name by a `=', or else the in next ARGV-element.
- When `getopt' finds a long-named option, it returns 0 if that option's
- `flag' field is nonzero, the value of the option's `val' field
- if the `flag' field is zero.
-
- The elements of ARGV aren't really const, because we permute them.
- But we pretend they're const in the prototype to be compatible
- with other systems.
-
- LONGOPTS is a vector of `struct option' terminated by an
- element containing a name which is zero.
-
- LONGIND returns the index in LONGOPT of the long-named option found.
- It is only valid when a long-named option has been found by the most
- recent call.
-
- If LONG_ONLY is nonzero, '-' as well as '--' can introduce
- long-named options. */
-
-int _getopt_internal(argc, argv, optstring, longopts, longind,
- long_only) int argc;
-char *const *argv;
-const char *optstring;
-const struct option *longopts;
-int *longind;
-int long_only;
-{
- optarg = NULL;
-
- if (optind == 0 || !__getopt_initialized) {
- if (optind == 0)
- optind = 1; /* Don't scan ARGV[0], the program name. */
- optstring = _getopt_initialize(argc, argv, optstring);
- __getopt_initialized = 1;
- }
-
-/* Test whether ARGV[optind] points to a non-option argument.
- Either it does not have option syntax, or there is an environment flag
- from the shell indicating it is not an option. The later information
- is only used when the used in the GNU libc. */
-#ifdef _LIBC
-#define NONOPTION_P \
- (argv[optind][0] != '-' || argv[optind][1] == '\0' \
- || (optind < nonoption_flags_len \
- && __getopt_nonoption_flags[optind] == '1'))
-#else
-# define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0')
-#endif
-
- if (nextchar == NULL || *nextchar == '\0') {
- /* Advance to the next ARGV-element. */
-
- /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has
- been
- moved back by the user (who may also have changed the
- arguments). */
- if (last_nonopt > optind)
- last_nonopt = optind;
- if (first_nonopt > optind)
- first_nonopt = optind;
-
- if (ordering == PERMUTE) {
- /* If we have just processed some options following some
- non-options,
- exchange them so that the options come first. */
-
- if (first_nonopt != last_nonopt
- && last_nonopt != optind)
- exchange((char **)argv);
- else if (last_nonopt != optind)
- first_nonopt = optind;
-
- /* Skip any additional non-options
- and extend the range of non-options previously
- skipped. */
-
- while (optind < argc && NONOPTION_P)
- optind++;
- last_nonopt = optind;
- }
-
- /* The special ARGV-element `--' means premature end of options.
- Skip it like a null option,
- then exchange with previous non-options as if it were an
- option,
- then skip everything else like a non-option. */
-
- if (optind != argc && !strcmp(argv[optind], "--")) {
- optind++;
-
- if (first_nonopt != last_nonopt
- && last_nonopt != optind)
- exchange((char **)argv);
- else if (first_nonopt == last_nonopt)
- first_nonopt = optind;
- last_nonopt = argc;
-
- optind = argc;
- }
-
- /* If we have done all the ARGV-elements, stop the scan
- and back over any non-options that we skipped and permuted.
- */
-
- if (optind == argc) {
- /* Set the next-arg-index to point at the non-options
- that we previously skipped, so the caller will digest
- them. */
- if (first_nonopt != last_nonopt)
- optind = first_nonopt;
- return -1;
- }
-
- /* If we have come to a non-option and did not permute it,
- either stop the scan or describe it to the caller and pass it
- by. */
-
- if (NONOPTION_P) {
- if (ordering == REQUIRE_ORDER)
- return -1;
- optarg = argv[optind++];
- return 1;
- }
-
- /* We have found another option-ARGV-element.
- Skip the initial punctuation. */
-
- nextchar = (argv[optind] + 1
- + (longopts != NULL && argv[optind][1] == '-'));
- }
-
- /* Decode the current option-ARGV-element. */
-
- /* Check whether the ARGV-element is a long option.
-
- If long_only and the ARGV-element has the form "-f", where f is
- a valid short option, don't consider it an abbreviated form of
- a long option that starts with f. Otherwise there would be no
- way to give the -f short option.
-
- On the other hand, if there's a long option "fubar" and
- the ARGV-element is "-fu", do consider that an abbreviation of
- the long option, just like "--fu", and not "-f" with arg "u".
-
- This distinction seems to be the most useful approach. */
-
- if (longopts != NULL
- && (argv[optind][1] == '-'
- || (long_only && (argv[optind][2]
- || !my_index(optstring, argv[optind][1]))))) {
- char *nameend;
- const struct option *p;
- const struct option *pfound = NULL;
- int exact = 0;
- int ambig = 0;
- int indfound = -1;
- int option_index;
-
- for (nameend = nextchar; *nameend && *nameend != '='; nameend++)
- /* Do nothing. */;
-
- /* Test all long options for either exact match
- or abbreviated matches. */
- for (p = longopts, option_index = 0; p->name;
- p++, option_index++)
- if (!strncmp(p->name, nextchar, nameend - nextchar)) {
- if ((unsigned int)(nameend - nextchar)
- == (unsigned int)strlen(p->name)) {
- /* Exact match found. */
- pfound = p;
- indfound = option_index;
- exact = 1;
- break;
- } else if (pfound == NULL) {
- /* First nonexact match found. */
- pfound = p;
- indfound = option_index;
- } else
- /* Second or later nonexact match found.
- */
- ambig = 1;
- }
-
- if (ambig && !exact) {
- if (opterr)
- fprintf(stderr,
- _("%s: option `%s' is ambiguous\n"),
- argv[0], argv[optind]);
- nextchar += strlen(nextchar);
- optind++;
- optopt = 0;
- return '?';
- }
-
- if (pfound != NULL) {
- option_index = indfound;
- optind++;
- if (*nameend) {
- /* Don't test has_arg with >, because some C
- compilers don't
- allow it to be used on enums. */
- if (pfound->has_arg)
- optarg = nameend + 1;
- else {
- if (opterr) {
- if (argv[optind - 1][1] == '-')
- /* --option */
- fprintf(stderr,
- _("%s: option `--%s' doesn't allow an argument\n"),
- argv[0],
- pfound->name);
- else
- /* +option or -option */
- fprintf(stderr,
- _("%s: option `%c%s' doesn't allow an argument\n"),
- argv[0],
- argv[optind - 1]
- [0],
- pfound->name);
- }
-
- nextchar += strlen(nextchar);
-
- optopt = pfound->val;
- return '?';
- }
- } else if (pfound->has_arg == 1) {
- if (optind < argc)
- optarg = argv[optind++];
- else {
- if (opterr)
- fprintf(stderr,
- _("%s: option `%s' requires an argument\n"),
- argv[0],
- argv[optind - 1]);
- nextchar += strlen(nextchar);
- optopt = pfound->val;
- return optstring[0] == ':' ? ':' : '?';
- }
- }
- nextchar += strlen(nextchar);
- if (longind != NULL)
- *longind = option_index;
- if (pfound->flag) {
- *(pfound->flag) = pfound->val;
- return 0;
- }
- return pfound->val;
- }
-
- /* Can't find it as a long option. If this is not
- getopt_long_only,
- or the option starts with '--' or is not a valid short
- option, then it's an error.
- Otherwise interpret it as a short option. */
- if (!long_only || argv[optind][1] == '-'
- || my_index(optstring, *nextchar) == NULL) {
- if (opterr) {
- if (argv[optind][1] == '-')
- /* --option */
- fprintf(stderr,
- _("%s: unrecognized option `--%s'\n"),
- argv[0], nextchar);
- else
- /* +option or -option */
- fprintf(stderr,
- _("%s: unrecognized option `%c%s'\n"),
- argv[0], argv[optind][0],
- nextchar);
- }
- nextchar = (char *)"";
- optind++;
- optopt = 0;
- return '?';
- }
- }
-
- /* Look at and handle the next short option-character. */
-
- {
- char c = *nextchar++;
- char *temp = my_index(optstring, c);
-
- /* Increment `optind' when we start to process its last
- * character. */
- if (*nextchar == '\0')
- ++optind;
-
- if (temp == NULL || c == ':') {
- if (opterr) {
- if (posixly_correct)
- /* 1003.2 specifies the format of this
- * message. */
- fprintf(stderr,
- _("%s: illegal option -- %c\n"),
- argv[0], c);
- else
- fprintf(stderr,
- _("%s: invalid option -- %c\n"),
- argv[0], c);
- }
- optopt = c;
- return '?';
- }
- /* Convenience. Treat POSIX -W foo same as long option --foo */
- if (temp[0] == 'W' && temp[1] == ';') {
- char *nameend;
- const struct option *p;
- const struct option *pfound = NULL;
- int exact = 0;
- int ambig = 0;
- int indfound = 0;
- int option_index;
-
- /* This is an option that requires an argument. */
- if (*nextchar != '\0') {
- optarg = nextchar;
- /* If we end this ARGV-element by taking the
- rest as an arg,
- we must advance to the next element now. */
- optind++;
- } else if (optind == argc) {
- if (opterr) {
- /* 1003.2 specifies the format of this
- * message. */
- fprintf(stderr,
- _("%s: option requires an argument -- %c\n"),
- argv[0], c);
- }
- optopt = c;
- if (optstring[0] == ':')
- c = ':';
- else
- c = '?';
- return c;
- } else
- /* We already incremented `optind' once;
- increment it again when taking next ARGV-elt
- as argument. */
- optarg = argv[optind++];
-
- /* optarg is now the argument, see if it's in the
- table of longopts. */
-
- for (nextchar = nameend = optarg;
- *nameend && *nameend != '='; nameend++)
- /* Do nothing. */;
-
- /* Test all long options for either exact match
- or abbreviated matches. */
- for (p = longopts, option_index = 0; p->name;
- p++, option_index++)
- if (!strncmp(p->name, nextchar,
- nameend - nextchar)) {
- if ((unsigned int)(nameend - nextchar)
- == strlen(p->name)) {
- /* Exact match found. */
- pfound = p;
- indfound = option_index;
- exact = 1;
- break;
- } else if (pfound == NULL) {
- /* First nonexact match found.
- */
- pfound = p;
- indfound = option_index;
- } else
- /* Second or later nonexact
- * match found. */
- ambig = 1;
- }
- if (ambig && !exact) {
- if (opterr)
- fprintf(stderr,
- _("%s: option `-W %s' is ambiguous\n"),
- argv[0], argv[optind]);
- nextchar += strlen(nextchar);
- optind++;
- return '?';
- }
- if (pfound != NULL) {
- option_index = indfound;
- if (*nameend) {
- /* Don't test has_arg with >, because
- some C compilers don't
- allow it to be used on enums. */
- if (pfound->has_arg)
- optarg = nameend + 1;
- else {
- if (opterr)
- fprintf(stderr, _("\
-%s: option `-W %s' doesn't allow an argument\n"),
- argv[0],
- pfound->name);
-
- nextchar += strlen(nextchar);
- return '?';
- }
- } else if (pfound->has_arg == 1) {
- if (optind < argc)
- optarg = argv[optind++];
- else {
- if (opterr)
- fprintf(stderr,
- _("%s: option `%s' requires an argument\n"),
- argv[0],
- argv[optind
- - 1]);
- nextchar += strlen(nextchar);
- return optstring[0] == ':'
- ? ':'
- : '?';
- }
- }
- nextchar += strlen(nextchar);
- if (longind != NULL)
- *longind = option_index;
- if (pfound->flag) {
- *(pfound->flag) = pfound->val;
- return 0;
- }
- return pfound->val;
- }
- nextchar = NULL;
- return 'W'; /* Let the application handle it. */
- }
- if (temp[1] == ':') {
- if (temp[2] == ':') {
- /* This is an option that accepts an argument
- * optionally. */
- if (*nextchar != '\0') {
- optarg = nextchar;
- optind++;
- } else
- optarg = NULL;
- nextchar = NULL;
- } else {
- /* This is an option that requires an argument.
- */
- if (*nextchar != '\0') {
- optarg = nextchar;
- /* If we end this ARGV-element by taking
- the rest as an arg,
- we must advance to the next element
- now. */
- optind++;
- } else if (optind == argc) {
- if (opterr) {
- /* 1003.2 specifies the format
- * of this message. */
- fprintf(stderr,
- _("%s: option requires an argument -- %c\n"),
- argv[0], c);
- }
- optopt = c;
- if (optstring[0] == ':')
- c = ':';
- else
- c = '?';
- } else
- /* We already incremented `optind' once;
- increment it again when taking next
- ARGV-elt as argument. */
- optarg = argv[optind++];
- nextchar = NULL;
- }
- }
- return c;
- }
-}
-
-#ifdef REALLY_NEED_PLAIN_GETOPT
-
-int getopt(argc, argv, optstring) int argc;
-char *const *argv;
-const char *optstring;
-{
- return _getopt_internal(argc, argv, optstring, (const struct option *)0,
- (int *)0, 0);
-}
-
-#endif /* REALLY_NEED_PLAIN_GETOPT */
-
-#endif /* Not ELIDE_CODE. */
-
-#ifdef TEST
-
-/* Compile with -DTEST to make an executable for use in testing
- the above definition of `getopt'. */
-
-int main(argc, argv) int argc;
-char **argv;
-{
- int c;
- int digit_optind = 0;
-
- while (1) {
- int this_option_optind = optind ? optind : 1;
-
- c = getopt(argc, argv, "abc:d:0123456789");
- if (c == -1)
- break;
-
- switch (c) {
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- if (digit_optind != 0
- && digit_optind != this_option_optind)
- printf("digits occur in two different argv-elements.\n");
- digit_optind = this_option_optind;
- printf("option %c\n", c);
- break;
-
- case 'a':
- printf("option a\n");
- break;
-
- case 'b':
- printf("option b\n");
- break;
-
- case 'c':
- printf("option c with value `%s'\n", optarg);
- break;
-
- case '?':
- break;
-
- default:
- printf("?? getopt returned character code 0%o ??\n", c);
- }
- }
-
- if (optind < argc) {
- printf("non-option ARGV-elements: ");
- while (optind < argc)
- printf("%s ", argv[optind++]);
- printf("\n");
- }
-
- exit(0);
-}
-
-#endif /* TEST */
diff --git a/lib/getopt.h b/lib/getopt.h
deleted file mode 100644
index 7863bdb66a..0000000000
--- a/lib/getopt.h
+++ /dev/null
@@ -1,143 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/* Declarations for getopt.
- * Copyright (C) 1989,90,91,92,93,94,96,97 Free Software Foundation, Inc.
- *
- * NOTE: The canonical source of this file is maintained with the GNU C Library.
- * Bugs can be reported to bug-glibc@gnu.org.
- */
-
-#ifndef _GETOPT_H
-#define _GETOPT_H 1
-
-/*
- * The operating system may or may not provide getopt_long(), and if
- * so it may or may not be a version we are willing to use. Our
- * strategy is to declare getopt here, and then provide code unless
- * the supplied version is adequate. The difficult case is when a
- * declaration for getopt is provided, as our declaration must match.
- *
- * XXX Arguably this version should be named differently, and the
- * local names defined to refer to the system version when we choose
- * to use the system version.
- */
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* For communication from `getopt' to the caller.
- When `getopt' finds an option that takes an argument,
- the argument value is returned here.
- Also, when `ordering' is RETURN_IN_ORDER,
- each non-option ARGV-element is returned here. */
-
-extern char *optarg;
-
-/* Index in ARGV of the next element to be scanned.
- This is used for communication to and from the caller
- and for communication between successive calls to `getopt'.
-
- On entry to `getopt', zero means this is the first call; initialize.
-
- When `getopt' returns -1, this is the index of the first of the
- non-option elements that the caller should itself scan.
-
- Otherwise, `optind' communicates from one call to the next
- how much of ARGV has been scanned so far. */
-
-extern int optind;
-
-/* Callers store zero here to inhibit the error message `getopt' prints
- for unrecognized options. */
-
-extern int opterr;
-
-/* Set to an option character which was unrecognized. */
-
-extern int optopt;
-
-/* Describe the long-named options requested by the application.
- The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector
- of `struct option' terminated by an element containing a name which is
- zero.
-
- The field `has_arg' is:
- no_argument (or 0) if the option does not take an argument,
- required_argument (or 1) if the option requires an argument,
- optional_argument (or 2) if the option takes an optional argument.
-
- If the field `flag' is not NULL, it points to a variable that is set
- to the value given in the field `val' when the option is found, but
- left unchanged if the option is not found.
-
- To have a long-named option do something other than set an `int' to
- a compiled-in constant, such as set a value from `optarg', set the
- option's `flag' field to zero and its `val' field to a nonzero
- value (the equivalent single-letter option character, if there is
- one). For long options that have a zero `flag' field, `getopt'
- returns the contents of the `val' field. */
-
-struct option {
-#if defined(__STDC__) && __STDC__
- const char *name;
-#else
- char *name;
-#endif
- /* has_arg can't be an enum because some compilers complain about
- type mismatches in all the code that assumes it is an int. */
- int has_arg;
- int *flag;
- int val;
-};
-
-/* Names for the values of the `has_arg' field of `struct option'. */
-
-#define no_argument 0
-#define required_argument 1
-#define optional_argument 2
-
-#if defined(__STDC__) && __STDC__
-
-#ifdef REALLY_NEED_PLAIN_GETOPT
-
-/*
- * getopt is defined in POSIX.2. Assume that if the system defines
- * getopt that it complies with POSIX.2. If not, an autoconf test
- * should be written to define NONPOSIX_GETOPT_DEFINITION.
- */
-#ifndef NONPOSIX_GETOPT_DEFINITION
-extern int getopt(int argc, char *const *argv, const char *shortopts);
-#else /* NONPOSIX_GETOPT_DEFINITION */
-extern int getopt(void);
-#endif /* NONPOSIX_GETOPT_DEFINITION */
-
-#endif
-
-
-extern int getopt_long(int argc, char *const *argv, const char *shortopts,
- const struct option *longopts, int *longind);
-extern int getopt_long_only(int argc, char *const *argv, const char *shortopts,
- const struct option *longopts, int *longind);
-
-/* Internal only. Users should not call this directly. */
-extern int _getopt_internal(int argc, char *const *argv, const char *shortopts,
- const struct option *longopts, int *longind,
- int long_only);
-#else /* not __STDC__ */
-
-#ifdef REALLY_NEED_PLAIN_GETOPT
-extern int getopt();
-#endif /* REALLY_NEED_PLAIN_GETOPT */
-
-extern int getopt_long();
-extern int getopt_long_only();
-
-extern int _getopt_internal();
-
-#endif /* __STDC__ */
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* getopt.h */
diff --git a/lib/getopt1.c b/lib/getopt1.c
deleted file mode 100644
index cf21c3aab4..0000000000
--- a/lib/getopt1.c
+++ /dev/null
@@ -1,163 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/* getopt_long and getopt_long_only entry points for GNU getopt.
- * Copyright (C) 1987,88,89,90,91,92,93,94,96,97,98
- * Free Software Foundation, Inc.
- *
- * NOTE: The canonical source of this file is maintained with the GNU C Library.
- * Bugs can be reported to bug-glibc@gnu.org.
- */
-
-#include <zebra.h>
-#include "getopt.h"
-
-#if !defined __STDC__ || !__STDC__
-/* This is a separate conditional since some stdc systems
- reject `defined (const)'. */
-#ifndef const
-#define const
-#endif
-#endif
-
-#include <stdio.h>
-
-/* Comment out all this code if we are using the GNU C Library, and are not
- actually compiling the library itself. This code is part of the GNU C
- Library, but also included in many other GNU distributions. Compiling
- and linking in this code is a waste when using the GNU C library
- (especially if it is a shared library). Rather than having every GNU
- program understand `configure --with-gnu-libc' and omit the object files,
- it is simpler to just do this in the source for each such file. */
-
-#define GETOPT_INTERFACE_VERSION 2
-#if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2
-#include <gnu-versions.h>
-#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION
-#define ELIDE_CODE
-#endif
-#endif
-
-#ifndef ELIDE_CODE
-
-
-/* This needs to come after some library #include
- to get __GNU_LIBRARY__ defined. */
-#ifdef __GNU_LIBRARY__
-#include <stdlib.h>
-#endif
-
-#ifndef NULL
-#define NULL 0
-#endif
-
-int getopt_long(argc, argv, options, long_options, opt_index) int argc;
-char *const *argv;
-const char *options;
-const struct option *long_options;
-int *opt_index;
-{
- return _getopt_internal(argc, argv, options, long_options, opt_index,
- 0);
-}
-
-/* Like getopt_long, but '-' as well as '--' can indicate a long option.
- If an option that starts with '-' (not '--') doesn't match a long option,
- but does match a short option, it is parsed as a short option
- instead. */
-
-int getopt_long_only(argc, argv, options, long_options, opt_index) int argc;
-char *const *argv;
-const char *options;
-const struct option *long_options;
-int *opt_index;
-{
- return _getopt_internal(argc, argv, options, long_options, opt_index,
- 1);
-}
-
-
-#endif /* Not ELIDE_CODE. */
-
-#ifdef TEST
-
-#include <stdio.h>
-
-int main(argc, argv) int argc;
-char **argv;
-{
- int c;
- int digit_optind = 0;
-
- while (1) {
- int this_option_optind = optind ? optind : 1;
- int option_index = 0;
- static struct option long_options[] = {
- {"add", 1, 0, 0}, {"append", 0, 0, 0},
- {"delete", 1, 0, 0}, {"verbose", 0, 0, 0},
- {"create", 0, 0, 0}, {"file", 1, 0, 0},
- {0, 0, 0, 0}};
-
- c = getopt_long(argc, argv, "abc:d:0123456789", long_options,
- &option_index);
- if (c == -1)
- break;
-
- switch (c) {
- case 0:
- printf("option %s", long_options[option_index].name);
- if (optarg)
- printf(" with arg %s", optarg);
- printf("\n");
- break;
-
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- if (digit_optind != 0
- && digit_optind != this_option_optind)
- printf("digits occur in two different argv-elements.\n");
- digit_optind = this_option_optind;
- printf("option %c\n", c);
- break;
-
- case 'a':
- printf("option a\n");
- break;
-
- case 'b':
- printf("option b\n");
- break;
-
- case 'c':
- printf("option c with value `%s'\n", optarg);
- break;
-
- case 'd':
- printf("option d with value `%s'\n", optarg);
- break;
-
- case '?':
- break;
-
- default:
- printf("?? getopt returned character code 0%o ??\n", c);
- }
- }
-
- if (optind < argc) {
- printf("non-option ARGV-elements: ");
- while (optind < argc)
- printf("%s ", argv[optind++]);
- printf("\n");
- }
-
- exit(0);
-}
-
-#endif /* TEST */
diff --git a/lib/grammar_sandbox_main.c b/lib/grammar_sandbox_main.c
index cdb1c3adb4..abd42f359f 100644
--- a/lib/grammar_sandbox_main.c
+++ b/lib/grammar_sandbox_main.c
@@ -23,13 +23,13 @@ static void vty_do_exit(int isexit)
exit(0);
}
-struct thread_master *master;
+struct event_loop *master;
int main(int argc, char **argv)
{
- struct thread thread;
+ struct event event;
- master = thread_master_create(NULL);
+ master = event_master_create(NULL);
zlog_aux_init("NONE: ", LOG_DEBUG);
@@ -45,8 +45,8 @@ int main(int argc, char **argv)
vty_stdio(vty_do_exit);
/* Fetch next active thread. */
- while (thread_fetch(master, &thread))
- thread_call(&thread);
+ while (event_fetch(master, &event))
+ event_call(&event);
/* Not reached. */
exit(0);
diff --git a/lib/hash.c b/lib/hash.c
index 97a77a2b9a..df56243985 100644
--- a/lib/hash.c
+++ b/lib/hash.c
@@ -297,6 +297,16 @@ void hash_clean(struct hash *hash, void (*free_func)(void *))
hash->stats.empty = hash->size;
}
+void hash_clean_and_free(struct hash **hash, void (*free_func)(void *))
+{
+ if (!*hash)
+ return;
+
+ hash_clean(*hash, free_func);
+ hash_free(*hash);
+ *hash = NULL;
+}
+
static void hash_to_list_iter(struct hash_bucket *hb, void *arg)
{
struct list *list = arg;
diff --git a/lib/hash.h b/lib/hash.h
index f32309d804..810faf9030 100644
--- a/lib/hash.h
+++ b/lib/hash.h
@@ -278,6 +278,17 @@ extern void hash_walk(struct hash *hash,
extern void hash_clean(struct hash *hash, void (*free_func)(void *));
/*
+ * Remove all elements from a hash table and free the table,
+ * setting the pointer to NULL.
+ *
+ * hash
+ * hash table to operate on
+ * free_func
+ * function to call with each removed item, intended to free the data
+ */
+extern void hash_clean_and_free(struct hash **hash, void (*free_func)(void *));
+
+/*
* Delete a hash table.
*
* This function assumes the table is empty. Call hash_clean to delete the
diff --git a/lib/if_rmap.c b/lib/if_rmap.c
index fa8899e9f8..0d305a7af1 100644
--- a/lib/if_rmap.c
+++ b/lib/if_rmap.c
@@ -10,6 +10,7 @@
#include "memory.h"
#include "if.h"
#include "if_rmap.h"
+#include "ripd/ripd.h"
DEFINE_MTYPE_STATIC(LIB, IF_RMAP_CTX, "Interface route map container");
DEFINE_MTYPE_STATIC(LIB, IF_RMAP_CTX_NAME,
@@ -186,8 +187,13 @@ DEFUN (if_rmap,
int idx_in_out = 2;
int idx_ifname = 3;
enum if_rmap_type type;
- struct if_rmap_ctx *ctx =
- (struct if_rmap_ctx *)listnode_head(if_rmap_ctx_list);
+ struct if_rmap_ctx *ctx;
+ const struct lyd_node *dnode;
+ struct rip *rip;
+
+ dnode = yang_dnode_get(running_config->dnode, VTY_CURR_XPATH);
+ rip = nb_running_get_entry(dnode, NULL, true);
+ ctx = rip->if_rmap_ctx;
if (strncmp(argv[idx_in_out]->text, "in", 1) == 0)
type = IF_RMAP_IN;
@@ -276,9 +282,9 @@ int config_write_if_rmap(struct vty *vty,
void if_rmap_ctx_delete(struct if_rmap_ctx *ctx)
{
listnode_delete(if_rmap_ctx_list, ctx);
- hash_clean(ctx->ifrmaphash, (void (*)(void *))if_rmap_free);
+ hash_clean_and_free(&ctx->ifrmaphash, (void (*)(void *))if_rmap_free);
if (ctx->name)
- XFREE(MTYPE_IF_RMAP_CTX_NAME, ctx);
+ XFREE(MTYPE_IF_RMAP_CTX_NAME, ctx->name);
XFREE(MTYPE_IF_RMAP_CTX, ctx);
}
@@ -289,8 +295,7 @@ struct if_rmap_ctx *if_rmap_ctx_create(const char *name)
ctx = XCALLOC(MTYPE_IF_RMAP_CTX, sizeof(struct if_rmap_ctx));
- if (ctx->name)
- ctx->name = XSTRDUP(MTYPE_IF_RMAP_CTX_NAME, name);
+ ctx->name = XSTRDUP(MTYPE_IF_RMAP_CTX_NAME, name);
ctx->ifrmaphash = hash_create_size(4, if_rmap_hash_make, if_rmap_hash_cmp,
"Interface Route-Map Hash");
if (!if_rmap_ctx_list)
diff --git a/lib/iso.c b/lib/iso.c
new file mode 100644
index 0000000000..fe97776ade
--- /dev/null
+++ b/lib/iso.c
@@ -0,0 +1,144 @@
+/*
+ * ISO Network functions - iso_net.c
+ *
+ * Author: Olivier Dugeon <olivier.dugeon@orange.com>
+ *
+ * Copyright (C) 2023 Orange http://www.orange.com
+ *
+ * This file is part of Free Range Routing (FRR).
+ *
+ * FRR is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * FRR is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "compiler.h"
+
+#include <string.h>
+#include <ctype.h>
+#include <time.h>
+
+#include "printfrr.h"
+#include "iso.h"
+
+/**
+ * Print ISO System ID as 0000.0000.0000
+ *
+ * @param Print buffer
+ * @param Print argument
+ * @param Pointer to the System ID to be printed
+ *
+ * @return Number of printed characters
+ */
+printfrr_ext_autoreg_p("SY", printfrr_iso_sysid);
+static ssize_t printfrr_iso_sysid(struct fbuf *buf, struct printfrr_eargs *ea,
+ const void *vptr)
+{
+ const uint8_t *id = vptr;
+
+ if (!id)
+ return bputs(buf, "(null)");
+
+ return bprintfrr(buf, "%02x%02x.%02x%02x.%02x%02x",
+ id[0], id[1], id[2], id[3], id[4], id[5]);
+}
+
+/**
+ * Print ISO Pseudo Node system ID as 0000.0000.0000.00
+ *
+ * @param Print buffer
+ * @param Print argument
+ * @param Pointer to the System ID to be printed
+ *
+ * @return Number of printed characters
+ */
+printfrr_ext_autoreg_p("PN", printfrr_iso_pseudo);
+static ssize_t printfrr_iso_pseudo(struct fbuf *buf, struct printfrr_eargs *ea,
+ const void *vptr)
+{
+ const uint8_t *id = vptr;
+
+ if (!id)
+ return bputs(buf, "(null)");
+
+ return bprintfrr(buf, "%02x%02x.%02x%02x.%02x%02x.%02x",
+ id[0], id[1], id[2], id[3], id[4], id[5], id[6]);
+}
+
+/**
+ * Print ISO LSP Fragment System ID as 0000.0000.0000.00-00
+ *
+ * @param Print buffer
+ * @param Print argument
+ * @param Pointer to the System ID to be printed
+ *
+ * @return Number of printed characters
+ */
+printfrr_ext_autoreg_p("LS", printfrr_iso_frag_id);
+static ssize_t printfrr_iso_frag_id(struct fbuf *buf, struct printfrr_eargs *ea,
+ const void *vptr)
+{
+ const uint8_t *id = vptr;
+
+ if (!id)
+ return bputs(buf, "(null)");
+
+ return bprintfrr(buf, "%02x%02x.%02x%02x.%02x%02x.%02x-%02x",
+ id[0], id[1], id[2], id[3], id[4], id[5], id[6],
+ id[7]);
+}
+
+/**
+ * Print ISO Network address as 00.0000.0000.0000 ... with the System ID
+ * as 0000.0000.0000.00 when long 'l' option is added to '%pIS'
+ *
+ * @param Print buffer
+ * @param Print argument
+ * @param Pointer to the ISO Network address
+ *
+ * @return Number of printed characters
+ */
+printfrr_ext_autoreg_p("IS", printfrr_iso_addr);
+static ssize_t printfrr_iso_addr(struct fbuf *buf, struct printfrr_eargs *ea,
+ const void *vptr)
+{
+ const struct iso_address *ia = vptr;
+ uint8_t len = 0;
+ int i = 0;
+ ssize_t ret = 0;
+
+ if (ea->fmt[0] == 'l') {
+ len = 7; /* ISO SYSTEM ID + 1 */
+ ea->fmt++;
+ }
+
+ if (!ia)
+ return bputs(buf, "(null)");
+
+ len += ia->addr_len;
+ while (i < len) {
+ /* No dot for odd index and at the end of address */
+ if ((i & 1) || (i == (len - 1)))
+ ret += bprintfrr(buf, "%02x", ia->area_addr[i]);
+ else
+ ret += bprintfrr(buf, "%02x.", ia->area_addr[i]);
+ i++;
+ }
+
+ return ret;
+}
+
diff --git a/lib/iso.h b/lib/iso.h
new file mode 100644
index 0000000000..975d3c154b
--- /dev/null
+++ b/lib/iso.h
@@ -0,0 +1,49 @@
+/*
+ * ISO Network definition - iso_net.h
+ *
+ * Author: Olivier Dugeon <olivier.dugeon@orange.com>
+ *
+ * Copyright (C) 2023 Orange http://www.orange.com
+ *
+ * This file is part of Free Range Routing (FRR).
+ *
+ * FRR is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * FRR is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef LIB_ISO_H_
+#define LIB_ISO_H_
+
+#include "compiler.h"
+
+/* len of "xx.xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.xx" + '\0' */
+#define ISO_ADDR_STRLEN 51
+#define ISO_ADDR_MIN 8
+#define ISO_ADDR_SIZE 20
+struct iso_address {
+ uint8_t addr_len;
+ uint8_t area_addr[ISO_ADDR_SIZE];
+};
+
+/* len of "xxxx.xxxx.xxxx.xx-xx" + '\0' */
+#define ISO_SYSID_STRLEN 21
+
+#ifdef _FRR_ATTRIBUTE_PRINTFRR
+#pragma FRR printfrr_ext "%pSY" (uint8_t *)
+#pragma FRR printfrr_ext "%pPN" (uint8_t *)
+#pragma FRR printfrr_ext "%pLS" (uint8_t *)
+#pragma FRR printfrr_ext "%pIS" (struct iso_address *)
+#endif
+
+#endif /* LIB_ISO_H_ */
diff --git a/lib/ldp_sync.c b/lib/ldp_sync.c
index b01cf87287..d55819dfaf 100644
--- a/lib/ldp_sync.c
+++ b/lib/ldp_sync.c
@@ -10,7 +10,7 @@
#include "memory.h"
#include "prefix.h"
#include "log.h"
-#include "thread.h"
+#include "frrevent.h"
#include "stream.h"
#include "zclient.h"
#include "table.h"
@@ -66,7 +66,7 @@ bool ldp_sync_if_down(struct ldp_sync_info *ldp_sync_info)
* update state
*/
if (ldp_sync_info && ldp_sync_info->enabled == LDP_IGP_SYNC_ENABLED) {
- THREAD_OFF(ldp_sync_info->t_holddown);
+ EVENT_OFF(ldp_sync_info->t_holddown);
if (ldp_sync_info->state == LDP_IGP_SYNC_STATE_REQUIRED_UP)
ldp_sync_info->state =
diff --git a/lib/ldp_sync.h b/lib/ldp_sync.h
index 5b6ebbf887..f7601ebf9d 100644
--- a/lib/ldp_sync.h
+++ b/lib/ldp_sync.h
@@ -37,7 +37,7 @@ struct ldp_sync_info {
uint8_t enabled; /* enabled */
uint8_t state; /* running state */
uint16_t holddown; /* timer value */
- struct thread *t_holddown; /* holddown timer*/
+ struct event *t_holddown; /* holddown timer*/
uint32_t metric[2]; /* isis interface metric */
};
diff --git a/lib/lib_vty.c b/lib/lib_vty.c
index 2c8f2e9047..c13d88a1e8 100644
--- a/lib/lib_vty.c
+++ b/lib/lib_vty.c
@@ -242,6 +242,17 @@ DEFUN_NOSH(end_config, end_config_cmd, "XFRR_end_configuration",
ret = nb_cli_pending_commit_check(vty);
zlog_info("Configuration Read in Took: %s", readin_time_str);
+ zlog_debug("%s: VTY:%p, pending SET-CFG: %u", __func__, vty,
+ (uint32_t)vty->mgmt_num_pending_setcfg);
+
+ /*
+ * If (and only if) we have sent any CLI config commands to MGMTd
+ * FE interface using vty_mgmt_send_config_data() without implicit
+ * commit before, should we need to send an explicit COMMIT-REQ now
+ * to apply all those commands at once.
+ */
+ if (vty->mgmt_num_pending_setcfg && vty_mgmt_fe_enabled())
+ vty_mgmt_send_commit_config(vty, false, false);
if (callback.end_config)
(*callback.end_config)();
diff --git a/lib/libfrr.c b/lib/libfrr.c
index d1b7dd133e..07e2eafec5 100644
--- a/lib/libfrr.c
+++ b/lib/libfrr.c
@@ -33,10 +33,10 @@
#include "frrscript.h"
#include "systemd.h"
-DEFINE_HOOK(frr_early_init, (struct thread_master * tm), (tm));
-DEFINE_HOOK(frr_late_init, (struct thread_master * tm), (tm));
-DEFINE_HOOK(frr_config_pre, (struct thread_master * tm), (tm));
-DEFINE_HOOK(frr_config_post, (struct thread_master * tm), (tm));
+DEFINE_HOOK(frr_early_init, (struct event_loop * tm), (tm));
+DEFINE_HOOK(frr_late_init, (struct event_loop * tm), (tm));
+DEFINE_HOOK(frr_config_pre, (struct event_loop * tm), (tm));
+DEFINE_HOOK(frr_config_post, (struct event_loop * tm), (tm));
DEFINE_KOOH(frr_early_fini, (), ());
DEFINE_KOOH(frr_fini, (), ());
@@ -696,8 +696,8 @@ static void _err_print(const void *cookie, const char *errstr)
fprintf(stderr, "%s: %s\n", prefix, errstr);
}
-static struct thread_master *master;
-struct thread_master *frr_init(void)
+static struct event_loop *master;
+struct event_loop *frr_init(void)
{
struct option_chain *oc;
struct log_arg *log_arg;
@@ -769,7 +769,7 @@ struct thread_master *frr_init(void)
zprivs_init(di->privs);
- master = thread_master_create(NULL);
+ master = event_master_create(NULL);
signal_init(master, di->n_signals, di->signals);
hook_call(frr_early_init, master);
@@ -964,7 +964,7 @@ static void frr_daemonize(void)
* to read the config in after thread execution starts, so that
* we can match this behavior.
*/
-static void frr_config_read_in(struct thread *t)
+static void frr_config_read_in(struct event *t)
{
hook_call(frr_config_pre, master);
@@ -1015,8 +1015,8 @@ void frr_config_fork(void)
exit(0);
}
- thread_add_event(master, frr_config_read_in, NULL, 0,
- &di->read_in);
+ event_add_event(master, frr_config_read_in, NULL, 0,
+ &di->read_in);
}
if (di->daemon_mode || di->terminal)
@@ -1095,9 +1095,9 @@ static void frr_terminal_close(int isexit)
}
}
-static struct thread *daemon_ctl_thread = NULL;
+static struct event *daemon_ctl_thread = NULL;
-static void frr_daemon_ctl(struct thread *t)
+static void frr_daemon_ctl(struct event *t)
{
char buf[1];
ssize_t nr;
@@ -1129,8 +1129,8 @@ static void frr_daemon_ctl(struct thread *t)
}
out:
- thread_add_read(master, frr_daemon_ctl, NULL, daemon_ctl_sock,
- &daemon_ctl_thread);
+ event_add_read(master, frr_daemon_ctl, NULL, daemon_ctl_sock,
+ &daemon_ctl_thread);
}
void frr_detach(void)
@@ -1139,7 +1139,7 @@ void frr_detach(void)
frr_check_detach();
}
-void frr_run(struct thread_master *master)
+void frr_run(struct event_loop *master)
{
char instanceinfo[64] = "";
@@ -1158,8 +1158,8 @@ void frr_run(struct thread_master *master)
vty_stdio(frr_terminal_close);
if (daemon_ctl_sock != -1) {
set_nonblocking(daemon_ctl_sock);
- thread_add_read(master, frr_daemon_ctl, NULL,
- daemon_ctl_sock, &daemon_ctl_thread);
+ event_add_read(master, frr_daemon_ctl, NULL,
+ daemon_ctl_sock, &daemon_ctl_thread);
}
} else if (di->daemon_mode) {
int nullfd = open("/dev/null", O_RDONLY | O_NOCTTY);
@@ -1180,9 +1180,9 @@ void frr_run(struct thread_master *master)
/* end fixed stderr startup logging */
zlog_startup_end();
- struct thread thread;
- while (thread_fetch(master, &thread))
- thread_call(&thread);
+ struct event thread;
+ while (event_fetch(master, &thread))
+ event_call(&thread);
}
void frr_early_fini(void)
@@ -1213,7 +1213,7 @@ void frr_fini(void)
frr_pthread_finish();
zprivs_terminate(di->privs);
/* signal_init -> nothing needed */
- thread_master_free(master);
+ event_master_free(master);
master = NULL;
zlog_tls_buffer_fini();
zlog_fini();
diff --git a/lib/libfrr.h b/lib/libfrr.h
index 97e9b93c10..c05bc01e4f 100644
--- a/lib/libfrr.h
+++ b/lib/libfrr.h
@@ -11,7 +11,7 @@
#include "typesafe.h"
#include "sigevent.h"
#include "privs.h"
-#include "thread.h"
+#include "frrevent.h"
#include "log.h"
#include "getopt.h"
#include "module.h"
@@ -70,7 +70,7 @@ struct frr_daemon_info {
bool terminal;
enum frr_cli_mode cli_mode;
- struct thread *read_in;
+ struct event *read_in;
const char *config_file;
const char *backup_config_file;
const char *pid_file;
@@ -133,22 +133,22 @@ extern int frr_getopt(int argc, char *const argv[], int *longindex);
extern __attribute__((__noreturn__)) void frr_help_exit(int status);
-extern struct thread_master *frr_init(void);
+extern struct event_loop *frr_init(void);
extern const char *frr_get_progname(void);
extern enum frr_cli_mode frr_get_cli_mode(void);
extern uint32_t frr_get_fd_limit(void);
extern bool frr_is_startup_fd(int fd);
/* call order of these hooks is as ordered here */
-DECLARE_HOOK(frr_early_init, (struct thread_master * tm), (tm));
-DECLARE_HOOK(frr_late_init, (struct thread_master * tm), (tm));
+DECLARE_HOOK(frr_early_init, (struct event_loop * tm), (tm));
+DECLARE_HOOK(frr_late_init, (struct event_loop * tm), (tm));
/* fork() happens between late_init and config_pre */
-DECLARE_HOOK(frr_config_pre, (struct thread_master * tm), (tm));
-DECLARE_HOOK(frr_config_post, (struct thread_master * tm), (tm));
+DECLARE_HOOK(frr_config_pre, (struct event_loop * tm), (tm));
+DECLARE_HOOK(frr_config_post, (struct event_loop * tm), (tm));
extern void frr_config_fork(void);
-extern void frr_run(struct thread_master *master);
+extern void frr_run(struct event_loop *master);
extern void frr_detach(void);
extern bool frr_zclient_addr(struct sockaddr_storage *sa, socklen_t *sa_len,
diff --git a/lib/libfrr_trace.h b/lib/libfrr_trace.h
index 92c469706a..05c958fa42 100644
--- a/lib/libfrr_trace.h
+++ b/lib/libfrr_trace.h
@@ -21,7 +21,7 @@
#include <lttng/tracepoint.h>
#include "hash.h"
-#include "thread.h"
+#include "frrevent.h"
#include "memory.h"
#include "linklist.h"
#include "table.h"
@@ -73,8 +73,8 @@ TRACEPOINT_EVENT(
TRACEPOINT_LOGLEVEL(frr_libfrr, hash_release, TRACE_INFO)
#define THREAD_SCHEDULE_ARGS \
- TP_ARGS(struct thread_master *, master, const char *, funcname, \
- const char *, schedfrom, int, fromln, struct thread **, \
+ TP_ARGS(struct event_loop *, master, const char *, funcname, \
+ const char *, schedfrom, int, fromln, struct event **, \
thread_ptr, int, fd, int, val, void *, arg, long, time)
TRACEPOINT_EVENT_CLASS(
@@ -103,9 +103,9 @@ THREAD_OPERATION_TRACEPOINT_INSTANCE(schedule_timer)
THREAD_OPERATION_TRACEPOINT_INSTANCE(schedule_event)
THREAD_OPERATION_TRACEPOINT_INSTANCE(schedule_read)
THREAD_OPERATION_TRACEPOINT_INSTANCE(schedule_write)
-THREAD_OPERATION_TRACEPOINT_INSTANCE(thread_cancel)
-THREAD_OPERATION_TRACEPOINT_INSTANCE(thread_cancel_async)
-THREAD_OPERATION_TRACEPOINT_INSTANCE(thread_call)
+THREAD_OPERATION_TRACEPOINT_INSTANCE(event_cancel)
+THREAD_OPERATION_TRACEPOINT_INSTANCE(event_cancel_async)
+THREAD_OPERATION_TRACEPOINT_INSTANCE(event_call)
TRACEPOINT_EVENT(
frr_libfrr,
diff --git a/lib/link_state.c b/lib/link_state.c
index 589c0ae704..076030a839 100644
--- a/lib/link_state.c
+++ b/lib/link_state.c
@@ -26,6 +26,7 @@
#include "printfrr.h"
#include <lib/json.h>
#include "link_state.h"
+#include "iso.h"
/* Link State Memory allocation */
DEFINE_MTYPE_STATIC(LIB, LS_DB, "Link State Database");
@@ -333,7 +334,7 @@ int ls_attributes_same(struct ls_attributes *l1, struct ls_attributes *l2)
/**
* Link State prefix management functions
*/
-struct ls_prefix *ls_prefix_new(struct ls_node_id adv, struct prefix p)
+struct ls_prefix *ls_prefix_new(struct ls_node_id adv, struct prefix *p)
{
struct ls_prefix *new;
@@ -342,7 +343,7 @@ struct ls_prefix *ls_prefix_new(struct ls_node_id adv, struct prefix p)
new = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_prefix));
new->adv = adv;
- new->pref = p;
+ new->pref = *p;
return new;
}
@@ -889,7 +890,7 @@ struct ls_subnet *ls_subnet_update(struct ls_ted *ted, struct ls_prefix *pref)
if (pref == NULL)
return NULL;
- old = ls_find_subnet(ted, pref->pref);
+ old = ls_find_subnet(ted, &pref->pref);
if (old) {
if (!ls_prefix_same(old->ls_pref, pref)) {
ls_prefix_del(old->ls_pref);
@@ -942,11 +943,12 @@ void ls_subnet_del_all(struct ls_ted *ted, struct ls_subnet *subnet)
ls_subnet_del(ted, subnet);
}
-struct ls_subnet *ls_find_subnet(struct ls_ted *ted, const struct prefix prefix)
+struct ls_subnet *ls_find_subnet(struct ls_ted *ted,
+ const struct prefix *prefix)
{
struct ls_subnet subnet = {};
- subnet.key = prefix;
+ subnet.key = *prefix;
return subnets_find(&ted->subnets, &subnet);
}
@@ -1846,7 +1848,7 @@ struct ls_subnet *ls_msg2subnet(struct ls_ted *ted, struct ls_message *msg,
subnet->status = UPDATE;
break;
case LS_MSG_EVENT_DELETE:
- subnet = ls_find_subnet(ted, pref->pref);
+ subnet = ls_find_subnet(ted, &pref->pref);
if (subnet) {
if (delete)
ls_subnet_del_all(ted, subnet);
@@ -1965,13 +1967,9 @@ static const char *const status2txt[] = {
static const char *ls_node_id_to_text(struct ls_node_id lnid, char *str,
size_t size)
{
- if (lnid.origin == ISIS_L1 || lnid.origin == ISIS_L2) {
- uint8_t *id;
-
- id = lnid.id.iso.sys_id;
- snprintfrr(str, size, "%02x%02x.%02x%02x.%02x%02x", id[0],
- id[1], id[2], id[3], id[4], id[5]);
- } else
+ if (lnid.origin == ISIS_L1 || lnid.origin == ISIS_L2)
+ snprintfrr(str, size, "%pSY", lnid.id.iso.sys_id);
+ else
snprintfrr(str, size, "%pI4", &lnid.id.ip.addr);
return str;
diff --git a/lib/link_state.h b/lib/link_state.h
index e6a6388ba4..b75f035431 100644
--- a/lib/link_state.h
+++ b/lib/link_state.h
@@ -314,7 +314,7 @@ extern int ls_attributes_same(struct ls_attributes *a1,
*
* @return New Link State Prefix
*/
-extern struct ls_prefix *ls_prefix_new(struct ls_node_id adv, struct prefix p);
+extern struct ls_prefix *ls_prefix_new(struct ls_node_id adv, struct prefix *p);
/**
* Remove Link State Prefix. Data Structure is freed.
@@ -709,7 +709,7 @@ extern void ls_subnet_del_all(struct ls_ted *ted, struct ls_subnet *subnet);
* @return Subnet if found, NULL otherwise
*/
extern struct ls_subnet *ls_find_subnet(struct ls_ted *ted,
- const struct prefix prefix);
+ const struct prefix *prefix);
/**
* Create a new Link State Data Base.
diff --git a/lib/log.c b/lib/log.c
index b1a06bfb26..00b897dca1 100644
--- a/lib/log.c
+++ b/lib/log.c
@@ -140,7 +140,7 @@ void zlog_signal(int signo, const char *action, void *siginfo_v,
fb.pos = buf;
- struct thread *tc;
+ struct event *tc;
tc = pthread_getspecific(thread_current);
if (!tc)
@@ -284,7 +284,7 @@ void zlog_backtrace(int priority)
void zlog_thread_info(int log_level)
{
- struct thread *tc;
+ struct event *tc;
tc = pthread_getspecific(thread_current);
if (tc)
@@ -506,6 +506,26 @@ const char *zserv_command_string(unsigned int command)
return command_types[command].string;
}
+#define DESC_ENTRY(T) [(T)] = {(T), (#T), '\0'}
+static const struct zebra_desc_table gr_client_cap_types[] = {
+ DESC_ENTRY(ZEBRA_CLIENT_GR_CAPABILITIES),
+ DESC_ENTRY(ZEBRA_CLIENT_ROUTE_UPDATE_COMPLETE),
+ DESC_ENTRY(ZEBRA_CLIENT_ROUTE_UPDATE_PENDING),
+ DESC_ENTRY(ZEBRA_CLIENT_GR_DISABLE),
+ DESC_ENTRY(ZEBRA_CLIENT_RIB_STALE_TIME),
+};
+#undef DESC_ENTRY
+
+const char *zserv_gr_client_cap_string(uint32_t zcc)
+{
+ if (zcc >= array_size(gr_client_cap_types)) {
+ flog_err(EC_LIB_DEVELOPMENT, "unknown zserv command type: %u",
+ zcc);
+ return unknown.string;
+ }
+ return gr_client_cap_types[zcc].string;
+}
+
int proto_name2num(const char *s)
{
unsigned i;
diff --git a/lib/log.h b/lib/log.h
index b8452ac215..8a95b7a005 100644
--- a/lib/log.h
+++ b/lib/log.h
@@ -113,6 +113,7 @@ extern int proto_name2num(const char *s);
extern int proto_redistnum(int afi, const char *s);
extern const char *zserv_command_string(unsigned int command);
+extern const char *zserv_gr_client_cap_string(unsigned int zcc);
#define OSPF_LOG(level, cond, fmt, ...) \
do { \
diff --git a/lib/mgmt.proto b/lib/mgmt.proto
new file mode 100644
index 0000000000..8a11ff0fa5
--- /dev/null
+++ b/lib/mgmt.proto
@@ -0,0 +1,331 @@
+// SPDX-License-Identifier: ISC
+//
+// mgmt.proto
+//
+// @copyright Copyright (C) 2021 Vmware, Inc.
+//
+// @author Pushpasis Sarkar <spushpasis@vmware.com>
+//
+
+syntax = "proto2";
+
+//
+// Protobuf definitions pertaining to the MGMTD component.
+//
+
+package mgmtd;
+
+//
+// Common Sub-Messages
+//
+
+message YangDataXPath {
+ required string xpath = 1;
+}
+
+message YangDataValue {
+ oneof value {
+ //
+ // NOTE: For now let's use stringized value ONLY.
+ // We will enhance it later to pass native-format
+ // if needed.
+ //
+ // bool bool_val = 2;
+ // double double_val = 3;
+ // float float_val = 4;
+ // string string_val = 5;
+ // bytes bytes_val = 6;
+ // int32 int32_val = 7;
+ // int64 int64_val = 8;
+ // uint32 uint32_val = 9;
+ // uint64 uint64_val = 10;
+ // int32 int8_val = 11;
+ // uint32 uint8_val = 12;
+ // int32 int16_val = 13;
+ // uint32 uint16_val = 14;
+ string encoded_str_val = 100;
+ }
+}
+
+message YangData {
+ required string xpath = 1;
+ optional YangDataValue value = 2;
+}
+
+enum CfgDataReqType {
+ REQ_TYPE_NONE = 0;
+ SET_DATA = 1;
+ DELETE_DATA = 2;
+}
+
+message YangCfgDataReq {
+ required YangData data = 1;
+ required CfgDataReqType req_type = 2;
+}
+
+message YangGetDataReq {
+ required YangData data = 1;
+ required int64 next_indx = 2;
+}
+
+//
+// Backend Interface Messages
+//
+message BeSubscribeReq {
+ required string client_name = 1;
+ required bool subscribe_xpaths = 2;
+ repeated string xpath_reg = 3;
+}
+
+message BeSubscribeReply {
+ required bool success = 1;
+}
+
+message BeTxnReq {
+ required uint64 txn_id = 1;
+ required bool create = 2;
+}
+
+message BeTxnReply {
+ required uint64 txn_id = 1;
+ required bool create = 2;
+ required bool success = 3;
+}
+
+message BeCfgDataCreateReq {
+ required uint64 txn_id = 1;
+ required uint64 batch_id = 2;
+ repeated YangCfgDataReq data_req = 3;
+ required bool end_of_data = 4;
+}
+
+message BeCfgDataCreateReply {
+ required uint64 txn_id = 1;
+ required uint64 batch_id = 2;
+ required bool success = 3;
+ optional string error_if_any = 4;
+}
+
+message BeCfgDataApplyReq {
+ required uint64 txn_id = 1;
+}
+
+message BeCfgDataApplyReply {
+ required uint64 txn_id = 1;
+ repeated uint64 batch_ids = 2;
+ required bool success = 3;
+ optional string error_if_any = 4;
+}
+
+message BeOperDataGetReq {
+ required uint64 txn_id = 1;
+ required uint64 batch_id = 2;
+ repeated YangGetDataReq data = 3;
+}
+
+message YangDataReply {
+ repeated YangData data = 1;
+ required int64 next_indx = 2;
+}
+
+message BeOperDataGetReply {
+ required uint64 txn_id = 1;
+ required uint64 batch_id = 2;
+ required bool success = 3;
+ optional string error = 4;
+ optional YangDataReply data = 5;
+}
+
+message BeOperDataNotify {
+ required YangDataReply data = 5;
+}
+
+message BeConfigCmdReq {
+ required string cmd = 1;
+}
+
+message BeConfigCmdReply {
+ required bool success = 1;
+ required string error_if_any = 2;
+}
+
+message BeShowCmdReq {
+ required string cmd = 1;
+}
+
+message BeShowCmdReply {
+ required bool success = 1;
+ required string cmd_ouput = 2;
+}
+
+//
+// Any message on the MGMTD Backend Interface.
+//
+message BeMessage {
+ oneof message {
+ BeSubscribeReq subscr_req = 2;
+ BeSubscribeReply subscr_reply = 3;
+ BeTxnReq txn_req = 4;
+ BeTxnReply txn_reply = 5;
+ BeCfgDataCreateReq cfg_data_req = 6;
+ BeCfgDataCreateReply cfg_data_reply = 7;
+ BeCfgDataApplyReq cfg_apply_req = 8;
+ BeCfgDataApplyReply cfg_apply_reply = 9;
+ BeOperDataGetReq get_req = 10;
+ BeOperDataGetReply get_reply = 11;
+ BeOperDataNotify notify_data = 12;
+ BeConfigCmdReq cfg_cmd_req = 13;
+ BeConfigCmdReply cfg_cmd_reply = 14;
+ BeShowCmdReq show_cmd_req = 15;
+ BeShowCmdReply show_cmd_reply = 16;
+ }
+}
+
+
+//
+// Frontend Interface Messages
+//
+
+message FeRegisterReq {
+ required string client_name = 1;
+}
+
+message FeSessionReq {
+ required bool create = 1;
+ oneof id {
+ uint64 client_conn_id = 2; // Applicable for create request only
+ uint64 session_id = 3; // Applicable for delete request only
+ }
+}
+
+message FeSessionReply {
+ required bool create = 1;
+ required bool success = 2;
+ optional uint64 client_conn_id = 3; // Applicable for create request only
+ required uint64 session_id = 4;
+}
+
+enum DatastoreId {
+ DS_NONE = 0;
+ RUNNING_DS = 1;
+ CANDIDATE_DS = 2;
+ OPERATIONAL_DS = 3;
+ STARTUP_DS = 4;
+}
+
+message FeLockDsReq {
+ required uint64 session_id = 1;
+ required uint64 req_id = 2;
+ required DatastoreId ds_id = 3;
+ required bool lock = 4;
+}
+
+message FeLockDsReply {
+ required uint64 session_id = 1;
+ required uint64 req_id = 2;
+ required DatastoreId ds_id = 3;
+ required bool lock = 4;
+ required bool success = 5;
+ optional string error_if_any = 6;
+}
+
+message FeSetConfigReq {
+ required uint64 session_id = 1;
+ required DatastoreId ds_id = 2;
+ required uint64 req_id = 3;
+ repeated YangCfgDataReq data = 4;
+ required bool implicit_commit = 5;
+ required DatastoreId commit_ds_id = 6;
+}
+
+message FeSetConfigReply {
+ required uint64 session_id = 1;
+ required DatastoreId ds_id = 2;
+ required uint64 req_id = 3;
+ required bool success = 4;
+ optional string error_if_any = 5;
+}
+
+message FeCommitConfigReq {
+ required uint64 session_id = 1;
+ required DatastoreId src_ds_id = 2;
+ required DatastoreId dst_ds_id = 3;
+ required uint64 req_id = 4;
+ required bool validate_only = 5;
+ required bool abort = 6;
+}
+
+message FeCommitConfigReply {
+ required uint64 session_id = 1;
+ required DatastoreId src_ds_id = 2;
+ required DatastoreId dst_ds_id = 3;
+ required uint64 req_id = 4;
+ required bool validate_only = 5;
+ required bool success = 6;
+ required bool abort = 7;
+ optional string error_if_any = 8;
+}
+
+message FeGetConfigReq {
+ required uint64 session_id = 1;
+ required DatastoreId ds_id = 2;
+ required uint64 req_id = 3;
+ repeated YangGetDataReq data = 4;
+}
+
+message FeGetConfigReply {
+ required uint64 session_id = 1;
+ required DatastoreId ds_id = 2;
+ required uint64 req_id = 3;
+ required bool success = 4;
+ optional string error_if_any = 5;
+ optional YangDataReply data = 6;
+}
+
+message FeGetDataReq {
+ required uint64 session_id = 1;
+ required DatastoreId ds_id = 2;
+ required uint64 req_id = 3;
+ repeated YangGetDataReq data = 4;
+}
+
+message FeGetDataReply {
+ required uint64 session_id = 1;
+ required DatastoreId ds_id = 2;
+ required uint64 req_id = 3;
+ required bool success = 4;
+ optional string error_if_any = 5;
+ optional YangDataReply data = 6;
+}
+
+message FeNotifyDataReq {
+ repeated YangData data = 1;
+}
+
+message FeRegisterNotifyReq {
+ required uint64 session_id = 1;
+ required DatastoreId ds_id = 2;
+ required bool register_req = 3;
+ required uint64 req_id = 4;
+ repeated YangDataXPath data_xpath = 5;
+}
+
+message FeMessage {
+ oneof message {
+ FeRegisterReq register_req = 2;
+ FeSessionReq session_req = 3;
+ FeSessionReply session_reply = 4;
+ FeLockDsReq lockds_req = 5;
+ FeLockDsReply lockds_reply = 6;
+ FeSetConfigReq setcfg_req = 7;
+ FeSetConfigReply setcfg_reply = 8;
+ FeCommitConfigReq commcfg_req = 9;
+ FeCommitConfigReply commcfg_reply = 10;
+ FeGetConfigReq getcfg_req = 11;
+ FeGetConfigReply getcfg_reply = 12;
+ FeGetDataReq getdata_req = 13;
+ FeGetDataReply getdata_reply = 14;
+ FeNotifyDataReq notify_data_req = 15;
+ FeRegisterNotifyReq regnotify_req = 16;
+ }
+}
diff --git a/lib/mgmt_be_client.c b/lib/mgmt_be_client.c
new file mode 100644
index 0000000000..36c78052bc
--- /dev/null
+++ b/lib/mgmt_be_client.c
@@ -0,0 +1,1219 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * MGMTD Backend Client Library api interfaces
+ * Copyright (C) 2021 Vmware, Inc.
+ * Pushpasis Sarkar <spushpasis@vmware.com>
+ */
+
+#include <zebra.h>
+#include "libfrr.h"
+#include "mgmtd/mgmt.h"
+#include "mgmt_be_client.h"
+#include "mgmt_msg.h"
+#include "mgmt_pb.h"
+#include "network.h"
+#include "stream.h"
+#include "sockopt.h"
+
+#ifdef REDIRECT_DEBUG_TO_STDERR
+#define MGMTD_BE_CLIENT_DBG(fmt, ...) \
+ fprintf(stderr, "%s: " fmt "\n", __func__, ##__VA_ARGS__)
+#define MGMTD_BE_CLIENT_ERR(fmt, ...) \
+ fprintf(stderr, "%s: ERROR, " fmt "\n", __func__, ##__VA_ARGS__)
+#else /* REDIRECT_DEBUG_TO_STDERR */
+#define MGMTD_BE_CLIENT_DBG(fmt, ...) \
+ do { \
+ if (mgmt_debug_be_client) \
+ zlog_debug("%s: " fmt, __func__, ##__VA_ARGS__); \
+ } while (0)
+#define MGMTD_BE_CLIENT_ERR(fmt, ...) \
+ zlog_err("%s: ERROR: " fmt, __func__, ##__VA_ARGS__)
+#endif /* REDIRECT_DEBUG_TO_STDERR */
+
+DEFINE_MTYPE_STATIC(LIB, MGMTD_BE_BATCH,
+ "MGMTD backend transaction batch data");
+DEFINE_MTYPE_STATIC(LIB, MGMTD_BE_TXN, "MGMTD backend transaction data");
+
+enum mgmt_be_txn_event {
+ MGMTD_BE_TXN_PROC_SETCFG = 1,
+ MGMTD_BE_TXN_PROC_GETCFG,
+ MGMTD_BE_TXN_PROC_GETDATA
+};
+
+struct mgmt_be_set_cfg_req {
+ struct nb_cfg_change cfg_changes[MGMTD_MAX_CFG_CHANGES_IN_BATCH];
+ uint16_t num_cfg_changes;
+};
+
+struct mgmt_be_get_data_req {
+ char *xpaths[MGMTD_MAX_NUM_DATA_REQ_IN_BATCH];
+ uint16_t num_xpaths;
+};
+
+struct mgmt_be_txn_req {
+ enum mgmt_be_txn_event event;
+ union {
+ struct mgmt_be_set_cfg_req set_cfg;
+ struct mgmt_be_get_data_req get_data;
+ } req;
+};
+
+PREDECL_LIST(mgmt_be_batches);
+struct mgmt_be_batch_ctx {
+ /* Batch-Id as assigned by MGMTD */
+ uint64_t batch_id;
+
+ struct mgmt_be_txn_req txn_req;
+
+ uint32_t flags;
+
+ struct mgmt_be_batches_item list_linkage;
+};
+#define MGMTD_BE_BATCH_FLAGS_CFG_PREPARED (1U << 0)
+#define MGMTD_BE_TXN_FLAGS_CFG_APPLIED (1U << 1)
+DECLARE_LIST(mgmt_be_batches, struct mgmt_be_batch_ctx, list_linkage);
+
+struct mgmt_be_client_ctx;
+
+PREDECL_LIST(mgmt_be_txns);
+struct mgmt_be_txn_ctx {
+ /* Txn-Id as assigned by MGMTD */
+ uint64_t txn_id;
+ uint32_t flags;
+
+ struct mgmt_be_client_txn_ctx client_data;
+ struct mgmt_be_client_ctx *client_ctx;
+
+ /* List of batches belonging to this transaction */
+ struct mgmt_be_batches_head cfg_batches;
+ struct mgmt_be_batches_head apply_cfgs;
+
+ struct mgmt_be_txns_item list_linkage;
+
+ struct nb_transaction *nb_txn;
+ uint32_t nb_txn_id;
+};
+#define MGMTD_BE_TXN_FLAGS_CFGPREP_FAILED (1U << 1)
+
+DECLARE_LIST(mgmt_be_txns, struct mgmt_be_txn_ctx, list_linkage);
+
+#define FOREACH_BE_TXN_BATCH_IN_LIST(txn, batch) \
+ frr_each_safe (mgmt_be_batches, &(txn)->cfg_batches, (batch))
+
+#define FOREACH_BE_APPLY_BATCH_IN_LIST(txn, batch) \
+ frr_each_safe (mgmt_be_batches, &(txn)->apply_cfgs, (batch))
+
+struct mgmt_be_client_ctx {
+ int conn_fd;
+ struct event_loop *tm;
+ struct event *conn_retry_tmr;
+ struct event *conn_read_ev;
+ struct event *conn_write_ev;
+ struct event *conn_writes_on;
+ struct event *msg_proc_ev;
+ uint32_t flags;
+
+ struct mgmt_msg_state mstate;
+
+ struct nb_config *candidate_config;
+ struct nb_config *running_config;
+
+ unsigned long num_batch_find;
+ unsigned long avg_batch_find_tm;
+ unsigned long num_edit_nb_cfg;
+ unsigned long avg_edit_nb_cfg_tm;
+ unsigned long num_prep_nb_cfg;
+ unsigned long avg_prep_nb_cfg_tm;
+ unsigned long num_apply_nb_cfg;
+ unsigned long avg_apply_nb_cfg_tm;
+
+ struct mgmt_be_txns_head txn_head;
+ struct mgmt_be_client_params client_params;
+};
+
+#define MGMTD_BE_CLIENT_FLAGS_WRITES_OFF (1U << 0)
+
+#define FOREACH_BE_TXN_IN_LIST(client_ctx, txn) \
+ frr_each_safe (mgmt_be_txns, &(client_ctx)->txn_head, (txn))
+
+static bool mgmt_debug_be_client;
+
+static struct mgmt_be_client_ctx mgmt_be_client_ctx = {
+ .conn_fd = -1,
+};
+
+const char *mgmt_be_client_names[MGMTD_BE_CLIENT_ID_MAX + 1] = {
+#ifdef HAVE_STATICD
+ [MGMTD_BE_CLIENT_ID_STATICD] = "staticd",
+#endif
+ [MGMTD_BE_CLIENT_ID_MAX] = "Unknown/Invalid",
+};
+
+/* Forward declarations */
+static void
+mgmt_be_client_register_event(struct mgmt_be_client_ctx *client_ctx,
+ enum mgmt_be_event event);
+static void
+mgmt_be_client_schedule_conn_retry(struct mgmt_be_client_ctx *client_ctx,
+ unsigned long intvl_secs);
+static int mgmt_be_client_send_msg(struct mgmt_be_client_ctx *client_ctx,
+ Mgmtd__BeMessage *be_msg);
+
+static void
+mgmt_be_server_disconnect(struct mgmt_be_client_ctx *client_ctx,
+ bool reconnect)
+{
+ /* Notify client through registered callback (if any) */
+ if (client_ctx->client_params.client_connect_notify)
+ (void)(*client_ctx->client_params.client_connect_notify)(
+ (uintptr_t)client_ctx,
+ client_ctx->client_params.user_data, false);
+
+ if (client_ctx->conn_fd != -1) {
+ close(client_ctx->conn_fd);
+ client_ctx->conn_fd = -1;
+ }
+
+ if (reconnect)
+ mgmt_be_client_schedule_conn_retry(
+ client_ctx,
+ client_ctx->client_params.conn_retry_intvl_sec);
+}
+
+static struct mgmt_be_batch_ctx *
+mgmt_be_find_batch_by_id(struct mgmt_be_txn_ctx *txn,
+ uint64_t batch_id)
+{
+ struct mgmt_be_batch_ctx *batch = NULL;
+
+ FOREACH_BE_TXN_BATCH_IN_LIST (txn, batch) {
+ if (batch->batch_id == batch_id)
+ return batch;
+ }
+
+ return NULL;
+}
+
+static struct mgmt_be_batch_ctx *
+mgmt_be_batch_create(struct mgmt_be_txn_ctx *txn, uint64_t batch_id)
+{
+ struct mgmt_be_batch_ctx *batch = NULL;
+
+ batch = mgmt_be_find_batch_by_id(txn, batch_id);
+ if (!batch) {
+ batch = XCALLOC(MTYPE_MGMTD_BE_BATCH,
+ sizeof(struct mgmt_be_batch_ctx));
+ assert(batch);
+
+ batch->batch_id = batch_id;
+ mgmt_be_batches_add_tail(&txn->cfg_batches, batch);
+
+ MGMTD_BE_CLIENT_DBG("Added new batch 0x%llx to transaction",
+ (unsigned long long)batch_id);
+ }
+
+ return batch;
+}
+
+static void mgmt_be_batch_delete(struct mgmt_be_txn_ctx *txn,
+ struct mgmt_be_batch_ctx **batch)
+{
+ uint16_t indx;
+
+ if (!batch)
+ return;
+
+ mgmt_be_batches_del(&txn->cfg_batches, *batch);
+ if ((*batch)->txn_req.event == MGMTD_BE_TXN_PROC_SETCFG) {
+ for (indx = 0; indx < MGMTD_MAX_CFG_CHANGES_IN_BATCH; indx++) {
+ if ((*batch)->txn_req.req.set_cfg.cfg_changes[indx]
+ .value) {
+ free((char *)(*batch)
+ ->txn_req.req.set_cfg
+ .cfg_changes[indx]
+ .value);
+ }
+ }
+ }
+
+ XFREE(MTYPE_MGMTD_BE_BATCH, *batch);
+ *batch = NULL;
+}
+
+static void mgmt_be_cleanup_all_batches(struct mgmt_be_txn_ctx *txn)
+{
+ struct mgmt_be_batch_ctx *batch = NULL;
+
+ FOREACH_BE_TXN_BATCH_IN_LIST (txn, batch) {
+ mgmt_be_batch_delete(txn, &batch);
+ }
+
+ FOREACH_BE_APPLY_BATCH_IN_LIST (txn, batch) {
+ mgmt_be_batch_delete(txn, &batch);
+ }
+}
+
+static struct mgmt_be_txn_ctx *
+mgmt_be_find_txn_by_id(struct mgmt_be_client_ctx *client_ctx,
+ uint64_t txn_id)
+{
+ struct mgmt_be_txn_ctx *txn = NULL;
+
+ FOREACH_BE_TXN_IN_LIST (client_ctx, txn) {
+ if (txn->txn_id == txn_id)
+ return txn;
+ }
+
+ return NULL;
+}
+
+static struct mgmt_be_txn_ctx *
+mgmt_be_txn_create(struct mgmt_be_client_ctx *client_ctx,
+ uint64_t txn_id)
+{
+ struct mgmt_be_txn_ctx *txn = NULL;
+
+ txn = mgmt_be_find_txn_by_id(client_ctx, txn_id);
+ if (!txn) {
+ txn = XCALLOC(MTYPE_MGMTD_BE_TXN,
+ sizeof(struct mgmt_be_txn_ctx));
+ assert(txn);
+
+ txn->txn_id = txn_id;
+ txn->client_ctx = client_ctx;
+ mgmt_be_batches_init(&txn->cfg_batches);
+ mgmt_be_batches_init(&txn->apply_cfgs);
+ mgmt_be_txns_add_tail(&client_ctx->txn_head, txn);
+
+ MGMTD_BE_CLIENT_DBG("Added new transaction 0x%llx",
+ (unsigned long long)txn_id);
+ }
+
+ return txn;
+}
+
+static void mgmt_be_txn_delete(struct mgmt_be_client_ctx *client_ctx,
+ struct mgmt_be_txn_ctx **txn)
+{
+ char err_msg[] = "MGMT Transaction Delete";
+
+ if (!txn)
+ return;
+
+ /*
+ * Remove the transaction from the list of transactions
+ * so that future lookups with the same transaction id
+ * does not return this one.
+ */
+ mgmt_be_txns_del(&client_ctx->txn_head, *txn);
+
+ /*
+ * Time to delete the transaction which should also
+ * take care of cleaning up all batches created via
+ * CFGDATA_CREATE_REQs. But first notify the client
+ * about the transaction delete.
+ */
+ if (client_ctx->client_params.txn_notify)
+ (void)(*client_ctx->client_params
+ .txn_notify)(
+ (uintptr_t)client_ctx,
+ client_ctx->client_params.user_data,
+ &(*txn)->client_data, true);
+
+ mgmt_be_cleanup_all_batches(*txn);
+ if ((*txn)->nb_txn)
+ nb_candidate_commit_abort((*txn)->nb_txn, err_msg,
+ sizeof(err_msg));
+ XFREE(MTYPE_MGMTD_BE_TXN, *txn);
+
+ *txn = NULL;
+}
+
+static void
+mgmt_be_cleanup_all_txns(struct mgmt_be_client_ctx *client_ctx)
+{
+ struct mgmt_be_txn_ctx *txn = NULL;
+
+ FOREACH_BE_TXN_IN_LIST (client_ctx, txn) {
+ mgmt_be_txn_delete(client_ctx, &txn);
+ }
+}
+
+static int mgmt_be_send_txn_reply(struct mgmt_be_client_ctx *client_ctx,
+ uint64_t txn_id, bool create,
+ bool success)
+{
+ Mgmtd__BeMessage be_msg;
+ Mgmtd__BeTxnReply txn_reply;
+
+ mgmtd__be_txn_reply__init(&txn_reply);
+ txn_reply.create = create;
+ txn_reply.txn_id = txn_id;
+ txn_reply.success = success;
+
+ mgmtd__be_message__init(&be_msg);
+ be_msg.message_case = MGMTD__BE_MESSAGE__MESSAGE_TXN_REPLY;
+ be_msg.txn_reply = &txn_reply;
+
+ MGMTD_BE_CLIENT_DBG(
+ "Sending TXN_REPLY message to MGMTD for txn 0x%llx",
+ (unsigned long long)txn_id);
+
+ return mgmt_be_client_send_msg(client_ctx, &be_msg);
+}
+
+static int mgmt_be_process_txn_req(struct mgmt_be_client_ctx *client_ctx,
+ uint64_t txn_id, bool create)
+{
+ struct mgmt_be_txn_ctx *txn;
+
+ txn = mgmt_be_find_txn_by_id(client_ctx, txn_id);
+ if (create) {
+ if (txn) {
+ /*
+ * Transaction with same txn-id already exists.
+ * Should not happen under any circumstances.
+ */
+ MGMTD_BE_CLIENT_ERR(
+ "Transaction 0x%llx already exists!!!",
+ (unsigned long long)txn_id);
+ mgmt_be_send_txn_reply(client_ctx, txn_id, create,
+ false);
+ }
+
+ MGMTD_BE_CLIENT_DBG("Created new transaction 0x%llx",
+ (unsigned long long)txn_id);
+ txn = mgmt_be_txn_create(client_ctx, txn_id);
+
+ if (client_ctx->client_params.txn_notify)
+ (void)(*client_ctx->client_params
+ .txn_notify)(
+ (uintptr_t)client_ctx,
+ client_ctx->client_params.user_data,
+ &txn->client_data, false);
+ } else {
+ if (!txn) {
+ /*
+ * Transaction with same txn-id does not exists.
+ * Return sucess anyways.
+ */
+ MGMTD_BE_CLIENT_DBG(
+ "Transaction to delete 0x%llx does NOT exists!!!",
+ (unsigned long long)txn_id);
+ } else {
+ MGMTD_BE_CLIENT_DBG("Delete transaction 0x%llx",
+ (unsigned long long)txn_id);
+ mgmt_be_txn_delete(client_ctx, &txn);
+ }
+ }
+
+ mgmt_be_send_txn_reply(client_ctx, txn_id, create, true);
+
+ return 0;
+}
+
+static int
+mgmt_be_send_cfgdata_create_reply(struct mgmt_be_client_ctx *client_ctx,
+ uint64_t txn_id, uint64_t batch_id,
+ bool success, const char *error_if_any)
+{
+ Mgmtd__BeMessage be_msg;
+ Mgmtd__BeCfgDataCreateReply cfgdata_reply;
+
+ mgmtd__be_cfg_data_create_reply__init(&cfgdata_reply);
+ cfgdata_reply.txn_id = (uint64_t)txn_id;
+ cfgdata_reply.batch_id = (uint64_t)batch_id;
+ cfgdata_reply.success = success;
+ if (error_if_any)
+ cfgdata_reply.error_if_any = (char *)error_if_any;
+
+ mgmtd__be_message__init(&be_msg);
+ be_msg.message_case = MGMTD__BE_MESSAGE__MESSAGE_CFG_DATA_REPLY;
+ be_msg.cfg_data_reply = &cfgdata_reply;
+
+ MGMTD_BE_CLIENT_DBG(
+ "Sending CFGDATA_CREATE_REPLY message to MGMTD for txn 0x%llx batch 0x%llx",
+ (unsigned long long)txn_id, (unsigned long long)batch_id);
+
+ return mgmt_be_client_send_msg(client_ctx, &be_msg);
+}
+
+static void mgmt_be_txn_cfg_abort(struct mgmt_be_txn_ctx *txn)
+{
+ char errmsg[BUFSIZ] = {0};
+
+ assert(txn && txn->client_ctx);
+ if (txn->nb_txn) {
+ MGMTD_BE_CLIENT_ERR(
+ "Aborting configurations after prep for Txn 0x%llx",
+ (unsigned long long)txn->txn_id);
+ nb_candidate_commit_abort(txn->nb_txn, errmsg, sizeof(errmsg));
+ txn->nb_txn = 0;
+ }
+
+ /*
+ * revert candidate back to running
+ *
+ * This is one txn ctx but the candidate_config is per client ctx, how
+ * does that work?
+ */
+ MGMTD_BE_CLIENT_DBG(
+ "Reset candidate configurations after abort of Txn 0x%llx",
+ (unsigned long long)txn->txn_id);
+ nb_config_replace(txn->client_ctx->candidate_config,
+ txn->client_ctx->running_config, true);
+}
+
+static int mgmt_be_txn_cfg_prepare(struct mgmt_be_txn_ctx *txn)
+{
+ struct mgmt_be_client_ctx *client_ctx;
+ struct mgmt_be_txn_req *txn_req = NULL;
+ struct nb_context nb_ctx = {0};
+ struct timeval edit_nb_cfg_start;
+ struct timeval edit_nb_cfg_end;
+ unsigned long edit_nb_cfg_tm;
+ struct timeval prep_nb_cfg_start;
+ struct timeval prep_nb_cfg_end;
+ unsigned long prep_nb_cfg_tm;
+ struct mgmt_be_batch_ctx *batch;
+ bool error;
+ char err_buf[BUFSIZ];
+ size_t num_processed;
+ bool debug_be = mgmt_debug_be_client;
+ int err;
+
+ assert(txn && txn->client_ctx);
+ client_ctx = txn->client_ctx;
+
+ num_processed = 0;
+ FOREACH_BE_TXN_BATCH_IN_LIST (txn, batch) {
+ txn_req = &batch->txn_req;
+ error = false;
+ nb_ctx.client = NB_CLIENT_CLI;
+ nb_ctx.user = (void *)client_ctx->client_params.user_data;
+
+ if (!txn->nb_txn) {
+ /*
+ * This happens when the current backend client is only
+ * interested in consuming the config items but is not
+ * interested in validating it.
+ */
+ error = false;
+ if (debug_be)
+ gettimeofday(&edit_nb_cfg_start, NULL);
+ nb_candidate_edit_config_changes(
+ client_ctx->candidate_config,
+ txn_req->req.set_cfg.cfg_changes,
+ (size_t)txn_req->req.set_cfg.num_cfg_changes,
+ NULL, NULL, 0, err_buf, sizeof(err_buf),
+ &error);
+ if (error) {
+ err_buf[sizeof(err_buf) - 1] = 0;
+ MGMTD_BE_CLIENT_ERR(
+ "Failed to update configs for Txn %llx Batch %llx to Candidate! Err: '%s'",
+ (unsigned long long)txn->txn_id,
+ (unsigned long long)batch->batch_id,
+ err_buf);
+ return -1;
+ }
+ if (debug_be) {
+ gettimeofday(&edit_nb_cfg_end, NULL);
+ edit_nb_cfg_tm = timeval_elapsed(
+ edit_nb_cfg_end, edit_nb_cfg_start);
+ client_ctx->avg_edit_nb_cfg_tm =
+ ((client_ctx->avg_edit_nb_cfg_tm
+ * client_ctx->num_edit_nb_cfg)
+ + edit_nb_cfg_tm)
+ / (client_ctx->num_edit_nb_cfg + 1);
+ }
+ client_ctx->num_edit_nb_cfg++;
+ }
+
+ num_processed++;
+ }
+
+ if (!num_processed)
+ return 0;
+
+ /*
+ * Now prepare all the batches we have applied in one go.
+ */
+ nb_ctx.client = NB_CLIENT_CLI;
+ nb_ctx.user = (void *)client_ctx->client_params.user_data;
+ if (debug_be)
+ gettimeofday(&prep_nb_cfg_start, NULL);
+ err = nb_candidate_commit_prepare(nb_ctx, client_ctx->candidate_config,
+ "MGMTD Backend Txn", &txn->nb_txn,
+#ifdef MGMTD_LOCAL_VALIDATIONS_ENABLED
+ true, true,
+#else
+ false, true,
+#endif
+ err_buf, sizeof(err_buf) - 1);
+ if (err != NB_OK) {
+ err_buf[sizeof(err_buf) - 1] = 0;
+ if (err == NB_ERR_VALIDATION)
+ MGMTD_BE_CLIENT_ERR(
+ "Failed to validate configs for Txn %llx %u Batches! Err: '%s'",
+ (unsigned long long)txn->txn_id,
+ (uint32_t)num_processed, err_buf);
+ else
+ MGMTD_BE_CLIENT_ERR(
+ "Failed to prepare configs for Txn %llx, %u Batches! Err: '%s'",
+ (unsigned long long)txn->txn_id,
+ (uint32_t)num_processed, err_buf);
+ error = true;
+ SET_FLAG(txn->flags, MGMTD_BE_TXN_FLAGS_CFGPREP_FAILED);
+ } else
+ MGMTD_BE_CLIENT_DBG(
+ "Prepared configs for Txn %llx, %u Batches! successfully!",
+ (unsigned long long)txn->txn_id,
+ (uint32_t)num_processed);
+ if (debug_be) {
+ gettimeofday(&prep_nb_cfg_end, NULL);
+ prep_nb_cfg_tm =
+ timeval_elapsed(prep_nb_cfg_end, prep_nb_cfg_start);
+ client_ctx->avg_prep_nb_cfg_tm =
+ ((client_ctx->avg_prep_nb_cfg_tm
+ * client_ctx->num_prep_nb_cfg)
+ + prep_nb_cfg_tm)
+ / (client_ctx->num_prep_nb_cfg + 1);
+ }
+ client_ctx->num_prep_nb_cfg++;
+
+ FOREACH_BE_TXN_BATCH_IN_LIST (txn, batch) {
+ mgmt_be_send_cfgdata_create_reply(
+ client_ctx, txn->txn_id, batch->batch_id,
+ error ? false : true, error ? err_buf : NULL);
+ if (!error) {
+ SET_FLAG(batch->flags,
+ MGMTD_BE_BATCH_FLAGS_CFG_PREPARED);
+ mgmt_be_batches_del(&txn->cfg_batches, batch);
+ mgmt_be_batches_add_tail(&txn->apply_cfgs, batch);
+ }
+ }
+
+ if (debug_be)
+ MGMTD_BE_CLIENT_DBG(
+ "Avg-nb-edit-duration %lu uSec, nb-prep-duration %lu (avg: %lu) uSec, batch size %u",
+ client_ctx->avg_edit_nb_cfg_tm, prep_nb_cfg_tm,
+ client_ctx->avg_prep_nb_cfg_tm, (uint32_t)num_processed);
+
+ if (error)
+ mgmt_be_txn_cfg_abort(txn);
+
+ return 0;
+}
+
+/*
+ * Process all CFG_DATA_REQs received so far and prepare them all in one go.
+ */
+static int
+mgmt_be_update_setcfg_in_batch(struct mgmt_be_client_ctx *client_ctx,
+ struct mgmt_be_txn_ctx *txn,
+ uint64_t batch_id,
+ Mgmtd__YangCfgDataReq * cfg_req[],
+ int num_req)
+{
+ struct mgmt_be_batch_ctx *batch = NULL;
+ struct mgmt_be_txn_req *txn_req = NULL;
+ int index;
+ struct nb_cfg_change *cfg_chg;
+
+ batch = mgmt_be_batch_create(txn, batch_id);
+ if (!batch) {
+ MGMTD_BE_CLIENT_ERR("Batch create failed!");
+ return -1;
+ }
+
+ txn_req = &batch->txn_req;
+ txn_req->event = MGMTD_BE_TXN_PROC_SETCFG;
+ MGMTD_BE_CLIENT_DBG(
+ "Created Set-Config request for batch 0x%llx, txn id 0x%llx, cfg-items:%d",
+ (unsigned long long)batch_id, (unsigned long long)txn->txn_id,
+ num_req);
+
+ txn_req->req.set_cfg.num_cfg_changes = num_req;
+ for (index = 0; index < num_req; index++) {
+ cfg_chg = &txn_req->req.set_cfg.cfg_changes[index];
+
+ if (cfg_req[index]->req_type
+ == MGMTD__CFG_DATA_REQ_TYPE__DELETE_DATA)
+ cfg_chg->operation = NB_OP_DESTROY;
+ else
+ cfg_chg->operation = NB_OP_CREATE;
+
+ strlcpy(cfg_chg->xpath, cfg_req[index]->data->xpath,
+ sizeof(cfg_chg->xpath));
+ cfg_chg->value = (cfg_req[index]->data->value
+ && cfg_req[index]
+ ->data->value
+ ->encoded_str_val
+ ? strdup(cfg_req[index]
+ ->data->value
+ ->encoded_str_val)
+ : NULL);
+ if (cfg_chg->value
+ && !strncmp(cfg_chg->value, MGMTD_BE_CONTAINER_NODE_VAL,
+ strlen(MGMTD_BE_CONTAINER_NODE_VAL))) {
+ free((char *)cfg_chg->value);
+ cfg_chg->value = NULL;
+ }
+ }
+
+ return 0;
+}
+
+static int
+mgmt_be_process_cfgdata_req(struct mgmt_be_client_ctx *client_ctx,
+ uint64_t txn_id, uint64_t batch_id,
+ Mgmtd__YangCfgDataReq * cfg_req[], int num_req,
+ bool end_of_data)
+{
+ struct mgmt_be_txn_ctx *txn;
+
+ txn = mgmt_be_find_txn_by_id(client_ctx, txn_id);
+ if (!txn) {
+ MGMTD_BE_CLIENT_ERR(
+ "Invalid txn-id 0x%llx provided from MGMTD server",
+ (unsigned long long)txn_id);
+ mgmt_be_send_cfgdata_create_reply(
+ client_ctx, txn_id, batch_id, false,
+ "Transaction context not created yet");
+ } else {
+ mgmt_be_update_setcfg_in_batch(client_ctx, txn, batch_id,
+ cfg_req, num_req);
+ }
+
+ if (txn && end_of_data) {
+ MGMTD_BE_CLIENT_DBG("Triggering CFG_PREPARE_REQ processing");
+ mgmt_be_txn_cfg_prepare(txn);
+ }
+
+ return 0;
+}
+
+static int mgmt_be_send_apply_reply(struct mgmt_be_client_ctx *client_ctx,
+ uint64_t txn_id, uint64_t batch_ids[],
+ size_t num_batch_ids, bool success,
+ const char *error_if_any)
+{
+ Mgmtd__BeMessage be_msg;
+ Mgmtd__BeCfgDataApplyReply apply_reply;
+
+ mgmtd__be_cfg_data_apply_reply__init(&apply_reply);
+ apply_reply.success = success;
+ apply_reply.txn_id = txn_id;
+ apply_reply.batch_ids = (uint64_t *)batch_ids;
+ apply_reply.n_batch_ids = num_batch_ids;
+
+ if (error_if_any)
+ apply_reply.error_if_any = (char *)error_if_any;
+
+ mgmtd__be_message__init(&be_msg);
+ be_msg.message_case = MGMTD__BE_MESSAGE__MESSAGE_CFG_APPLY_REPLY;
+ be_msg.cfg_apply_reply = &apply_reply;
+
+ MGMTD_BE_CLIENT_DBG(
+ "Sending CFG_APPLY_REPLY message to MGMTD for txn 0x%llx, %d batches [0x%llx - 0x%llx]",
+ (unsigned long long)txn_id, (int)num_batch_ids,
+ success && num_batch_ids ?
+ (unsigned long long)batch_ids[0] : 0,
+ success && num_batch_ids ?
+ (unsigned long long)batch_ids[num_batch_ids - 1] : 0);
+
+ return mgmt_be_client_send_msg(client_ctx, &be_msg);
+}
+
+static int mgmt_be_txn_proc_cfgapply(struct mgmt_be_txn_ctx *txn)
+{
+ struct mgmt_be_client_ctx *client_ctx;
+ struct timeval apply_nb_cfg_start;
+ struct timeval apply_nb_cfg_end;
+ unsigned long apply_nb_cfg_tm;
+ struct mgmt_be_batch_ctx *batch;
+ char err_buf[BUFSIZ];
+ size_t num_processed;
+ static uint64_t batch_ids[MGMTD_BE_MAX_BATCH_IDS_IN_REQ];
+ bool debug_be = mgmt_debug_be_client;
+
+ assert(txn && txn->client_ctx);
+ client_ctx = txn->client_ctx;
+
+ assert(txn->nb_txn);
+ num_processed = 0;
+
+ /*
+ * Now apply all the batches we have applied in one go.
+ */
+ if (debug_be)
+ gettimeofday(&apply_nb_cfg_start, NULL);
+ (void)nb_candidate_commit_apply(txn->nb_txn, true, &txn->nb_txn_id,
+ err_buf, sizeof(err_buf) - 1);
+ if (debug_be) {
+ gettimeofday(&apply_nb_cfg_end, NULL);
+ apply_nb_cfg_tm =
+ timeval_elapsed(apply_nb_cfg_end, apply_nb_cfg_start);
+ client_ctx->avg_apply_nb_cfg_tm =
+ ((client_ctx->avg_apply_nb_cfg_tm
+ * client_ctx->num_apply_nb_cfg)
+ + apply_nb_cfg_tm)
+ / (client_ctx->num_apply_nb_cfg + 1);
+ }
+ client_ctx->num_apply_nb_cfg++;
+ txn->nb_txn = NULL;
+
+ /*
+ * Send back CFG_APPLY_REPLY for all batches applied.
+ */
+ FOREACH_BE_APPLY_BATCH_IN_LIST (txn, batch) {
+ /*
+ * No need to delete the batch yet. Will be deleted during
+ * transaction cleanup on receiving TXN_DELETE_REQ.
+ */
+ SET_FLAG(batch->flags, MGMTD_BE_TXN_FLAGS_CFG_APPLIED);
+ mgmt_be_batches_del(&txn->apply_cfgs, batch);
+ mgmt_be_batches_add_tail(&txn->cfg_batches, batch);
+
+ batch_ids[num_processed] = batch->batch_id;
+ num_processed++;
+ if (num_processed == MGMTD_BE_MAX_BATCH_IDS_IN_REQ) {
+ mgmt_be_send_apply_reply(client_ctx, txn->txn_id,
+ batch_ids, num_processed,
+ true, NULL);
+ num_processed = 0;
+ }
+ }
+
+ mgmt_be_send_apply_reply(client_ctx, txn->txn_id, batch_ids,
+ num_processed, true, NULL);
+
+ if (debug_be)
+ MGMTD_BE_CLIENT_DBG("Nb-apply-duration %lu (avg: %lu) uSec",
+ apply_nb_cfg_tm,
+ client_ctx->avg_apply_nb_cfg_tm);
+
+ return 0;
+}
+
+static int
+mgmt_be_process_cfg_apply(struct mgmt_be_client_ctx *client_ctx,
+ uint64_t txn_id)
+{
+ struct mgmt_be_txn_ctx *txn;
+
+ txn = mgmt_be_find_txn_by_id(client_ctx, txn_id);
+ if (!txn) {
+ mgmt_be_send_apply_reply(client_ctx, txn_id, NULL, 0, false,
+ "Transaction not created yet!");
+ return -1;
+ }
+
+ MGMTD_BE_CLIENT_DBG("Trigger CFG_APPLY_REQ processing");
+ mgmt_be_txn_proc_cfgapply(txn);
+
+ return 0;
+}
+
+static int
+mgmt_be_client_handle_msg(struct mgmt_be_client_ctx *client_ctx,
+ 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_REPLY:
+ MGMTD_BE_CLIENT_DBG("Subscribe Reply Msg from mgmt, status %u",
+ be_msg->subscr_reply->success);
+ break;
+ case MGMTD__BE_MESSAGE__MESSAGE_TXN_REQ:
+ mgmt_be_process_txn_req(client_ctx,
+ be_msg->txn_req->txn_id,
+ be_msg->txn_req->create);
+ break;
+ case MGMTD__BE_MESSAGE__MESSAGE_CFG_DATA_REQ:
+ mgmt_be_process_cfgdata_req(
+ client_ctx, be_msg->cfg_data_req->txn_id,
+ be_msg->cfg_data_req->batch_id,
+ be_msg->cfg_data_req->data_req,
+ be_msg->cfg_data_req->n_data_req,
+ be_msg->cfg_data_req->end_of_data);
+ break;
+ case MGMTD__BE_MESSAGE__MESSAGE_CFG_APPLY_REQ:
+ mgmt_be_process_cfg_apply(
+ client_ctx, (uint64_t)be_msg->cfg_apply_req->txn_id);
+ break;
+ case MGMTD__BE_MESSAGE__MESSAGE_GET_REQ:
+ case MGMTD__BE_MESSAGE__MESSAGE_SUBSCR_REQ:
+ case MGMTD__BE_MESSAGE__MESSAGE_CFG_CMD_REQ:
+ case MGMTD__BE_MESSAGE__MESSAGE_SHOW_CMD_REQ:
+ /*
+ * TODO: Add handling code in future.
+ */
+ break;
+ /*
+ * NOTE: The following messages are always sent from Backend
+ * clients to MGMTd only and/or need not be handled here.
+ */
+ case MGMTD__BE_MESSAGE__MESSAGE_GET_REPLY:
+ case MGMTD__BE_MESSAGE__MESSAGE_TXN_REPLY:
+ case MGMTD__BE_MESSAGE__MESSAGE_CFG_DATA_REPLY:
+ case MGMTD__BE_MESSAGE__MESSAGE_CFG_APPLY_REPLY:
+ case MGMTD__BE_MESSAGE__MESSAGE_CFG_CMD_REPLY:
+ case MGMTD__BE_MESSAGE__MESSAGE_SHOW_CMD_REPLY:
+ case MGMTD__BE_MESSAGE__MESSAGE_NOTIFY_DATA:
+ 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;
+}
+
+static void mgmt_be_client_process_msg(void *user_ctx, uint8_t *data,
+ size_t len)
+{
+ struct mgmt_be_client_ctx *client_ctx = user_ctx;
+ Mgmtd__BeMessage *be_msg;
+
+ be_msg = mgmtd__be_message__unpack(NULL, len, data);
+ if (!be_msg) {
+ MGMTD_BE_CLIENT_DBG("Failed to decode %zu bytes from server",
+ len);
+ return;
+ }
+ MGMTD_BE_CLIENT_DBG(
+ "Decoded %zu bytes of message(msg: %u/%u) from server", len,
+ be_msg->message_case, be_msg->message_case);
+ (void)mgmt_be_client_handle_msg(client_ctx, be_msg);
+ mgmtd__be_message__free_unpacked(be_msg, NULL);
+}
+
+static void mgmt_be_client_proc_msgbufs(struct event *thread)
+{
+ struct mgmt_be_client_ctx *client_ctx = EVENT_ARG(thread);
+
+ if (mgmt_msg_procbufs(&client_ctx->mstate, mgmt_be_client_process_msg,
+ client_ctx, mgmt_debug_be_client))
+ mgmt_be_client_register_event(client_ctx, MGMTD_BE_PROC_MSG);
+}
+
+static void mgmt_be_client_read(struct event *thread)
+{
+ struct mgmt_be_client_ctx *client_ctx = EVENT_ARG(thread);
+ enum mgmt_msg_rsched rv;
+
+ rv = mgmt_msg_read(&client_ctx->mstate, client_ctx->conn_fd,
+ mgmt_debug_be_client);
+ if (rv == MSR_DISCONNECT) {
+ mgmt_be_server_disconnect(client_ctx, true);
+ return;
+ }
+ if (rv == MSR_SCHED_BOTH)
+ mgmt_be_client_register_event(client_ctx, MGMTD_BE_PROC_MSG);
+ mgmt_be_client_register_event(client_ctx, MGMTD_BE_CONN_READ);
+}
+
+static inline void
+mgmt_be_client_sched_msg_write(struct mgmt_be_client_ctx *client_ctx)
+{
+ if (!CHECK_FLAG(client_ctx->flags, MGMTD_BE_CLIENT_FLAGS_WRITES_OFF))
+ mgmt_be_client_register_event(client_ctx,
+ MGMTD_BE_CONN_WRITE);
+}
+
+static inline void
+mgmt_be_client_writes_on(struct mgmt_be_client_ctx *client_ctx)
+{
+ MGMTD_BE_CLIENT_DBG("Resume writing msgs");
+ UNSET_FLAG(client_ctx->flags, MGMTD_BE_CLIENT_FLAGS_WRITES_OFF);
+ mgmt_be_client_sched_msg_write(client_ctx);
+}
+
+static inline void
+mgmt_be_client_writes_off(struct mgmt_be_client_ctx *client_ctx)
+{
+ SET_FLAG(client_ctx->flags, MGMTD_BE_CLIENT_FLAGS_WRITES_OFF);
+ MGMTD_BE_CLIENT_DBG("Paused writing msgs");
+}
+
+static int mgmt_be_client_send_msg(struct mgmt_be_client_ctx *client_ctx,
+ Mgmtd__BeMessage *be_msg)
+{
+ if (client_ctx->conn_fd == -1) {
+ MGMTD_BE_CLIENT_DBG("can't send message on closed connection");
+ return -1;
+ }
+
+ int rv = mgmt_msg_send_msg(
+ &client_ctx->mstate, be_msg,
+ mgmtd__be_message__get_packed_size(be_msg),
+ (size_t(*)(void *, void *))mgmtd__be_message__pack,
+ mgmt_debug_be_client);
+ mgmt_be_client_sched_msg_write(client_ctx);
+ return rv;
+}
+
+static void mgmt_be_client_write(struct event *thread)
+{
+ struct mgmt_be_client_ctx *client_ctx = EVENT_ARG(thread);
+ enum mgmt_msg_wsched rv;
+
+ rv = mgmt_msg_write(&client_ctx->mstate, client_ctx->conn_fd,
+ mgmt_debug_be_client);
+ if (rv == MSW_SCHED_STREAM)
+ mgmt_be_client_register_event(client_ctx, MGMTD_BE_CONN_WRITE);
+ else if (rv == MSW_DISCONNECT)
+ mgmt_be_server_disconnect(client_ctx, true);
+ else if (rv == MSW_SCHED_WRITES_OFF) {
+ mgmt_be_client_writes_off(client_ctx);
+ mgmt_be_client_register_event(client_ctx,
+ MGMTD_BE_CONN_WRITES_ON);
+ } else
+ assert(rv == MSW_SCHED_NONE);
+}
+
+static void mgmt_be_client_resume_writes(struct event *thread)
+{
+ struct mgmt_be_client_ctx *client_ctx;
+
+ client_ctx = (struct mgmt_be_client_ctx *)EVENT_ARG(thread);
+ assert(client_ctx && client_ctx->conn_fd != -1);
+
+ mgmt_be_client_writes_on(client_ctx);
+}
+
+static int mgmt_be_send_subscr_req(struct mgmt_be_client_ctx *client_ctx,
+ bool subscr_xpaths, uint16_t num_reg_xpaths,
+ char **reg_xpaths)
+{
+ Mgmtd__BeMessage be_msg;
+ Mgmtd__BeSubscribeReq subscr_req;
+
+ mgmtd__be_subscribe_req__init(&subscr_req);
+ subscr_req.client_name = client_ctx->client_params.name;
+ subscr_req.n_xpath_reg = num_reg_xpaths;
+ if (num_reg_xpaths)
+ subscr_req.xpath_reg = reg_xpaths;
+ else
+ subscr_req.xpath_reg = NULL;
+ subscr_req.subscribe_xpaths = subscr_xpaths;
+
+ mgmtd__be_message__init(&be_msg);
+ be_msg.message_case = MGMTD__BE_MESSAGE__MESSAGE_SUBSCR_REQ;
+ be_msg.subscr_req = &subscr_req;
+
+ return mgmt_be_client_send_msg(client_ctx, &be_msg);
+}
+
+static void mgmt_be_server_connect(struct mgmt_be_client_ctx *client_ctx)
+{
+ const char *dbgtag = mgmt_debug_be_client ? "BE-client" : NULL;
+
+ assert(client_ctx->conn_fd == -1);
+ client_ctx->conn_fd = mgmt_msg_connect(
+ MGMTD_BE_SERVER_PATH, MGMTD_SOCKET_BE_SEND_BUF_SIZE,
+ MGMTD_SOCKET_BE_RECV_BUF_SIZE, dbgtag);
+
+ /* Send SUBSCRIBE_REQ message */
+ if (client_ctx->conn_fd == -1 ||
+ mgmt_be_send_subscr_req(client_ctx, false, 0, NULL) != 0) {
+ mgmt_be_server_disconnect(client_ctx, true);
+ return;
+ }
+
+ /* Start reading from the socket */
+ mgmt_be_client_register_event(client_ctx, MGMTD_BE_CONN_READ);
+
+ /* Notify client through registered callback (if any) */
+ if (client_ctx->client_params.client_connect_notify)
+ (void)(*client_ctx->client_params.client_connect_notify)(
+ (uintptr_t)client_ctx,
+ client_ctx->client_params.user_data, true);
+}
+
+static void mgmt_be_client_conn_timeout(struct event *thread)
+{
+ mgmt_be_server_connect(EVENT_ARG(thread));
+}
+
+static void
+mgmt_be_client_register_event(struct mgmt_be_client_ctx *client_ctx,
+ enum mgmt_be_event event)
+{
+ struct timeval tv = {0};
+
+ switch (event) {
+ case MGMTD_BE_CONN_READ:
+ event_add_read(client_ctx->tm, mgmt_be_client_read,
+ client_ctx, client_ctx->conn_fd,
+ &client_ctx->conn_read_ev);
+ break;
+ case MGMTD_BE_CONN_WRITE:
+ event_add_write(client_ctx->tm, mgmt_be_client_write,
+ client_ctx, client_ctx->conn_fd,
+ &client_ctx->conn_write_ev);
+ break;
+ case MGMTD_BE_PROC_MSG:
+ tv.tv_usec = MGMTD_BE_MSG_PROC_DELAY_USEC;
+ event_add_timer_tv(client_ctx->tm, mgmt_be_client_proc_msgbufs,
+ client_ctx, &tv, &client_ctx->msg_proc_ev);
+ break;
+ case MGMTD_BE_CONN_WRITES_ON:
+ event_add_timer_msec(client_ctx->tm,
+ mgmt_be_client_resume_writes, client_ctx,
+ MGMTD_BE_MSG_WRITE_DELAY_MSEC,
+ &client_ctx->conn_writes_on);
+ break;
+ case MGMTD_BE_SERVER:
+ case MGMTD_BE_CONN_INIT:
+ case MGMTD_BE_SCHED_CFG_PREPARE:
+ case MGMTD_BE_RESCHED_CFG_PREPARE:
+ case MGMTD_BE_SCHED_CFG_APPLY:
+ case MGMTD_BE_RESCHED_CFG_APPLY:
+ assert(!"mgmt_be_client_post_event() called incorrectly");
+ break;
+ }
+}
+
+static void
+mgmt_be_client_schedule_conn_retry(struct mgmt_be_client_ctx *client_ctx,
+ unsigned long intvl_secs)
+{
+ MGMTD_BE_CLIENT_DBG(
+ "Scheduling MGMTD Backend server connection retry after %lu seconds",
+ intvl_secs);
+ event_add_timer(client_ctx->tm, mgmt_be_client_conn_timeout,
+ (void *)client_ctx, intvl_secs,
+ &client_ctx->conn_retry_tmr);
+}
+
+extern struct nb_config *running_config;
+
+/*
+ * Initialize library and try connecting with MGMTD.
+ */
+uintptr_t mgmt_be_client_lib_init(struct mgmt_be_client_params *params,
+ struct event_loop *master_thread)
+{
+ assert(master_thread && params && strlen(params->name)
+ && !mgmt_be_client_ctx.tm);
+
+ mgmt_be_client_ctx.tm = master_thread;
+
+ if (!running_config)
+ assert(!"MGMTD Be Client lib_init() after frr_init() only!");
+ mgmt_be_client_ctx.running_config = running_config;
+ mgmt_be_client_ctx.candidate_config = nb_config_new(NULL);
+
+ memcpy(&mgmt_be_client_ctx.client_params, params,
+ sizeof(mgmt_be_client_ctx.client_params));
+ if (!mgmt_be_client_ctx.client_params.conn_retry_intvl_sec)
+ mgmt_be_client_ctx.client_params.conn_retry_intvl_sec =
+ MGMTD_BE_DEFAULT_CONN_RETRY_INTVL_SEC;
+
+ mgmt_be_txns_init(&mgmt_be_client_ctx.txn_head);
+ mgmt_msg_init(&mgmt_be_client_ctx.mstate, MGMTD_BE_MAX_NUM_MSG_PROC,
+ MGMTD_BE_MAX_NUM_MSG_WRITE, MGMTD_BE_MSG_MAX_LEN,
+ "BE-client");
+
+ /* Start trying to connect to MGMTD backend server immediately */
+ mgmt_be_client_schedule_conn_retry(&mgmt_be_client_ctx, 1);
+
+ MGMTD_BE_CLIENT_DBG("Initialized client '%s'", params->name);
+
+ return (uintptr_t)&mgmt_be_client_ctx;
+}
+
+/*
+ * Subscribe with MGMTD for one or more YANG subtree(s).
+ */
+enum mgmt_result mgmt_be_subscribe_yang_data(uintptr_t lib_hndl,
+ char *reg_yang_xpaths[],
+ int num_reg_xpaths)
+{
+ struct mgmt_be_client_ctx *client_ctx;
+
+ client_ctx = (struct mgmt_be_client_ctx *)lib_hndl;
+ if (!client_ctx)
+ return MGMTD_INVALID_PARAM;
+
+ if (mgmt_be_send_subscr_req(client_ctx, true, num_reg_xpaths,
+ reg_yang_xpaths)
+ != 0)
+ return MGMTD_INTERNAL_ERROR;
+
+ return MGMTD_SUCCESS;
+}
+
+/*
+ * Unsubscribe with MGMTD for one or more YANG subtree(s).
+ */
+enum mgmt_result mgmt_be_unsubscribe_yang_data(uintptr_t lib_hndl,
+ char *reg_yang_xpaths[],
+ int num_reg_xpaths)
+{
+ struct mgmt_be_client_ctx *client_ctx;
+
+ client_ctx = (struct mgmt_be_client_ctx *)lib_hndl;
+ if (!client_ctx)
+ return MGMTD_INVALID_PARAM;
+
+
+ if (mgmt_be_send_subscr_req(client_ctx, false, num_reg_xpaths,
+ reg_yang_xpaths)
+ < 0)
+ return MGMTD_INTERNAL_ERROR;
+
+ return MGMTD_SUCCESS;
+}
+
+/*
+ * Send one or more YANG notifications to MGMTD daemon.
+ */
+enum mgmt_result mgmt_be_send_yang_notify(uintptr_t lib_hndl,
+ Mgmtd__YangData * data_elems[],
+ int num_elems)
+{
+ struct mgmt_be_client_ctx *client_ctx;
+
+ client_ctx = (struct mgmt_be_client_ctx *)lib_hndl;
+ if (!client_ctx)
+ return MGMTD_INVALID_PARAM;
+
+ return MGMTD_SUCCESS;
+}
+
+/*
+ * Destroy library and cleanup everything.
+ */
+void mgmt_be_client_lib_destroy(uintptr_t lib_hndl)
+{
+ struct mgmt_be_client_ctx *client_ctx;
+
+ client_ctx = (struct mgmt_be_client_ctx *)lib_hndl;
+ assert(client_ctx);
+
+ MGMTD_BE_CLIENT_DBG("Destroying MGMTD Backend Client '%s'",
+ client_ctx->client_params.name);
+
+ mgmt_be_server_disconnect(client_ctx, false);
+
+ mgmt_msg_destroy(&client_ctx->mstate);
+
+ EVENT_OFF(client_ctx->conn_retry_tmr);
+ EVENT_OFF(client_ctx->conn_read_ev);
+ EVENT_OFF(client_ctx->conn_write_ev);
+ EVENT_OFF(client_ctx->conn_writes_on);
+ EVENT_OFF(client_ctx->msg_proc_ev);
+ mgmt_be_cleanup_all_txns(client_ctx);
+ mgmt_be_txns_fini(&client_ctx->txn_head);
+}
diff --git a/lib/mgmt_be_client.h b/lib/mgmt_be_client.h
new file mode 100644
index 0000000000..db427457a4
--- /dev/null
+++ b/lib/mgmt_be_client.h
@@ -0,0 +1,260 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * MGMTD Backend Client Library api interfaces
+ * Copyright (C) 2021 Vmware, Inc.
+ * Pushpasis Sarkar <spushpasis@vmware.com>
+ */
+
+#ifndef _FRR_MGMTD_BE_CLIENT_H_
+#define _FRR_MGMTD_BE_CLIENT_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "northbound.h"
+#include "mgmt_pb.h"
+#include "mgmtd/mgmt_defines.h"
+
+/***************************************************************
+ * Client IDs
+ ***************************************************************/
+
+/*
+ * Add enum value for each supported component, wrap with
+ * #ifdef HAVE_COMPONENT
+ */
+enum mgmt_be_client_id {
+ MGMTD_BE_CLIENT_ID_MIN = 0,
+ MGMTD_BE_CLIENT_ID_INIT = -1,
+#ifdef HAVE_STATICD
+ MGMTD_BE_CLIENT_ID_STATICD,
+#endif
+ MGMTD_BE_CLIENT_ID_MAX
+};
+
+#define FOREACH_MGMTD_BE_CLIENT_ID(id) \
+ for ((id) = MGMTD_BE_CLIENT_ID_MIN; \
+ (id) < MGMTD_BE_CLIENT_ID_MAX; (id)++)
+
+/***************************************************************
+ * Constants
+ ***************************************************************/
+
+#define MGMTD_BE_CLIENT_ERROR_STRING_MAX_LEN 32
+
+#define MGMTD_BE_DEFAULT_CONN_RETRY_INTVL_SEC 5
+
+#define MGMTD_BE_MSG_PROC_DELAY_USEC 10
+#define MGMTD_BE_MAX_NUM_MSG_PROC 500
+
+#define MGMTD_BE_MSG_WRITE_DELAY_MSEC 1
+#define MGMTD_BE_MAX_NUM_MSG_WRITE 1000
+
+#define GMGD_BE_MAX_NUM_REQ_ITEMS 64
+
+#define MGMTD_BE_MSG_MAX_LEN 16384
+
+#define MGMTD_SOCKET_BE_SEND_BUF_SIZE 65535
+#define MGMTD_SOCKET_BE_RECV_BUF_SIZE MGMTD_SOCKET_BE_SEND_BUF_SIZE
+
+#define MGMTD_MAX_CFG_CHANGES_IN_BATCH \
+ ((10 * MGMTD_BE_MSG_MAX_LEN) / \
+ (MGMTD_MAX_XPATH_LEN + MGMTD_MAX_YANG_VALUE_LEN))
+
+/*
+ * MGMTD_BE_MSG_MAX_LEN must be used 80%
+ * since there is overhead of google protobuf
+ * that gets added to sent message
+ */
+#define MGMTD_BE_CFGDATA_PACKING_EFFICIENCY 0.8
+#define MGMTD_BE_CFGDATA_MAX_MSG_LEN \
+ (MGMTD_BE_MSG_MAX_LEN * MGMTD_BE_CFGDATA_PACKING_EFFICIENCY)
+
+#define MGMTD_BE_MAX_BATCH_IDS_IN_REQ \
+ (MGMTD_BE_MSG_MAX_LEN - 128) / sizeof(uint64_t)
+
+#define MGMTD_BE_CONTAINER_NODE_VAL "<<container>>"
+
+/***************************************************************
+ * Data-structures
+ ***************************************************************/
+
+#define MGMTD_BE_MAX_CLIENTS_PER_XPATH_REG 32
+
+struct mgmt_be_client_txn_ctx {
+ uintptr_t *user_ctx;
+};
+
+/*
+ * All the client-specific information this library needs to
+ * initialize itself, setup connection with MGMTD BackEnd interface
+ * and carry on all required procedures appropriately.
+ *
+ * BackEnd clients need to initialise a instance of this structure
+ * with appropriate data and pass it while calling the API
+ * to initialize the library (See mgmt_be_client_lib_init for
+ * more details).
+ */
+struct mgmt_be_client_params {
+ char name[MGMTD_CLIENT_NAME_MAX_LEN];
+ uintptr_t user_data;
+ unsigned long conn_retry_intvl_sec;
+
+ void (*client_connect_notify)(uintptr_t lib_hndl,
+ uintptr_t usr_data,
+ bool connected);
+
+ void (*client_subscribe_notify)(
+ uintptr_t lib_hndl, uintptr_t usr_data,
+ struct nb_yang_xpath **xpath,
+ enum mgmt_result subscribe_result[], int num_paths);
+
+ void (*txn_notify)(
+ uintptr_t lib_hndl, uintptr_t usr_data,
+ struct mgmt_be_client_txn_ctx *txn_ctx, bool destroyed);
+
+ enum mgmt_result (*data_validate)(
+ uintptr_t lib_hndl, uintptr_t usr_data,
+ struct mgmt_be_client_txn_ctx *txn_ctx,
+ struct nb_yang_xpath *xpath, struct nb_yang_value *data,
+ bool delete, char *error_if_any);
+
+ enum mgmt_result (*data_apply)(
+ uintptr_t lib_hndl, uintptr_t usr_data,
+ struct mgmt_be_client_txn_ctx *txn_ctx,
+ struct nb_yang_xpath *xpath, struct nb_yang_value *data,
+ bool delete);
+
+ enum mgmt_result (*get_data_elem)(
+ uintptr_t lib_hndl, uintptr_t usr_data,
+ struct mgmt_be_client_txn_ctx *txn_ctx,
+ struct nb_yang_xpath *xpath, struct nb_yang_xpath_elem *elem);
+
+ enum mgmt_result (*get_data)(
+ uintptr_t lib_hndl, uintptr_t usr_data,
+ struct mgmt_be_client_txn_ctx *txn_ctx,
+ struct nb_yang_xpath *xpath, bool keys_only,
+ struct nb_yang_xpath_elem **elems, int *num_elems,
+ int *next_key);
+
+ enum mgmt_result (*get_next_data)(
+ uintptr_t lib_hndl, uintptr_t usr_data,
+ struct mgmt_be_client_txn_ctx *txn_ctx,
+ struct nb_yang_xpath *xpath, bool keys_only,
+ struct nb_yang_xpath_elem **elems, int *num_elems);
+};
+
+/***************************************************************
+ * Global data exported
+ ***************************************************************/
+
+extern const char *mgmt_be_client_names[MGMTD_BE_CLIENT_ID_MAX + 1];
+
+static inline const char *mgmt_be_client_id2name(enum mgmt_be_client_id id)
+{
+ if (id > MGMTD_BE_CLIENT_ID_MAX)
+ id = MGMTD_BE_CLIENT_ID_MAX;
+ return mgmt_be_client_names[id];
+}
+
+static inline enum mgmt_be_client_id
+mgmt_be_client_name2id(const char *name)
+{
+ enum mgmt_be_client_id id;
+
+ FOREACH_MGMTD_BE_CLIENT_ID (id) {
+ if (!strncmp(mgmt_be_client_names[id], name,
+ MGMTD_CLIENT_NAME_MAX_LEN))
+ return id;
+ }
+
+ return MGMTD_BE_CLIENT_ID_MAX;
+}
+
+/***************************************************************
+ * API prototypes
+ ***************************************************************/
+
+/*
+ * Initialize library and try connecting with MGMTD.
+ *
+ * params
+ * Backend client parameters.
+ *
+ * master_thread
+ * Thread master.
+ *
+ * Returns:
+ * Backend client lib handler (nothing but address of mgmt_be_client_ctx)
+ */
+extern uintptr_t mgmt_be_client_lib_init(struct mgmt_be_client_params *params,
+ struct event_loop *master_thread);
+
+/*
+ * Subscribe with MGMTD for one or more YANG subtree(s).
+ *
+ * lib_hndl
+ * Client library handler.
+ *
+ * reg_yang_xpaths
+ * Yang xpath(s) that needs to be subscribed to.
+ *
+ * num_xpaths
+ * Number of xpaths
+ *
+ * Returns:
+ * MGMTD_SUCCESS on success, MGMTD_* otherwise.
+ */
+extern enum mgmt_result mgmt_be_subscribe_yang_data(uintptr_t lib_hndl,
+ char **reg_yang_xpaths,
+ int num_xpaths);
+
+/*
+ * Send one or more YANG notifications to MGMTD daemon.
+ *
+ * lib_hndl
+ * Client library handler.
+ *
+ * data_elems
+ * Yang data elements from data tree.
+ *
+ * num_elems
+ * Number of data elements.
+ *
+ * Returns:
+ * MGMTD_SUCCESS on success, MGMTD_* otherwise.
+ */
+extern enum mgmt_result
+mgmt_be_send_yang_notify(uintptr_t lib_hndl, Mgmtd__YangData **data_elems,
+ int num_elems);
+
+/*
+ * Un-subscribe with MGMTD for one or more YANG subtree(s).
+ *
+ * lib_hndl
+ * Client library handler.
+ *
+ * reg_yang_xpaths
+ * Yang xpath(s) that needs to be un-subscribed from.
+ *
+ * num_reg_xpaths
+ * Number of subscribed xpaths
+ *
+ * Returns:
+ * MGMTD_SUCCESS on success, MGMTD_* otherwise.
+ */
+enum mgmt_result mgmt_be_unsubscribe_yang_data(uintptr_t lib_hndl,
+ char **reg_yang_xpaths,
+ int num_reg_xpaths);
+
+/*
+ * Destroy library and cleanup everything.
+ */
+extern void mgmt_be_client_lib_destroy(uintptr_t lib_hndl);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _FRR_MGMTD_BE_CLIENT_H_ */
diff --git a/lib/mgmt_fe_client.c b/lib/mgmt_fe_client.c
new file mode 100644
index 0000000000..a2c4fd6572
--- /dev/null
+++ b/lib/mgmt_fe_client.c
@@ -0,0 +1,1072 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * MGMTD Frontend Client Library api interfaces
+ * Copyright (C) 2021 Vmware, Inc.
+ * Pushpasis Sarkar <spushpasis@vmware.com>
+ */
+
+#include <zebra.h>
+#include "memory.h"
+#include "libfrr.h"
+#include "mgmt_fe_client.h"
+#include "mgmt_msg.h"
+#include "mgmt_pb.h"
+#include "network.h"
+#include "stream.h"
+#include "sockopt.h"
+
+#ifdef REDIRECT_DEBUG_TO_STDERR
+#define MGMTD_FE_CLIENT_DBG(fmt, ...) \
+ fprintf(stderr, "%s: " fmt "\n", __func__, ##__VA_ARGS__)
+#define MGMTD_FE_CLIENT_ERR(fmt, ...) \
+ fprintf(stderr, "%s: ERROR, " fmt "\n", __func__, ##__VA_ARGS__)
+#else /* REDIRECT_DEBUG_TO_STDERR */
+#define MGMTD_FE_CLIENT_DBG(fmt, ...) \
+ do { \
+ if (mgmt_debug_fe_client) \
+ zlog_debug("%s: " fmt, __func__, ##__VA_ARGS__); \
+ } while (0)
+#define MGMTD_FE_CLIENT_ERR(fmt, ...) \
+ zlog_err("%s: ERROR: " fmt, __func__, ##__VA_ARGS__)
+#endif /* REDIRECT_DEBUG_TO_STDERR */
+
+struct mgmt_fe_client_ctx;
+
+PREDECL_LIST(mgmt_sessions);
+
+struct mgmt_fe_client_session {
+ uint64_t client_id;
+ uint64_t session_id;
+ struct mgmt_fe_client_ctx *client_ctx;
+ uintptr_t user_ctx;
+
+ struct mgmt_sessions_item list_linkage;
+};
+
+DECLARE_LIST(mgmt_sessions, struct mgmt_fe_client_session, list_linkage);
+
+DEFINE_MTYPE_STATIC(LIB, MGMTD_FE_SESSION, "MGMTD Frontend session");
+
+struct mgmt_fe_client_ctx {
+ int conn_fd;
+ struct event_loop *tm;
+ struct event *conn_retry_tmr;
+ struct event *conn_read_ev;
+ struct event *conn_write_ev;
+ struct event *conn_writes_on;
+ struct event *msg_proc_ev;
+ uint32_t flags;
+
+ struct mgmt_msg_state mstate;
+
+ struct mgmt_fe_client_params client_params;
+
+ struct mgmt_sessions_head client_sessions;
+};
+
+#define MGMTD_FE_CLIENT_FLAGS_WRITES_OFF (1U << 0)
+
+#define FOREACH_SESSION_IN_LIST(client_ctx, session) \
+ frr_each_safe (mgmt_sessions, &(client_ctx)->client_sessions, (session))
+
+static bool mgmt_debug_fe_client;
+
+static struct mgmt_fe_client_ctx mgmt_fe_client_ctx = {
+ .conn_fd = -1,
+};
+
+/* Forward declarations */
+static void
+mgmt_fe_client_register_event(struct mgmt_fe_client_ctx *client_ctx,
+ enum mgmt_fe_event event);
+static void mgmt_fe_client_schedule_conn_retry(
+ struct mgmt_fe_client_ctx *client_ctx, unsigned long intvl_secs);
+
+static struct mgmt_fe_client_session *
+mgmt_fe_find_session_by_client_id(struct mgmt_fe_client_ctx *client_ctx,
+ uint64_t client_id)
+{
+ struct mgmt_fe_client_session *session;
+
+ FOREACH_SESSION_IN_LIST (client_ctx, session) {
+ if (session->client_id == client_id) {
+ MGMTD_FE_CLIENT_DBG(
+ "Found session %p for client-id %llu.", session,
+ (unsigned long long)client_id);
+ return session;
+ }
+ }
+
+ return NULL;
+}
+
+static struct mgmt_fe_client_session *
+mgmt_fe_find_session_by_session_id(struct mgmt_fe_client_ctx *client_ctx,
+ uint64_t session_id)
+{
+ struct mgmt_fe_client_session *session;
+
+ FOREACH_SESSION_IN_LIST (client_ctx, session) {
+ if (session->session_id == session_id) {
+ MGMTD_FE_CLIENT_DBG(
+ "Found session %p for session-id %llu.", session,
+ (unsigned long long)session_id);
+ return session;
+ }
+ }
+
+ return NULL;
+}
+
+static void
+mgmt_fe_server_disconnect(struct mgmt_fe_client_ctx *client_ctx,
+ bool reconnect)
+{
+ if (client_ctx->conn_fd != -1) {
+ close(client_ctx->conn_fd);
+ client_ctx->conn_fd = -1;
+ }
+
+ if (reconnect)
+ mgmt_fe_client_schedule_conn_retry(
+ client_ctx,
+ client_ctx->client_params.conn_retry_intvl_sec);
+}
+
+static inline void
+mgmt_fe_client_sched_msg_write(struct mgmt_fe_client_ctx *client_ctx)
+{
+ if (!CHECK_FLAG(client_ctx->flags, MGMTD_FE_CLIENT_FLAGS_WRITES_OFF))
+ mgmt_fe_client_register_event(client_ctx,
+ MGMTD_FE_CONN_WRITE);
+}
+
+static inline void
+mgmt_fe_client_writes_on(struct mgmt_fe_client_ctx *client_ctx)
+{
+ MGMTD_FE_CLIENT_DBG("Resume writing msgs");
+ UNSET_FLAG(client_ctx->flags, MGMTD_FE_CLIENT_FLAGS_WRITES_OFF);
+ mgmt_fe_client_sched_msg_write(client_ctx);
+}
+
+static inline void
+mgmt_fe_client_writes_off(struct mgmt_fe_client_ctx *client_ctx)
+{
+ SET_FLAG(client_ctx->flags, MGMTD_FE_CLIENT_FLAGS_WRITES_OFF);
+ MGMTD_FE_CLIENT_DBG("Paused writing msgs");
+}
+
+static int mgmt_fe_client_send_msg(struct mgmt_fe_client_ctx *client_ctx,
+ Mgmtd__FeMessage *fe_msg)
+{
+ /* users current expect this to fail here */
+ if (client_ctx->conn_fd == -1) {
+ MGMTD_FE_CLIENT_DBG("can't send message on closed connection");
+ return -1;
+ }
+
+ int rv = mgmt_msg_send_msg(
+ &client_ctx->mstate, fe_msg,
+ mgmtd__fe_message__get_packed_size(fe_msg),
+ (size_t(*)(void *, void *))mgmtd__fe_message__pack,
+ mgmt_debug_fe_client);
+ mgmt_fe_client_sched_msg_write(client_ctx);
+ return rv;
+}
+
+static void mgmt_fe_client_write(struct event *thread)
+{
+ struct mgmt_fe_client_ctx *client_ctx;
+ enum mgmt_msg_wsched rv;
+
+ client_ctx = (struct mgmt_fe_client_ctx *)EVENT_ARG(thread);
+ rv = mgmt_msg_write(&client_ctx->mstate, client_ctx->conn_fd,
+ mgmt_debug_fe_client);
+ if (rv == MSW_SCHED_STREAM)
+ mgmt_fe_client_register_event(client_ctx, MGMTD_FE_CONN_WRITE);
+ else if (rv == MSW_DISCONNECT)
+ mgmt_fe_server_disconnect(client_ctx, true);
+ else if (rv == MSW_SCHED_WRITES_OFF) {
+ mgmt_fe_client_writes_off(client_ctx);
+ mgmt_fe_client_register_event(client_ctx,
+ MGMTD_FE_CONN_WRITES_ON);
+ } else
+ assert(rv == MSW_SCHED_NONE);
+}
+
+static void mgmt_fe_client_resume_writes(struct event *thread)
+{
+ struct mgmt_fe_client_ctx *client_ctx;
+
+ client_ctx = (struct mgmt_fe_client_ctx *)EVENT_ARG(thread);
+ assert(client_ctx && client_ctx->conn_fd != -1);
+
+ mgmt_fe_client_writes_on(client_ctx);
+}
+
+static int
+mgmt_fe_send_register_req(struct mgmt_fe_client_ctx *client_ctx)
+{
+ Mgmtd__FeMessage fe_msg;
+ Mgmtd__FeRegisterReq rgstr_req;
+
+ mgmtd__fe_register_req__init(&rgstr_req);
+ rgstr_req.client_name = client_ctx->client_params.name;
+
+ mgmtd__fe_message__init(&fe_msg);
+ fe_msg.message_case = MGMTD__FE_MESSAGE__MESSAGE_REGISTER_REQ;
+ fe_msg.register_req = &rgstr_req;
+
+ MGMTD_FE_CLIENT_DBG(
+ "Sending REGISTER_REQ message to MGMTD Frontend server");
+
+ return mgmt_fe_client_send_msg(client_ctx, &fe_msg);
+}
+
+static int
+mgmt_fe_send_session_req(struct mgmt_fe_client_ctx *client_ctx,
+ struct mgmt_fe_client_session *session,
+ bool create)
+{
+ Mgmtd__FeMessage fe_msg;
+ Mgmtd__FeSessionReq sess_req;
+
+ mgmtd__fe_session_req__init(&sess_req);
+ sess_req.create = create;
+ if (create) {
+ sess_req.id_case = MGMTD__FE_SESSION_REQ__ID_CLIENT_CONN_ID;
+ sess_req.client_conn_id = session->client_id;
+ } else {
+ sess_req.id_case = MGMTD__FE_SESSION_REQ__ID_SESSION_ID;
+ sess_req.session_id = session->session_id;
+ }
+
+ mgmtd__fe_message__init(&fe_msg);
+ fe_msg.message_case = MGMTD__FE_MESSAGE__MESSAGE_SESSION_REQ;
+ fe_msg.session_req = &sess_req;
+
+ MGMTD_FE_CLIENT_DBG(
+ "Sending SESSION_REQ message for %s session %llu to MGMTD Frontend server",
+ create ? "creating" : "destroying",
+ (unsigned long long)session->client_id);
+
+ return mgmt_fe_client_send_msg(client_ctx, &fe_msg);
+}
+
+static int
+mgmt_fe_send_lockds_req(struct mgmt_fe_client_ctx *client_ctx,
+ struct mgmt_fe_client_session *session, bool lock,
+ uint64_t req_id, Mgmtd__DatastoreId ds_id)
+{
+ (void)req_id;
+ Mgmtd__FeMessage fe_msg;
+ Mgmtd__FeLockDsReq lockds_req;
+
+ mgmtd__fe_lock_ds_req__init(&lockds_req);
+ lockds_req.session_id = session->session_id;
+ lockds_req.req_id = req_id;
+ lockds_req.ds_id = ds_id;
+ lockds_req.lock = lock;
+
+ mgmtd__fe_message__init(&fe_msg);
+ fe_msg.message_case = MGMTD__FE_MESSAGE__MESSAGE_LOCKDS_REQ;
+ fe_msg.lockds_req = &lockds_req;
+
+ MGMTD_FE_CLIENT_DBG(
+ "Sending %sLOCK_REQ message for Ds:%d session %llu to MGMTD Frontend server",
+ lock ? "" : "UN", ds_id, (unsigned long long)session->client_id);
+
+ return mgmt_fe_client_send_msg(client_ctx, &fe_msg);
+}
+
+static int
+mgmt_fe_send_setcfg_req(struct mgmt_fe_client_ctx *client_ctx,
+ struct mgmt_fe_client_session *session,
+ uint64_t req_id, Mgmtd__DatastoreId ds_id,
+ Mgmtd__YangCfgDataReq **data_req, int num_data_reqs,
+ bool implicit_commit, Mgmtd__DatastoreId dst_ds_id)
+{
+ (void)req_id;
+ Mgmtd__FeMessage fe_msg;
+ Mgmtd__FeSetConfigReq setcfg_req;
+
+ mgmtd__fe_set_config_req__init(&setcfg_req);
+ setcfg_req.session_id = session->session_id;
+ setcfg_req.ds_id = ds_id;
+ setcfg_req.req_id = req_id;
+ setcfg_req.data = data_req;
+ setcfg_req.n_data = (size_t)num_data_reqs;
+ setcfg_req.implicit_commit = implicit_commit;
+ setcfg_req.commit_ds_id = dst_ds_id;
+
+ mgmtd__fe_message__init(&fe_msg);
+ fe_msg.message_case = MGMTD__FE_MESSAGE__MESSAGE_SETCFG_REQ;
+ fe_msg.setcfg_req = &setcfg_req;
+
+ MGMTD_FE_CLIENT_DBG(
+ "Sending SET_CONFIG_REQ message for Ds:%d session %llu (#xpaths:%d) to MGMTD Frontend server",
+ ds_id, (unsigned long long)session->client_id, num_data_reqs);
+
+ return mgmt_fe_client_send_msg(client_ctx, &fe_msg);
+}
+
+static int
+mgmt_fe_send_commitcfg_req(struct mgmt_fe_client_ctx *client_ctx,
+ struct mgmt_fe_client_session *session,
+ uint64_t req_id, Mgmtd__DatastoreId src_ds_id,
+ Mgmtd__DatastoreId dest_ds_id, bool validate_only,
+ bool abort)
+{
+ (void)req_id;
+ Mgmtd__FeMessage fe_msg;
+ Mgmtd__FeCommitConfigReq commitcfg_req;
+
+ mgmtd__fe_commit_config_req__init(&commitcfg_req);
+ commitcfg_req.session_id = session->session_id;
+ commitcfg_req.src_ds_id = src_ds_id;
+ commitcfg_req.dst_ds_id = dest_ds_id;
+ commitcfg_req.req_id = req_id;
+ commitcfg_req.validate_only = validate_only;
+ commitcfg_req.abort = abort;
+
+ mgmtd__fe_message__init(&fe_msg);
+ fe_msg.message_case = MGMTD__FE_MESSAGE__MESSAGE_COMMCFG_REQ;
+ fe_msg.commcfg_req = &commitcfg_req;
+
+ MGMTD_FE_CLIENT_DBG(
+ "Sending COMMIT_CONFIG_REQ message for Src-Ds:%d, Dst-Ds:%d session %llu to MGMTD Frontend server",
+ src_ds_id, dest_ds_id, (unsigned long long)session->client_id);
+
+ return mgmt_fe_client_send_msg(client_ctx, &fe_msg);
+}
+
+static int
+mgmt_fe_send_getcfg_req(struct mgmt_fe_client_ctx *client_ctx,
+ struct mgmt_fe_client_session *session,
+ uint64_t req_id, Mgmtd__DatastoreId ds_id,
+ Mgmtd__YangGetDataReq * data_req[],
+ int num_data_reqs)
+{
+ (void)req_id;
+ Mgmtd__FeMessage fe_msg;
+ Mgmtd__FeGetConfigReq getcfg_req;
+
+ mgmtd__fe_get_config_req__init(&getcfg_req);
+ getcfg_req.session_id = session->session_id;
+ getcfg_req.ds_id = ds_id;
+ getcfg_req.req_id = req_id;
+ getcfg_req.data = data_req;
+ getcfg_req.n_data = (size_t)num_data_reqs;
+
+ mgmtd__fe_message__init(&fe_msg);
+ fe_msg.message_case = MGMTD__FE_MESSAGE__MESSAGE_GETCFG_REQ;
+ fe_msg.getcfg_req = &getcfg_req;
+
+ MGMTD_FE_CLIENT_DBG(
+ "Sending GET_CONFIG_REQ message for Ds:%d session %llu (#xpaths:%d) to MGMTD Frontend server",
+ ds_id, (unsigned long long)session->client_id, num_data_reqs);
+
+ return mgmt_fe_client_send_msg(client_ctx, &fe_msg);
+}
+
+static int
+mgmt_fe_send_getdata_req(struct mgmt_fe_client_ctx *client_ctx,
+ struct mgmt_fe_client_session *session,
+ uint64_t req_id, Mgmtd__DatastoreId ds_id,
+ Mgmtd__YangGetDataReq * data_req[],
+ int num_data_reqs)
+{
+ (void)req_id;
+ Mgmtd__FeMessage fe_msg;
+ Mgmtd__FeGetDataReq getdata_req;
+
+ mgmtd__fe_get_data_req__init(&getdata_req);
+ getdata_req.session_id = session->session_id;
+ getdata_req.ds_id = ds_id;
+ getdata_req.req_id = req_id;
+ getdata_req.data = data_req;
+ getdata_req.n_data = (size_t)num_data_reqs;
+
+ mgmtd__fe_message__init(&fe_msg);
+ fe_msg.message_case = MGMTD__FE_MESSAGE__MESSAGE_GETDATA_REQ;
+ fe_msg.getdata_req = &getdata_req;
+
+ MGMTD_FE_CLIENT_DBG(
+ "Sending GET_CONFIG_REQ message for Ds:%d session %llu (#xpaths:%d) to MGMTD Frontend server",
+ ds_id, (unsigned long long)session->client_id, num_data_reqs);
+
+ return mgmt_fe_client_send_msg(client_ctx, &fe_msg);
+}
+
+static int mgmt_fe_send_regnotify_req(
+ struct mgmt_fe_client_ctx *client_ctx,
+ struct mgmt_fe_client_session *session, uint64_t req_id,
+ Mgmtd__DatastoreId ds_id, bool register_req,
+ Mgmtd__YangDataXPath * data_req[], int num_data_reqs)
+{
+ (void)req_id;
+ Mgmtd__FeMessage fe_msg;
+ Mgmtd__FeRegisterNotifyReq regntfy_req;
+
+ mgmtd__fe_register_notify_req__init(&regntfy_req);
+ regntfy_req.session_id = session->session_id;
+ regntfy_req.ds_id = ds_id;
+ regntfy_req.register_req = register_req;
+ regntfy_req.data_xpath = data_req;
+ regntfy_req.n_data_xpath = (size_t)num_data_reqs;
+
+ mgmtd__fe_message__init(&fe_msg);
+ fe_msg.message_case = MGMTD__FE_MESSAGE__MESSAGE_REGNOTIFY_REQ;
+ fe_msg.regnotify_req = &regntfy_req;
+
+ return mgmt_fe_client_send_msg(client_ctx, &fe_msg);
+}
+
+static int
+mgmt_fe_client_handle_msg(struct mgmt_fe_client_ctx *client_ctx,
+ Mgmtd__FeMessage *fe_msg)
+{
+ struct mgmt_fe_client_session *session = NULL;
+
+ /*
+ * 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)fe_msg->message_case) {
+ case MGMTD__FE_MESSAGE__MESSAGE_SESSION_REPLY:
+ if (fe_msg->session_reply->create
+ && fe_msg->session_reply->has_client_conn_id) {
+ MGMTD_FE_CLIENT_DBG(
+ "Got Session Create Reply Msg for client-id %llu with session-id: %llu.",
+ (unsigned long long)
+ fe_msg->session_reply->client_conn_id,
+ (unsigned long long)
+ fe_msg->session_reply->session_id);
+
+ session = mgmt_fe_find_session_by_client_id(
+ client_ctx,
+ fe_msg->session_reply->client_conn_id);
+
+ if (session && fe_msg->session_reply->success) {
+ MGMTD_FE_CLIENT_DBG(
+ "Session Create for client-id %llu successful.",
+ (unsigned long long)fe_msg
+ ->session_reply->client_conn_id);
+ session->session_id =
+ fe_msg->session_reply->session_id;
+ } else {
+ MGMTD_FE_CLIENT_ERR(
+ "Session Create for client-id %llu failed.",
+ (unsigned long long)fe_msg
+ ->session_reply->client_conn_id);
+ }
+ } else if (!fe_msg->session_reply->create) {
+ MGMTD_FE_CLIENT_DBG(
+ "Got Session Destroy Reply Msg for session-id %llu",
+ (unsigned long long)
+ fe_msg->session_reply->session_id);
+
+ session = mgmt_fe_find_session_by_session_id(
+ client_ctx, fe_msg->session_req->session_id);
+ }
+
+ if (session && session->client_ctx
+ && session->client_ctx->client_params
+ .client_session_notify)
+ (*session->client_ctx->client_params
+ .client_session_notify)(
+ (uintptr_t)client_ctx,
+ client_ctx->client_params.user_data,
+ session->client_id,
+ fe_msg->session_reply->create,
+ fe_msg->session_reply->success,
+ (uintptr_t)session, session->user_ctx);
+ break;
+ case MGMTD__FE_MESSAGE__MESSAGE_LOCKDS_REPLY:
+ MGMTD_FE_CLIENT_DBG(
+ "Got LockDs Reply Msg for session-id %llu",
+ (unsigned long long)
+ fe_msg->lockds_reply->session_id);
+ session = mgmt_fe_find_session_by_session_id(
+ client_ctx, fe_msg->lockds_reply->session_id);
+
+ if (session && session->client_ctx
+ && session->client_ctx->client_params
+ .lock_ds_notify)
+ (*session->client_ctx->client_params
+ .lock_ds_notify)(
+ (uintptr_t)client_ctx,
+ client_ctx->client_params.user_data,
+ session->client_id, (uintptr_t)session,
+ session->user_ctx,
+ fe_msg->lockds_reply->req_id,
+ fe_msg->lockds_reply->lock,
+ fe_msg->lockds_reply->success,
+ fe_msg->lockds_reply->ds_id,
+ fe_msg->lockds_reply->error_if_any);
+ break;
+ case MGMTD__FE_MESSAGE__MESSAGE_SETCFG_REPLY:
+ MGMTD_FE_CLIENT_DBG(
+ "Got Set Config Reply Msg for session-id %llu",
+ (unsigned long long)
+ fe_msg->setcfg_reply->session_id);
+
+ session = mgmt_fe_find_session_by_session_id(
+ client_ctx, fe_msg->setcfg_reply->session_id);
+
+ if (session && session->client_ctx
+ && session->client_ctx->client_params
+ .set_config_notify)
+ (*session->client_ctx->client_params
+ .set_config_notify)(
+ (uintptr_t)client_ctx,
+ client_ctx->client_params.user_data,
+ session->client_id, (uintptr_t)session,
+ session->user_ctx,
+ fe_msg->setcfg_reply->req_id,
+ fe_msg->setcfg_reply->success,
+ fe_msg->setcfg_reply->ds_id,
+ fe_msg->setcfg_reply->error_if_any);
+ break;
+ case MGMTD__FE_MESSAGE__MESSAGE_COMMCFG_REPLY:
+ MGMTD_FE_CLIENT_DBG(
+ "Got Commit Config Reply Msg for session-id %llu",
+ (unsigned long long)
+ fe_msg->commcfg_reply->session_id);
+
+ session = mgmt_fe_find_session_by_session_id(
+ client_ctx, fe_msg->commcfg_reply->session_id);
+
+ if (session && session->client_ctx
+ && session->client_ctx->client_params
+ .commit_config_notify)
+ (*session->client_ctx->client_params
+ .commit_config_notify)(
+ (uintptr_t)client_ctx,
+ client_ctx->client_params.user_data,
+ session->client_id, (uintptr_t)session,
+ session->user_ctx,
+ fe_msg->commcfg_reply->req_id,
+ fe_msg->commcfg_reply->success,
+ fe_msg->commcfg_reply->src_ds_id,
+ fe_msg->commcfg_reply->dst_ds_id,
+ fe_msg->commcfg_reply->validate_only,
+ fe_msg->commcfg_reply->error_if_any);
+ break;
+ case MGMTD__FE_MESSAGE__MESSAGE_GETCFG_REPLY:
+ MGMTD_FE_CLIENT_DBG(
+ "Got Get Config Reply Msg for session-id %llu",
+ (unsigned long long)
+ fe_msg->getcfg_reply->session_id);
+
+ session = mgmt_fe_find_session_by_session_id(
+ client_ctx, fe_msg->getcfg_reply->session_id);
+
+ if (session && session->client_ctx
+ && session->client_ctx->client_params
+ .get_data_notify)
+ (*session->client_ctx->client_params
+ .get_data_notify)(
+ (uintptr_t)client_ctx,
+ client_ctx->client_params.user_data,
+ session->client_id, (uintptr_t)session,
+ session->user_ctx,
+ fe_msg->getcfg_reply->req_id,
+ fe_msg->getcfg_reply->success,
+ fe_msg->getcfg_reply->ds_id,
+ fe_msg->getcfg_reply->data
+ ? fe_msg->getcfg_reply->data->data
+ : NULL,
+ fe_msg->getcfg_reply->data
+ ? fe_msg->getcfg_reply->data->n_data
+ : 0,
+ fe_msg->getcfg_reply->data
+ ? fe_msg->getcfg_reply->data
+ ->next_indx
+ : 0,
+ fe_msg->getcfg_reply->error_if_any);
+ break;
+ case MGMTD__FE_MESSAGE__MESSAGE_GETDATA_REPLY:
+ MGMTD_FE_CLIENT_DBG(
+ "Got Get Data Reply Msg for session-id %llu",
+ (unsigned long long)
+ fe_msg->getdata_reply->session_id);
+
+ session = mgmt_fe_find_session_by_session_id(
+ client_ctx, fe_msg->getdata_reply->session_id);
+
+ if (session && session->client_ctx
+ && session->client_ctx->client_params
+ .get_data_notify)
+ (*session->client_ctx->client_params
+ .get_data_notify)(
+ (uintptr_t)client_ctx,
+ client_ctx->client_params.user_data,
+ session->client_id, (uintptr_t)session,
+ session->user_ctx,
+ fe_msg->getdata_reply->req_id,
+ fe_msg->getdata_reply->success,
+ fe_msg->getdata_reply->ds_id,
+ fe_msg->getdata_reply->data
+ ? fe_msg->getdata_reply->data->data
+ : NULL,
+ fe_msg->getdata_reply->data
+ ? fe_msg->getdata_reply->data
+ ->n_data
+ : 0,
+ fe_msg->getdata_reply->data
+ ? fe_msg->getdata_reply->data
+ ->next_indx
+ : 0,
+ fe_msg->getdata_reply->error_if_any);
+ break;
+ case MGMTD__FE_MESSAGE__MESSAGE_NOTIFY_DATA_REQ:
+ case MGMTD__FE_MESSAGE__MESSAGE_REGNOTIFY_REQ:
+ /*
+ * TODO: Add handling code in future.
+ */
+ break;
+ /*
+ * NOTE: The following messages are always sent from Frontend
+ * clients to MGMTd only and/or need not be handled here.
+ */
+ case MGMTD__FE_MESSAGE__MESSAGE_REGISTER_REQ:
+ case MGMTD__FE_MESSAGE__MESSAGE_SESSION_REQ:
+ case MGMTD__FE_MESSAGE__MESSAGE_LOCKDS_REQ:
+ case MGMTD__FE_MESSAGE__MESSAGE_SETCFG_REQ:
+ case MGMTD__FE_MESSAGE__MESSAGE_COMMCFG_REQ:
+ case MGMTD__FE_MESSAGE__MESSAGE_GETCFG_REQ:
+ case MGMTD__FE_MESSAGE__MESSAGE_GETDATA_REQ:
+ case MGMTD__FE_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;
+}
+
+static void mgmt_fe_client_process_msg(void *user_ctx, uint8_t *data,
+ size_t len)
+{
+ struct mgmt_fe_client_ctx *client_ctx = user_ctx;
+ Mgmtd__FeMessage *fe_msg;
+
+ fe_msg = mgmtd__fe_message__unpack(NULL, len, data);
+ if (!fe_msg) {
+ MGMTD_FE_CLIENT_DBG("Failed to decode %zu bytes from server.",
+ len);
+ return;
+ }
+ MGMTD_FE_CLIENT_DBG(
+ "Decoded %zu bytes of message(msg: %u/%u) from server", len,
+ fe_msg->message_case, fe_msg->message_case);
+ (void)mgmt_fe_client_handle_msg(client_ctx, fe_msg);
+ mgmtd__fe_message__free_unpacked(fe_msg, NULL);
+}
+
+static void mgmt_fe_client_proc_msgbufs(struct event *thread)
+{
+ struct mgmt_fe_client_ctx *client_ctx;
+
+ client_ctx = (struct mgmt_fe_client_ctx *)EVENT_ARG(thread);
+ if (mgmt_msg_procbufs(&client_ctx->mstate, mgmt_fe_client_process_msg,
+ client_ctx, mgmt_debug_fe_client))
+ mgmt_fe_client_register_event(client_ctx, MGMTD_FE_PROC_MSG);
+}
+
+static void mgmt_fe_client_read(struct event *thread)
+{
+ struct mgmt_fe_client_ctx *client_ctx;
+ enum mgmt_msg_rsched rv;
+
+ client_ctx = (struct mgmt_fe_client_ctx *)EVENT_ARG(thread);
+
+ rv = mgmt_msg_read(&client_ctx->mstate, client_ctx->conn_fd,
+ mgmt_debug_fe_client);
+ if (rv == MSR_DISCONNECT) {
+ mgmt_fe_server_disconnect(client_ctx, true);
+ return;
+ }
+ if (rv == MSR_SCHED_BOTH)
+ mgmt_fe_client_register_event(client_ctx, MGMTD_FE_PROC_MSG);
+ mgmt_fe_client_register_event(client_ctx, MGMTD_FE_CONN_READ);
+}
+
+static void mgmt_fe_server_connect(struct mgmt_fe_client_ctx *client_ctx)
+{
+ const char *dbgtag = mgmt_debug_fe_client ? "FE-client" : NULL;
+
+ assert(client_ctx->conn_fd == -1);
+ client_ctx->conn_fd = mgmt_msg_connect(
+ MGMTD_FE_SERVER_PATH, MGMTD_SOCKET_FE_SEND_BUF_SIZE,
+ MGMTD_SOCKET_FE_RECV_BUF_SIZE, dbgtag);
+
+ /* Send REGISTER_REQ message */
+ if (client_ctx->conn_fd == -1 ||
+ mgmt_fe_send_register_req(client_ctx) != 0) {
+ mgmt_fe_server_disconnect(client_ctx, true);
+ return;
+ }
+
+ /* Start reading from the socket */
+ mgmt_fe_client_register_event(client_ctx, MGMTD_FE_CONN_READ);
+
+ /* Notify client through registered callback (if any) */
+ if (client_ctx->client_params.client_connect_notify)
+ (void)(*client_ctx->client_params.client_connect_notify)(
+ (uintptr_t)client_ctx,
+ client_ctx->client_params.user_data, true);
+}
+
+
+static void mgmt_fe_client_conn_timeout(struct event *thread)
+{
+ mgmt_fe_server_connect(EVENT_ARG(thread));
+}
+
+static void
+mgmt_fe_client_register_event(struct mgmt_fe_client_ctx *client_ctx,
+ enum mgmt_fe_event event)
+{
+ struct timeval tv = {0};
+
+ switch (event) {
+ case MGMTD_FE_CONN_READ:
+ event_add_read(client_ctx->tm, mgmt_fe_client_read,
+ client_ctx, client_ctx->conn_fd,
+ &client_ctx->conn_read_ev);
+ break;
+ case MGMTD_FE_CONN_WRITE:
+ event_add_write(client_ctx->tm, mgmt_fe_client_write,
+ client_ctx, client_ctx->conn_fd,
+ &client_ctx->conn_write_ev);
+ break;
+ case MGMTD_FE_PROC_MSG:
+ tv.tv_usec = MGMTD_FE_MSG_PROC_DELAY_USEC;
+ event_add_timer_tv(client_ctx->tm,
+ mgmt_fe_client_proc_msgbufs, client_ctx,
+ &tv, &client_ctx->msg_proc_ev);
+ break;
+ case MGMTD_FE_CONN_WRITES_ON:
+ event_add_timer_msec(
+ client_ctx->tm, mgmt_fe_client_resume_writes,
+ client_ctx, MGMTD_FE_MSG_WRITE_DELAY_MSEC,
+ &client_ctx->conn_writes_on);
+ break;
+ case MGMTD_FE_SERVER:
+ assert(!"mgmt_fe_client_ctx_post_event called incorrectly");
+ break;
+ }
+}
+
+static void mgmt_fe_client_schedule_conn_retry(
+ struct mgmt_fe_client_ctx *client_ctx, unsigned long intvl_secs)
+{
+ MGMTD_FE_CLIENT_DBG(
+ "Scheduling MGMTD Frontend server connection retry after %lu seconds",
+ intvl_secs);
+ event_add_timer(client_ctx->tm, mgmt_fe_client_conn_timeout,
+ (void *)client_ctx, intvl_secs,
+ &client_ctx->conn_retry_tmr);
+}
+
+/*
+ * Initialize library and try connecting with MGMTD.
+ */
+uintptr_t mgmt_fe_client_lib_init(struct mgmt_fe_client_params *params,
+ struct event_loop *master_thread)
+{
+ assert(master_thread && params && strlen(params->name)
+ && !mgmt_fe_client_ctx.tm);
+
+ mgmt_fe_client_ctx.tm = master_thread;
+ memcpy(&mgmt_fe_client_ctx.client_params, params,
+ sizeof(mgmt_fe_client_ctx.client_params));
+ if (!mgmt_fe_client_ctx.client_params.conn_retry_intvl_sec)
+ mgmt_fe_client_ctx.client_params.conn_retry_intvl_sec =
+ MGMTD_FE_DEFAULT_CONN_RETRY_INTVL_SEC;
+
+ mgmt_msg_init(&mgmt_fe_client_ctx.mstate, MGMTD_FE_MAX_NUM_MSG_PROC,
+ MGMTD_FE_MAX_NUM_MSG_WRITE, MGMTD_FE_MSG_MAX_LEN,
+ "FE-client");
+
+ mgmt_sessions_init(&mgmt_fe_client_ctx.client_sessions);
+
+ /* Start trying to connect to MGMTD frontend server immediately */
+ mgmt_fe_client_schedule_conn_retry(&mgmt_fe_client_ctx, 1);
+
+ MGMTD_FE_CLIENT_DBG("Initialized client '%s'", params->name);
+
+ return (uintptr_t)&mgmt_fe_client_ctx;
+}
+
+/*
+ * Create a new Session for a Frontend Client connection.
+ */
+enum mgmt_result mgmt_fe_create_client_session(uintptr_t lib_hndl,
+ uint64_t client_id,
+ uintptr_t user_ctx)
+{
+ struct mgmt_fe_client_ctx *client_ctx;
+ struct mgmt_fe_client_session *session;
+
+ client_ctx = (struct mgmt_fe_client_ctx *)lib_hndl;
+ if (!client_ctx)
+ return MGMTD_INVALID_PARAM;
+
+ session = XCALLOC(MTYPE_MGMTD_FE_SESSION,
+ sizeof(struct mgmt_fe_client_session));
+ assert(session);
+ session->user_ctx = user_ctx;
+ session->client_id = client_id;
+ session->client_ctx = client_ctx;
+ session->session_id = 0;
+
+ if (mgmt_fe_send_session_req(client_ctx, session, true) != 0) {
+ XFREE(MTYPE_MGMTD_FE_SESSION, session);
+ return MGMTD_INTERNAL_ERROR;
+ }
+ mgmt_sessions_add_tail(&client_ctx->client_sessions, session);
+
+ return MGMTD_SUCCESS;
+}
+
+/*
+ * Delete an existing Session for a Frontend Client connection.
+ */
+enum mgmt_result mgmt_fe_destroy_client_session(uintptr_t lib_hndl,
+ uint64_t client_id)
+{
+ struct mgmt_fe_client_ctx *client_ctx;
+ struct mgmt_fe_client_session *session;
+
+ client_ctx = (struct mgmt_fe_client_ctx *)lib_hndl;
+ if (!client_ctx)
+ return MGMTD_INVALID_PARAM;
+
+ session = mgmt_fe_find_session_by_client_id(client_ctx, client_id);
+ if (!session || session->client_ctx != client_ctx)
+ return MGMTD_INVALID_PARAM;
+
+ if (session->session_id &&
+ mgmt_fe_send_session_req(client_ctx, session, false) != 0)
+ MGMTD_FE_CLIENT_ERR(
+ "Failed to send session destroy request for the session-id %lu",
+ (unsigned long)session->session_id);
+
+ mgmt_sessions_del(&client_ctx->client_sessions, session);
+ XFREE(MTYPE_MGMTD_FE_SESSION, session);
+
+ return MGMTD_SUCCESS;
+}
+
+static void mgmt_fe_destroy_client_sessions(uintptr_t lib_hndl)
+{
+ struct mgmt_fe_client_ctx *client_ctx;
+ struct mgmt_fe_client_session *session;
+
+ client_ctx = (struct mgmt_fe_client_ctx *)lib_hndl;
+ if (!client_ctx)
+ return;
+
+ FOREACH_SESSION_IN_LIST (client_ctx, session)
+ mgmt_fe_destroy_client_session(lib_hndl, session->client_id);
+}
+
+/*
+ * Send UN/LOCK_DS_REQ to MGMTD for a specific Datastore DS.
+ */
+enum mgmt_result mgmt_fe_lock_ds(uintptr_t lib_hndl, uintptr_t session_id,
+ uint64_t req_id, Mgmtd__DatastoreId ds_id,
+ bool lock_ds)
+{
+ struct mgmt_fe_client_ctx *client_ctx;
+ struct mgmt_fe_client_session *session;
+
+ client_ctx = (struct mgmt_fe_client_ctx *)lib_hndl;
+ if (!client_ctx)
+ return MGMTD_INVALID_PARAM;
+
+ session = (struct mgmt_fe_client_session *)session_id;
+ if (!session || session->client_ctx != client_ctx)
+ return MGMTD_INVALID_PARAM;
+
+ if (mgmt_fe_send_lockds_req(client_ctx, session, lock_ds, req_id,
+ ds_id)
+ != 0)
+ return MGMTD_INTERNAL_ERROR;
+
+ return MGMTD_SUCCESS;
+}
+
+/*
+ * Send SET_CONFIG_REQ to MGMTD for one or more config data(s).
+ */
+enum mgmt_result
+mgmt_fe_set_config_data(uintptr_t lib_hndl, uintptr_t session_id,
+ uint64_t req_id, Mgmtd__DatastoreId ds_id,
+ Mgmtd__YangCfgDataReq **config_req, int num_reqs,
+ bool implicit_commit, Mgmtd__DatastoreId dst_ds_id)
+{
+ struct mgmt_fe_client_ctx *client_ctx;
+ struct mgmt_fe_client_session *session;
+
+ client_ctx = (struct mgmt_fe_client_ctx *)lib_hndl;
+ if (!client_ctx)
+ return MGMTD_INVALID_PARAM;
+
+ session = (struct mgmt_fe_client_session *)session_id;
+ if (!session || session->client_ctx != client_ctx)
+ return MGMTD_INVALID_PARAM;
+
+ if (mgmt_fe_send_setcfg_req(client_ctx, session, req_id, ds_id,
+ config_req, num_reqs, implicit_commit,
+ dst_ds_id)
+ != 0)
+ return MGMTD_INTERNAL_ERROR;
+
+ return MGMTD_SUCCESS;
+}
+
+/*
+ * Send SET_CONFIG_REQ to MGMTD for one or more config data(s).
+ */
+enum mgmt_result mgmt_fe_commit_config_data(uintptr_t lib_hndl,
+ uintptr_t session_id,
+ uint64_t req_id,
+ Mgmtd__DatastoreId src_ds_id,
+ Mgmtd__DatastoreId dst_ds_id,
+ bool validate_only, bool abort)
+{
+ struct mgmt_fe_client_ctx *client_ctx;
+ struct mgmt_fe_client_session *session;
+
+ client_ctx = (struct mgmt_fe_client_ctx *)lib_hndl;
+ if (!client_ctx)
+ return MGMTD_INVALID_PARAM;
+
+ session = (struct mgmt_fe_client_session *)session_id;
+ if (!session || session->client_ctx != client_ctx)
+ return MGMTD_INVALID_PARAM;
+
+ if (mgmt_fe_send_commitcfg_req(client_ctx, session, req_id, src_ds_id,
+ dst_ds_id, validate_only, abort)
+ != 0)
+ return MGMTD_INTERNAL_ERROR;
+
+ return MGMTD_SUCCESS;
+}
+
+/*
+ * Send GET_CONFIG_REQ to MGMTD for one or more config data item(s).
+ */
+enum mgmt_result
+mgmt_fe_get_config_data(uintptr_t lib_hndl, uintptr_t session_id,
+ uint64_t req_id, Mgmtd__DatastoreId ds_id,
+ Mgmtd__YangGetDataReq * data_req[], int num_reqs)
+{
+ struct mgmt_fe_client_ctx *client_ctx;
+ struct mgmt_fe_client_session *session;
+
+ client_ctx = (struct mgmt_fe_client_ctx *)lib_hndl;
+ if (!client_ctx)
+ return MGMTD_INVALID_PARAM;
+
+ session = (struct mgmt_fe_client_session *)session_id;
+ if (!session || session->client_ctx != client_ctx)
+ return MGMTD_INVALID_PARAM;
+
+ if (mgmt_fe_send_getcfg_req(client_ctx, session, req_id, ds_id,
+ data_req, num_reqs)
+ != 0)
+ return MGMTD_INTERNAL_ERROR;
+
+ return MGMTD_SUCCESS;
+}
+
+/*
+ * Send GET_DATA_REQ to MGMTD for one or more config data item(s).
+ */
+enum mgmt_result mgmt_fe_get_data(uintptr_t lib_hndl, uintptr_t session_id,
+ uint64_t req_id, Mgmtd__DatastoreId ds_id,
+ Mgmtd__YangGetDataReq * data_req[],
+ int num_reqs)
+{
+ struct mgmt_fe_client_ctx *client_ctx;
+ struct mgmt_fe_client_session *session;
+
+ client_ctx = (struct mgmt_fe_client_ctx *)lib_hndl;
+ if (!client_ctx)
+ return MGMTD_INVALID_PARAM;
+
+ session = (struct mgmt_fe_client_session *)session_id;
+ if (!session || session->client_ctx != client_ctx)
+ return MGMTD_INVALID_PARAM;
+
+ if (mgmt_fe_send_getdata_req(client_ctx, session, req_id, ds_id,
+ data_req, num_reqs)
+ != 0)
+ return MGMTD_INTERNAL_ERROR;
+
+ return MGMTD_SUCCESS;
+}
+
+/*
+ * Send NOTIFY_REGISTER_REQ to MGMTD daemon.
+ */
+enum mgmt_result
+mgmt_fe_register_yang_notify(uintptr_t lib_hndl, uintptr_t session_id,
+ uint64_t req_id, Mgmtd__DatastoreId ds_id,
+ bool register_req,
+ Mgmtd__YangDataXPath * data_req[],
+ int num_reqs)
+{
+ struct mgmt_fe_client_ctx *client_ctx;
+ struct mgmt_fe_client_session *session;
+
+ client_ctx = (struct mgmt_fe_client_ctx *)lib_hndl;
+ if (!client_ctx)
+ return MGMTD_INVALID_PARAM;
+
+ session = (struct mgmt_fe_client_session *)session_id;
+ if (!session || session->client_ctx != client_ctx)
+ return MGMTD_INVALID_PARAM;
+
+ if (mgmt_fe_send_regnotify_req(client_ctx, session, req_id, ds_id,
+ register_req, data_req, num_reqs)
+ != 0)
+ return MGMTD_INTERNAL_ERROR;
+
+ return MGMTD_SUCCESS;
+}
+
+/*
+ * Destroy library and cleanup everything.
+ */
+void mgmt_fe_client_lib_destroy(uintptr_t lib_hndl)
+{
+ struct mgmt_fe_client_ctx *client_ctx;
+
+ client_ctx = (struct mgmt_fe_client_ctx *)lib_hndl;
+ assert(client_ctx);
+
+ MGMTD_FE_CLIENT_DBG("Destroying MGMTD Frontend Client '%s'",
+ client_ctx->client_params.name);
+
+ mgmt_fe_server_disconnect(client_ctx, false);
+
+ mgmt_fe_destroy_client_sessions(lib_hndl);
+
+ EVENT_OFF(client_ctx->conn_retry_tmr);
+ EVENT_OFF(client_ctx->conn_read_ev);
+ EVENT_OFF(client_ctx->conn_write_ev);
+ EVENT_OFF(client_ctx->conn_writes_on);
+ EVENT_OFF(client_ctx->msg_proc_ev);
+ mgmt_msg_destroy(&client_ctx->mstate);
+}
diff --git a/lib/mgmt_fe_client.h b/lib/mgmt_fe_client.h
new file mode 100644
index 0000000000..aa3371f03c
--- /dev/null
+++ b/lib/mgmt_fe_client.h
@@ -0,0 +1,346 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * MGMTD Frontend Client Library api interfaces
+ * Copyright (C) 2021 Vmware, Inc.
+ * Pushpasis Sarkar <spushpasis@vmware.com>
+ */
+
+#ifndef _FRR_MGMTD_FE_CLIENT_H_
+#define _FRR_MGMTD_FE_CLIENT_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "mgmt_pb.h"
+#include "frrevent.h"
+#include "mgmtd/mgmt_defines.h"
+
+/***************************************************************
+ * Macros
+ ***************************************************************/
+
+/*
+ * The server port MGMTD daemon is listening for Backend Client
+ * connections.
+ */
+
+#define MGMTD_FE_CLIENT_ERROR_STRING_MAX_LEN 32
+
+#define MGMTD_FE_DEFAULT_CONN_RETRY_INTVL_SEC 5
+
+#define MGMTD_FE_MSG_PROC_DELAY_USEC 10
+#define MGMTD_FE_MAX_NUM_MSG_PROC 500
+
+#define MGMTD_FE_MSG_WRITE_DELAY_MSEC 1
+#define MGMTD_FE_MAX_NUM_MSG_WRITE 100
+
+#define GMGD_FE_MAX_NUM_REQ_ITEMS 64
+
+#define MGMTD_FE_MSG_MAX_LEN 9000
+
+#define MGMTD_SOCKET_FE_SEND_BUF_SIZE 65535
+#define MGMTD_SOCKET_FE_RECV_BUF_SIZE MGMTD_SOCKET_FE_SEND_BUF_SIZE
+
+/***************************************************************
+ * Data-structures
+ ***************************************************************/
+
+#define MGMTD_SESSION_ID_NONE 0
+
+#define MGMTD_CLIENT_ID_NONE 0
+
+#define MGMTD_DS_NONE MGMTD__DATASTORE_ID__DS_NONE
+#define MGMTD_DS_RUNNING MGMTD__DATASTORE_ID__RUNNING_DS
+#define MGMTD_DS_CANDIDATE MGMTD__DATASTORE_ID__CANDIDATE_DS
+#define MGMTD_DS_OPERATIONAL MGMTD__DATASTORE_ID__OPERATIONAL_DS
+#define MGMTD_DS_MAX_ID MGMTD_DS_OPERATIONAL + 1
+
+/*
+ * All the client specific information this library needs to
+ * initialize itself, setup connection with MGMTD FrontEnd interface
+ * and carry on all required procedures appropriately.
+ *
+ * FrontEnd clients need to initialise a instance of this structure
+ * with appropriate data and pass it while calling the API
+ * to initialize the library (See mgmt_fe_client_lib_init for
+ * more details).
+ */
+struct mgmt_fe_client_params {
+ char name[MGMTD_CLIENT_NAME_MAX_LEN];
+ uintptr_t user_data;
+ unsigned long conn_retry_intvl_sec;
+
+ void (*client_connect_notify)(uintptr_t lib_hndl,
+ uintptr_t user_data,
+ bool connected);
+
+ void (*client_session_notify)(uintptr_t lib_hndl,
+ uintptr_t user_data,
+ uint64_t client_id,
+ bool create, bool success,
+ uintptr_t session_id,
+ uintptr_t user_session_ctx);
+
+ void (*lock_ds_notify)(uintptr_t lib_hndl, uintptr_t user_data,
+ uint64_t client_id, uintptr_t session_id,
+ uintptr_t user_session_ctx, uint64_t req_id,
+ bool lock_ds, bool success,
+ Mgmtd__DatastoreId ds_id, char *errmsg_if_any);
+
+ void (*set_config_notify)(uintptr_t lib_hndl, uintptr_t user_data,
+ uint64_t client_id, uintptr_t session_id,
+ uintptr_t user_session_ctx, uint64_t req_id,
+ bool success, Mgmtd__DatastoreId ds_id,
+ char *errmsg_if_any);
+
+ void (*commit_config_notify)(
+ uintptr_t lib_hndl, uintptr_t user_data, uint64_t client_id,
+ uintptr_t session_id, uintptr_t user_session_ctx,
+ uint64_t req_id, bool success, Mgmtd__DatastoreId src_ds_id,
+ Mgmtd__DatastoreId dst_ds_id, bool validate_only,
+ char *errmsg_if_any);
+
+ enum mgmt_result (*get_data_notify)(
+ uintptr_t lib_hndl, uintptr_t user_data, uint64_t client_id,
+ uintptr_t session_id, uintptr_t user_session_ctx,
+ uint64_t req_id, bool success, Mgmtd__DatastoreId ds_id,
+ Mgmtd__YangData **yang_data, size_t num_data, int next_key,
+ char *errmsg_if_any);
+
+ enum mgmt_result (*data_notify)(
+ uint64_t client_id, uint64_t session_id, uintptr_t user_data,
+ uint64_t req_id, Mgmtd__DatastoreId ds_id,
+ Mgmtd__YangData **yang_data, size_t num_data);
+};
+
+/***************************************************************
+ * API prototypes
+ ***************************************************************/
+
+/*
+ * Initialize library and try connecting with MGMTD FrontEnd interface.
+ *
+ * params
+ * Frontend client parameters.
+ *
+ * master_thread
+ * Thread master.
+ *
+ * Returns:
+ * Frontend client lib handler (nothing but address of mgmt_fe_client_ctx)
+ */
+extern uintptr_t mgmt_fe_client_lib_init(struct mgmt_fe_client_params *params,
+ struct event_loop *master_thread);
+
+/*
+ * Create a new Session for a Frontend Client connection.
+ *
+ * lib_hndl
+ * Client library handler.
+ *
+ * client_id
+ * Unique identifier of client.
+ *
+ * user_ctx
+ * Client context.
+ *
+ * Returns:
+ * MGMTD_SUCCESS on success, MGMTD_* otherwise.
+ */
+extern enum mgmt_result mgmt_fe_create_client_session(uintptr_t lib_hndl,
+ uint64_t client_id,
+ uintptr_t user_ctx);
+
+/*
+ * Delete an existing Session for a Frontend Client connection.
+ *
+ * lib_hndl
+ * Client library handler.
+ *
+ * client_id
+ * Unique identifier of client.
+ *
+ * Returns:
+ * MGMTD_SUCCESS on success, MGMTD_* otherwise.
+ */
+extern enum mgmt_result mgmt_fe_destroy_client_session(uintptr_t lib_hndl,
+ uint64_t client_id);
+
+/*
+ * Send UN/LOCK_DS_REQ to MGMTD for a specific Datastore DS.
+ *
+ * lib_hndl
+ * Client library handler.
+ *
+ * session_id
+ * Client session ID.
+ *
+ * req_id
+ * Client request ID.
+ *
+ * ds_id
+ * Datastore ID (Running/Candidate/Oper/Startup)
+ *
+ * lock_ds
+ * TRUE for lock request, FALSE for unlock request.
+ *
+ * Returns:
+ * MGMTD_SUCCESS on success, MGMTD_* otherwise.
+ */
+extern enum mgmt_result
+mgmt_fe_lock_ds(uintptr_t lib_hndl, uintptr_t session_id, uint64_t req_id,
+ Mgmtd__DatastoreId ds_id, bool lock_ds);
+
+/*
+ * Send SET_CONFIG_REQ to MGMTD for one or more config data(s).
+ *
+ * lib_hndl
+ * Client library handler.
+ *
+ * session_id
+ * Client session ID.
+ *
+ * req_id
+ * Client request ID.
+ *
+ * ds_id
+ * Datastore ID (Running/Candidate/Oper/Startup)
+ *
+ * conf_req
+ * Details regarding the SET_CONFIG_REQ.
+ *
+ * num_req
+ * Number of config requests.
+ *
+ * implcit commit
+ * TRUE for implicit commit, FALSE otherwise.
+ *
+ * dst_ds_id
+ * Destination Datastore ID where data needs to be set.
+ *
+ * Returns:
+ * MGMTD_SUCCESS on success, MGMTD_* otherwise.
+ */
+extern enum mgmt_result
+mgmt_fe_set_config_data(uintptr_t lib_hndl, uintptr_t session_id,
+ uint64_t req_id, Mgmtd__DatastoreId ds_id,
+ Mgmtd__YangCfgDataReq **config_req, int num_req,
+ bool implicit_commit, Mgmtd__DatastoreId dst_ds_id);
+
+/*
+ * Send SET_COMMMIT_REQ to MGMTD for one or more config data(s).
+ *
+ * lib_hndl
+ * Client library handler.
+ *
+ * session_id
+ * Client session ID.
+ *
+ * req_id
+ * Client request ID.
+ *
+ * src_ds_id
+ * Source datastore ID from where data needs to be committed from.
+ *
+ * dst_ds_id
+ * Destination datastore ID where data needs to be committed to.
+ *
+ * validate_only
+ * TRUE if data needs to be validated only, FALSE otherwise.
+ *
+ * abort
+ * TRUE if need to restore Src DS back to Dest DS, FALSE otherwise.
+ *
+ * Returns:
+ * MGMTD_SUCCESS on success, MGMTD_* otherwise.
+ */
+extern enum mgmt_result
+mgmt_fe_commit_config_data(uintptr_t lib_hndl, uintptr_t session_id,
+ uint64_t req_id, Mgmtd__DatastoreId src_ds_id,
+ Mgmtd__DatastoreId dst_ds_id, bool validate_only,
+ bool abort);
+
+/*
+ * Send GET_CONFIG_REQ to MGMTD for one or more config data item(s).
+ *
+ * lib_hndl
+ * Client library handler.
+ *
+ * session_id
+ * Client session ID.
+ *
+ * req_id
+ * Client request ID.
+ *
+ * ds_id
+ * Datastore ID (Running/Candidate)
+ *
+ * data_req
+ * Get config requested.
+ *
+ * num_req
+ * Number of get config requests.
+ *
+ * Returns:
+ * MGMTD_SUCCESS on success, MGMTD_* otherwise.
+ */
+extern enum mgmt_result
+mgmt_fe_get_config_data(uintptr_t lib_hndl, uintptr_t session_id,
+ uint64_t req_id, Mgmtd__DatastoreId ds_id,
+ Mgmtd__YangGetDataReq **data_req, int num_reqs);
+
+/*
+ * Send GET_DATA_REQ to MGMTD for one or more data item(s).
+ *
+ * Similar to get config request but supports getting data
+ * from operational ds aka backend clients directly.
+ */
+extern enum mgmt_result
+mgmt_fe_get_data(uintptr_t lib_hndl, uintptr_t session_id, uint64_t req_id,
+ Mgmtd__DatastoreId ds_id, Mgmtd__YangGetDataReq **data_req,
+ int num_reqs);
+
+/*
+ * Send NOTIFY_REGISTER_REQ to MGMTD daemon.
+ *
+ * lib_hndl
+ * Client library handler.
+ *
+ * session_id
+ * Client session ID.
+ *
+ * req_id
+ * Client request ID.
+ *
+ * ds_id
+ * Datastore ID.
+ *
+ * register_req
+ * TRUE if registering, FALSE otherwise.
+ *
+ * data_req
+ * Details of the YANG notification data.
+ *
+ * num_reqs
+ * Number of data requests.
+ *
+ * Returns:
+ * MGMTD_SUCCESS on success, MGMTD_* otherwise.
+ */
+extern enum mgmt_result
+mgmt_fe_register_yang_notify(uintptr_t lib_hndl, uintptr_t session_id,
+ uint64_t req_id, Mgmtd__DatastoreId ds_id,
+ bool register_req,
+ Mgmtd__YangDataXPath **data_req, int num_reqs);
+
+/*
+ * Destroy library and cleanup everything.
+ */
+extern void mgmt_fe_client_lib_destroy(uintptr_t lib_hndl);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _FRR_MGMTD_FE_CLIENT_H_ */
diff --git a/lib/mgmt_msg.c b/lib/mgmt_msg.c
new file mode 100644
index 0000000000..3f55f82024
--- /dev/null
+++ b/lib/mgmt_msg.c
@@ -0,0 +1,414 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * March 6 2023, Christian Hopps <chopps@labn.net>
+ *
+ * Copyright (C) 2021 Vmware, Inc.
+ * Pushpasis Sarkar <spushpasis@vmware.com>
+ * Copyright (c) 2023, LabN Consulting, L.L.C.
+ */
+#include <zebra.h>
+#include "network.h"
+#include "sockopt.h"
+#include "stream.h"
+#include "frrevent.h"
+#include "mgmt_msg.h"
+
+
+#define MGMT_MSG_DBG(dbgtag, fmt, ...) \
+ do { \
+ if (dbgtag) \
+ zlog_debug("%s: %s: " fmt, dbgtag, __func__, \
+ ##__VA_ARGS__); \
+ } while (0)
+
+#define MGMT_MSG_ERR(ms, fmt, ...) \
+ zlog_err("%s: %s: " fmt, ms->idtag, __func__, ##__VA_ARGS__)
+
+/**
+ * Read data from a socket into streams containing 1 or more full msgs headed by
+ * mgmt_msg_hdr which contain API messages (currently protobuf).
+ *
+ * Args:
+ * ms: mgmt_msg_state for this process.
+ * fd: socket/file to read data from.
+ * debug: true to enable debug logging.
+ *
+ * Returns:
+ * MPP_DISCONNECT - socket should be closed and connect retried.
+ * MSV_SCHED_STREAM - this call should be rescheduled to run.
+ * MPP_SCHED_BOTH - this call and the procmsg buf should be scheduled to
+ *run.
+ */
+enum mgmt_msg_rsched mgmt_msg_read(struct mgmt_msg_state *ms, int fd,
+ bool debug)
+{
+ const char *dbgtag = debug ? ms->idtag : NULL;
+ size_t avail = STREAM_WRITEABLE(ms->ins);
+ struct mgmt_msg_hdr *mhdr = NULL;
+ size_t total = 0;
+ size_t mcount = 0;
+ ssize_t n, left;
+
+ assert(ms && fd != -1);
+
+ /*
+ * Read as much as we can into the stream.
+ */
+ while (avail > sizeof(struct mgmt_msg_hdr)) {
+ n = stream_read_try(ms->ins, fd, avail);
+ MGMT_MSG_DBG(dbgtag, "got %zd bytes", n);
+
+ /* -2 is normal nothing read, and to retry */
+ if (n == -2)
+ break;
+ if (n <= 0) {
+ if (n == 0)
+ MGMT_MSG_ERR(ms, "got EOF/disconnect");
+ else
+ MGMT_MSG_ERR(ms,
+ "got error while reading: '%s'",
+ safe_strerror(errno));
+ return MSR_DISCONNECT;
+ }
+ ms->nrxb += n;
+ avail -= n;
+ }
+
+ /*
+ * Check if we have read a complete messages or not.
+ */
+ assert(stream_get_getp(ms->ins) == 0);
+ left = stream_get_endp(ms->ins);
+ while (left > (long)sizeof(struct mgmt_msg_hdr)) {
+ mhdr = (struct mgmt_msg_hdr *)(STREAM_DATA(ms->ins) + total);
+ if (mhdr->marker != MGMT_MSG_MARKER) {
+ MGMT_MSG_DBG(dbgtag, "recv corrupt buffer, disconnect");
+ return MSR_DISCONNECT;
+ }
+ if ((ssize_t)mhdr->len > left)
+ break;
+
+ MGMT_MSG_DBG(dbgtag, "read full message len %u", mhdr->len);
+ total += mhdr->len;
+ left -= mhdr->len;
+ mcount++;
+ }
+
+ if (!mcount)
+ return MSR_SCHED_STREAM;
+
+ /*
+ * We have read at least one message into the stream, queue it up.
+ */
+ mhdr = (struct mgmt_msg_hdr *)(STREAM_DATA(ms->ins) + total);
+ stream_set_endp(ms->ins, total);
+ stream_fifo_push(&ms->inq, ms->ins);
+ ms->ins = stream_new(ms->max_msg_sz);
+ if (left) {
+ stream_put(ms->ins, mhdr, left);
+ stream_set_endp(ms->ins, left);
+ }
+
+ return MSR_SCHED_BOTH;
+}
+
+/**
+ * Process streams containing whole messages that have been pushed onto the
+ * FIFO. This should be called from an event/timer handler and should be
+ * reschedulable.
+ *
+ * Args:
+ * ms: mgmt_msg_state for this process.
+ * handle_mgs: function to call for each received message.
+ * user: opaque value passed through to handle_msg.
+ * debug: true to enable debug logging.
+ *
+ * Returns:
+ * true if more to process (so reschedule) else false
+ */
+bool mgmt_msg_procbufs(struct mgmt_msg_state *ms,
+ void (*handle_msg)(void *user, uint8_t *msg,
+ size_t msglen),
+ void *user, bool debug)
+{
+ const char *dbgtag = debug ? ms->idtag : NULL;
+ struct mgmt_msg_hdr *mhdr;
+ struct stream *work;
+ uint8_t *data;
+ size_t left, nproc;
+
+ MGMT_MSG_DBG(dbgtag, "Have %zu streams to process", ms->inq.count);
+
+ nproc = 0;
+ while (nproc < ms->max_read_buf) {
+ work = stream_fifo_pop(&ms->inq);
+ if (!work)
+ break;
+
+ data = STREAM_DATA(work);
+ left = stream_get_endp(work);
+ MGMT_MSG_DBG(dbgtag, "Processing stream of len %zu", left);
+
+ for (; left > sizeof(struct mgmt_msg_hdr);
+ left -= mhdr->len, data += mhdr->len) {
+ mhdr = (struct mgmt_msg_hdr *)data;
+
+ assert(mhdr->marker == MGMT_MSG_MARKER);
+ assert(left >= mhdr->len);
+
+ handle_msg(user, (uint8_t *)(mhdr + 1),
+ mhdr->len - sizeof(struct mgmt_msg_hdr));
+ ms->nrxm++;
+ nproc++;
+ }
+
+ if (work != ms->ins)
+ stream_free(work); /* Free it up */
+ else
+ stream_reset(work); /* Reset stream for next read */
+ }
+
+ /* return true if should reschedule b/c more to process. */
+ return stream_fifo_head(&ms->inq) != NULL;
+}
+
+/**
+ * Write data from a onto the socket, using streams that have been queued for
+ * sending by mgmt_msg_send_msg. This function should be reschedulable.
+ *
+ * Args:
+ * ms: mgmt_msg_state for this process.
+ * fd: socket/file to read data from.
+ * debug: true to enable debug logging.
+ *
+ * Returns:
+ * MSW_SCHED_NONE - do not reschedule anything.
+ * MSW_SCHED_STREAM - this call should be rescheduled to run again.
+ * MSW_SCHED_WRITES_OFF - writes should be disabled with a timer to
+ * re-enable them a short time later
+ * MSW_DISCONNECT - socket should be closed and reconnect retried.
+ *run.
+ */
+enum mgmt_msg_wsched mgmt_msg_write(struct mgmt_msg_state *ms, int fd,
+ bool debug)
+{
+ const char *dbgtag = debug ? ms->idtag : NULL;
+ struct stream *s;
+ size_t nproc = 0;
+ ssize_t left;
+ ssize_t n;
+
+ if (ms->outs) {
+ MGMT_MSG_DBG(dbgtag,
+ "found unqueued stream with %zu bytes, queueing",
+ stream_get_endp(ms->outs));
+ stream_fifo_push(&ms->outq, ms->outs);
+ ms->outs = NULL;
+ }
+
+ for (s = stream_fifo_head(&ms->outq); s && nproc < ms->max_write_buf;
+ s = stream_fifo_head(&ms->outq)) {
+ left = STREAM_READABLE(s);
+ assert(left);
+
+ n = stream_flush(s, fd);
+ if (n <= 0) {
+ if (n == 0)
+ MGMT_MSG_ERR(ms,
+ "connection closed while writing");
+ else if (ERRNO_IO_RETRY(errno)) {
+ MGMT_MSG_DBG(
+ dbgtag,
+ "retry error while writing %zd bytes: %s (%d)",
+ left, safe_strerror(errno), errno);
+ return MSW_SCHED_STREAM;
+ } else
+ MGMT_MSG_ERR(
+ ms,
+ "error while writing %zd bytes: %s (%d)",
+ left, safe_strerror(errno), errno);
+
+ n = mgmt_msg_reset_writes(ms);
+ MGMT_MSG_DBG(dbgtag, "drop and freed %zd streams", n);
+
+ return MSW_DISCONNECT;
+ }
+
+ ms->ntxb += n;
+ if (n != left) {
+ MGMT_MSG_DBG(dbgtag, "short stream write %zd of %zd", n,
+ left);
+ stream_forward_getp(s, n);
+ return MSW_SCHED_STREAM;
+ }
+
+ stream_free(stream_fifo_pop(&ms->outq));
+ MGMT_MSG_DBG(dbgtag, "wrote stream of %zd bytes", n);
+ nproc++;
+ }
+ if (s) {
+ MGMT_MSG_DBG(
+ dbgtag,
+ "reached %zu buffer writes, pausing with %zu streams left",
+ ms->max_write_buf, ms->outq.count);
+ return MSW_SCHED_WRITES_OFF;
+ }
+ MGMT_MSG_DBG(dbgtag, "flushed all streams from output q");
+ return MSW_SCHED_NONE;
+}
+
+
+/**
+ * Send a message by enqueueing it to be written over the socket by
+ * mgmt_msg_write.
+ *
+ * Args:
+ * ms: mgmt_msg_state for this process.
+ * fd: socket/file to read data from.
+ * debug: true to enable debug logging.
+ *
+ * Returns:
+ * 0 on success, otherwise -1 on failure. The only failure mode is if a
+ * the message exceeds the maximum message size configured on init.
+ */
+int mgmt_msg_send_msg(struct mgmt_msg_state *ms, void *msg, size_t len,
+ mgmt_msg_packf packf, bool debug)
+{
+ const char *dbgtag = debug ? ms->idtag : NULL;
+ struct mgmt_msg_hdr *mhdr;
+ struct stream *s;
+ uint8_t *dstbuf;
+ size_t endp, n;
+ size_t mlen = len + sizeof(*mhdr);
+
+ if (mlen > ms->max_msg_sz) {
+ MGMT_MSG_ERR(ms, "Message %zu > max size %zu, dropping", mlen,
+ ms->max_msg_sz);
+ return -1;
+ }
+
+ if (!ms->outs) {
+ MGMT_MSG_DBG(dbgtag, "creating new stream for msg len %zu",
+ len);
+ ms->outs = stream_new(ms->max_msg_sz);
+ } else if (STREAM_WRITEABLE(ms->outs) < mlen) {
+ MGMT_MSG_DBG(
+ dbgtag,
+ "enq existing stream len %zu and creating new stream for msg len %zu",
+ STREAM_WRITEABLE(ms->outs), mlen);
+ stream_fifo_push(&ms->outq, ms->outs);
+ ms->outs = stream_new(ms->max_msg_sz);
+ } else {
+ MGMT_MSG_DBG(
+ dbgtag,
+ "using existing stream with avail %zu for msg len %zu",
+ STREAM_WRITEABLE(ms->outs), mlen);
+ }
+ s = ms->outs;
+
+ /* We have a stream with space, pack the message into it. */
+ mhdr = (struct mgmt_msg_hdr *)(STREAM_DATA(s) + s->endp);
+ mhdr->marker = MGMT_MSG_MARKER;
+ mhdr->len = mlen;
+ stream_forward_endp(s, sizeof(*mhdr));
+ endp = stream_get_endp(s);
+ dstbuf = STREAM_DATA(s) + endp;
+ n = packf(msg, dstbuf);
+ stream_set_endp(s, endp + n);
+ ms->ntxm++;
+
+ return 0;
+}
+
+/**
+ * Create and open a unix domain stream socket on the given path
+ * setting non-blocking and send and receive buffer sizes.
+ *
+ * Args:
+ * path: path of unix domain socket to connect to.
+ * sendbuf: size of socket send buffer.
+ * recvbuf: size of socket receive buffer.
+ * dbgtag: if non-NULL enable log debug, and use this tag.
+ *
+ * Returns:
+ * socket fd or -1 on error.
+ */
+int mgmt_msg_connect(const char *path, size_t sendbuf, size_t recvbuf,
+ const char *dbgtag)
+{
+ int ret, sock, len;
+ struct sockaddr_un addr;
+
+ MGMT_MSG_DBG(dbgtag, "connecting to server on %s", path);
+ sock = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (sock < 0) {
+ MGMT_MSG_DBG(dbgtag, "socket failed: %s", safe_strerror(errno));
+ return -1;
+ }
+
+ memset(&addr, 0, sizeof(struct sockaddr_un));
+ addr.sun_family = AF_UNIX;
+ strlcpy(addr.sun_path, path, sizeof(addr.sun_path));
+#ifdef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN
+ len = addr.sun_len = SUN_LEN(&addr);
+#else
+ len = sizeof(addr.sun_family) + strlen(addr.sun_path);
+#endif /* HAVE_STRUCT_SOCKADDR_UN_SUN_LEN */
+ ret = connect(sock, (struct sockaddr *)&addr, len);
+ if (ret < 0) {
+ MGMT_MSG_DBG(dbgtag, "failed to connect on %s: %s", path,
+ safe_strerror(errno));
+ close(sock);
+ return -1;
+ }
+
+ MGMT_MSG_DBG(dbgtag, "connected to server on %s", path);
+ set_nonblocking(sock);
+ setsockopt_so_sendbuf(sock, sendbuf);
+ setsockopt_so_recvbuf(sock, recvbuf);
+ return sock;
+}
+
+/**
+ * Reset the sending queue, by dequeueing all streams and freeing them. Return
+ * the number of streams freed.
+ *
+ * Args:
+ * ms: mgmt_msg_state for this process.
+ *
+ * Returns:
+ * Number of streams that were freed.
+ *
+ */
+size_t mgmt_msg_reset_writes(struct mgmt_msg_state *ms)
+{
+ struct stream *s;
+ size_t nproc = 0;
+
+ for (s = stream_fifo_pop(&ms->outq); s;
+ s = stream_fifo_pop(&ms->outq), nproc++)
+ stream_free(s);
+
+ return nproc;
+}
+
+void mgmt_msg_init(struct mgmt_msg_state *ms, size_t max_read_buf,
+ size_t max_write_buf, size_t max_msg_sz, const char *idtag)
+{
+ memset(ms, 0, sizeof(*ms));
+ ms->ins = stream_new(max_msg_sz);
+ stream_fifo_init(&ms->inq);
+ stream_fifo_init(&ms->outq);
+ ms->max_read_buf = max_write_buf;
+ ms->max_write_buf = max_read_buf;
+ ms->max_msg_sz = max_msg_sz;
+ ms->idtag = strdup(idtag);
+}
+
+void mgmt_msg_destroy(struct mgmt_msg_state *ms)
+{
+ mgmt_msg_reset_writes(ms);
+ if (ms->ins)
+ stream_free(ms->ins);
+ free(ms->idtag);
+}
diff --git a/lib/mgmt_msg.h b/lib/mgmt_msg.h
new file mode 100644
index 0000000000..e2dd2d476a
--- /dev/null
+++ b/lib/mgmt_msg.h
@@ -0,0 +1,73 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * March 6 2023, Christian Hopps <chopps@labn.net>
+ *
+ * Copyright (c) 2023, LabN Consulting, L.L.C.
+ */
+#ifndef _MGMT_MSG_H
+#define _MGMT_MSG_H
+
+#include "stream.h"
+#include "frrevent.h"
+
+#define MGMT_MSG_MARKER (0x4D724B21u) /* ASCII - "MrK!"*/
+
+struct mgmt_msg_state {
+ struct stream *ins;
+ struct stream *outs;
+ struct stream_fifo inq;
+ struct stream_fifo outq;
+ uint64_t nrxm; /* number of received messages */
+ uint64_t nrxb; /* number of received bytes */
+ uint64_t ntxm; /* number of sent messages */
+ uint64_t ntxb; /* number of sent bytes */
+ size_t max_read_buf; /* should replace with max time value */
+ size_t max_write_buf; /* should replace with max time value */
+ size_t max_msg_sz;
+ char *idtag; /* identifying tag for messages */
+};
+
+struct mgmt_msg_hdr {
+ uint32_t marker;
+ uint32_t len;
+};
+
+enum mgmt_msg_rsched {
+ MSR_SCHED_BOTH, /* schedule both queue and read */
+ MSR_SCHED_STREAM, /* schedule read */
+ MSR_DISCONNECT, /* disconnect and start reconnecting */
+};
+
+enum mgmt_msg_wsched {
+ MSW_SCHED_NONE, /* no scheduling required */
+ MSW_SCHED_STREAM, /* schedule writing */
+ MSW_SCHED_WRITES_OFF, /* toggle writes off */
+ MSW_DISCONNECT, /* disconnect and start reconnecting */
+};
+
+static inline uint8_t *msg_payload(struct mgmt_msg_hdr *mhdr)
+{
+ return (uint8_t *)(mhdr + 1);
+}
+
+typedef size_t (*mgmt_msg_packf)(void *msg, void *data);
+
+extern int mgmt_msg_connect(const char *path, size_t sendbuf, size_t recvbuf,
+ const char *dbgtag);
+extern void mgmt_msg_destroy(struct mgmt_msg_state *ms);
+extern void mgmt_msg_init(struct mgmt_msg_state *ms, size_t max_read_buf,
+ size_t max_write_buf, size_t max_msg_sz,
+ const char *idtag);
+extern bool mgmt_msg_procbufs(struct mgmt_msg_state *ms,
+ void (*handle_msg)(void *user, uint8_t *msg,
+ size_t msglen),
+ void *user, bool debug);
+extern enum mgmt_msg_rsched mgmt_msg_read(struct mgmt_msg_state *ms, int fd,
+ bool debug);
+extern size_t mgmt_msg_reset_writes(struct mgmt_msg_state *ms);
+extern int mgmt_msg_send_msg(struct mgmt_msg_state *ms, void *msg, size_t len,
+ size_t (*packf)(void *msg, void *buf), bool debug);
+extern enum mgmt_msg_wsched mgmt_msg_write(struct mgmt_msg_state *ms, int fd,
+ bool debug);
+
+#endif /* _MGMT_MSG_H */
diff --git a/lib/mgmt_pb.h b/lib/mgmt_pb.h
new file mode 100644
index 0000000000..08bb748233
--- /dev/null
+++ b/lib/mgmt_pb.h
@@ -0,0 +1,25 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * MGMTD protobuf main header file
+ * Copyright (C) 2021 Vmware, Inc.
+ * Pushpasis Sarkar <spushpasis@vmware.com>
+ */
+
+#ifndef _FRR_MGMTD_PB_H_
+#define _FRR_MGMTD_PB_H_
+
+#include "lib/mgmt.pb-c.h"
+
+#define mgmt_yang_data_xpath_init(ptr) mgmtd__yang_data_xpath__init(ptr)
+
+#define mgmt_yang_data_value_init(ptr) mgmtd__yang_data_value__init(ptr)
+
+#define mgmt_yang_data_init(ptr) mgmtd__yang_data__init(ptr)
+
+#define mgmt_yang_data_reply_init(ptr) mgmtd__yang_data_reply__init(ptr)
+
+#define mgmt_yang_cfg_data_req_init(ptr) mgmtd__yang_cfg_data_req__init(ptr)
+
+#define mgmt_yang_get_data_req_init(ptr) mgmtd__yang_get_data_req__init(ptr)
+
+#endif /* _FRR_MGMTD_PB_H_ */
diff --git a/lib/northbound.c b/lib/northbound.c
index 6f2c522a29..307cf0fb49 100644
--- a/lib/northbound.c
+++ b/lib/northbound.c
@@ -93,7 +93,9 @@ static int nb_node_new_cb(const struct lysc_node *snode, void *arg)
{
struct nb_node *nb_node;
struct lysc_node *sparent, *sparent_list;
+ struct frr_yang_module_info *module;
+ module = (struct frr_yang_module_info *)arg;
nb_node = XCALLOC(MTYPE_NB_NODE, sizeof(*nb_node));
yang_snode_get_path(snode, YANG_PATH_DATA, nb_node->xpath,
sizeof(nb_node->xpath));
@@ -128,6 +130,9 @@ static int nb_node_new_cb(const struct lysc_node *snode, void *arg)
assert(snode->priv == NULL);
((struct lysc_node *)snode)->priv = nb_node;
+ if (module && module->ignore_cbs)
+ SET_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS);
+
return YANG_ITER_CONTINUE;
}
@@ -230,6 +235,9 @@ static unsigned int nb_node_validate_cbs(const struct nb_node *nb_node)
{
unsigned int error = 0;
+ if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS))
+ return error;
+
error += nb_node_validate_cb(nb_node, NB_OP_CREATE,
!!nb_node->cbs.create, false);
error += nb_node_validate_cb(nb_node, NB_OP_MODIFY,
@@ -297,6 +305,8 @@ struct nb_config *nb_config_new(struct lyd_node *dnode)
config->dnode = yang_dnode_new(ly_native_ctx, true);
config->version = 0;
+ RB_INIT(nb_config_cbs, &config->cfg_chgs);
+
return config;
}
@@ -304,6 +314,7 @@ void nb_config_free(struct nb_config *config)
{
if (config->dnode)
yang_dnode_free(config->dnode);
+ nb_config_diff_del_changes(&config->cfg_chgs);
XFREE(MTYPE_NB_CONFIG, config);
}
@@ -315,6 +326,8 @@ struct nb_config *nb_config_dup(const struct nb_config *config)
dup->dnode = yang_dnode_dup(config->dnode);
dup->version = config->version;
+ RB_INIT(nb_config_cbs, &dup->cfg_chgs);
+
return dup;
}
@@ -405,7 +418,7 @@ static void nb_config_diff_add_change(struct nb_config_cbs *changes,
RB_INSERT(nb_config_cbs, changes, &change->cb);
}
-static void nb_config_diff_del_changes(struct nb_config_cbs *changes)
+void nb_config_diff_del_changes(struct nb_config_cbs *changes)
{
while (!RB_EMPTY(nb_config_cbs, changes)) {
struct nb_config_change *change;
@@ -422,8 +435,8 @@ static void nb_config_diff_del_changes(struct nb_config_cbs *changes)
* configurations. Given a new subtree, calculate all new YANG data nodes,
* excluding default leafs and leaf-lists. This is a recursive function.
*/
-static void nb_config_diff_created(const struct lyd_node *dnode, uint32_t *seq,
- struct nb_config_cbs *changes)
+void nb_config_diff_created(const struct lyd_node *dnode, uint32_t *seq,
+ struct nb_config_cbs *changes)
{
enum nb_operation operation;
struct lyd_node *child;
@@ -525,10 +538,16 @@ static inline void nb_config_diff_dnode_log(const char *context,
}
#endif
-/* Calculate the delta between two different configurations. */
-static void nb_config_diff(const struct nb_config *config1,
- const struct nb_config *config2,
- struct nb_config_cbs *changes)
+/*
+ * Calculate the delta between two different configurations.
+ *
+ * NOTE: 'config1' is the reference DB, while 'config2' is
+ * the DB being compared against 'config1'. Typically 'config1'
+ * should be the Running DB and 'config2' is the Candidate DB.
+ */
+void nb_config_diff(const struct nb_config *config1,
+ const struct nb_config *config2,
+ struct nb_config_cbs *changes)
{
struct lyd_node *diff = NULL;
const struct lyd_node *root, *dnode;
@@ -734,6 +753,169 @@ int nb_candidate_edit(struct nb_config *candidate,
return NB_OK;
}
+static void nb_update_candidate_changes(struct nb_config *candidate,
+ struct nb_cfg_change *change,
+ uint32_t *seq)
+{
+ enum nb_operation oper = change->operation;
+ char *xpath = change->xpath;
+ struct lyd_node *root = NULL;
+ struct lyd_node *dnode;
+ struct nb_config_cbs *cfg_chgs = &candidate->cfg_chgs;
+ int op;
+
+ switch (oper) {
+ case NB_OP_CREATE:
+ case NB_OP_MODIFY:
+ root = yang_dnode_get(candidate->dnode, xpath);
+ break;
+ case NB_OP_DESTROY:
+ root = yang_dnode_get(running_config->dnode, xpath);
+ /* code */
+ break;
+ case NB_OP_MOVE:
+ case NB_OP_PRE_VALIDATE:
+ case NB_OP_APPLY_FINISH:
+ case NB_OP_GET_ELEM:
+ case NB_OP_GET_NEXT:
+ case NB_OP_GET_KEYS:
+ case NB_OP_LOOKUP_ENTRY:
+ case NB_OP_RPC:
+ break;
+ default:
+ assert(!"non-enum value, invalid");
+ }
+
+ if (!root)
+ return;
+
+ LYD_TREE_DFS_BEGIN (root, dnode) {
+ op = nb_lyd_diff_get_op(dnode);
+ switch (op) {
+ case 'c':
+ nb_config_diff_created(dnode, seq, cfg_chgs);
+ LYD_TREE_DFS_continue = 1;
+ break;
+ case 'd':
+ nb_config_diff_deleted(dnode, seq, cfg_chgs);
+ LYD_TREE_DFS_continue = 1;
+ break;
+ case 'r':
+ nb_config_diff_add_change(cfg_chgs, NB_OP_MODIFY, seq,
+ dnode);
+ break;
+ default:
+ break;
+ }
+ LYD_TREE_DFS_END(root, dnode);
+ }
+}
+
+static bool nb_is_operation_allowed(struct nb_node *nb_node,
+ struct nb_cfg_change *change)
+{
+ enum nb_operation oper = change->operation;
+
+ if (lysc_is_key(nb_node->snode)) {
+ if (oper == NB_OP_MODIFY || oper == NB_OP_DESTROY)
+ return false;
+ }
+ return true;
+}
+
+void nb_candidate_edit_config_changes(
+ struct nb_config *candidate_config, struct nb_cfg_change cfg_changes[],
+ size_t num_cfg_changes, const char *xpath_base, const char *curr_xpath,
+ int xpath_index, char *err_buf, int err_bufsize, bool *error)
+{
+ uint32_t seq = 0;
+
+ if (error)
+ *error = false;
+
+ if (xpath_base == NULL)
+ xpath_base = "";
+
+ /* Edit candidate configuration. */
+ for (size_t i = 0; i < num_cfg_changes; i++) {
+ struct nb_cfg_change *change = &cfg_changes[i];
+ struct nb_node *nb_node;
+ char xpath[XPATH_MAXLEN];
+ struct yang_data *data;
+ int ret;
+
+ /* Handle relative XPaths. */
+ memset(xpath, 0, sizeof(xpath));
+ if (xpath_index > 0 &&
+ (xpath_base[0] == '.' || change->xpath[0] == '.'))
+ strlcpy(xpath, curr_xpath, sizeof(xpath));
+ if (xpath_base[0]) {
+ if (xpath_base[0] == '.')
+ strlcat(xpath, xpath_base + 1, sizeof(xpath));
+ else
+ strlcat(xpath, xpath_base, sizeof(xpath));
+ }
+ if (change->xpath[0] == '.')
+ strlcat(xpath, change->xpath + 1, sizeof(xpath));
+ else
+ strlcpy(xpath, change->xpath, sizeof(xpath));
+
+ /* Find the northbound node associated to the data path. */
+ nb_node = nb_node_find(xpath);
+ if (!nb_node) {
+ flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH,
+ "%s: unknown data path: %s", __func__, xpath);
+ if (error)
+ *error = true;
+ continue;
+ }
+ /* Find if the node to be edited is not a key node */
+ if (!nb_is_operation_allowed(nb_node, change)) {
+ zlog_err(" Xpath %s points to key node", xpath);
+ if (error)
+ *error = true;
+ break;
+ }
+
+ /* If the value is not set, get the default if it exists. */
+ if (change->value == NULL)
+ change->value = yang_snode_get_default(nb_node->snode);
+ data = yang_data_new(xpath, change->value);
+
+ /*
+ * Ignore "not found" errors when editing the candidate
+ * configuration.
+ */
+ ret = nb_candidate_edit(candidate_config, nb_node,
+ change->operation, xpath, NULL, data);
+ yang_data_free(data);
+ if (ret != NB_OK && ret != NB_ERR_NOT_FOUND) {
+ flog_warn(
+ EC_LIB_NB_CANDIDATE_EDIT_ERROR,
+ "%s: failed to edit candidate configuration: operation [%s] xpath [%s]",
+ __func__, nb_operation_name(change->operation),
+ xpath);
+ if (error)
+ *error = true;
+ continue;
+ }
+ nb_update_candidate_changes(candidate_config, change, &seq);
+ }
+
+ if (error && *error) {
+ char buf[BUFSIZ];
+
+ /*
+ * Failure to edit the candidate configuration should never
+ * happen in practice, unless there's a bug in the code. When
+ * that happens, log the error but otherwise ignore it.
+ */
+ snprintf(err_buf, err_bufsize,
+ "%% Failed to edit configuration.\n\n%s",
+ yang_print_errors(ly_native_ctx, buf, sizeof(buf)));
+ }
+}
+
bool nb_candidate_needs_update(const struct nb_config *candidate)
{
if (candidate->version < running_config->version)
@@ -761,12 +943,13 @@ int nb_candidate_update(struct nb_config *candidate)
* WARNING: lyd_validate() can change the configuration as part of the
* validation process.
*/
-static int nb_candidate_validate_yang(struct nb_config *candidate, char *errmsg,
- size_t errmsg_len)
+int nb_candidate_validate_yang(struct nb_config *candidate, bool no_state,
+ char *errmsg, size_t errmsg_len)
{
if (lyd_validate_all(&candidate->dnode, ly_native_ctx,
- LYD_VALIDATE_NO_STATE, NULL)
- != 0) {
+ no_state ? LYD_VALIDATE_NO_STATE
+ : LYD_VALIDATE_PRESENT,
+ NULL) != 0) {
yang_print_errors(ly_native_ctx, errmsg, errmsg_len);
return NB_ERR_VALIDATION;
}
@@ -775,10 +958,10 @@ static int nb_candidate_validate_yang(struct nb_config *candidate, char *errmsg,
}
/* Perform code-level validation using the northbound callbacks. */
-static int nb_candidate_validate_code(struct nb_context *context,
- struct nb_config *candidate,
- struct nb_config_cbs *changes,
- char *errmsg, size_t errmsg_len)
+int nb_candidate_validate_code(struct nb_context *context,
+ struct nb_config *candidate,
+ struct nb_config_cbs *changes, char *errmsg,
+ size_t errmsg_len)
{
struct nb_config_cb *cb;
struct lyd_node *root, *child;
@@ -816,6 +999,21 @@ static int nb_candidate_validate_code(struct nb_context *context,
return NB_OK;
}
+int nb_candidate_diff_and_validate_yang(struct nb_context *context,
+ struct nb_config *candidate,
+ struct nb_config_cbs *changes,
+ char *errmsg, size_t errmsg_len)
+{
+ if (nb_candidate_validate_yang(candidate, true, errmsg,
+ sizeof(errmsg_len)) != NB_OK)
+ return NB_ERR_VALIDATION;
+
+ RB_INIT(nb_config_cbs, changes);
+ nb_config_diff(running_config, candidate, changes);
+
+ return NB_OK;
+}
+
int nb_candidate_validate(struct nb_context *context,
struct nb_config *candidate, char *errmsg,
size_t errmsg_len)
@@ -823,11 +1021,11 @@ int nb_candidate_validate(struct nb_context *context,
struct nb_config_cbs changes;
int ret;
- if (nb_candidate_validate_yang(candidate, errmsg, errmsg_len) != NB_OK)
- return NB_ERR_VALIDATION;
+ ret = nb_candidate_diff_and_validate_yang(context, candidate, &changes,
+ errmsg, errmsg_len);
+ if (ret != NB_OK)
+ return ret;
- RB_INIT(nb_config_cbs, &changes);
- nb_config_diff(running_config, candidate, &changes);
ret = nb_candidate_validate_code(context, candidate, &changes, errmsg,
errmsg_len);
nb_config_diff_del_changes(&changes);
@@ -839,12 +1037,14 @@ int nb_candidate_commit_prepare(struct nb_context context,
struct nb_config *candidate,
const char *comment,
struct nb_transaction **transaction,
+ bool skip_validate, bool ignore_zero_change,
char *errmsg, size_t errmsg_len)
{
struct nb_config_cbs changes;
- if (nb_candidate_validate_yang(candidate, errmsg, errmsg_len)
- != NB_OK) {
+ if (!skip_validate &&
+ nb_candidate_validate_yang(candidate, true, errmsg, errmsg_len) !=
+ NB_OK) {
flog_warn(EC_LIB_NB_CANDIDATE_INVALID,
"%s: failed to validate candidate configuration",
__func__);
@@ -853,14 +1053,15 @@ int nb_candidate_commit_prepare(struct nb_context context,
RB_INIT(nb_config_cbs, &changes);
nb_config_diff(running_config, candidate, &changes);
- if (RB_EMPTY(nb_config_cbs, &changes)) {
+ if (!ignore_zero_change && RB_EMPTY(nb_config_cbs, &changes)) {
snprintf(
errmsg, errmsg_len,
"No changes to apply were found during preparation phase");
return NB_ERR_NO_CHANGES;
}
- if (nb_candidate_validate_code(&context, candidate, &changes, errmsg,
+ if (!skip_validate &&
+ nb_candidate_validate_code(&context, candidate, &changes, errmsg,
errmsg_len) != NB_OK) {
flog_warn(EC_LIB_NB_CANDIDATE_INVALID,
"%s: failed to validate candidate configuration",
@@ -869,8 +1070,12 @@ int nb_candidate_commit_prepare(struct nb_context context,
return NB_ERR_VALIDATION;
}
- *transaction = nb_transaction_new(context, candidate, &changes, comment,
- errmsg, errmsg_len);
+ /*
+ * Re-use an existing transaction if provided. Else allocate a new one.
+ */
+ if (!*transaction)
+ *transaction = nb_transaction_new(context, candidate, &changes,
+ comment, errmsg, errmsg_len);
if (*transaction == NULL) {
flog_warn(EC_LIB_NB_TRANSACTION_CREATION_FAILED,
"%s: failed to create transaction: %s", __func__,
@@ -921,7 +1126,8 @@ int nb_candidate_commit(struct nb_context context, struct nb_config *candidate,
int ret;
ret = nb_candidate_commit_prepare(context, candidate, comment,
- &transaction, errmsg, errmsg_len);
+ &transaction, false, false, errmsg,
+ errmsg_len);
/*
* Apply the changes if the preparation phase succeeded. Otherwise abort
* the transaction.
@@ -1015,6 +1221,8 @@ static int nb_callback_create(struct nb_context *context,
bool unexpected_error = false;
int ret;
+ assert(!CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS));
+
nb_log_config_callback(event, NB_OP_CREATE, dnode);
args.context = context;
@@ -1064,6 +1272,8 @@ static int nb_callback_modify(struct nb_context *context,
bool unexpected_error = false;
int ret;
+ assert(!CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS));
+
nb_log_config_callback(event, NB_OP_MODIFY, dnode);
args.context = context;
@@ -1113,6 +1323,8 @@ static int nb_callback_destroy(struct nb_context *context,
bool unexpected_error = false;
int ret;
+ assert(!CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS));
+
nb_log_config_callback(event, NB_OP_DESTROY, dnode);
args.context = context;
@@ -1156,6 +1368,8 @@ static int nb_callback_move(struct nb_context *context,
bool unexpected_error = false;
int ret;
+ assert(!CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS));
+
nb_log_config_callback(event, NB_OP_MOVE, dnode);
args.context = context;
@@ -1199,6 +1413,9 @@ static int nb_callback_pre_validate(struct nb_context *context,
bool unexpected_error = false;
int ret;
+ if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS))
+ return 0;
+
nb_log_config_callback(NB_EV_VALIDATE, NB_OP_PRE_VALIDATE, dnode);
args.dnode = dnode;
@@ -1230,6 +1447,9 @@ static void nb_callback_apply_finish(struct nb_context *context,
{
struct nb_cb_apply_finish_args args = {};
+ if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS))
+ return;
+
nb_log_config_callback(NB_EV_APPLY, NB_OP_APPLY_FINISH, dnode);
args.context = context;
@@ -1245,6 +1465,9 @@ struct yang_data *nb_callback_get_elem(const struct nb_node *nb_node,
{
struct nb_cb_get_elem_args args = {};
+ if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS))
+ return NULL;
+
DEBUGD(&nb_dbg_cbs_state,
"northbound callback (get_elem): xpath [%s] list_entry [%p]",
xpath, list_entry);
@@ -1260,6 +1483,9 @@ const void *nb_callback_get_next(const struct nb_node *nb_node,
{
struct nb_cb_get_next_args args = {};
+ if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS))
+ return NULL;
+
DEBUGD(&nb_dbg_cbs_state,
"northbound callback (get_next): node [%s] parent_list_entry [%p] list_entry [%p]",
nb_node->xpath, parent_list_entry, list_entry);
@@ -1274,6 +1500,9 @@ int nb_callback_get_keys(const struct nb_node *nb_node, const void *list_entry,
{
struct nb_cb_get_keys_args args = {};
+ if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS))
+ return 0;
+
DEBUGD(&nb_dbg_cbs_state,
"northbound callback (get_keys): node [%s] list_entry [%p]",
nb_node->xpath, list_entry);
@@ -1289,6 +1518,9 @@ const void *nb_callback_lookup_entry(const struct nb_node *nb_node,
{
struct nb_cb_lookup_entry_args args = {};
+ if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS))
+ return NULL;
+
DEBUGD(&nb_dbg_cbs_state,
"northbound callback (lookup_entry): node [%s] parent_list_entry [%p]",
nb_node->xpath, parent_list_entry);
@@ -1304,6 +1536,9 @@ int nb_callback_rpc(const struct nb_node *nb_node, const char *xpath,
{
struct nb_cb_rpc_args args = {};
+ if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS))
+ return 0;
+
DEBUGD(&nb_dbg_cbs_rpc, "northbound RPC: %s", xpath);
args.xpath = xpath;
@@ -1330,6 +1565,9 @@ static int nb_callback_configuration(struct nb_context *context,
union nb_resource *resource;
int ret = NB_ERR;
+ if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS))
+ return NB_OK;
+
if (event == NB_EV_VALIDATE)
resource = NULL;
else
@@ -1733,7 +1971,7 @@ static int nb_oper_data_iter_list(const struct nb_node *nb_node,
/* Iterate over all list entries. */
do {
const struct lysc_node_leaf *skey;
- struct yang_list_keys list_keys;
+ struct yang_list_keys list_keys = {};
char xpath[XPATH_MAXLEN * 2];
int ret;
@@ -2389,6 +2627,10 @@ const char *nb_client_name(enum nb_client client)
return "gRPC";
case NB_CLIENT_PCEP:
return "Pcep";
+ case NB_CLIENT_MGMTD_SERVER:
+ return "MGMTD Server";
+ case NB_CLIENT_MGMTD_BE:
+ return "MGMT Backend";
case NB_CLIENT_NONE:
return "None";
}
@@ -2398,6 +2640,10 @@ const char *nb_client_name(enum nb_client client)
static void nb_load_callbacks(const struct frr_yang_module_info *module)
{
+
+ if (module->ignore_cbs)
+ return;
+
for (size_t i = 0; module->nodes[i].xpath; i++) {
struct nb_node *nb_node;
uint32_t priority;
@@ -2439,7 +2685,7 @@ void nb_validate_callbacks(void)
}
-void nb_init(struct thread_master *tm,
+void nb_init(struct event_loop *tm,
const struct frr_yang_module_info *const modules[],
size_t nmodules, bool db_enabled)
{
@@ -2471,7 +2717,8 @@ void nb_init(struct thread_master *tm,
/* Initialize the compiled nodes with northbound data */
for (size_t i = 0; i < nmodules; i++) {
- yang_snodes_iterate(loaded[i]->info, nb_node_new_cb, 0, NULL);
+ yang_snodes_iterate(loaded[i]->info, nb_node_new_cb, 0,
+ (void *)modules[i]);
nb_load_callbacks(modules[i]);
}
@@ -2498,8 +2745,7 @@ void nb_terminate(void)
nb_nodes_delete();
/* Delete the running configuration. */
- hash_clean(running_config_entries, running_config_entry_free);
- hash_free(running_config_entries);
+ hash_clean_and_free(&running_config_entries, running_config_entry_free);
nb_config_free(running_config);
pthread_mutex_destroy(&running_config_mgmt_lock.mtx);
}
diff --git a/lib/northbound.h b/lib/northbound.h
index 152810b3a9..1723a87e4e 100644
--- a/lib/northbound.h
+++ b/lib/northbound.h
@@ -7,7 +7,7 @@
#ifndef _FRR_NORTHBOUND_H_
#define _FRR_NORTHBOUND_H_
-#include "thread.h"
+#include "frrevent.h"
#include "hook.h"
#include "linklist.h"
#include "openbsd-tree.h"
@@ -22,6 +22,39 @@ extern "C" {
struct vty;
struct debug;
+struct nb_yang_xpath_tag {
+ uint32_t ns;
+ uint32_t id;
+};
+
+struct nb_yang_value {
+ struct lyd_value value;
+ LY_DATA_TYPE value_type;
+ uint8_t value_flags;
+};
+
+struct nb_yang_xpath_elem {
+ struct nb_yang_xpath_tag tag;
+ struct nb_yang_value val;
+};
+
+#define NB_MAX_NUM_KEYS UINT8_MAX
+#define NB_MAX_NUM_XPATH_TAGS UINT8_MAX
+
+struct nb_yang_xpath {
+ uint8_t length;
+ struct {
+ uint8_t num_keys;
+ struct nb_yang_xpath_elem keys[NB_MAX_NUM_KEYS];
+ } tags[NB_MAX_NUM_XPATH_TAGS];
+};
+
+#define NB_YANG_XPATH_KEY(__xpath, __indx1, __indx2) \
+ ((__xpath->num_tags > __indx1) && \
+ (__xpath->tags[__indx1].num_keys > __indx2) \
+ ? &__xpath->tags[__indx1].keys[__indx2] \
+ : NULL)
+
/* Northbound events. */
enum nb_event {
/*
@@ -68,6 +101,12 @@ enum nb_operation {
NB_OP_RPC,
};
+struct nb_cfg_change {
+ char xpath[XPATH_MAXLEN];
+ enum nb_operation operation;
+ const char *value;
+};
+
union nb_resource {
int fd;
void *ptr;
@@ -558,6 +597,8 @@ struct nb_node {
#define F_NB_NODE_CONFIG_ONLY 0x01
/* The YANG list doesn't contain key leafs. */
#define F_NB_NODE_KEYLESS_LIST 0x02
+/* Ignore callbacks for this node */
+#define F_NB_NODE_IGNORE_CBS 0x04
/*
* HACK: old gcc versions (< 5.x) have a bug that prevents C99 flexible arrays
@@ -570,6 +611,12 @@ struct frr_yang_module_info {
/* YANG module name. */
const char *name;
+ /*
+ * Ignore callbacks for this module. Set this to true to
+ * load module without any callbacks.
+ */
+ bool ignore_cbs;
+
/* Northbound callbacks. */
const struct {
/* Data path of this YANG node. */
@@ -613,6 +660,8 @@ enum nb_client {
NB_CLIENT_SYSREPO,
NB_CLIENT_GRPC,
NB_CLIENT_PCEP,
+ NB_CLIENT_MGMTD_SERVER,
+ NB_CLIENT_MGMTD_BE,
};
/* Northbound context. */
@@ -624,12 +673,6 @@ struct nb_context {
const void *user;
};
-/* Northbound configuration. */
-struct nb_config {
- struct lyd_node *dnode;
- uint32_t version;
-};
-
/* Northbound configuration callback. */
struct nb_config_cb {
RB_ENTRY(nb_config_cb) entry;
@@ -656,6 +699,13 @@ struct nb_transaction {
struct nb_config_cbs changes;
};
+/* Northbound configuration. */
+struct nb_config {
+ struct lyd_node *dnode;
+ uint32_t version;
+ struct nb_config_cbs cfg_chgs;
+};
+
/* Callback function used by nb_oper_data_iterate(). */
typedef int (*nb_oper_data_cb)(const struct lysc_node *snode,
struct yang_translator *translator,
@@ -826,6 +876,22 @@ extern int nb_candidate_edit(struct nb_config *candidate,
const struct yang_data *data);
/*
+ * Create diff for configuration.
+ *
+ * dnode
+ * Pointer to a libyang data node containing the configuration data. If NULL
+ * is given, an empty configuration will be created.
+ *
+ * seq
+ * Returns sequence number assigned to the specific change.
+ *
+ * changes
+ * Northbound config callback head.
+ */
+extern void nb_config_diff_created(const struct lyd_node *dnode, uint32_t *seq,
+ struct nb_config_cbs *changes);
+
+/*
* Check if a candidate configuration is outdated and needs to be updated.
*
* candidate
@@ -837,6 +903,140 @@ extern int nb_candidate_edit(struct nb_config *candidate,
extern bool nb_candidate_needs_update(const struct nb_config *candidate);
/*
+ * Edit candidate configuration changes.
+ *
+ * candidate_config
+ * Candidate configuration to edit.
+ *
+ * cfg_changes
+ * Northbound config changes.
+ *
+ * num_cfg_changes
+ * Number of config changes.
+ *
+ * xpath_base
+ * Base xpath for config.
+ *
+ * curr_xpath
+ * Current xpath for config.
+ *
+ * xpath_index
+ * Index of xpath being processed.
+ *
+ * err_buf
+ * Buffer to store human-readable error message in case of error.
+ *
+ * err_bufsize
+ * Size of err_buf.
+ *
+ * error
+ * TRUE on error, FALSE on success
+ */
+extern void nb_candidate_edit_config_changes(
+ struct nb_config *candidate_config, struct nb_cfg_change cfg_changes[],
+ size_t num_cfg_changes, const char *xpath_base, const char *curr_xpath,
+ int xpath_index, char *err_buf, int err_bufsize, bool *error);
+
+/*
+ * Delete candidate configuration changes.
+ *
+ * changes
+ * Northbound config changes.
+ */
+extern void nb_config_diff_del_changes(struct nb_config_cbs *changes);
+
+/*
+ * Create candidate diff and validate on yang tree
+ *
+ * context
+ * Context of the northbound transaction.
+ *
+ * candidate
+ * Candidate DB configuration.
+ *
+ * changes
+ * Northbound config changes.
+ *
+ * errmsg
+ * Buffer to store human-readable error message in case of error.
+ *
+ * errmsg_len
+ * Size of errmsg.
+ *
+ * Returns:
+ * NB_OK on success, NB_ERR_VALIDATION otherwise
+ */
+extern int nb_candidate_diff_and_validate_yang(struct nb_context *context,
+ struct nb_config *candidate,
+ struct nb_config_cbs *changes,
+ char *errmsg, size_t errmsg_len);
+
+/*
+ * Calculate the delta between two different configurations.
+ *
+ * reference
+ * Running DB config changes to be compared against.
+ *
+ * incremental
+ * Candidate DB config changes that will be compared against reference.
+ *
+ * changes
+ * Will hold the final diff generated.
+ *
+ */
+extern void nb_config_diff(const struct nb_config *reference,
+ const struct nb_config *incremental,
+ struct nb_config_cbs *changes);
+
+/*
+ * Perform YANG syntactic and semantic validation.
+ *
+ * WARNING: lyd_validate() can change the configuration as part of the
+ * validation process.
+ *
+ * candidate
+ * Candidate DB configuration.
+ *
+ * errmsg
+ * Buffer to store human-readable error message in case of error.
+ *
+ * errmsg_len
+ * Size of errmsg.
+ *
+ * Returns:
+ * NB_OK on success, NB_ERR_VALIDATION otherwise
+ */
+extern int nb_candidate_validate_yang(struct nb_config *candidate,
+ bool no_state, char *errmsg,
+ size_t errmsg_len);
+
+/*
+ * Perform code-level validation using the northbound callbacks.
+ *
+ * context
+ * Context of the northbound transaction.
+ *
+ * candidate
+ * Candidate DB configuration.
+ *
+ * changes
+ * Northbound config changes.
+ *
+ * errmsg
+ * Buffer to store human-readable error message in case of error.
+ *
+ * errmsg_len
+ * Size of errmsg.
+ *
+ * Returns:
+ * NB_OK on success, NB_ERR_VALIDATION otherwise
+ */
+extern int nb_candidate_validate_code(struct nb_context *context,
+ struct nb_config *candidate,
+ struct nb_config_cbs *changes,
+ char *errmsg, size_t errmsg_len);
+
+/*
* Update a candidate configuration by rebasing the changes on top of the latest
* running configuration. Resolve conflicts automatically by giving preference
* to the changes done in the candidate configuration.
@@ -895,6 +1095,12 @@ extern int nb_candidate_validate(struct nb_context *context,
* nb_candidate_commit_abort() or committed using
* nb_candidate_commit_apply().
*
+ * skip_validate
+ * TRUE to skip commit validation, FALSE otherwise.
+ *
+ * ignore_zero_change
+ * TRUE to ignore if zero changes, FALSE otherwise.
+ *
* errmsg
* Buffer to store human-readable error message in case of error.
*
@@ -915,7 +1121,9 @@ extern int nb_candidate_commit_prepare(struct nb_context context,
struct nb_config *candidate,
const char *comment,
struct nb_transaction **transaction,
- char *errmsg, size_t errmsg_len);
+ bool skip_validate,
+ bool ignore_zero_change, char *errmsg,
+ size_t errmsg_len);
/*
* Abort a previously created configuration transaction, releasing all resources
@@ -1270,7 +1478,7 @@ void nb_validate_callbacks(void);
* db_enabled
* Set this to record the transactions in the transaction log.
*/
-extern void nb_init(struct thread_master *tm,
+extern void nb_init(struct event_loop *tm,
const struct frr_yang_module_info *const modules[],
size_t nmodules, bool db_enabled);
diff --git a/lib/northbound_cli.c b/lib/northbound_cli.c
index fa5884fb78..5cf5f93b43 100644
--- a/lib/northbound_cli.c
+++ b/lib/northbound_cli.c
@@ -29,7 +29,7 @@ struct debug nb_dbg_events = {0, "Northbound events"};
struct debug nb_dbg_libyang = {0, "libyang debugging"};
struct nb_config *vty_shared_candidate_config;
-static struct thread_master *master;
+static struct event_loop *master;
static void vty_show_nb_errors(struct vty *vty, int error, const char *errmsg)
{
@@ -120,7 +120,7 @@ static int nb_cli_schedule_command(struct vty *vty)
void nb_cli_enqueue_change(struct vty *vty, const char *xpath,
enum nb_operation operation, const char *value)
{
- struct vty_cfg_change *change;
+ struct nb_cfg_change *change;
if (vty->num_cfg_changes == VTY_MAXCFGCHANGES) {
/* Not expected to happen. */
@@ -141,79 +141,21 @@ static int nb_cli_apply_changes_internal(struct vty *vty,
bool clear_pending)
{
bool error = false;
-
- if (xpath_base == NULL)
- xpath_base = "";
+ char buf[BUFSIZ];
VTY_CHECK_XPATH;
- /* Edit candidate configuration. */
- for (size_t i = 0; i < vty->num_cfg_changes; i++) {
- struct vty_cfg_change *change = &vty->cfg_changes[i];
- struct nb_node *nb_node;
- char xpath[XPATH_MAXLEN];
- struct yang_data *data;
- int ret;
-
- /* Handle relative XPaths. */
- memset(xpath, 0, sizeof(xpath));
- if (vty->xpath_index > 0
- && (xpath_base[0] == '.' || change->xpath[0] == '.'))
- strlcpy(xpath, VTY_CURR_XPATH, sizeof(xpath));
- if (xpath_base[0]) {
- if (xpath_base[0] == '.')
- strlcat(xpath, xpath_base + 1, sizeof(xpath));
- else
- strlcat(xpath, xpath_base, sizeof(xpath));
- }
- if (change->xpath[0] == '.')
- strlcat(xpath, change->xpath + 1, sizeof(xpath));
- else
- strlcpy(xpath, change->xpath, sizeof(xpath));
-
- /* Find the northbound node associated to the data path. */
- nb_node = nb_node_find(xpath);
- if (!nb_node) {
- flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH,
- "%s: unknown data path: %s", __func__, xpath);
- error = true;
- continue;
- }
-
- /* If the value is not set, get the default if it exists. */
- if (change->value == NULL)
- change->value = yang_snode_get_default(nb_node->snode);
- data = yang_data_new(xpath, change->value);
-
- /*
- * Ignore "not found" errors when editing the candidate
- * configuration.
- */
- ret = nb_candidate_edit(vty->candidate_config, nb_node,
- change->operation, xpath, NULL, data);
- yang_data_free(data);
- if (ret != NB_OK && ret != NB_ERR_NOT_FOUND) {
- flog_warn(
- EC_LIB_NB_CANDIDATE_EDIT_ERROR,
- "%s: failed to edit candidate configuration: operation [%s] xpath [%s]",
- __func__, nb_operation_name(change->operation),
- xpath);
- error = true;
- continue;
- }
- }
-
+ nb_candidate_edit_config_changes(
+ vty->candidate_config, vty->cfg_changes, vty->num_cfg_changes,
+ xpath_base, VTY_CURR_XPATH, vty->xpath_index, buf, sizeof(buf),
+ &error);
if (error) {
- char buf[BUFSIZ];
-
/*
* Failure to edit the candidate configuration should never
* happen in practice, unless there's a bug in the code. When
* that happens, log the error but otherwise ignore it.
*/
- vty_out(vty, "%% Failed to edit configuration.\n\n");
- vty_out(vty, "%s",
- yang_print_errors(ly_native_ctx, buf, sizeof(buf)));
+ vty_out(vty, "%s", buf);
}
/*
@@ -241,6 +183,8 @@ static int nb_cli_apply_changes_internal(struct vty *vty,
int nb_cli_apply_changes(struct vty *vty, const char *xpath_base_fmt, ...)
{
char xpath_base[XPATH_MAXLEN] = {};
+ bool implicit_commit;
+ int ret;
/* Parse the base XPath format string. */
if (xpath_base_fmt) {
@@ -250,6 +194,17 @@ int nb_cli_apply_changes(struct vty *vty, const char *xpath_base_fmt, ...)
vsnprintf(xpath_base, sizeof(xpath_base), xpath_base_fmt, ap);
va_end(ap);
}
+
+ if (vty_mgmt_fe_enabled()) {
+ VTY_CHECK_XPATH;
+
+ implicit_commit = vty_needs_implicit_commit(vty);
+ ret = vty_mgmt_send_config_data(vty);
+ if (ret >= 0 && !implicit_commit)
+ vty->mgmt_num_pending_setcfg++;
+ return ret;
+ }
+
return nb_cli_apply_changes_internal(vty, xpath_base, false);
}
@@ -257,6 +212,8 @@ int nb_cli_apply_changes_clear_pending(struct vty *vty,
const char *xpath_base_fmt, ...)
{
char xpath_base[XPATH_MAXLEN] = {};
+ bool implicit_commit;
+ int ret;
/* Parse the base XPath format string. */
if (xpath_base_fmt) {
@@ -266,6 +223,17 @@ int nb_cli_apply_changes_clear_pending(struct vty *vty,
vsnprintf(xpath_base, sizeof(xpath_base), xpath_base_fmt, ap);
va_end(ap);
}
+
+ if (vty_mgmt_fe_enabled()) {
+ VTY_CHECK_XPATH;
+
+ implicit_commit = vty_needs_implicit_commit(vty);
+ ret = vty_mgmt_send_config_data(vty);
+ if (ret >= 0 && !implicit_commit)
+ vty->mgmt_num_pending_setcfg++;
+ return ret;
+ }
+
return nb_cli_apply_changes_internal(vty, xpath_base, true);
}
@@ -297,7 +265,7 @@ int nb_cli_rpc(struct vty *vty, const char *xpath, struct list *input,
void nb_cli_confirmed_commit_clean(struct vty *vty)
{
- thread_cancel(&vty->t_confirmed_commit_timeout);
+ event_cancel(&vty->t_confirmed_commit_timeout);
nb_config_free(vty->confirmed_commit_rollback);
vty->confirmed_commit_rollback = NULL;
}
@@ -332,9 +300,9 @@ int nb_cli_confirmed_commit_rollback(struct vty *vty)
return ret;
}
-static void nb_cli_confirmed_commit_timeout(struct thread *thread)
+static void nb_cli_confirmed_commit_timeout(struct event *thread)
{
- struct vty *vty = THREAD_ARG(thread);
+ struct vty *vty = EVENT_ARG(thread);
/* XXX: broadcast this message to all logged-in users? */
vty_out(vty,
@@ -360,11 +328,10 @@ static int nb_cli_commit(struct vty *vty, bool force,
"%% Resetting confirmed-commit timeout to %u minute(s)\n\n",
confirmed_timeout);
- thread_cancel(&vty->t_confirmed_commit_timeout);
- thread_add_timer(master,
- nb_cli_confirmed_commit_timeout, vty,
- confirmed_timeout * 60,
- &vty->t_confirmed_commit_timeout);
+ event_cancel(&vty->t_confirmed_commit_timeout);
+ event_add_timer(master, nb_cli_confirmed_commit_timeout,
+ vty, confirmed_timeout * 60,
+ &vty->t_confirmed_commit_timeout);
} else {
/* Accept commit confirmation. */
vty_out(vty, "%% Commit complete.\n\n");
@@ -387,9 +354,9 @@ static int nb_cli_commit(struct vty *vty, bool force,
vty->confirmed_commit_rollback = nb_config_dup(running_config);
vty->t_confirmed_commit_timeout = NULL;
- thread_add_timer(master, nb_cli_confirmed_commit_timeout, vty,
- confirmed_timeout * 60,
- &vty->t_confirmed_commit_timeout);
+ event_add_timer(master, nb_cli_confirmed_commit_timeout, vty,
+ confirmed_timeout * 60,
+ &vty->t_confirmed_commit_timeout);
}
context.client = NB_CLIENT_CLI;
@@ -1909,7 +1876,7 @@ static const struct cmd_variable_handler yang_var_handlers[] = {
.completions = yang_translator_autocomplete},
{.completions = NULL}};
-void nb_cli_init(struct thread_master *tm)
+void nb_cli_init(struct event_loop *tm)
{
master = tm;
diff --git a/lib/northbound_cli.h b/lib/northbound_cli.h
index ef2ef44eb0..c8f8a8481a 100644
--- a/lib/northbound_cli.h
+++ b/lib/northbound_cli.h
@@ -137,7 +137,7 @@ extern void nb_cli_show_config_prepare(struct nb_config *config,
extern void nb_cli_confirmed_commit_clean(struct vty *vty);
extern int nb_cli_confirmed_commit_rollback(struct vty *vty);
extern void nb_cli_install_default(int node);
-extern void nb_cli_init(struct thread_master *tm);
+extern void nb_cli_init(struct event_loop *tm);
extern void nb_cli_terminate(void);
#ifdef __cplusplus
diff --git a/lib/northbound_confd.c b/lib/northbound_confd.c
index 2b57ff2707..34406a110b 100644
--- a/lib/northbound_confd.c
+++ b/lib/northbound_confd.c
@@ -23,10 +23,10 @@ DEFINE_MTYPE_STATIC(LIB, CONFD, "ConfD module");
static struct debug nb_dbg_client_confd = {0, "Northbound client: ConfD"};
-static struct thread_master *master;
+static struct event_loop *master;
static struct sockaddr confd_addr;
static int cdb_sub_sock, dp_ctl_sock, dp_worker_sock;
-static struct thread *t_cdb_sub, *t_dp_ctl, *t_dp_worker;
+static struct event *t_cdb_sub, *t_dp_ctl, *t_dp_worker;
static struct confd_daemon_ctx *dctx;
static struct confd_notification_ctx *live_ctx;
static bool confd_connected;
@@ -312,7 +312,8 @@ static void frr_confd_cdb_read_cb_prepare(int fd, int *subp, int reslen)
transaction = NULL;
context.client = NB_CLIENT_CONFD;
ret = nb_candidate_commit_prepare(context, candidate, NULL,
- &transaction, errmsg, sizeof(errmsg));
+ &transaction, false, false, errmsg,
+ sizeof(errmsg));
if (ret != NB_OK && ret != NB_ERR_NO_CHANGES) {
enum confd_errcode errcode;
@@ -400,15 +401,15 @@ static int frr_confd_cdb_read_cb_abort(int fd, int *subp, int reslen)
return 0;
}
-static void frr_confd_cdb_read_cb(struct thread *thread)
+static void frr_confd_cdb_read_cb(struct event *thread)
{
- int fd = THREAD_FD(thread);
+ int fd = EVENT_FD(thread);
enum cdb_sub_notification cdb_ev;
int flags;
int *subp = NULL;
int reslen = 0;
- thread_add_read(master, frr_confd_cdb_read_cb, NULL, fd, &t_cdb_sub);
+ event_add_read(master, frr_confd_cdb_read_cb, NULL, fd, &t_cdb_sub);
if (cdb_read_subscription_socket2(fd, &cdb_ev, &flags, &subp, &reslen)
!= CONFD_OK) {
@@ -573,8 +574,8 @@ static int frr_confd_init_cdb(void)
}
pthread_detach(cdb_trigger_thread);
- thread_add_read(master, frr_confd_cdb_read_cb, NULL, cdb_sub_sock,
- &t_cdb_sub);
+ event_add_read(master, frr_confd_cdb_read_cb, NULL, cdb_sub_sock,
+ &t_cdb_sub);
return 0;
@@ -587,7 +588,7 @@ error:
static void frr_confd_finish_cdb(void)
{
if (cdb_sub_sock > 0) {
- THREAD_OFF(t_cdb_sub);
+ EVENT_OFF(t_cdb_sub);
cdb_close(cdb_sub_sock);
}
}
@@ -1172,22 +1173,23 @@ static int frr_confd_dp_read(struct confd_daemon_ctx *dctx, int fd)
return 0;
}
-static void frr_confd_dp_ctl_read(struct thread *thread)
+static void frr_confd_dp_ctl_read(struct event *thread)
{
- struct confd_daemon_ctx *dctx = THREAD_ARG(thread);
- int fd = THREAD_FD(thread);
+ struct confd_daemon_ctx *dctx = EVENT_ARG(thread);
+ int fd = EVENT_FD(thread);
- thread_add_read(master, frr_confd_dp_ctl_read, dctx, fd, &t_dp_ctl);
+ event_add_read(master, frr_confd_dp_ctl_read, dctx, fd, &t_dp_ctl);
frr_confd_dp_read(dctx, fd);
}
-static void frr_confd_dp_worker_read(struct thread *thread)
+static void frr_confd_dp_worker_read(struct event *thread)
{
- struct confd_daemon_ctx *dctx = THREAD_ARG(thread);
- int fd = THREAD_FD(thread);
+ struct confd_daemon_ctx *dctx = EVENT_ARG(thread);
+ int fd = EVENT_FD(thread);
- thread_add_read(master, frr_confd_dp_worker_read, dctx, fd, &t_dp_worker);
+ event_add_read(master, frr_confd_dp_worker_read, dctx, fd,
+ &t_dp_worker);
frr_confd_dp_read(dctx, fd);
}
@@ -1319,10 +1321,10 @@ static int frr_confd_init_dp(const char *program_name)
goto error;
}
- thread_add_read(master, frr_confd_dp_ctl_read, dctx, dp_ctl_sock,
- &t_dp_ctl);
- thread_add_read(master, frr_confd_dp_worker_read, dctx, dp_worker_sock,
- &t_dp_worker);
+ event_add_read(master, frr_confd_dp_ctl_read, dctx, dp_ctl_sock,
+ &t_dp_ctl);
+ event_add_read(master, frr_confd_dp_worker_read, dctx, dp_worker_sock,
+ &t_dp_worker);
return 0;
@@ -1335,11 +1337,11 @@ error:
static void frr_confd_finish_dp(void)
{
if (dp_worker_sock > 0) {
- THREAD_OFF(t_dp_worker);
+ EVENT_OFF(t_dp_worker);
close(dp_worker_sock);
}
if (dp_ctl_sock > 0) {
- THREAD_OFF(t_dp_ctl);
+ EVENT_OFF(t_dp_ctl);
close(dp_ctl_sock);
}
if (dctx != NULL)
@@ -1463,7 +1465,7 @@ static int frr_confd_finish(void)
return 0;
}
-static int frr_confd_module_late_init(struct thread_master *tm)
+static int frr_confd_module_late_init(struct event_loop *tm)
{
master = tm;
diff --git a/lib/northbound_grpc.cpp b/lib/northbound_grpc.cpp
index 1459146eab..6c33351cef 100644
--- a/lib/northbound_grpc.cpp
+++ b/lib/northbound_grpc.cpp
@@ -12,7 +12,7 @@
#include "log.h"
#include "libfrr.h"
#include "lib/version.h"
-#include "lib/thread.h"
+#include "frrevent.h"
#include "command.h"
#include "lib_errors.h"
#include "northbound.h"
@@ -38,7 +38,7 @@
*/
static bool nb_dbg_client_grpc = 0;
-static struct thread_master *main_master;
+static struct event_loop *main_master;
static struct frr_pthread *fpt;
@@ -157,8 +157,7 @@ class RpcStateBase
* state will either be MORE or FINISH. It will always be FINISH
* for Unary RPCs.
*/
- thread_add_event(main_master, c_callback, (void *)this, 0,
- NULL);
+ event_add_event(main_master, c_callback, (void *)this, 0, NULL);
pthread_mutex_lock(&this->cmux);
while (this->state == PROCESS)
@@ -181,11 +180,11 @@ class RpcStateBase
}
protected:
- virtual CallState run_mainthread(struct thread *thread) = 0;
+ virtual CallState run_mainthread(struct event *thread) = 0;
- static void c_callback(struct thread *thread)
+ static void c_callback(struct event *thread)
{
- auto _tag = static_cast<RpcStateBase *>(THREAD_ARG(thread));
+ auto _tag = static_cast<RpcStateBase *>(EVENT_ARG(thread));
/*
* We hold the lock until the callback finishes and has updated
* _tag->state, then we signal done and release.
@@ -250,7 +249,7 @@ template <typename Q, typename S> class UnaryRpcState : public RpcStateBase
&copy->responder, cq, cq, copy);
}
- CallState run_mainthread(struct thread *thread) override
+ CallState run_mainthread(struct event *thread) override
{
// Unary RPC are always finished, see "Unary" :)
grpc::Status status = this->callback(this);
@@ -302,7 +301,7 @@ class StreamRpcState : public RpcStateBase
&copy->async_responder, cq, cq, copy);
}
- CallState run_mainthread(struct thread *thread) override
+ CallState run_mainthread(struct event *thread) override
{
if (this->callback(this))
return MORE;
@@ -825,7 +824,8 @@ HandleUnaryCommit(UnaryRpcState<frr::CommitRequest, frr::CommitResponse> *tag)
grpc_debug("`-> Performing PREPARE");
ret = nb_candidate_commit_prepare(
context, candidate->config, comment.c_str(),
- &candidate->transaction, errmsg, sizeof(errmsg));
+ &candidate->transaction, false, false, errmsg,
+ sizeof(errmsg));
break;
case frr::CommitRequest::ABORT:
grpc_debug("`-> Performing ABORT");
@@ -1274,7 +1274,7 @@ static int frr_grpc_finish(void)
* fork. This is done by scheduling this init function as an event task, since
* the event loop doesn't run until after fork.
*/
-static void frr_grpc_module_very_late_init(struct thread *thread)
+static void frr_grpc_module_very_late_init(struct event *thread)
{
const char *args = THIS_MODULE->load_args;
uint port = GRPC_DEFAULT_PORT;
@@ -1298,11 +1298,11 @@ error:
flog_err(EC_LIB_GRPC_INIT, "failed to initialize the gRPC module");
}
-static int frr_grpc_module_late_init(struct thread_master *tm)
+static int frr_grpc_module_late_init(struct event_loop *tm)
{
main_master = tm;
hook_register(frr_fini, frr_grpc_finish);
- thread_add_event(tm, frr_grpc_module_very_late_init, NULL, 0, NULL);
+ event_add_event(tm, frr_grpc_module_very_late_init, NULL, 0, NULL);
return 0;
}
diff --git a/lib/northbound_sysrepo.c b/lib/northbound_sysrepo.c
index 096414ff24..86105d2e77 100644
--- a/lib/northbound_sysrepo.c
+++ b/lib/northbound_sysrepo.c
@@ -23,12 +23,12 @@ DEFINE_MTYPE_STATIC(LIB, SYSREPO, "Sysrepo module");
static struct debug nb_dbg_client_sysrepo = {0, "Northbound client: Sysrepo"};
-static struct thread_master *master;
+static struct event_loop *master;
static sr_session_ctx_t *session;
static sr_conn_ctx_t *connection;
static struct nb_transaction *transaction;
-static void frr_sr_read_cb(struct thread *thread);
+static void frr_sr_read_cb(struct event *thread);
static int frr_sr_finish(void);
/* Convert FRR YANG data value to sysrepo YANG data value. */
@@ -269,7 +269,8 @@ static int frr_sr_config_change_cb_prepare(sr_session_ctx_t *session,
* required to apply them.
*/
ret = nb_candidate_commit_prepare(context, candidate, NULL,
- &transaction, errmsg, sizeof(errmsg));
+ &transaction, false, false, errmsg,
+ sizeof(errmsg));
if (ret != NB_OK && ret != NB_ERR_NO_CHANGES)
flog_warn(
EC_LIB_LIBSYSREPO,
@@ -513,10 +514,10 @@ static int frr_sr_notification_send(const char *xpath, struct list *arguments)
return NB_OK;
}
-static void frr_sr_read_cb(struct thread *thread)
+static void frr_sr_read_cb(struct event *thread)
{
- struct yang_module *module = THREAD_ARG(thread);
- int fd = THREAD_FD(thread);
+ struct yang_module *module = EVENT_ARG(thread);
+ int fd = EVENT_FD(thread);
int ret;
ret = sr_subscription_process_events(module->sr_subscription, session,
@@ -527,7 +528,7 @@ static void frr_sr_read_cb(struct thread *thread)
return;
}
- thread_add_read(master, frr_sr_read_cb, module, fd, &module->sr_thread);
+ event_add_read(master, frr_sr_read_cb, module, fd, &module->sr_thread);
}
static void frr_sr_subscribe_config(struct yang_module *module)
@@ -687,8 +688,8 @@ static int frr_sr_init(void)
sr_strerror(ret));
goto cleanup;
}
- thread_add_read(master, frr_sr_read_cb, module,
- event_pipe, &module->sr_thread);
+ event_add_read(master, frr_sr_read_cb, module, event_pipe,
+ &module->sr_thread);
}
hook_register(nb_notification_send, frr_sr_notification_send);
@@ -709,7 +710,7 @@ static int frr_sr_finish(void)
if (!module->sr_subscription)
continue;
sr_unsubscribe(module->sr_subscription);
- THREAD_OFF(module->sr_thread);
+ EVENT_OFF(module->sr_thread);
}
if (session)
@@ -720,7 +721,7 @@ static int frr_sr_finish(void)
return 0;
}
-static int frr_sr_module_config_loaded(struct thread_master *tm)
+static int frr_sr_module_config_loaded(struct event_loop *tm)
{
master = tm;
@@ -735,7 +736,7 @@ static int frr_sr_module_config_loaded(struct thread_master *tm)
return 0;
}
-static int frr_sr_module_late_init(struct thread_master *tm)
+static int frr_sr_module_late_init(struct event_loop *tm)
{
frr_sr_cli_init();
diff --git a/lib/prefix.c b/lib/prefix.c
index a6aae08a6a..b8cad910f4 100644
--- a/lib/prefix.c
+++ b/lib/prefix.c
@@ -1399,7 +1399,7 @@ bool ipv4_unicast_valid(const struct in_addr *addr)
if (IPV4_CLASS_D(ip))
return false;
- if (IPV4_CLASS_E(ip)) {
+ if (IPV4_NET0(ip) || IPV4_NET127(ip) || IPV4_CLASS_E(ip)) {
if (cmd_allow_reserved_ranges_get())
return true;
else
diff --git a/lib/prefix.h b/lib/prefix.h
index 9c57283706..88a228b55c 100644
--- a/lib/prefix.h
+++ b/lib/prefix.h
@@ -499,11 +499,8 @@ extern int macstr2prefix_evpn(const char *str, struct prefix_evpn *p);
/* NOTE: This routine expects the address argument in network byte order. */
static inline bool ipv4_martian(const struct in_addr *addr)
{
- in_addr_t ip = ntohl(addr->s_addr);
-
- if (IPV4_NET0(ip) || IPV4_NET127(ip) || !ipv4_unicast_valid(addr)) {
+ if (!ipv4_unicast_valid(addr))
return true;
- }
return false;
}
diff --git a/lib/pullwr.c b/lib/pullwr.c
index 5bc566c285..3967eb5875 100644
--- a/lib/pullwr.c
+++ b/lib/pullwr.c
@@ -16,9 +16,9 @@
struct pullwr {
int fd;
- struct thread_master *tm;
+ struct event_loop *tm;
/* writer == NULL <=> we're idle */
- struct thread *writer;
+ struct event *writer;
void *arg;
void (*fill)(void *, struct pullwr *);
@@ -38,12 +38,11 @@ struct pullwr {
DEFINE_MTYPE_STATIC(LIB, PULLWR_HEAD, "pull-driven write controller");
DEFINE_MTYPE_STATIC(LIB, PULLWR_BUF, "pull-driven write buffer");
-static void pullwr_run(struct thread *t);
+static void pullwr_run(struct event *t);
-struct pullwr *_pullwr_new(struct thread_master *tm, int fd,
- void *arg,
- void (*fill)(void *, struct pullwr *),
- void (*err)(void *, struct pullwr *, bool))
+struct pullwr *_pullwr_new(struct event_loop *tm, int fd, void *arg,
+ void (*fill)(void *, struct pullwr *),
+ void (*err)(void *, struct pullwr *, bool))
{
struct pullwr *pullwr;
@@ -62,7 +61,7 @@ struct pullwr *_pullwr_new(struct thread_master *tm, int fd,
void pullwr_del(struct pullwr *pullwr)
{
- THREAD_OFF(pullwr->writer);
+ EVENT_OFF(pullwr->writer);
XFREE(MTYPE_PULLWR_BUF, pullwr->buffer);
XFREE(MTYPE_PULLWR_HEAD, pullwr);
@@ -80,7 +79,7 @@ void pullwr_bump(struct pullwr *pullwr)
if (pullwr->writer)
return;
- thread_add_timer(pullwr->tm, pullwr_run, pullwr, 0, &pullwr->writer);
+ event_add_timer(pullwr->tm, pullwr_run, pullwr, 0, &pullwr->writer);
}
static size_t pullwr_iov(struct pullwr *pullwr, struct iovec *iov)
@@ -176,9 +175,9 @@ void pullwr_write(struct pullwr *pullwr, const void *data, size_t len)
pullwr_bump(pullwr);
}
-static void pullwr_run(struct thread *t)
+static void pullwr_run(struct event *t)
{
- struct pullwr *pullwr = THREAD_ARG(t);
+ struct pullwr *pullwr = EVENT_ARG(t);
struct iovec iov[2];
size_t niov, lastvalid;
ssize_t nwr;
@@ -206,7 +205,7 @@ static void pullwr_run(struct thread *t)
if (pullwr->valid == 0) {
/* we made a fill() call above that didn't feed any
* data in, and we have nothing more queued, so we go
- * into idle, i.e. no calling thread_add_write()
+ * into idle, i.e. no calling event_add_write()
*/
pullwr_resize(pullwr, 0);
return;
@@ -237,7 +236,7 @@ static void pullwr_run(struct thread *t)
* is full and we go wait until it's available for writing again.
*/
- thread_add_write(pullwr->tm, pullwr_run, pullwr, pullwr->fd,
+ event_add_write(pullwr->tm, pullwr_run, pullwr, pullwr->fd,
&pullwr->writer);
/* if we hit the time limit, just keep the buffer, we'll probably need
diff --git a/lib/pullwr.h b/lib/pullwr.h
index 77ecf855b4..ef2e01c04e 100644
--- a/lib/pullwr.h
+++ b/lib/pullwr.h
@@ -10,7 +10,7 @@
#include <stdbool.h>
#include <stdint.h>
-#include "thread.h"
+#include "frrevent.h"
#include "stream.h"
#ifdef __cplusplus
@@ -45,10 +45,10 @@ struct pullwr;
* and released with pullwr_del(). This can be done from inside the callback,
* the pullwr code holds no more references on it when calling err().
*/
-extern struct pullwr *_pullwr_new(struct thread_master *tm, int fd,
- void *arg,
- void (*fill)(void *, struct pullwr *),
- void (*err)(void *, struct pullwr *, bool eof));
+extern struct pullwr *_pullwr_new(struct event_loop *tm, int fd, void *arg,
+ void (*fill)(void *, struct pullwr *),
+ void (*err)(void *, struct pullwr *,
+ bool eof));
extern void pullwr_del(struct pullwr *pullwr);
/* type-checking wrapper. makes sure fill() and err() take a first argument
diff --git a/lib/qobj.c b/lib/qobj.c
index 09b156ba39..b9630e7547 100644
--- a/lib/qobj.c
+++ b/lib/qobj.c
@@ -7,7 +7,7 @@
#include <zebra.h>
-#include "thread.h"
+#include "frrevent.h"
#include "memory.h"
#include "hash.h"
#include "log.h"
diff --git a/lib/resolver.c b/lib/resolver.c
index 2918576c03..99bf356eb3 100644
--- a/lib/resolver.c
+++ b/lib/resolver.c
@@ -12,7 +12,7 @@
#include "typesafe.h"
#include "jhash.h"
-#include "thread.h"
+#include "frrevent.h"
#include "lib_errors.h"
#include "resolver.h"
#include "command.h"
@@ -23,8 +23,8 @@ XREF_SETUP();
struct resolver_state {
ares_channel channel;
- struct thread_master *master;
- struct thread *timeout;
+ struct event_loop *master;
+ struct event *timeout;
};
static struct resolver_state state;
@@ -47,7 +47,7 @@ struct resolver_fd {
int fd;
struct resolver_state *state;
- struct thread *t_read, *t_write;
+ struct event *t_read, *t_write;
};
static int resolver_fd_cmp(const struct resolver_fd *a,
@@ -100,38 +100,38 @@ static void resolver_fd_drop_maybe(struct resolver_fd *resfd)
static void resolver_update_timeouts(struct resolver_state *r);
-static void resolver_cb_timeout(struct thread *t)
+static void resolver_cb_timeout(struct event *t)
{
- struct resolver_state *r = THREAD_ARG(t);
+ struct resolver_state *r = EVENT_ARG(t);
ares_process(r->channel, NULL, NULL);
resolver_update_timeouts(r);
}
-static void resolver_cb_socket_readable(struct thread *t)
+static void resolver_cb_socket_readable(struct event *t)
{
- struct resolver_fd *resfd = THREAD_ARG(t);
+ struct resolver_fd *resfd = EVENT_ARG(t);
struct resolver_state *r = resfd->state;
- thread_add_read(r->master, resolver_cb_socket_readable, resfd,
- resfd->fd, &resfd->t_read);
+ event_add_read(r->master, resolver_cb_socket_readable, resfd, resfd->fd,
+ &resfd->t_read);
/* ^ ordering important:
- * ares_process_fd may transitively call THREAD_OFF(resfd->t_read)
+ * ares_process_fd may transitively call EVENT_OFF(resfd->t_read)
* combined with resolver_fd_drop_maybe, so resfd may be free'd after!
*/
ares_process_fd(r->channel, resfd->fd, ARES_SOCKET_BAD);
resolver_update_timeouts(r);
}
-static void resolver_cb_socket_writable(struct thread *t)
+static void resolver_cb_socket_writable(struct event *t)
{
- struct resolver_fd *resfd = THREAD_ARG(t);
+ struct resolver_fd *resfd = EVENT_ARG(t);
struct resolver_state *r = resfd->state;
- thread_add_write(r->master, resolver_cb_socket_writable, resfd,
- resfd->fd, &resfd->t_write);
+ event_add_write(r->master, resolver_cb_socket_writable, resfd,
+ resfd->fd, &resfd->t_write);
/* ^ ordering important:
- * ares_process_fd may transitively call THREAD_OFF(resfd->t_write)
+ * ares_process_fd may transitively call EVENT_OFF(resfd->t_write)
* combined with resolver_fd_drop_maybe, so resfd may be free'd after!
*/
ares_process_fd(r->channel, ARES_SOCKET_BAD, resfd->fd);
@@ -142,13 +142,13 @@ static void resolver_update_timeouts(struct resolver_state *r)
{
struct timeval *tv, tvbuf;
- THREAD_OFF(r->timeout);
+ EVENT_OFF(r->timeout);
tv = ares_timeout(r->channel, NULL, &tvbuf);
if (tv) {
unsigned int timeoutms = tv->tv_sec * 1000 + tv->tv_usec / 1000;
- thread_add_timer_msec(r->master, resolver_cb_timeout, r,
- timeoutms, &r->timeout);
+ event_add_timer_msec(r->master, resolver_cb_timeout, r,
+ timeoutms, &r->timeout);
}
}
@@ -165,16 +165,16 @@ static void ares_socket_cb(void *data, ares_socket_t fd, int readable,
assert(resfd->state == r);
if (!readable)
- THREAD_OFF(resfd->t_read);
+ EVENT_OFF(resfd->t_read);
else if (!resfd->t_read)
- thread_add_read(r->master, resolver_cb_socket_readable, resfd,
- fd, &resfd->t_read);
+ event_add_read(r->master, resolver_cb_socket_readable, resfd,
+ fd, &resfd->t_read);
if (!writable)
- THREAD_OFF(resfd->t_write);
+ EVENT_OFF(resfd->t_write);
else if (!resfd->t_write)
- thread_add_write(r->master, resolver_cb_socket_writable, resfd,
- fd, &resfd->t_write);
+ event_add_write(r->master, resolver_cb_socket_writable, resfd,
+ fd, &resfd->t_write);
resolver_fd_drop_maybe(resfd);
}
@@ -222,9 +222,9 @@ static void ares_address_cb(void *arg, int status, int timeouts,
callback(query, NULL, i, &addr[0]);
}
-static void resolver_cb_literal(struct thread *t)
+static void resolver_cb_literal(struct event *t)
{
- struct resolver_query *query = THREAD_ARG(t);
+ struct resolver_query *query = EVENT_ARG(t);
void (*callback)(struct resolver_query *, const char *, int,
union sockunion *);
@@ -264,8 +264,8 @@ void resolver_resolve(struct resolver_query *query, int af, vrf_id_t vrf_id,
/* for consistency with proper name lookup, don't call the
* callback immediately; defer to thread loop
*/
- thread_add_timer_msec(state.master, resolver_cb_literal,
- query, 0, &query->literal_cb);
+ event_add_timer_msec(state.master, resolver_cb_literal, query,
+ 0, &query->literal_cb);
return;
}
@@ -314,7 +314,7 @@ static int resolver_config_write_debug(struct vty *vty)
}
-void resolver_init(struct thread_master *tm)
+void resolver_init(struct event_loop *tm)
{
struct ares_options ares_opts;
diff --git a/lib/resolver.h b/lib/resolver.h
index d3f38f742d..87e8ecdc4a 100644
--- a/lib/resolver.h
+++ b/lib/resolver.h
@@ -6,7 +6,7 @@
#ifndef _FRR_RESOLVER_H
#define _FRR_RESOLVER_H
-#include "thread.h"
+#include "frrevent.h"
#include "sockunion.h"
#ifdef __cplusplus
@@ -19,10 +19,10 @@ struct resolver_query {
/* used to immediate provide the result if IP literal is passed in */
union sockunion literal_addr;
- struct thread *literal_cb;
+ struct event *literal_cb;
};
-void resolver_init(struct thread_master *tm);
+void resolver_init(struct event_loop *tm);
void resolver_resolve(struct resolver_query *query, int af, vrf_id_t vrf_id,
const char *hostname,
void (*cb)(struct resolver_query *, const char *, int,
diff --git a/lib/routemap.c b/lib/routemap.c
index 9f5c9e693e..20dcd2a53d 100644
--- a/lib/routemap.c
+++ b/lib/routemap.c
@@ -669,7 +669,7 @@ static struct route_map *route_map_add(const char *name)
if (!map->ipv6_prefix_table)
map->ipv6_prefix_table = route_table_init();
- if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))
+ if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)))
zlog_debug("Add route-map %s", name);
return map;
}
@@ -689,7 +689,7 @@ static void route_map_free_map(struct route_map *map)
while ((index = map->head) != NULL)
route_map_index_delete(index, 0);
- if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))
+ if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)))
zlog_debug("Deleting route-map %s", map->name);
list = &route_map_master;
@@ -706,6 +706,9 @@ static void route_map_free_map(struct route_map *map)
else
list->head = map->next;
+ route_table_finish(map->ipv4_prefix_table);
+ route_table_finish(map->ipv6_prefix_table);
+
hash_release(route_map_master_hash, map);
XFREE(MTYPE_ROUTE_MAP_NAME, map->name);
XFREE(MTYPE_ROUTE_MAP, map);
@@ -1120,7 +1123,7 @@ void route_map_index_delete(struct route_map_index *index, int notify)
QOBJ_UNREG(index);
- if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))
+ if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)))
zlog_debug("Deleting route-map %s sequence %d",
index->map->name, index->pref);
@@ -1231,7 +1234,7 @@ route_map_index_add(struct route_map *map, enum route_map_type type, int pref)
route_map_notify_dependencies(map->name, RMAP_EVENT_CALL_ADDED);
}
- if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))
+ if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)))
zlog_debug("Route-map %s add sequence %d, type: %s",
map->name, pref, route_map_type_str(type));
@@ -1811,10 +1814,8 @@ route_map_get_index(struct route_map *map, const struct prefix *prefix,
* must be AF_INET or AF_INET6 in order for the lookup to succeed. So if
* the AF doesn't line up with the LPM trees, skip the optimization.
*/
- if (map->optimization_disabled ||
- (prefix->family == AF_INET && !map->ipv4_prefix_table) ||
- (prefix->family == AF_INET6 && !map->ipv6_prefix_table)) {
- if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL))
+ if (map->optimization_disabled) {
+ if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL)))
zlog_debug(
"Skipping route-map optimization for route-map: %s, pfx: %pFX, family: %d",
map->name, prefix, prefix->family);
@@ -1826,9 +1827,6 @@ route_map_get_index(struct route_map *map, const struct prefix *prefix,
else
table = map->ipv6_prefix_table;
- if (!table)
- return NULL;
-
do {
candidate_rmap_list =
route_map_get_index_list(&rn, prefix, table);
@@ -1914,19 +1912,10 @@ static void route_map_pfx_table_add_default(afi_t afi,
p.family = afi2family(afi);
p.prefixlen = 0;
- if (p.family == AF_INET) {
- table = index->map->ipv4_prefix_table;
- if (!table)
- index->map->ipv4_prefix_table = route_table_init();
-
+ if (p.family == AF_INET)
table = index->map->ipv4_prefix_table;
- } else {
+ else
table = index->map->ipv6_prefix_table;
- if (!table)
- index->map->ipv6_prefix_table = route_table_init();
-
- table = index->map->ipv6_prefix_table;
- }
/* Add default route to table */
rn = route_node_get(table, &p);
@@ -2317,8 +2306,6 @@ static void route_map_pfx_tbl_update(route_map_event_t event,
struct route_map_index *index, afi_t afi,
const char *plist_name)
{
- struct route_map *rmap = NULL;
-
if (!index)
return;
@@ -2332,19 +2319,6 @@ static void route_map_pfx_tbl_update(route_map_event_t event,
route_map_pfx_table_del_default(AFI_IP, index);
route_map_pfx_table_del_default(AFI_IP6, index);
- if ((index->map->head == NULL) && (index->map->tail == NULL)) {
- rmap = index->map;
-
- if (rmap->ipv4_prefix_table) {
- route_table_finish(rmap->ipv4_prefix_table);
- rmap->ipv4_prefix_table = NULL;
- }
-
- if (rmap->ipv6_prefix_table) {
- route_table_finish(rmap->ipv6_prefix_table);
- rmap->ipv6_prefix_table = NULL;
- }
- }
return;
}
@@ -2569,12 +2543,14 @@ route_map_result_t route_map_apply_ext(struct route_map *map,
*/
if (prefix->family == AF_EVPN) {
if (evpn_prefix2prefix(prefix, &conv) != 0) {
- if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL))
+ if (unlikely(CHECK_FLAG(rmap_debug,
+ DEBUG_ROUTEMAP_DETAIL)))
zlog_debug(
"Unable to convert EVPN prefix %pFX into IPv4/IPv6 prefix. Falling back to non-optimized route-map lookup",
prefix);
} else {
- if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL))
+ if (unlikely(CHECK_FLAG(rmap_debug,
+ DEBUG_ROUTEMAP_DETAIL)))
zlog_debug(
"Converted EVPN prefix %pFX into %pFX for optimized route-map lookup",
prefix, &conv);
@@ -2586,13 +2562,13 @@ route_map_result_t route_map_apply_ext(struct route_map *map,
index = route_map_get_index(map, prefix, match_object, &match_ret);
if (index) {
index->applied++;
- if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))
+ if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)))
zlog_debug(
"Best match route-map: %s, sequence: %d for pfx: %pFX, result: %s",
map->name, index->pref, prefix,
route_map_cmd_result_str(match_ret));
} else {
- if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))
+ if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)))
zlog_debug(
"No best match sequence for pfx: %pFX in route-map: %s, result: %s",
prefix, map->name,
@@ -2615,7 +2591,7 @@ route_map_result_t route_map_apply_ext(struct route_map *map,
/* Apply this index. */
match_ret = route_map_apply_match(&index->match_list,
prefix, match_object);
- if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)) {
+ if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))) {
zlog_debug(
"Route-map: %s, sequence: %d, prefix: %pFX, result: %s",
map->name, index->pref, prefix,
@@ -2728,7 +2704,7 @@ route_map_result_t route_map_apply_ext(struct route_map *map,
}
route_map_apply_end:
- if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))
+ if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)))
zlog_debug("Route-map: %s, prefix: %pFX, result: %s",
(map ? map->name : "null"), prefix,
route_map_result_str(ret));
@@ -2783,7 +2759,7 @@ static void route_map_clear_reference(struct hash_bucket *bucket, void *arg)
tmp_dep_data.rname = arg;
dep_data = hash_release(dep->dep_rmap_hash, &tmp_dep_data);
if (dep_data) {
- if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))
+ if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)))
zlog_debug("Clearing reference for %s to %s count: %d",
dep->dep_name, tmp_dep_data.rname,
dep_data->refcnt);
@@ -2803,7 +2779,7 @@ static void route_map_clear_all_references(char *rmap_name)
{
int i;
- if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))
+ if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)))
zlog_debug("Clearing references for %s", rmap_name);
for (i = 1; i < ROUTE_MAP_DEP_MAX; i++) {
@@ -2879,7 +2855,7 @@ static int route_map_dep_update(struct hash *dephash, const char *dep_name,
case RMAP_EVENT_LLIST_ADDED:
case RMAP_EVENT_CALL_ADDED:
case RMAP_EVENT_FILTER_ADDED:
- if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))
+ if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)))
zlog_debug("Adding dependency for filter %s in route-map %s",
dep_name, rmap_name);
dep = (struct route_map_dep *)hash_get(
@@ -2908,7 +2884,7 @@ static int route_map_dep_update(struct hash *dephash, const char *dep_name,
case RMAP_EVENT_LLIST_DELETED:
case RMAP_EVENT_CALL_DELETED:
case RMAP_EVENT_FILTER_DELETED:
- if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))
+ if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)))
zlog_debug("Deleting dependency for filter %s in route-map %s",
dep_name, rmap_name);
dep = (struct route_map_dep *)hash_get(dephash, dname, NULL);
@@ -3034,7 +3010,7 @@ static void route_map_process_dependency(struct hash_bucket *bucket, void *data)
dep_data = bucket->data;
rmap_name = dep_data->rname;
- if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))
+ if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)))
zlog_debug("Notifying %s of dependency", rmap_name);
if (route_map_master.event_hook)
(*route_map_master.event_hook)(rmap_name);
@@ -3082,7 +3058,7 @@ void route_map_notify_dependencies(const char *affected_name,
if (!dep->this_hash)
dep->this_hash = upd8_hash;
- if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))
+ if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)))
zlog_debug("Filter %s updated", dep->dep_name);
hash_iterate(dep->dep_rmap_hash, route_map_process_dependency,
(void *)event);
@@ -3101,27 +3077,24 @@ static void clear_route_map_helper(struct route_map *map)
index->applied_clear = index->applied;
}
-DEFUN (rmap_clear_counters,
+DEFPY (rmap_clear_counters,
rmap_clear_counters_cmd,
- "clear route-map counters [WORD]",
+ "clear route-map counters [RMAP_NAME$rmapname]",
CLEAR_STR
"route-map information\n"
"counters associated with the specified route-map\n"
"route-map name\n")
{
- int idx_word = 2;
struct route_map *map;
- const char *name = (argc == 3 ) ? argv[idx_word]->arg : NULL;
-
- if (name) {
- map = route_map_lookup_by_name(name);
+ if (rmapname) {
+ map = route_map_lookup_by_name(rmapname);
if (map)
clear_route_map_helper(map);
else {
vty_out(vty, "%s: 'route-map %s' not found\n",
- frr_protonameinst, name);
+ frr_protonameinst, rmapname);
return CMD_SUCCESS;
}
} else {
diff --git a/lib/sigevent.c b/lib/sigevent.c
index 3ed34894e5..3cd65eb800 100644
--- a/lib/sigevent.c
+++ b/lib/sigevent.c
@@ -22,7 +22,7 @@
/* master signals descriptor struct */
static struct frr_sigevent_master_t {
- struct thread *t;
+ struct event *t;
struct frr_signal_t *signals;
int sigc;
@@ -127,14 +127,14 @@ int frr_sigevent_process(void)
#ifdef SIGEVENT_SCHEDULE_THREAD
/* timer thread to check signals. shouldn't be needed */
-void frr_signal_timer(struct thread *t)
+void frr_signal_timer(struct event *t)
{
struct frr_sigevent_master_t *sigm;
- sigm = THREAD_ARG(t);
+ sigm = EVENT_ARG(t);
sigm->t = NULL;
- thread_add_timer(sigm->t->master, frr_signal_timer, &sigmaster,
- FRR_SIGNAL_TIMER_INTERVAL, &sigm->t);
+ event_add_timer(sigm->t->master, frr_signal_timer, &sigmaster,
+ FRR_SIGNAL_TIMER_INTERVAL, &sigm->t);
frr_sigevent_process();
}
#endif /* SIGEVENT_SCHEDULE_THREAD */
@@ -331,8 +331,7 @@ static void trap_default_signals(void)
}
}
-void signal_init(struct thread_master *m, int sigc,
- struct frr_signal_t signals[])
+void signal_init(struct event_loop *m, int sigc, struct frr_signal_t signals[])
{
int i = 0;
@@ -354,7 +353,7 @@ void signal_init(struct thread_master *m, int sigc,
#ifdef SIGEVENT_SCHEDULE_THREAD
sigmaster.t = NULL;
- thread_add_timer(m, frr_signal_timer, &sigmaster,
- FRR_SIGNAL_TIMER_INTERVAL, &sigmaster.t);
+ event_add_timer(m, frr_signal_timer, &sigmaster,
+ FRR_SIGNAL_TIMER_INTERVAL, &sigmaster.t);
#endif /* SIGEVENT_SCHEDULE_THREAD */
}
diff --git a/lib/sigevent.h b/lib/sigevent.h
index e58b9a70c0..0b07f594c1 100644
--- a/lib/sigevent.h
+++ b/lib/sigevent.h
@@ -8,7 +8,7 @@
#ifndef _FRR_SIGNAL_H
#define _FRR_SIGNAL_H
-#include <thread.h>
+#include <frrevent.h>
#ifdef __cplusplus
extern "C" {
@@ -25,12 +25,12 @@ struct frr_signal_t {
/* initialise sigevent system
* takes:
- * - pointer to valid struct thread_master
+ * - pointer to valid struct event_loop
* - number of elements in passed in signals array
* - array of frr_signal_t's describing signals to handle
* and handlers to use for each signal
*/
-extern void signal_init(struct thread_master *m, int sigc,
+extern void signal_init(struct event_loop *m, int sigc,
struct frr_signal_t *signals);
diff --git a/lib/smux.h b/lib/smux.h
index 28a303cf72..cec4d2a1bf 100644
--- a/lib/smux.h
+++ b/lib/smux.h
@@ -9,7 +9,7 @@
#include <net-snmp/agent/net-snmp-agent-includes.h>
#include <net-snmp/agent/snmp_vars.h>
-#include "thread.h"
+#include "frrevent.h"
#include "hook.h"
#ifdef __cplusplus
@@ -99,7 +99,7 @@ struct index_oid {
*/
extern bool smux_enabled(void);
-extern void smux_init(struct thread_master *tm);
+extern void smux_init(struct event_loop *tm);
extern void smux_agentx_enable(void);
extern void smux_register_mib(const char *, struct variable *, size_t, int,
oid[], size_t);
diff --git a/lib/spf_backoff.c b/lib/spf_backoff.c
index 1e80b5ec2d..b05c44ddf7 100644
--- a/lib/spf_backoff.c
+++ b/lib/spf_backoff.c
@@ -17,7 +17,7 @@
#include "command.h"
#include "memory.h"
-#include "thread.h"
+#include "frrevent.h"
#include "vty.h"
DEFINE_MTYPE_STATIC(LIB, SPF_BACKOFF, "SPF backoff");
@@ -37,7 +37,7 @@ enum spf_backoff_state {
};
struct spf_backoff {
- struct thread_master *m;
+ struct event_loop *m;
/* Timers as per draft */
long init_delay;
@@ -48,8 +48,8 @@ struct spf_backoff {
/* State machine */
enum spf_backoff_state state;
- struct thread *t_holddown;
- struct thread *t_timetolearn;
+ struct event *t_holddown;
+ struct event *t_timetolearn;
/* For debugging */
char *name;
@@ -70,7 +70,7 @@ static const char *spf_backoff_state2str(enum spf_backoff_state state)
return "???";
}
-struct spf_backoff *spf_backoff_new(struct thread_master *m, const char *name,
+struct spf_backoff *spf_backoff_new(struct event_loop *m, const char *name,
long init_delay, long short_delay,
long long_delay, long holddown,
long timetolearn)
@@ -97,27 +97,27 @@ void spf_backoff_free(struct spf_backoff *backoff)
if (!backoff)
return;
- thread_cancel(&backoff->t_holddown);
- thread_cancel(&backoff->t_timetolearn);
+ event_cancel(&backoff->t_holddown);
+ event_cancel(&backoff->t_timetolearn);
XFREE(MTYPE_SPF_BACKOFF_NAME, backoff->name);
XFREE(MTYPE_SPF_BACKOFF, backoff);
}
-static void spf_backoff_timetolearn_elapsed(struct thread *thread)
+static void spf_backoff_timetolearn_elapsed(struct event *thread)
{
- struct spf_backoff *backoff = THREAD_ARG(thread);
+ struct spf_backoff *backoff = EVENT_ARG(thread);
backoff->state = SPF_BACKOFF_LONG_WAIT;
backoff_debug("SPF Back-off(%s) TIMETOLEARN elapsed, move to state %s",
backoff->name, spf_backoff_state2str(backoff->state));
}
-static void spf_backoff_holddown_elapsed(struct thread *thread)
+static void spf_backoff_holddown_elapsed(struct event *thread)
{
- struct spf_backoff *backoff = THREAD_ARG(thread);
+ struct spf_backoff *backoff = EVENT_ARG(thread);
- THREAD_OFF(backoff->t_timetolearn);
+ EVENT_OFF(backoff->t_timetolearn);
timerclear(&backoff->first_event_time);
backoff->state = SPF_BACKOFF_QUIET;
backoff_debug("SPF Back-off(%s) HOLDDOWN elapsed, move to state %s",
@@ -139,21 +139,21 @@ long spf_backoff_schedule(struct spf_backoff *backoff)
switch (backoff->state) {
case SPF_BACKOFF_QUIET:
backoff->state = SPF_BACKOFF_SHORT_WAIT;
- thread_add_timer_msec(
+ event_add_timer_msec(
backoff->m, spf_backoff_timetolearn_elapsed, backoff,
backoff->timetolearn, &backoff->t_timetolearn);
- thread_add_timer_msec(backoff->m, spf_backoff_holddown_elapsed,
- backoff, backoff->holddown,
- &backoff->t_holddown);
+ event_add_timer_msec(backoff->m, spf_backoff_holddown_elapsed,
+ backoff, backoff->holddown,
+ &backoff->t_holddown);
backoff->first_event_time = now;
rv = backoff->init_delay;
break;
case SPF_BACKOFF_SHORT_WAIT:
case SPF_BACKOFF_LONG_WAIT:
- thread_cancel(&backoff->t_holddown);
- thread_add_timer_msec(backoff->m, spf_backoff_holddown_elapsed,
- backoff, backoff->holddown,
- &backoff->t_holddown);
+ event_cancel(&backoff->t_holddown);
+ event_add_timer_msec(backoff->m, spf_backoff_holddown_elapsed,
+ backoff, backoff->holddown,
+ &backoff->t_holddown);
if (backoff->state == SPF_BACKOFF_SHORT_WAIT)
rv = backoff->short_delay;
else
@@ -204,8 +204,8 @@ void spf_backoff_show(struct spf_backoff *backoff, struct vty *vty,
vty_out(vty, "%sHolddown timer: %ld msec\n", prefix,
backoff->holddown);
if (backoff->t_holddown) {
- struct timeval remain =
- thread_timer_remain(backoff->t_holddown);
+ struct timeval remain = event_timer_remain(backoff->t_holddown);
+
vty_out(vty, "%s Still runs for %lld msec\n",
prefix,
(long long)remain.tv_sec * 1000
@@ -218,7 +218,7 @@ void spf_backoff_show(struct spf_backoff *backoff, struct vty *vty,
backoff->timetolearn);
if (backoff->t_timetolearn) {
struct timeval remain =
- thread_timer_remain(backoff->t_timetolearn);
+ event_timer_remain(backoff->t_timetolearn);
vty_out(vty, "%s Still runs for %lld msec\n",
prefix,
(long long)remain.tv_sec * 1000
diff --git a/lib/spf_backoff.h b/lib/spf_backoff.h
index 87aa4a0825..83f1b76adc 100644
--- a/lib/spf_backoff.h
+++ b/lib/spf_backoff.h
@@ -18,10 +18,10 @@ extern "C" {
#endif
struct spf_backoff;
-struct thread_master;
+struct event_loop;
struct vty;
-struct spf_backoff *spf_backoff_new(struct thread_master *m, const char *name,
+struct spf_backoff *spf_backoff_new(struct event_loop *m, const char *name,
long init_delay, long short_delay,
long long_delay, long holddown,
long timetolearn);
diff --git a/lib/subdir.am b/lib/subdir.am
index beef8675aa..83eecbee57 100644
--- a/lib/subdir.am
+++ b/lib/subdir.am
@@ -38,8 +38,6 @@ lib_libfrr_la_SOURCES = \
lib/frrscript.c \
lib/frr_pthread.c \
lib/frrstr.c \
- lib/getopt.c \
- lib/getopt1.c \
lib/grammar_sandbox.c \
lib/graph.c \
lib/hash.c \
@@ -49,6 +47,7 @@ lib_libfrr_la_SOURCES = \
lib/if_rmap.c \
lib/imsg-buffer.c \
lib/imsg.c \
+ lib/iso.c \
lib/jhash.c \
lib/json.c \
lib/keychain.c \
@@ -64,6 +63,9 @@ lib_libfrr_la_SOURCES = \
lib/log_vty.c \
lib/md5.c \
lib/memory.c \
+ lib/mgmt_be_client.c \
+ lib/mgmt_fe_client.c \
+ lib/mgmt_msg.c \
lib/mlag.c \
lib/module.c \
lib/mpls.c \
@@ -105,7 +107,7 @@ lib_libfrr_la_SOURCES = \
lib/systemd.c \
lib/table.c \
lib/termtable.c \
- lib/thread.c \
+ lib/event.c \
lib/typerb.c \
lib/typesafe.c \
lib/vector.c \
@@ -146,6 +148,23 @@ nodist_lib_libfrr_la_SOURCES = \
yang/frr-module-translator.yang.c \
# end
+# Add logic to build mgmt.proto
+lib_libfrr_la_LIBADD += $(PROTOBUF_C_LIBS)
+
+BUILT_SOURCES += \
+ lib/mgmt.pb-c.c \
+ lib/mgmt.pb-c.h \
+ # end
+
+CLEANFILES += \
+ lib/mgmt.pb-c.h \
+ lib/mgmt.pb-c.c \
+ # end
+
+lib_libfrr_la_SOURCES += \
+ lib/mgmt.pb-c.c \
+ #end
+
if SQLITE3
lib_libfrr_la_LIBADD += $(SQLITE3_LIBS)
lib_libfrr_la_SOURCES += lib/db.c
@@ -161,7 +180,7 @@ clippy_scan += \
lib/plist.c \
lib/routemap.c \
lib/routemap_cli.c \
- lib/thread.c \
+ lib/event.c \
lib/vty.c \
lib/zlog_5424_cli.c \
# end
@@ -197,7 +216,6 @@ pkginclude_HEADERS += \
lib/frratomic.h \
lib/frrcu.h \
lib/frrstr.h \
- lib/getopt.h \
lib/graph.h \
lib/hash.h \
lib/hook.h \
@@ -207,6 +225,7 @@ pkginclude_HEADERS += \
lib/if_rmap.h \
lib/imsg.h \
lib/ipaddr.h \
+ lib/iso.h \
lib/jhash.h \
lib/json.h \
lib/keychain.h \
@@ -222,6 +241,11 @@ pkginclude_HEADERS += \
lib/log_vty.h \
lib/md5.h \
lib/memory.h \
+ lib/mgmt.pb-c.h \
+ lib/mgmt_be_client.h \
+ lib/mgmt_fe_client.h \
+ lib/mgmt_msg.h \
+ lib/mgmt_pb.h \
lib/module.h \
lib/monotime.h \
lib/mpls.h \
@@ -263,7 +287,7 @@ pkginclude_HEADERS += \
lib/systemd.h \
lib/table.h \
lib/termtable.h \
- lib/thread.h \
+ lib/frrevent.h \
lib/trace.h \
lib/typerb.h \
lib/typesafe.h \
diff --git a/lib/systemd.c b/lib/systemd.c
index 0106e88b93..56a53a6e78 100644
--- a/lib/systemd.c
+++ b/lib/systemd.c
@@ -7,7 +7,7 @@
#include <zebra.h>
#include <sys/un.h>
-#include "thread.h"
+#include "frrevent.h"
#include "systemd.h"
#include "lib_errors.h"
@@ -63,18 +63,18 @@ void systemd_send_stopping(void)
systemd_send_information("STOPPING=1");
}
-static struct thread_master *systemd_master = NULL;
+static struct event_loop *systemd_master = NULL;
-static void systemd_send_watchdog(struct thread *t)
+static void systemd_send_watchdog(struct event *t)
{
systemd_send_information("WATCHDOG=1");
assert(watchdog_msec > 0);
- thread_add_timer_msec(systemd_master, systemd_send_watchdog, NULL,
- watchdog_msec, NULL);
+ event_add_timer_msec(systemd_master, systemd_send_watchdog, NULL,
+ watchdog_msec, NULL);
}
-void systemd_send_started(struct thread_master *m)
+void systemd_send_started(struct event_loop *m)
{
assert(m != NULL);
diff --git a/lib/systemd.h b/lib/systemd.h
index 09f27a70ba..ba2461b8a1 100644
--- a/lib/systemd.h
+++ b/lib/systemd.h
@@ -21,11 +21,11 @@ extern bool sd_stderr_is_journal;
void systemd_send_stopping(void);
/*
- * master - The struct thread_master * to use to schedule ourself
+ * master - The struct event_loop * to use to schedule ourself
* the_process - Should we send watchdog if we are not the requested
* process?
*/
-void systemd_send_started(struct thread_master *master);
+void systemd_send_started(struct event_loop *master);
/*
* status - A status string to send to systemd
diff --git a/lib/thread.h b/lib/thread.h
deleted file mode 100644
index 128d11b6eb..0000000000
--- a/lib/thread.h
+++ /dev/null
@@ -1,291 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/* Thread management routine header.
- * Copyright (C) 1998 Kunihiro Ishiguro
- */
-
-#ifndef _ZEBRA_THREAD_H
-#define _ZEBRA_THREAD_H
-
-#include <zebra.h>
-#include <pthread.h>
-#include <poll.h>
-#include "monotime.h"
-#include "frratomic.h"
-#include "typesafe.h"
-#include "xref.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-extern bool cputime_enabled;
-extern unsigned long cputime_threshold;
-/* capturing wallclock time is always enabled since it is fast (reading
- * hardware TSC w/o syscalls)
- */
-extern unsigned long walltime_threshold;
-
-struct rusage_t {
-#ifdef HAVE_CLOCK_THREAD_CPUTIME_ID
- struct timespec cpu;
-#else
- struct rusage cpu;
-#endif
- struct timeval real;
-};
-#define RUSAGE_T struct rusage_t
-
-#define GETRUSAGE(X) thread_getrusage(X)
-
-PREDECL_LIST(thread_list);
-PREDECL_HEAP(thread_timer_list);
-
-struct fd_handler {
- /* number of pfd that fit in the allocated space of pfds. This is a
- * constant and is the same for both pfds and copy.
- */
- nfds_t pfdsize;
-
- /* file descriptors to monitor for i/o */
- struct pollfd *pfds;
- /* number of pollfds stored in pfds */
- nfds_t pfdcount;
-
- /* chunk used for temp copy of pollfds */
- struct pollfd *copy;
- /* number of pollfds stored in copy */
- nfds_t copycount;
-};
-
-struct xref_threadsched {
- struct xref xref;
-
- const char *funcname;
- const char *dest;
- uint32_t thread_type;
-};
-
-/* Master of the theads. */
-struct thread_master {
- char *name;
-
- struct thread **read;
- struct thread **write;
- struct thread_timer_list_head timer;
- struct thread_list_head event, ready, unuse;
- struct list *cancel_req;
- bool canceled;
- pthread_cond_t cancel_cond;
- struct hash *cpu_record;
- int io_pipe[2];
- int fd_limit;
- struct fd_handler handler;
- unsigned long alloc;
- long selectpoll_timeout;
- bool spin;
- bool handle_signals;
- pthread_mutex_t mtx;
- pthread_t owner;
-
- bool ready_run_loop;
- RUSAGE_T last_getrusage;
-};
-
-/* Thread itself. */
-struct thread {
- uint8_t type; /* thread type */
- uint8_t add_type; /* thread type */
- struct thread_list_item threaditem;
- struct thread_timer_list_item timeritem;
- struct thread **ref; /* external reference (if given) */
- struct thread_master *master; /* pointer to the struct thread_master */
- void (*func)(struct thread *); /* event function */
- void *arg; /* event argument */
- union {
- int val; /* second argument of the event. */
- int fd; /* file descriptor in case of r/w */
- struct timeval sands; /* rest of time sands value. */
- } u;
- struct timeval real;
- struct cpu_thread_history *hist; /* cache pointer to cpu_history */
- unsigned long yield; /* yield time in microseconds */
- const struct xref_threadsched *xref; /* origin location */
- pthread_mutex_t mtx; /* mutex for thread.c functions */
- bool ignore_timer_late;
-};
-
-#ifdef _FRR_ATTRIBUTE_PRINTFRR
-#pragma FRR printfrr_ext "%pTH" (struct thread *)
-#endif
-
-struct cpu_thread_history {
- void (*func)(struct thread *);
- atomic_size_t total_cpu_warn;
- atomic_size_t total_wall_warn;
- atomic_size_t total_starv_warn;
- atomic_size_t total_calls;
- atomic_size_t total_active;
- struct time_stats {
- atomic_size_t total, max;
- } real;
- struct time_stats cpu;
- atomic_uint_fast32_t types;
- const char *funcname;
-};
-
-/* Struct timeval's tv_usec one second value. */
-#define TIMER_SECOND_MICRO 1000000L
-
-/* Thread types. */
-#define THREAD_READ 0
-#define THREAD_WRITE 1
-#define THREAD_TIMER 2
-#define THREAD_EVENT 3
-#define THREAD_READY 4
-#define THREAD_UNUSED 5
-#define THREAD_EXECUTE 6
-
-/* Thread yield time. */
-#define THREAD_YIELD_TIME_SLOT 10 * 1000L /* 10ms */
-
-#define THREAD_TIMER_STRLEN 12
-
-/* Macros. */
-#define THREAD_ARG(X) ((X)->arg)
-#define THREAD_FD(X) ((X)->u.fd)
-#define THREAD_VAL(X) ((X)->u.val)
-
-/*
- * Please consider this macro deprecated, and do not use it in new code.
- */
-#define THREAD_OFF(thread) \
- do { \
- if ((thread)) \
- thread_cancel(&(thread)); \
- } while (0)
-
-/*
- * Macro wrappers to generate xrefs for all thread add calls. Includes
- * file/line/function info for debugging/tracing.
- */
-#include "lib/xref.h"
-
-#define _xref_t_a(addfn, type, m, f, a, v, t) \
- ({ \
- static const struct xref_threadsched _xref \
- __attribute__((used)) = { \
- .xref = XREF_INIT(XREFT_THREADSCHED, NULL, __func__), \
- .funcname = #f, \
- .dest = #t, \
- .thread_type = THREAD_ ## type, \
- }; \
- XREF_LINK(_xref.xref); \
- _thread_add_ ## addfn(&_xref, m, f, a, v, t); \
- }) \
- /* end */
-
-#define thread_add_read(m,f,a,v,t) _xref_t_a(read_write, READ, m,f,a,v,t)
-#define thread_add_write(m,f,a,v,t) _xref_t_a(read_write, WRITE, m,f,a,v,t)
-#define thread_add_timer(m,f,a,v,t) _xref_t_a(timer, TIMER, m,f,a,v,t)
-#define thread_add_timer_msec(m,f,a,v,t) _xref_t_a(timer_msec, TIMER, m,f,a,v,t)
-#define thread_add_timer_tv(m,f,a,v,t) _xref_t_a(timer_tv, TIMER, m,f,a,v,t)
-#define thread_add_event(m,f,a,v,t) _xref_t_a(event, EVENT, m,f,a,v,t)
-
-#define thread_execute(m,f,a,v) \
- ({ \
- static const struct xref_threadsched _xref \
- __attribute__((used)) = { \
- .xref = XREF_INIT(XREFT_THREADSCHED, NULL, __func__), \
- .funcname = #f, \
- .dest = NULL, \
- .thread_type = THREAD_EXECUTE, \
- }; \
- XREF_LINK(_xref.xref); \
- _thread_execute(&_xref, m, f, a, v); \
- }) /* end */
-
-/* Prototypes. */
-extern struct thread_master *thread_master_create(const char *);
-void thread_master_set_name(struct thread_master *master, const char *name);
-extern void thread_master_free(struct thread_master *);
-extern void thread_master_free_unused(struct thread_master *);
-
-extern void _thread_add_read_write(const struct xref_threadsched *xref,
- struct thread_master *master,
- void (*fn)(struct thread *), void *arg,
- int fd, struct thread **tref);
-
-extern void _thread_add_timer(const struct xref_threadsched *xref,
- struct thread_master *master,
- void (*fn)(struct thread *), void *arg, long t,
- struct thread **tref);
-
-extern void _thread_add_timer_msec(const struct xref_threadsched *xref,
- struct thread_master *master,
- void (*fn)(struct thread *), void *arg,
- long t, struct thread **tref);
-
-extern void _thread_add_timer_tv(const struct xref_threadsched *xref,
- struct thread_master *master,
- void (*fn)(struct thread *), void *arg,
- struct timeval *tv, struct thread **tref);
-
-extern void _thread_add_event(const struct xref_threadsched *xref,
- struct thread_master *master,
- void (*fn)(struct thread *), void *arg, int val,
- struct thread **tref);
-
-extern void _thread_execute(const struct xref_threadsched *xref,
- struct thread_master *master,
- void (*fn)(struct thread *), void *arg, int val);
-
-extern void thread_cancel(struct thread **event);
-extern void thread_cancel_async(struct thread_master *, struct thread **,
- void *);
-/* Cancel ready tasks with an arg matching 'arg' */
-extern void thread_cancel_event_ready(struct thread_master *m, void *arg);
-/* Cancel all tasks with an arg matching 'arg', including timers and io */
-extern void thread_cancel_event(struct thread_master *m, void *arg);
-extern struct thread *thread_fetch(struct thread_master *, struct thread *);
-extern void thread_call(struct thread *);
-extern unsigned long thread_timer_remain_second(struct thread *);
-extern struct timeval thread_timer_remain(struct thread *);
-extern unsigned long thread_timer_remain_msec(struct thread *);
-extern int thread_should_yield(struct thread *);
-/* set yield time for thread */
-extern void thread_set_yield_time(struct thread *, unsigned long);
-
-/* Internal libfrr exports */
-extern void thread_getrusage(RUSAGE_T *);
-extern void thread_cmd_init(void);
-
-/* Returns elapsed real (wall clock) time. */
-extern unsigned long thread_consumed_time(RUSAGE_T *after, RUSAGE_T *before,
- unsigned long *cpu_time_elapsed);
-
-/* only for use in logging functions! */
-extern pthread_key_t thread_current;
-extern char *thread_timer_to_hhmmss(char *buf, int buf_size,
- struct thread *t_timer);
-
-static inline bool thread_is_scheduled(struct thread *thread)
-{
- if (thread)
- return true;
-
- return false;
-}
-
-/* Debug signal mask */
-void debug_signals(const sigset_t *sigs);
-
-static inline void thread_ignore_late_timer(struct thread *thread)
-{
- thread->ignore_timer_late = true;
-}
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _ZEBRA_THREAD_H */
diff --git a/lib/vrf.c b/lib/vrf.c
index a60f532f41..73f5d8ff72 100644
--- a/lib/vrf.c
+++ b/lib/vrf.c
@@ -394,11 +394,7 @@ void vrf_bitmap_free(vrf_bitmap_t bmap)
{
struct hash *vrf_hash = bmap;
- if (vrf_hash == NULL)
- return;
-
- hash_clean(vrf_hash, vrf_hash_bitmap_free);
- hash_free(vrf_hash);
+ hash_clean_and_free(&vrf_hash, vrf_hash_bitmap_free);
}
void vrf_bitmap_set(vrf_bitmap_t bmap, vrf_id_t vrf_id)
diff --git a/lib/vty.c b/lib/vty.c
index 786271abe8..974f5380d3 100644
--- a/lib/vty.c
+++ b/lib/vty.c
@@ -22,7 +22,7 @@
#include <stdio.h>
#include "linklist.h"
-#include "thread.h"
+#include "frrevent.h"
#include "buffer.h"
#include "command.h"
#include "sockunion.h"
@@ -65,6 +65,14 @@ enum vty_event {
#endif /* VTYSH */
};
+struct nb_config *vty_mgmt_candidate_config;
+
+static uintptr_t mgmt_lib_hndl;
+static bool mgmt_fe_connected;
+static bool mgmt_candidate_ds_wr_locked;
+static uint64_t mgmt_client_id_next;
+static uint64_t mgmt_last_req_id = UINT64_MAX;
+
PREDECL_DLIST(vtyservs);
struct vty_serv {
@@ -73,13 +81,14 @@ struct vty_serv {
int sock;
bool vtysh;
- struct thread *t_accept;
+ struct event *t_accept;
};
DECLARE_DLIST(vtyservs, struct vty_serv, itm);
static void vty_event_serv(enum vty_event event, struct vty_serv *);
static void vty_event(enum vty_event, struct vty *);
+static int vtysh_flush(struct vty *vty);
/* Extern host structure from command.c */
extern struct host host;
@@ -112,6 +121,36 @@ static char integrate_default[] = SYSCONFDIR INTEGRATE_DEFAULT_CONFIG;
static bool do_log_commands;
static bool do_log_commands_perm;
+void vty_mgmt_resume_response(struct vty *vty, bool success)
+{
+ uint8_t header[4] = {0, 0, 0, 0};
+ int ret = CMD_SUCCESS;
+
+ if (!vty->mgmt_req_pending) {
+ zlog_err(
+ "vty response called without setting mgmt_req_pending");
+ return;
+ }
+
+ if (!success)
+ ret = CMD_WARNING_CONFIG_FAILED;
+
+ vty->mgmt_req_pending = false;
+ header[3] = ret;
+ buffer_put(vty->obuf, header, 4);
+
+ if (!vty->t_write && (vtysh_flush(vty) < 0))
+ /* Try to flush results; exit if a write
+ * error occurs.
+ */
+ return;
+
+ if (vty->status == VTY_CLOSE)
+ vty_close(vty);
+ else
+ vty_event(VTYSH_READ, vty);
+}
+
void vty_frame(struct vty *vty, const char *format, ...)
{
va_list args;
@@ -506,7 +545,7 @@ static int vty_command(struct vty *vty, char *buf)
GETRUSAGE(&after);
- walltime = thread_consumed_time(&after, &before, &cputime);
+ walltime = event_consumed_time(&after, &before, &cputime);
if (cputime_enabled_here && cputime_enabled && cputime_threshold
&& cputime > cputime_threshold)
@@ -1321,13 +1360,13 @@ static void vty_buffer_reset(struct vty *vty)
}
/* Read data via vty socket. */
-static void vty_read(struct thread *thread)
+static void vty_read(struct event *thread)
{
int i;
int nbytes;
unsigned char buf[VTY_READ_BUFSIZ];
- struct vty *vty = THREAD_ARG(thread);
+ struct vty *vty = EVENT_ARG(thread);
/* Read raw data from socket */
if ((nbytes = read(vty->fd, buf, VTY_READ_BUFSIZ)) <= 0) {
@@ -1524,15 +1563,15 @@ static void vty_read(struct thread *thread)
}
/* Flush buffer to the vty. */
-static void vty_flush(struct thread *thread)
+static void vty_flush(struct event *thread)
{
int erase;
buffer_status_t flushrc;
- struct vty *vty = THREAD_ARG(thread);
+ struct vty *vty = EVENT_ARG(thread);
/* Tempolary disable read thread. */
if (vty->lines == 0)
- THREAD_OFF(vty->t_read);
+ EVENT_OFF(vty->t_read);
/* Function execution continue. */
erase = ((vty->status == VTY_MORE || vty->status == VTY_MORELINE));
@@ -1586,6 +1625,16 @@ struct vty *vty_new(void)
new->max = VTY_BUFSIZ;
new->pass_fd = -1;
+ if (mgmt_lib_hndl) {
+ new->mgmt_client_id = mgmt_client_id_next++;
+ if (mgmt_fe_create_client_session(
+ mgmt_lib_hndl, new->mgmt_client_id,
+ (uintptr_t) new) != MGMTD_SUCCESS)
+ zlog_err(
+ "Failed to open a MGMTD Frontend session for VTY session %p!!",
+ new);
+ }
+
return new;
}
@@ -1708,9 +1757,9 @@ void vty_stdio_suspend(void)
if (!stdio_vty)
return;
- THREAD_OFF(stdio_vty->t_write);
- THREAD_OFF(stdio_vty->t_read);
- THREAD_OFF(stdio_vty->t_timeout);
+ EVENT_OFF(stdio_vty->t_write);
+ EVENT_OFF(stdio_vty->t_read);
+ EVENT_OFF(stdio_vty->t_timeout);
if (stdio_termios)
tcsetattr(0, TCSANOW, &stdio_orig_termios);
@@ -1774,9 +1823,9 @@ struct vty *vty_stdio(void (*atclose)(int isexit))
}
/* Accept connection from the network. */
-static void vty_accept(struct thread *thread)
+static void vty_accept(struct event *thread)
{
- struct vty_serv *vtyserv = THREAD_ARG(thread);
+ struct vty_serv *vtyserv = EVENT_ARG(thread);
int vty_sock;
union sockunion su;
int ret;
@@ -1987,9 +2036,9 @@ static void vty_serv_un(const char *path)
/* #define VTYSH_DEBUG 1 */
-static void vtysh_accept(struct thread *thread)
+static void vtysh_accept(struct event *thread)
{
- struct vty_serv *vtyserv = THREAD_ARG(thread);
+ struct vty_serv *vtyserv = EVENT_ARG(thread);
int accept_sock = vtyserv->sock;
int sock;
int client_len;
@@ -2117,7 +2166,7 @@ void vty_pass_fd(struct vty *vty, int fd)
vty->pass_fd = fd;
}
-static void vtysh_read(struct thread *thread)
+static void vtysh_read(struct event *thread)
{
int ret;
int sock;
@@ -2127,8 +2176,8 @@ static void vtysh_read(struct thread *thread)
unsigned char *p;
uint8_t header[4] = {0, 0, 0, 0};
- sock = THREAD_FD(thread);
- vty = THREAD_ARG(thread);
+ sock = EVENT_FD(thread);
+ vty = EVENT_ARG(thread);
if ((nbytes = read(sock, buf, VTY_READ_BUFSIZ)) <= 0) {
if (nbytes < 0) {
@@ -2201,6 +2250,12 @@ static void vtysh_read(struct thread *thread)
if (ret == CMD_SUSPEND)
break;
+ /* with new infra we need to stop response till
+ * we get response through callback.
+ */
+ if (vty->mgmt_req_pending)
+ return;
+
/* warning: watchfrr hardcodes this result write
*/
header[3] = ret;
@@ -2220,9 +2275,9 @@ static void vtysh_read(struct thread *thread)
vty_event(VTYSH_READ, vty);
}
-static void vtysh_write(struct thread *thread)
+static void vtysh_write(struct event *thread)
{
- struct vty *vty = THREAD_ARG(thread);
+ struct vty *vty = EVENT_ARG(thread);
vtysh_flush(vty);
}
@@ -2257,13 +2312,19 @@ void vty_close(struct vty *vty)
int i;
bool was_stdio = false;
+ if (mgmt_lib_hndl) {
+ mgmt_fe_destroy_client_session(mgmt_lib_hndl,
+ vty->mgmt_client_id);
+ vty->mgmt_session_id = 0;
+ }
+
/* Drop out of configure / transaction if needed. */
vty_config_exit(vty);
/* Cancel threads.*/
- THREAD_OFF(vty->t_read);
- THREAD_OFF(vty->t_write);
- THREAD_OFF(vty->t_timeout);
+ EVENT_OFF(vty->t_read);
+ EVENT_OFF(vty->t_write);
+ EVENT_OFF(vty->t_timeout);
if (vty->pass_fd != -1) {
close(vty->pass_fd);
@@ -2321,11 +2382,11 @@ void vty_close(struct vty *vty)
}
/* When time out occur output message then close connection. */
-static void vty_timeout(struct thread *thread)
+static void vty_timeout(struct event *thread)
{
struct vty *vty;
- vty = THREAD_ARG(thread);
+ vty = EVENT_ARG(thread);
vty->v_timeout = 0;
/* Clear buffer*/
@@ -2339,7 +2400,7 @@ static void vty_timeout(struct thread *thread)
}
/* Read up configuration file from file_name. */
-static void vty_read_file(struct nb_config *config, FILE *confp)
+void vty_read_file(struct nb_config *config, FILE *confp)
{
int ret;
struct vty *vty;
@@ -2632,6 +2693,23 @@ int vty_config_enter(struct vty *vty, bool private_config, bool exclusive)
return CMD_WARNING;
}
+ if (vty_mgmt_fe_enabled()) {
+ if (!mgmt_candidate_ds_wr_locked) {
+ if (vty_mgmt_send_lockds_req(vty, MGMTD_DS_CANDIDATE,
+ true) != 0) {
+ vty_out(vty, "Not able to lock candidate DS\n");
+ return CMD_WARNING;
+ }
+ } else {
+ vty_out(vty,
+ "Candidate DS already locked by different session\n");
+ return CMD_WARNING;
+ }
+
+ vty->mgmt_locked_candidate_ds = true;
+ mgmt_candidate_ds_wr_locked = true;
+ }
+
vty->node = CONFIG_NODE;
vty->config = true;
vty->private_config = private_config;
@@ -2643,7 +2721,14 @@ int vty_config_enter(struct vty *vty, bool private_config, bool exclusive)
vty_out(vty,
"Warning: uncommitted changes will be discarded on exit.\n\n");
} else {
- vty->candidate_config = vty_shared_candidate_config;
+ /*
+ * NOTE: On the MGMTD daemon we point the VTY candidate DS to
+ * the global MGMTD candidate DS. Else we point to the VTY
+ * Shared Candidate Config.
+ */
+ vty->candidate_config = vty_mgmt_candidate_config
+ ? vty_mgmt_candidate_config
+ : vty_shared_candidate_config;
if (frr_get_cli_mode() == FRR_CLI_TRANSACTIONAL)
vty->candidate_config_base =
nb_config_dup(running_config);
@@ -2676,6 +2761,18 @@ int vty_config_node_exit(struct vty *vty)
{
vty->xpath_index = 0;
+ if (vty_mgmt_fe_enabled() && mgmt_candidate_ds_wr_locked &&
+ vty->mgmt_locked_candidate_ds) {
+ if (vty_mgmt_send_lockds_req(vty, MGMTD_DS_CANDIDATE, false) !=
+ 0) {
+ vty_out(vty, "Not able to unlock candidate DS\n");
+ return CMD_WARNING;
+ }
+
+ vty->mgmt_locked_candidate_ds = false;
+ mgmt_candidate_ds_wr_locked = false;
+ }
+
/* Perform any pending commits. */
(void)nb_cli_pending_commit_check(vty);
@@ -2704,19 +2801,19 @@ int vty_config_node_exit(struct vty *vty)
}
/* Master of the threads. */
-static struct thread_master *vty_master;
+static struct event_loop *vty_master;
static void vty_event_serv(enum vty_event event, struct vty_serv *vty_serv)
{
switch (event) {
case VTY_SERV:
- thread_add_read(vty_master, vty_accept, vty_serv,
- vty_serv->sock, &vty_serv->t_accept);
+ event_add_read(vty_master, vty_accept, vty_serv, vty_serv->sock,
+ &vty_serv->t_accept);
break;
#ifdef VTYSH
case VTYSH_SERV:
- thread_add_read(vty_master, vtysh_accept, vty_serv,
- vty_serv->sock, &vty_serv->t_accept);
+ event_add_read(vty_master, vtysh_accept, vty_serv,
+ vty_serv->sock, &vty_serv->t_accept);
break;
#endif /* VTYSH */
case VTY_READ:
@@ -2733,34 +2830,34 @@ static void vty_event(enum vty_event event, struct vty *vty)
switch (event) {
#ifdef VTYSH
case VTYSH_READ:
- thread_add_read(vty_master, vtysh_read, vty, vty->fd,
- &vty->t_read);
+ event_add_read(vty_master, vtysh_read, vty, vty->fd,
+ &vty->t_read);
break;
case VTYSH_WRITE:
- thread_add_write(vty_master, vtysh_write, vty, vty->wfd,
- &vty->t_write);
+ event_add_write(vty_master, vtysh_write, vty, vty->wfd,
+ &vty->t_write);
break;
#endif /* VTYSH */
case VTY_READ:
- thread_add_read(vty_master, vty_read, vty, vty->fd,
- &vty->t_read);
+ event_add_read(vty_master, vty_read, vty, vty->fd,
+ &vty->t_read);
/* Time out treatment. */
if (vty->v_timeout) {
- THREAD_OFF(vty->t_timeout);
- thread_add_timer(vty_master, vty_timeout, vty,
- vty->v_timeout, &vty->t_timeout);
+ EVENT_OFF(vty->t_timeout);
+ event_add_timer(vty_master, vty_timeout, vty,
+ vty->v_timeout, &vty->t_timeout);
}
break;
case VTY_WRITE:
- thread_add_write(vty_master, vty_flush, vty, vty->wfd,
- &vty->t_write);
+ event_add_write(vty_master, vty_flush, vty, vty->wfd,
+ &vty->t_write);
break;
case VTY_TIMEOUT_RESET:
- THREAD_OFF(vty->t_timeout);
+ EVENT_OFF(vty->t_timeout);
if (vty->v_timeout)
- thread_add_timer(vty_master, vty_timeout, vty,
- vty->v_timeout, &vty->t_timeout);
+ event_add_timer(vty_master, vty_timeout, vty,
+ vty->v_timeout, &vty->t_timeout);
break;
case VTY_SERV:
case VTYSH_SERV:
@@ -3173,8 +3270,392 @@ void vty_init_vtysh(void)
/* currently nothing to do, but likely to have future use */
}
+static void vty_mgmt_server_connected(uintptr_t lib_hndl, uintptr_t usr_data,
+ bool connected)
+{
+ zlog_err("%sGot %sconnected %s MGMTD Frontend Server",
+ !connected ? "ERROR: " : "", !connected ? "dis: " : "",
+ !connected ? "from" : "to");
+
+ mgmt_fe_connected = connected;
+
+ /*
+ * TODO: Setup or teardown front-end sessions for existing
+ * VTY connections.
+ */
+}
+
+static void vty_mgmt_session_created(uintptr_t lib_hndl, uintptr_t usr_data,
+ uint64_t client_id, bool create,
+ bool success, uintptr_t session_id,
+ uintptr_t session_ctx)
+{
+ struct vty *vty;
+
+ vty = (struct vty *)session_ctx;
+
+ if (!success) {
+ zlog_err("%s session for client %llu failed!",
+ create ? "Creating" : "Destroying",
+ (unsigned long long)client_id);
+ return;
+ }
+
+ zlog_err("%s session for client %llu successfully!",
+ create ? "Created" : "Destroyed",
+ (unsigned long long)client_id);
+ if (create)
+ vty->mgmt_session_id = session_id;
+}
+
+static void vty_mgmt_ds_lock_notified(uintptr_t lib_hndl, uintptr_t usr_data,
+ uint64_t client_id, uintptr_t session_id,
+ uintptr_t session_ctx, uint64_t req_id,
+ bool lock_ds, bool success,
+ Mgmtd__DatastoreId ds_id,
+ char *errmsg_if_any)
+{
+ struct vty *vty;
+
+ vty = (struct vty *)session_ctx;
+
+ if (!success) {
+ zlog_err("%socking for DS %u failed! Err: '%s'",
+ lock_ds ? "L" : "Unl", ds_id, errmsg_if_any);
+ vty_out(vty, "ERROR: %socking for DS %u failed! Err: '%s'\n",
+ lock_ds ? "L" : "Unl", ds_id, errmsg_if_any);
+ } else {
+ zlog_err("%socked DS %u successfully!", lock_ds ? "L" : "Unl",
+ ds_id);
+ }
+
+ vty_mgmt_resume_response(vty, success);
+}
+
+static void vty_mgmt_set_config_result_notified(
+ uintptr_t lib_hndl, uintptr_t usr_data, uint64_t client_id,
+ uintptr_t session_id, uintptr_t session_ctx, uint64_t req_id,
+ bool success, Mgmtd__DatastoreId ds_id, char *errmsg_if_any)
+{
+ struct vty *vty;
+
+ vty = (struct vty *)session_ctx;
+
+ if (!success) {
+ zlog_err(
+ "SET_CONFIG request for client 0x%llx failed! Error: '%s'",
+ (unsigned long long)client_id,
+ errmsg_if_any ? errmsg_if_any : "Unknown");
+ vty_out(vty, "ERROR: SET_CONFIG request failed! Error: %s\n",
+ errmsg_if_any ? errmsg_if_any : "Unknown");
+ } else {
+ zlog_err(
+ "SET_CONFIG request for client 0x%llx req-id %llu was successfull!",
+ (unsigned long long)client_id,
+ (unsigned long long)req_id);
+ }
+
+ vty_mgmt_resume_response(vty, success);
+}
+
+static void vty_mgmt_commit_config_result_notified(
+ uintptr_t lib_hndl, uintptr_t usr_data, uint64_t client_id,
+ uintptr_t session_id, uintptr_t session_ctx, uint64_t req_id,
+ bool success, Mgmtd__DatastoreId src_ds_id,
+ Mgmtd__DatastoreId dst_ds_id, bool validate_only, char *errmsg_if_any)
+{
+ struct vty *vty;
+
+ vty = (struct vty *)session_ctx;
+
+ if (!success) {
+ zlog_err(
+ "COMMIT_CONFIG request for client 0x%llx failed! Error: '%s'",
+ (unsigned long long)client_id,
+ errmsg_if_any ? errmsg_if_any : "Unknown");
+ vty_out(vty, "ERROR: COMMIT_CONFIG request failed! Error: %s\n",
+ errmsg_if_any ? errmsg_if_any : "Unknown");
+ } else {
+ zlog_err(
+ "COMMIT_CONFIG request for client 0x%llx req-id %llu was successfull!",
+ (unsigned long long)client_id,
+ (unsigned long long)req_id);
+ if (errmsg_if_any)
+ vty_out(vty, "MGMTD: %s\n", errmsg_if_any);
+ }
+
+ vty_mgmt_resume_response(vty, success);
+}
+
+static enum mgmt_result vty_mgmt_get_data_result_notified(
+ uintptr_t lib_hndl, uintptr_t usr_data, uint64_t client_id,
+ uintptr_t session_id, uintptr_t session_ctx, uint64_t req_id,
+ bool success, Mgmtd__DatastoreId ds_id, Mgmtd__YangData **yang_data,
+ size_t num_data, int next_key, char *errmsg_if_any)
+{
+ struct vty *vty;
+ size_t indx;
+
+ vty = (struct vty *)session_ctx;
+
+ if (!success) {
+ zlog_err(
+ "GET_DATA request for client 0x%llx failed! Error: '%s'",
+ (unsigned long long)client_id,
+ errmsg_if_any ? errmsg_if_any : "Unknown");
+ vty_out(vty, "ERROR: GET_DATA request failed! Error: %s\n",
+ errmsg_if_any ? errmsg_if_any : "Unknown");
+ vty_mgmt_resume_response(vty, success);
+ return MGMTD_INTERNAL_ERROR;
+ }
+
+ zlog_debug(
+ "GET_DATA request for client 0x%llx req-id %llu was successfull!",
+ (unsigned long long)client_id, (unsigned long long)req_id);
+
+ if (req_id != mgmt_last_req_id) {
+ mgmt_last_req_id = req_id;
+ vty_out(vty, "[\n");
+ }
+
+ for (indx = 0; indx < num_data; indx++) {
+ vty_out(vty, " \"%s\": \"%s\"\n", yang_data[indx]->xpath,
+ yang_data[indx]->value->encoded_str_val);
+ }
+ if (next_key < 0) {
+ vty_out(vty, "]\n");
+ vty_mgmt_resume_response(vty, success);
+ }
+
+ return MGMTD_SUCCESS;
+}
+
+static struct mgmt_fe_client_params client_params = {
+ .client_connect_notify = vty_mgmt_server_connected,
+ .client_session_notify = vty_mgmt_session_created,
+ .lock_ds_notify = vty_mgmt_ds_lock_notified,
+ .set_config_notify = vty_mgmt_set_config_result_notified,
+ .commit_config_notify = vty_mgmt_commit_config_result_notified,
+ .get_data_notify = vty_mgmt_get_data_result_notified,
+};
+
+void vty_init_mgmt_fe(void)
+{
+ if (!vty_master) {
+ zlog_err("Always call vty_mgmt_init_fe() after vty_init()!!");
+ return;
+ }
+
+ assert(!mgmt_lib_hndl);
+ snprintf(client_params.name, sizeof(client_params.name), "%s-%lld",
+ frr_get_progname(), (long long)getpid());
+ mgmt_lib_hndl = mgmt_fe_client_lib_init(&client_params, vty_master);
+ assert(mgmt_lib_hndl);
+}
+
+bool vty_mgmt_fe_enabled(void)
+{
+ return mgmt_lib_hndl && mgmt_fe_connected ? true : false;
+}
+
+int vty_mgmt_send_lockds_req(struct vty *vty, Mgmtd__DatastoreId ds_id,
+ bool lock)
+{
+ enum mgmt_result ret;
+
+ if (mgmt_lib_hndl && vty->mgmt_session_id) {
+ vty->mgmt_req_id++;
+ ret = mgmt_fe_lock_ds(mgmt_lib_hndl, vty->mgmt_session_id,
+ vty->mgmt_req_id, ds_id, lock);
+ if (ret != MGMTD_SUCCESS) {
+ zlog_err(
+ "Failed to send %sLOCK-DS-REQ to MGMTD for req-id %llu.",
+ lock ? "" : "UN",
+ (unsigned long long)vty->mgmt_req_id);
+ vty_out(vty, "Failed to send %sLOCK-DS-REQ to MGMTD!",
+ lock ? "" : "UN");
+ return -1;
+ }
+
+ vty->mgmt_req_pending = true;
+ }
+
+ return 0;
+}
+
+int vty_mgmt_send_config_data(struct vty *vty)
+{
+ Mgmtd__YangDataValue value[VTY_MAXCFGCHANGES];
+ Mgmtd__YangData cfg_data[VTY_MAXCFGCHANGES];
+ Mgmtd__YangCfgDataReq cfg_req[VTY_MAXCFGCHANGES];
+ Mgmtd__YangCfgDataReq *cfgreq[VTY_MAXCFGCHANGES] = {0};
+ size_t indx;
+ int cnt;
+ bool implicit_commit = false;
+
+ if (mgmt_lib_hndl && vty->mgmt_session_id) {
+ cnt = 0;
+ for (indx = 0; indx < vty->num_cfg_changes; indx++) {
+ mgmt_yang_data_init(&cfg_data[cnt]);
+
+ if (vty->cfg_changes[indx].value) {
+ mgmt_yang_data_value_init(&value[cnt]);
+ value[cnt].encoded_str_val =
+ (char *)vty->cfg_changes[indx].value;
+ value[cnt].value_case =
+ MGMTD__YANG_DATA_VALUE__VALUE_ENCODED_STR_VAL;
+ cfg_data[cnt].value = &value[cnt];
+ }
+
+ cfg_data[cnt].xpath = vty->cfg_changes[indx].xpath;
+
+ mgmt_yang_cfg_data_req_init(&cfg_req[cnt]);
+ cfg_req[cnt].data = &cfg_data[cnt];
+ switch (vty->cfg_changes[indx].operation) {
+ case NB_OP_DESTROY:
+ cfg_req[cnt].req_type =
+ MGMTD__CFG_DATA_REQ_TYPE__DELETE_DATA;
+ break;
+
+ case NB_OP_CREATE:
+ case NB_OP_MODIFY:
+ case NB_OP_MOVE:
+ case NB_OP_PRE_VALIDATE:
+ case NB_OP_APPLY_FINISH:
+ cfg_req[cnt].req_type =
+ MGMTD__CFG_DATA_REQ_TYPE__SET_DATA;
+ break;
+ case NB_OP_GET_ELEM:
+ case NB_OP_GET_NEXT:
+ case NB_OP_GET_KEYS:
+ case NB_OP_LOOKUP_ENTRY:
+ case NB_OP_RPC:
+ assert(!"Invalid type of operation");
+ break;
+ default:
+ assert(!"non-enum value, invalid");
+ }
+
+ cfgreq[cnt] = &cfg_req[cnt];
+ cnt++;
+ }
+
+ vty->mgmt_req_id++;
+ implicit_commit = vty_needs_implicit_commit(vty);
+ if (cnt && mgmt_fe_set_config_data(
+ mgmt_lib_hndl, vty->mgmt_session_id,
+ vty->mgmt_req_id, MGMTD_DS_CANDIDATE, cfgreq,
+ cnt, implicit_commit,
+ MGMTD_DS_RUNNING) != MGMTD_SUCCESS) {
+ zlog_err("Failed to send %d Config Xpaths to MGMTD!!",
+ (int)indx);
+ return -1;
+ }
+
+ vty->mgmt_req_pending = true;
+ }
+
+ return 0;
+}
+
+int vty_mgmt_send_commit_config(struct vty *vty, bool validate_only, bool abort)
+{
+ enum mgmt_result ret;
+
+ if (mgmt_lib_hndl && vty->mgmt_session_id) {
+ vty->mgmt_req_id++;
+ ret = mgmt_fe_commit_config_data(
+ mgmt_lib_hndl, vty->mgmt_session_id, vty->mgmt_req_id,
+ MGMTD_DS_CANDIDATE, MGMTD_DS_RUNNING, validate_only,
+ abort);
+ if (ret != MGMTD_SUCCESS) {
+ zlog_err(
+ "Failed to send COMMIT-REQ to MGMTD for req-id %llu.",
+ (unsigned long long)vty->mgmt_req_id);
+ vty_out(vty, "Failed to send COMMIT-REQ to MGMTD!");
+ return -1;
+ }
+
+ vty->mgmt_req_pending = true;
+ vty->mgmt_num_pending_setcfg = 0;
+ }
+
+ return 0;
+}
+
+int vty_mgmt_send_get_config(struct vty *vty, Mgmtd__DatastoreId datastore,
+ const char **xpath_list, int num_req)
+{
+ enum mgmt_result ret;
+ Mgmtd__YangData yang_data[VTY_MAXCFGCHANGES];
+ Mgmtd__YangGetDataReq get_req[VTY_MAXCFGCHANGES];
+ Mgmtd__YangGetDataReq *getreq[VTY_MAXCFGCHANGES];
+ int i;
+
+ vty->mgmt_req_id++;
+
+ for (i = 0; i < num_req; i++) {
+ mgmt_yang_get_data_req_init(&get_req[i]);
+ mgmt_yang_data_init(&yang_data[i]);
+
+ yang_data->xpath = (char *)xpath_list[i];
+
+ get_req[i].data = &yang_data[i];
+ getreq[i] = &get_req[i];
+ }
+ ret = mgmt_fe_get_config_data(mgmt_lib_hndl, vty->mgmt_session_id,
+ vty->mgmt_req_id, datastore, getreq,
+ num_req);
+
+ if (ret != MGMTD_SUCCESS) {
+ zlog_err("Failed to send GET-CONFIG to MGMTD for req-id %llu.",
+ (unsigned long long)vty->mgmt_req_id);
+ vty_out(vty, "Failed to send GET-CONFIG to MGMTD!");
+ return -1;
+ }
+
+ vty->mgmt_req_pending = true;
+
+ return 0;
+}
+
+int vty_mgmt_send_get_data(struct vty *vty, Mgmtd__DatastoreId datastore,
+ const char **xpath_list, int num_req)
+{
+ enum mgmt_result ret;
+ Mgmtd__YangData yang_data[VTY_MAXCFGCHANGES];
+ Mgmtd__YangGetDataReq get_req[VTY_MAXCFGCHANGES];
+ Mgmtd__YangGetDataReq *getreq[VTY_MAXCFGCHANGES];
+ int i;
+
+ vty->mgmt_req_id++;
+
+ for (i = 0; i < num_req; i++) {
+ mgmt_yang_get_data_req_init(&get_req[i]);
+ mgmt_yang_data_init(&yang_data[i]);
+
+ yang_data->xpath = (char *)xpath_list[i];
+
+ get_req[i].data = &yang_data[i];
+ getreq[i] = &get_req[i];
+ }
+ ret = mgmt_fe_get_data(mgmt_lib_hndl, vty->mgmt_session_id,
+ vty->mgmt_req_id, datastore, getreq, num_req);
+
+ if (ret != MGMTD_SUCCESS) {
+ zlog_err("Failed to send GET-DATA to MGMTD for req-id %llu.",
+ (unsigned long long)vty->mgmt_req_id);
+ vty_out(vty, "Failed to send GET-DATA to MGMTD!");
+ return -1;
+ }
+
+ vty->mgmt_req_pending = true;
+
+ return 0;
+}
+
/* Install vty's own commands like `who' command. */
-void vty_init(struct thread_master *master_thread, bool do_command_logging)
+void vty_init(struct event_loop *master_thread, bool do_command_logging)
{
/* For further configuration read, preserve current directory. */
vty_save_cwd();
@@ -3220,6 +3701,11 @@ void vty_terminate(void)
struct vty *vty;
struct vty_serv *vtyserv;
+ if (mgmt_lib_hndl) {
+ mgmt_fe_client_lib_destroy(mgmt_lib_hndl);
+ mgmt_lib_hndl = 0;
+ }
+
memset(vty_cwd, 0x00, sizeof(vty_cwd));
vty_reset();
@@ -3240,7 +3726,7 @@ void vty_terminate(void)
vtys_init(vtysh_sessions);
while ((vtyserv = vtyservs_pop(vty_servs))) {
- THREAD_OFF(vtyserv->t_accept);
+ EVENT_OFF(vtyserv->t_accept);
close(vtyserv->sock);
XFREE(MTYPE_VTY_SERV, vtyserv);
}
diff --git a/lib/vty.h b/lib/vty.h
index 3cab9590f1..5114238f6a 100644
--- a/lib/vty.h
+++ b/lib/vty.h
@@ -18,13 +18,15 @@
#include <regex.h>
#endif /* HAVE_LIBPCRE2_POSIX */
-#include "thread.h"
+#include "frrevent.h"
#include "log.h"
#include "sockunion.h"
#include "qobj.h"
#include "compiler.h"
#include "northbound.h"
#include "zlog_live.h"
+#include "libfrr.h"
+#include "mgmt_fe_client.h"
#ifdef __cplusplus
extern "C" {
@@ -113,12 +115,18 @@ struct vty {
/* Changes enqueued to be applied in the candidate configuration. */
size_t num_cfg_changes;
- struct vty_cfg_change cfg_changes[VTY_MAXCFGCHANGES];
+ struct nb_cfg_change cfg_changes[VTY_MAXCFGCHANGES];
/* XPath of the current node */
int xpath_index;
char xpath[VTY_MAXDEPTH][XPATH_MAXLEN];
+ /*
+ * Keep track of how many SET_CFG requests has been sent so far that
+ * has not been committed yet.
+ */
+ size_t mgmt_num_pending_setcfg;
+
/* In configure mode. */
bool config;
@@ -134,12 +142,13 @@ struct vty {
/* Dynamic transaction information. */
bool pending_allowed;
bool pending_commit;
+ bool no_implicit_commit;
char *pending_cmds_buf;
size_t pending_cmds_buflen;
size_t pending_cmds_bufpos;
/* Confirmed-commit timeout and rollback configuration. */
- struct thread *t_confirmed_commit_timeout;
+ struct event *t_confirmed_commit_timeout;
struct nb_config *confirmed_commit_rollback;
/* qobj object ID (replacement for "index") */
@@ -193,12 +202,12 @@ struct vty {
int lines;
/* Read and write thread. */
- struct thread *t_read;
- struct thread *t_write;
+ struct event *t_read;
+ struct event *t_write;
/* Timeout seconds and thread. */
unsigned long v_timeout;
- struct thread *t_timeout;
+ struct event *t_timeout;
/* What address is this vty comming from. */
char address[SU_ADDRSTRLEN];
@@ -208,6 +217,12 @@ struct vty {
* without any output. */
size_t frame_pos;
char frame[1024];
+
+ uintptr_t mgmt_session_id;
+ uint64_t mgmt_client_id;
+ uint64_t mgmt_req_id;
+ bool mgmt_req_pending;
+ bool mgmt_locked_candidate_ds;
};
static inline void vty_push_context(struct vty *vty, int node, uint64_t id)
@@ -319,8 +334,10 @@ struct vty_arg {
#define IS_DIRECTORY_SEP(c) ((c) == DIRECTORY_SEP)
#endif
+extern struct nb_config *vty_mgmt_candidate_config;
+
/* Prototypes. */
-extern void vty_init(struct thread_master *, bool do_command_logging);
+extern void vty_init(struct event_loop *m, bool do_command_logging);
extern void vty_init_vtysh(void);
extern void vty_terminate(void);
extern void vty_reset(void);
@@ -352,6 +369,7 @@ extern void vty_pass_fd(struct vty *vty, int fd);
extern bool vty_read_config(struct nb_config *config, const char *config_file,
char *config_default_dir);
+extern void vty_read_file(struct nb_config *config, FILE *confp);
extern void vty_time_print(struct vty *, int);
extern void vty_serv_sock(const char *, unsigned short, const char *);
extern void vty_close(struct vty *);
@@ -370,6 +388,29 @@ extern void vty_stdio_suspend(void);
extern void vty_stdio_resume(void);
extern void vty_stdio_close(void);
+extern void vty_init_mgmt_fe(void);
+extern bool vty_mgmt_fe_enabled(void);
+extern int vty_mgmt_send_config_data(struct vty *vty);
+extern int vty_mgmt_send_commit_config(struct vty *vty, bool validate_only,
+ bool abort);
+extern int vty_mgmt_send_get_config(struct vty *vty,
+ Mgmtd__DatastoreId datastore,
+ const char **xpath_list, int num_req);
+extern int vty_mgmt_send_get_data(struct vty *vty, Mgmtd__DatastoreId datastore,
+ const char **xpath_list, int num_req);
+extern int vty_mgmt_send_lockds_req(struct vty *vty, Mgmtd__DatastoreId ds_id,
+ bool lock);
+extern void vty_mgmt_resume_response(struct vty *vty, bool success);
+
+static inline bool vty_needs_implicit_commit(struct vty *vty)
+{
+ return (frr_get_cli_mode() == FRR_CLI_CLASSIC
+ ? ((vty->pending_allowed || vty->no_implicit_commit)
+ ? false
+ : true)
+ : false);
+}
+
#ifdef __cplusplus
}
#endif
diff --git a/lib/wheel.c b/lib/wheel.c
index 4aca23481b..e17995c64a 100644
--- a/lib/wheel.c
+++ b/lib/wheel.c
@@ -6,7 +6,7 @@
*/
#include "zebra.h"
#include "linklist.h"
-#include "thread.h"
+#include "frrevent.h"
#include "memory.h"
#include "wheel.h"
#include "log.h"
@@ -16,9 +16,9 @@ DEFINE_MTYPE_STATIC(LIB, TIMER_WHEEL_LIST, "Timer Wheel Slot List");
static int debug_timer_wheel = 0;
-static void wheel_timer_thread(struct thread *t);
+static void wheel_timer_thread(struct event *t);
-static void wheel_timer_thread_helper(struct thread *t)
+static void wheel_timer_thread_helper(struct event *t)
{
struct listnode *node, *nextnode;
unsigned long long curr_slot;
@@ -26,7 +26,7 @@ static void wheel_timer_thread_helper(struct thread *t)
struct timer_wheel *wheel;
void *data;
- wheel = THREAD_ARG(t);
+ wheel = EVENT_ARG(t);
wheel->curr_slot += wheel->slots_to_skip;
@@ -47,23 +47,23 @@ static void wheel_timer_thread_helper(struct thread *t)
slots_to_skip++;
wheel->slots_to_skip = slots_to_skip;
- thread_add_timer_msec(wheel->master, wheel_timer_thread, wheel,
- wheel->nexttime * slots_to_skip, &wheel->timer);
+ event_add_timer_msec(wheel->master, wheel_timer_thread, wheel,
+ wheel->nexttime * slots_to_skip, &wheel->timer);
}
-static void wheel_timer_thread(struct thread *t)
+static void wheel_timer_thread(struct event *t)
{
struct timer_wheel *wheel;
- wheel = THREAD_ARG(t);
+ wheel = EVENT_ARG(t);
- thread_execute(wheel->master, wheel_timer_thread_helper, wheel, 0);
+ event_execute(wheel->master, wheel_timer_thread_helper, wheel, 0);
}
-struct timer_wheel *wheel_init(struct thread_master *master, int period,
- size_t slots, unsigned int (*slot_key)(const void *),
- void (*slot_run)(void *),
- const char *run_name)
+struct timer_wheel *wheel_init(struct event_loop *master, int period,
+ size_t slots,
+ unsigned int (*slot_key)(const void *),
+ void (*slot_run)(void *), const char *run_name)
{
struct timer_wheel *wheel;
size_t i;
@@ -85,8 +85,8 @@ struct timer_wheel *wheel_init(struct thread_master *master, int period,
for (i = 0; i < slots; i++)
wheel->wheel_slot_lists[i] = list_new();
- thread_add_timer_msec(wheel->master, wheel_timer_thread, wheel,
- wheel->nexttime, &wheel->timer);
+ event_add_timer_msec(wheel->master, wheel_timer_thread, wheel,
+ wheel->nexttime, &wheel->timer);
return wheel;
}
@@ -99,7 +99,7 @@ void wheel_delete(struct timer_wheel *wheel)
list_delete(&wheel->wheel_slot_lists[i]);
}
- THREAD_OFF(wheel->timer);
+ EVENT_OFF(wheel->timer);
XFREE(MTYPE_TIMER_WHEEL_LIST, wheel->wheel_slot_lists);
XFREE(MTYPE_TIMER_WHEEL, wheel->name);
XFREE(MTYPE_TIMER_WHEEL, wheel);
diff --git a/lib/wheel.h b/lib/wheel.h
index 9aa808cdfd..0d9ac10020 100644
--- a/lib/wheel.h
+++ b/lib/wheel.h
@@ -13,7 +13,7 @@ extern "C" {
struct timer_wheel {
char *name;
- struct thread_master *master;
+ struct event_loop *master;
int slots;
long long curr_slot;
unsigned int period;
@@ -21,7 +21,7 @@ struct timer_wheel {
unsigned int slots_to_skip;
struct list **wheel_slot_lists;
- struct thread *timer;
+ struct event *timer;
/*
* Key to determine what slot the item belongs in
*/
@@ -66,7 +66,7 @@ struct timer_wheel {
* and cause significant amount of time handling thread events instead
* of running your code.
*/
-struct timer_wheel *wheel_init(struct thread_master *master, int period,
+struct timer_wheel *wheel_init(struct event_loop *master, int period,
size_t slots,
unsigned int (*slot_key)(const void *),
void (*slot_run)(void *), const char *run_name);
diff --git a/lib/workqueue.c b/lib/workqueue.c
index 5477aadd65..fa5d585360 100644
--- a/lib/workqueue.c
+++ b/lib/workqueue.c
@@ -6,7 +6,7 @@
*/
#include <zebra.h>
-#include "thread.h"
+#include "frrevent.h"
#include "memory.h"
#include "workqueue.h"
#include "linklist.h"
@@ -59,8 +59,7 @@ static void work_queue_item_remove(struct work_queue *wq,
}
/* create new work queue */
-struct work_queue *work_queue_new(struct thread_master *m,
- const char *queue_name)
+struct work_queue *work_queue_new(struct event_loop *m, const char *queue_name)
{
struct work_queue *new;
@@ -78,7 +77,7 @@ struct work_queue *work_queue_new(struct thread_master *m,
/* Default values, can be overridden by caller */
new->spec.hold = WORK_QUEUE_DEFAULT_HOLD;
- new->spec.yield = THREAD_YIELD_TIME_SLOT;
+ new->spec.yield = EVENT_YIELD_TIME_SLOT;
new->spec.retry = WORK_QUEUE_DEFAULT_RETRY;
return new;
@@ -88,7 +87,7 @@ void work_queue_free_and_null(struct work_queue **wqp)
{
struct work_queue *wq = *wqp;
- THREAD_OFF(wq->thread);
+ EVENT_OFF(wq->thread);
while (!work_queue_empty(wq)) {
struct work_queue_item *item = work_queue_last_item(wq);
@@ -106,29 +105,29 @@ void work_queue_free_and_null(struct work_queue **wqp)
bool work_queue_is_scheduled(struct work_queue *wq)
{
- return thread_is_scheduled(wq->thread);
+ return event_is_scheduled(wq->thread);
}
static int work_queue_schedule(struct work_queue *wq, unsigned int delay)
{
/* if appropriate, schedule work queue thread */
if (CHECK_FLAG(wq->flags, WQ_UNPLUGGED) &&
- !thread_is_scheduled(wq->thread) && !work_queue_empty(wq)) {
+ !event_is_scheduled(wq->thread) && !work_queue_empty(wq)) {
/* Schedule timer if there's a delay, otherwise just schedule
* as an 'event'
*/
if (delay > 0) {
- thread_add_timer_msec(wq->master, work_queue_run, wq,
- delay, &wq->thread);
- thread_ignore_late_timer(wq->thread);
+ event_add_timer_msec(wq->master, work_queue_run, wq,
+ delay, &wq->thread);
+ event_ignore_late_timer(wq->thread);
} else
- thread_add_event(wq->master, work_queue_run, wq, 0,
- &wq->thread);
+ event_add_event(wq->master, work_queue_run, wq, 0,
+ &wq->thread);
/* set thread yield time, if needed */
- if (thread_is_scheduled(wq->thread) &&
- wq->spec.yield != THREAD_YIELD_TIME_SLOT)
- thread_set_yield_time(wq->thread, wq->spec.yield);
+ if (event_is_scheduled(wq->thread) &&
+ wq->spec.yield != EVENT_YIELD_TIME_SLOT)
+ event_set_yield_time(wq->thread, wq->spec.yield);
return 1;
} else
return 0;
@@ -198,7 +197,7 @@ void workqueue_cmd_init(void)
*/
void work_queue_plug(struct work_queue *wq)
{
- THREAD_OFF(wq->thread);
+ EVENT_OFF(wq->thread);
UNSET_FLAG(wq->flags, WQ_UNPLUGGED);
}
@@ -218,7 +217,7 @@ void work_queue_unplug(struct work_queue *wq)
* will reschedule itself if required,
* otherwise work_queue_item_add
*/
-void work_queue_run(struct thread *thread)
+void work_queue_run(struct event *thread)
{
struct work_queue *wq;
struct work_queue_item *item, *titem;
@@ -226,7 +225,7 @@ void work_queue_run(struct thread *thread)
unsigned int cycles = 0;
char yielded = 0;
- wq = THREAD_ARG(thread);
+ wq = EVENT_ARG(thread);
assert(wq);
@@ -311,8 +310,8 @@ void work_queue_run(struct thread *thread)
cycles++;
/* test if we should yield */
- if (!(cycles % wq->cycles.granularity)
- && thread_should_yield(thread)) {
+ if (!(cycles % wq->cycles.granularity) &&
+ event_should_yield(thread)) {
yielded = 1;
goto stats;
}
diff --git a/lib/workqueue.h b/lib/workqueue.h
index c7ed14b056..5d84739d5c 100644
--- a/lib/workqueue.h
+++ b/lib/workqueue.h
@@ -47,8 +47,8 @@ struct work_queue {
/* Everything but the specification struct is private
* the following may be read
*/
- struct thread_master *master; /* thread master */
- struct thread *thread; /* thread, if one is active */
+ struct event_loop *master; /* thread master */
+ struct event *thread; /* thread, if one is active */
char *name; /* work queue name */
/* Specification for this work queue.
@@ -137,7 +137,7 @@ static inline void work_queue_item_dequeue(struct work_queue *wq,
* user must fill in the spec of the returned work queue before adding
* anything to it
*/
-extern struct work_queue *work_queue_new(struct thread_master *m,
+extern struct work_queue *work_queue_new(struct event_loop *m,
const char *queue_name);
/* destroy work queue */
@@ -158,7 +158,7 @@ extern void work_queue_unplug(struct work_queue *wq);
bool work_queue_is_scheduled(struct work_queue *wq);
/* Helpers, exported for thread.c and command.c */
-extern void work_queue_run(struct thread *thread);
+extern void work_queue_run(struct event *thread);
extern void workqueue_cmd_init(void);
diff --git a/lib/xref.h b/lib/xref.h
index b49c9eb2f2..f06d65b422 100644
--- a/lib/xref.h
+++ b/lib/xref.h
@@ -20,7 +20,7 @@ extern "C" {
enum xref_type {
XREFT_NONE = 0,
- XREFT_THREADSCHED = 0x100,
+ XREFT_EVENTSCHED = 0x100,
XREFT_LOGMSG = 0x200,
XREFT_ASSERT = 0x280,
diff --git a/lib/yang.c b/lib/yang.c
index 78738f7d4d..70a3251ab3 100644
--- a/lib/yang.c
+++ b/lib/yang.c
@@ -395,7 +395,12 @@ struct lyd_node *yang_dnode_get(const struct lyd_node *dnode, const char *xpath)
xpath += 2;
if (lyd_find_xpath(dnode, xpath, &set)) {
- assert(0); /* XXX replicates old libyang1 base code */
+ /*
+ * Commenting out the below assert failure as it crashes mgmtd
+ * when bad xpath is passed.
+ *
+ * assert(0); XXX replicates old libyang1 base code
+ */
goto exit;
}
if (set->count == 0)
diff --git a/lib/yang.h b/lib/yang.h
index 91cd641ce8..654c246f0d 100644
--- a/lib/yang.h
+++ b/lib/yang.h
@@ -50,7 +50,7 @@ struct yang_module {
#endif
#ifdef HAVE_SYSREPO
sr_subscription_ctx_t *sr_subscription;
- struct thread *sr_thread;
+ struct event *sr_thread;
#endif
};
RB_HEAD(yang_modules, yang_module);
diff --git a/lib/zclient.c b/lib/zclient.c
index 0e49d65528..d42cb20191 100644
--- a/lib/zclient.c
+++ b/lib/zclient.c
@@ -14,7 +14,7 @@
#include "vrf_int.h"
#include "if.h"
#include "log.h"
-#include "thread.h"
+#include "frrevent.h"
#include "zclient.h"
#include "memory.h"
#include "table.h"
@@ -51,7 +51,7 @@ socklen_t zclient_addr_len;
static int zclient_debug;
/* Allocate zclient structure. */
-struct zclient *zclient_new(struct thread_master *master,
+struct zclient *zclient_new(struct event_loop *master,
struct zclient_options *opt,
zclient_handler *const *handlers, size_t n_handlers)
{
@@ -160,9 +160,9 @@ void zclient_stop(struct zclient *zclient)
zlog_debug("zclient %p stopped", zclient);
/* Stop threads. */
- THREAD_OFF(zclient->t_read);
- THREAD_OFF(zclient->t_connect);
- THREAD_OFF(zclient->t_write);
+ EVENT_OFF(zclient->t_read);
+ EVENT_OFF(zclient->t_connect);
+ EVENT_OFF(zclient->t_write);
/* Reset streams. */
stream_reset(zclient->ibuf);
@@ -249,9 +249,9 @@ static enum zclient_send_status zclient_failed(struct zclient *zclient)
return ZCLIENT_SEND_FAILURE;
}
-static void zclient_flush_data(struct thread *thread)
+static void zclient_flush_data(struct event *thread)
{
- struct zclient *zclient = THREAD_ARG(thread);
+ struct zclient *zclient = EVENT_ARG(thread);
zclient->t_write = NULL;
if (zclient->sock < 0)
@@ -266,8 +266,8 @@ static void zclient_flush_data(struct thread *thread)
return;
case BUFFER_PENDING:
zclient->t_write = NULL;
- thread_add_write(zclient->master, zclient_flush_data, zclient,
- zclient->sock, &zclient->t_write);
+ event_add_write(zclient->master, zclient_flush_data, zclient,
+ zclient->sock, &zclient->t_write);
break;
case BUFFER_EMPTY:
if (zclient->zebra_buffer_write_ready)
@@ -295,11 +295,11 @@ enum zclient_send_status zclient_send_message(struct zclient *zclient)
__func__, zclient->sock);
return zclient_failed(zclient);
case BUFFER_EMPTY:
- THREAD_OFF(zclient->t_write);
+ EVENT_OFF(zclient->t_write);
return ZCLIENT_SEND_SUCCESS;
case BUFFER_PENDING:
- thread_add_write(zclient->master, zclient_flush_data, zclient,
- zclient->sock, &zclient->t_write);
+ event_add_write(zclient->master, zclient_flush_data, zclient,
+ zclient->sock, &zclient->t_write);
return ZCLIENT_SEND_BUFFERED;
}
@@ -744,11 +744,11 @@ void zclient_init(struct zclient *zclient, int redist_default,
/* This function is a wrapper function for calling zclient_start from
timer or event thread. */
-static void zclient_connect(struct thread *t)
+static void zclient_connect(struct event *t)
{
struct zclient *zclient;
- zclient = THREAD_ARG(t);
+ zclient = EVENT_ARG(t);
zclient->t_connect = NULL;
if (zclient_debug)
@@ -4026,7 +4026,7 @@ static zclient_handler *const lib_handlers[] = {
};
/* Zebra client message read function. */
-static void zclient_read(struct thread *thread)
+static void zclient_read(struct event *thread)
{
size_t already;
uint16_t length, command;
@@ -4035,7 +4035,7 @@ static void zclient_read(struct thread *thread)
struct zclient *zclient;
/* Get socket to zebra. */
- zclient = THREAD_ARG(thread);
+ zclient = EVENT_ARG(thread);
zclient->t_read = NULL;
/* Read zebra header (if we don't have it already). */
@@ -4204,22 +4204,22 @@ static void zclient_event(enum zclient_event event, struct zclient *zclient)
{
switch (event) {
case ZCLIENT_SCHEDULE:
- thread_add_event(zclient->master, zclient_connect, zclient, 0,
- &zclient->t_connect);
+ event_add_event(zclient->master, zclient_connect, zclient, 0,
+ &zclient->t_connect);
break;
case ZCLIENT_CONNECT:
if (zclient_debug)
zlog_debug(
"zclient connect failures: %d schedule interval is now %d",
zclient->fail, zclient->fail < 3 ? 10 : 60);
- thread_add_timer(zclient->master, zclient_connect, zclient,
- zclient->fail < 3 ? 10 : 60,
- &zclient->t_connect);
+ event_add_timer(zclient->master, zclient_connect, zclient,
+ zclient->fail < 3 ? 10 : 60,
+ &zclient->t_connect);
break;
case ZCLIENT_READ:
zclient->t_read = NULL;
- thread_add_read(zclient->master, zclient_read, zclient,
- zclient->sock, &zclient->t_read);
+ event_add_read(zclient->master, zclient_read, zclient,
+ zclient->sock, &zclient->t_read);
break;
}
}
@@ -4294,6 +4294,8 @@ int32_t zapi_capabilities_decode(struct stream *s, struct zapi_cap *api)
memset(api, 0, sizeof(*api));
+ api->safi = SAFI_UNICAST;
+
STREAM_GETL(s, api->cap);
switch (api->cap) {
case ZEBRA_CLIENT_GR_CAPABILITIES:
diff --git a/lib/zclient.h b/lib/zclient.h
index 53c7038c88..367e5b1474 100644
--- a/lib/zclient.h
+++ b/lib/zclient.h
@@ -290,7 +290,7 @@ typedef int (zclient_handler)(ZAPI_CALLBACK_ARGS);
/* Structure for the zebra client. */
struct zclient {
/* The thread master we schedule ourselves on */
- struct thread_master *master;
+ struct event_loop *master;
/* Privileges to change socket values */
struct zebra_privs_t *privs;
@@ -323,11 +323,11 @@ struct zclient {
struct buffer *wb;
/* Read and connect thread. */
- struct thread *t_read;
- struct thread *t_connect;
+ struct event *t_read;
+ struct event *t_connect;
/* Thread to write buffered data to zebra. */
- struct thread *t_write;
+ struct event *t_write;
/* Redistribute information. */
uint8_t redist_default; /* clients protocol */
@@ -862,7 +862,7 @@ int zclient_neigh_ip_encode(struct stream *s, uint16_t cmd, union sockunion *in,
extern uint32_t zclient_get_nhg_start(uint32_t proto);
-extern struct zclient *zclient_new(struct thread_master *m,
+extern struct zclient *zclient_new(struct event_loop *m,
struct zclient_options *opt,
zclient_handler *const *handlers,
size_t n_handlers);
diff --git a/lib/zlog.c b/lib/zlog.c
index e05720fd9e..309a955fa9 100644
--- a/lib/zlog.c
+++ b/lib/zlog.c
@@ -48,7 +48,7 @@
#include "frrcu.h"
#include "zlog.h"
#include "libfrr_trace.h"
-#include "thread.h"
+#include "frrevent.h"
DEFINE_MTYPE_STATIC(LIB, LOG_MESSAGE, "log message");
DEFINE_MTYPE_STATIC(LIB, LOG_TLSBUF, "log thread-local buffer");
@@ -506,7 +506,7 @@ static void vzlog_tls(struct zlog_tls *zlog_tls, const struct xref_logmsg *xref,
static void zlog_backtrace_msg(const struct xref_logmsg *xref, int prio)
{
- struct thread *tc = pthread_getspecific(thread_current);
+ struct event *tc = pthread_getspecific(thread_current);
const char *uid = xref->xref.xrefdata->uid;
bool found_thread = false;
diff --git a/lib/zlog_5424.c b/lib/zlog_5424.c
index 5264dda0f8..c15bdece29 100644
--- a/lib/zlog_5424.c
+++ b/lib/zlog_5424.c
@@ -26,7 +26,7 @@
#include "frr_pthread.h"
#include "command.h"
#include "monotime.h"
-#include "thread.h"
+#include "frrevent.h"
#include "lib/version.h"
#include "lib/lib_errors.h"
@@ -789,10 +789,10 @@ static void zlog_5424_cycle(struct zlog_cfg_5424 *zcf, int fd)
rcu_free(MTYPE_LOG_5424, oldt, zt.rcu_head);
}
-static void zlog_5424_reconnect(struct thread *t)
+static void zlog_5424_reconnect(struct event *t)
{
- struct zlog_cfg_5424 *zcf = THREAD_ARG(t);
- int fd = THREAD_FD(t);
+ struct zlog_cfg_5424 *zcf = EVENT_ARG(t);
+ int fd = EVENT_FD(t);
char dummy[256];
ssize_t ret;
@@ -800,8 +800,8 @@ static void zlog_5424_reconnect(struct thread *t)
ret = read(fd, dummy, sizeof(dummy));
if (ret > 0) {
/* logger is sending us something?!?! */
- thread_add_read(t->master, zlog_5424_reconnect, zcf, fd,
- &zcf->t_reconnect);
+ event_add_read(t->master, zlog_5424_reconnect, zcf, fd,
+ &zcf->t_reconnect);
return;
}
@@ -1030,14 +1030,14 @@ static int zlog_5424_open(struct zlog_cfg_5424 *zcf, int sock_type)
assert(zcf->master);
if (fd != -1) {
- thread_add_read(zcf->master, zlog_5424_reconnect, zcf,
- fd, &zcf->t_reconnect);
+ event_add_read(zcf->master, zlog_5424_reconnect, zcf,
+ fd, &zcf->t_reconnect);
zcf->reconn_backoff_cur = zcf->reconn_backoff;
} else {
- thread_add_timer_msec(zcf->master, zlog_5424_reconnect,
- zcf, zcf->reconn_backoff_cur,
- &zcf->t_reconnect);
+ event_add_timer_msec(zcf->master, zlog_5424_reconnect,
+ zcf, zcf->reconn_backoff_cur,
+ &zcf->t_reconnect);
zcf->reconn_backoff_cur += zcf->reconn_backoff_cur / 2;
if (zcf->reconn_backoff_cur > zcf->reconn_backoff_max)
@@ -1053,7 +1053,7 @@ bool zlog_5424_apply_dst(struct zlog_cfg_5424 *zcf)
{
int fd = -1;
- thread_cancel(&zcf->t_reconnect);
+ event_cancel(&zcf->t_reconnect);
if (zcf->prio_min != ZLOG_DISABLED)
fd = zlog_5424_open(zcf, -1);
@@ -1106,7 +1106,7 @@ bool zlog_5424_rotate(struct zlog_cfg_5424 *zcf)
if (!zcf->active)
return true;
- thread_cancel(&zcf->t_reconnect);
+ event_cancel(&zcf->t_reconnect);
/* need to retain the socket type because it also influences
* other fields (packets) and we can't atomically swap these
diff --git a/lib/zlog_5424.h b/lib/zlog_5424.h
index 377e7be220..06525c0044 100644
--- a/lib/zlog_5424.h
+++ b/lib/zlog_5424.h
@@ -13,8 +13,8 @@
#include "zlog_targets.h"
#include "qobj.h"
-struct thread;
-struct thread_master;
+struct event;
+struct event_loop;
enum zlog_5424_dst {
/* can be used to disable a target temporarily */
@@ -78,8 +78,8 @@ struct zlog_cfg_5424 {
*/
/* sockets only - read handler to reconnect on errors */
- struct thread_master *master;
- struct thread *t_reconnect;
+ struct event_loop *master;
+ struct event *t_reconnect;
unsigned int reconn_backoff, reconn_backoff_cur, reconn_backoff_max;
int sock_type;
struct sockaddr_storage sa;
diff --git a/lib/zlog_5424_cli.c b/lib/zlog_5424_cli.c
index 7f070afbc5..3003df542f 100644
--- a/lib/zlog_5424_cli.c
+++ b/lib/zlog_5424_cli.c
@@ -28,7 +28,7 @@ DECLARE_RBTREE_UNIQ(targets, struct zlog_cfg_5424_user, targets_item,
DEFINE_QOBJ_TYPE(zlog_cfg_5424_user);
static struct targets_head targets = INIT_RBTREE_UNIQ(targets);
-static struct thread_master *log_5424_master;
+static struct event_loop *log_5424_master;
static void clear_dst(struct zlog_cfg_5424_user *cfg);
@@ -948,7 +948,7 @@ void log_5424_cmd_init(void)
/* hooks */
-static int log_5424_early_init(struct thread_master *master);
+static int log_5424_early_init(struct event_loop *master);
static int log_5424_rotate(void);
static int log_5424_fini(void);
@@ -959,7 +959,7 @@ __attribute__((_CONSTRUCTOR(475))) static void zlog_5424_startup_init(void)
hook_register(frr_fini, log_5424_fini);
}
-static int log_5424_early_init(struct thread_master *master)
+static int log_5424_early_init(struct event_loop *master)
{
log_5424_master = master;