diff options
Diffstat (limited to 'lib')
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; @@ -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); @@ -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, ©); + vty_out_cpu_event_history(vty, ©); 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. @@ -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; @@ -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(®ntfy_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 = ®ntfy_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 ©->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 ©->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 */ @@ -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) @@ -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); } @@ -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; |
