back to topotato report
topotato coverage report
Current view: top level - lib - northbound.c (source / functions) Hit Total Coverage
Test: test_bgp_aspath_zero.py::BGPAggregatorZero Lines: 436 1083 40.3 %
Date: 2023-02-24 18:36:48 Functions: 47 91 51.6 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (C) 2018  NetDEF, Inc.
       3             :  *                     Renato Westphal
       4             :  *
       5             :  * This program is free software; you can redistribute it and/or modify it
       6             :  * under the terms of the GNU General Public License as published by the Free
       7             :  * Software Foundation; either version 2 of the License, or (at your option)
       8             :  * any later version.
       9             :  *
      10             :  * This program is distributed in the hope that it will be useful, but WITHOUT
      11             :  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
      12             :  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
      13             :  * more details.
      14             :  *
      15             :  * You should have received a copy of the GNU General Public License along
      16             :  * with this program; see the file COPYING; if not, write to the Free Software
      17             :  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
      18             :  */
      19             : 
      20             : #include <zebra.h>
      21             : 
      22             : #include "libfrr.h"
      23             : #include "log.h"
      24             : #include "lib_errors.h"
      25             : #include "hash.h"
      26             : #include "command.h"
      27             : #include "debug.h"
      28             : #include "db.h"
      29             : #include "frr_pthread.h"
      30             : #include "northbound.h"
      31             : #include "northbound_cli.h"
      32             : #include "northbound_db.h"
      33             : #include "frrstr.h"
      34             : 
      35          12 : DEFINE_MTYPE_STATIC(LIB, NB_NODE, "Northbound Node");
      36          12 : DEFINE_MTYPE_STATIC(LIB, NB_CONFIG, "Northbound Configuration");
      37          12 : DEFINE_MTYPE_STATIC(LIB, NB_CONFIG_ENTRY, "Northbound Configuration Entry");
      38             : 
      39             : /* Running configuration - shouldn't be modified directly. */
      40             : struct nb_config *running_config;
      41             : 
      42             : /* Hash table of user pointers associated with configuration entries. */
      43             : static struct hash *running_config_entries;
      44             : 
      45             : /* Management lock for the running configuration. */
      46             : static struct {
      47             :         /* Mutex protecting this structure. */
      48             :         pthread_mutex_t mtx;
      49             : 
      50             :         /* Actual lock. */
      51             :         bool locked;
      52             : 
      53             :         /* Northbound client who owns this lock. */
      54             :         enum nb_client owner_client;
      55             : 
      56             :         /* Northbound user who owns this lock. */
      57             :         const void *owner_user;
      58             : } running_config_mgmt_lock;
      59             : 
      60             : /* Knob to record config transaction */
      61             : static bool nb_db_enabled;
      62             : /*
      63             :  * Global lock used to prevent multiple configuration transactions from
      64             :  * happening concurrently.
      65             :  */
      66             : static bool transaction_in_progress;
      67             : 
      68             : static int nb_callback_pre_validate(struct nb_context *context,
      69             :                                     const struct nb_node *nb_node,
      70             :                                     const struct lyd_node *dnode, char *errmsg,
      71             :                                     size_t errmsg_len);
      72             : static int nb_callback_configuration(struct nb_context *context,
      73             :                                      const enum nb_event event,
      74             :                                      struct nb_config_change *change,
      75             :                                      char *errmsg, size_t errmsg_len);
      76             : static struct nb_transaction *
      77             : nb_transaction_new(struct nb_context *context, struct nb_config *config,
      78             :                    struct nb_config_cbs *changes, const char *comment,
      79             :                    char *errmsg, size_t errmsg_len);
      80             : static void nb_transaction_free(struct nb_transaction *transaction);
      81             : static int nb_transaction_process(enum nb_event event,
      82             :                                   struct nb_transaction *transaction,
      83             :                                   char *errmsg, size_t errmsg_len);
      84             : static void nb_transaction_apply_finish(struct nb_transaction *transaction,
      85             :                                         char *errmsg, size_t errmsg_len);
      86             : static int nb_oper_data_iter_node(const struct lysc_node *snode,
      87             :                                   const char *xpath, const void *list_entry,
      88             :                                   const struct yang_list_keys *list_keys,
      89             :                                   struct yang_translator *translator,
      90             :                                   bool first, uint32_t flags,
      91             :                                   nb_oper_data_cb cb, void *arg);
      92             : 
      93        4220 : static int nb_node_check_config_only(const struct lysc_node *snode, void *arg)
      94             : {
      95        4220 :         bool *config_only = arg;
      96             : 
      97        4220 :         if (CHECK_FLAG(snode->flags, LYS_CONFIG_R)) {
      98          50 :                 *config_only = false;
      99          50 :                 return YANG_ITER_STOP;
     100             :         }
     101             : 
     102             :         return YANG_ITER_CONTINUE;
     103             : }
     104             : 
     105        1802 : static int nb_node_new_cb(const struct lysc_node *snode, void *arg)
     106             : {
     107        1802 :         struct nb_node *nb_node;
     108        1802 :         struct lysc_node *sparent, *sparent_list;
     109             : 
     110        1802 :         nb_node = XCALLOC(MTYPE_NB_NODE, sizeof(*nb_node));
     111        1802 :         yang_snode_get_path(snode, YANG_PATH_DATA, nb_node->xpath,
     112             :                             sizeof(nb_node->xpath));
     113        1802 :         nb_node->priority = NB_DFLT_PRIORITY;
     114        1802 :         sparent = yang_snode_real_parent(snode);
     115        1802 :         if (sparent)
     116        1324 :                 nb_node->parent = sparent->priv;
     117        1802 :         sparent_list = yang_snode_parent_list(snode);
     118        1802 :         if (sparent_list)
     119        1324 :                 nb_node->parent_list = sparent_list->priv;
     120             : 
     121             :         /* Set flags. */
     122        1802 :         if (CHECK_FLAG(snode->nodetype, LYS_CONTAINER | LYS_LIST)) {
     123         206 :                 bool config_only = true;
     124             : 
     125         206 :                 (void)yang_snodes_iterate_subtree(snode, NULL,
     126             :                                                   nb_node_check_config_only, 0,
     127             :                                                   &config_only);
     128         206 :                 if (config_only)
     129         156 :                         SET_FLAG(nb_node->flags, F_NB_NODE_CONFIG_ONLY);
     130             :         }
     131        1802 :         if (CHECK_FLAG(snode->nodetype, LYS_LIST)) {
     132         102 :                 if (yang_snode_num_keys(snode) == 0)
     133          36 :                         SET_FLAG(nb_node->flags, F_NB_NODE_KEYLESS_LIST);
     134             :         }
     135             : 
     136             :         /*
     137             :          * Link the northbound node and the libyang schema node with one
     138             :          * another.
     139             :          */
     140        1802 :         nb_node->snode = snode;
     141        1802 :         assert(snode->priv == NULL);
     142        1802 :         ((struct lysc_node *)snode)->priv = nb_node;
     143             : 
     144        1802 :         return YANG_ITER_CONTINUE;
     145             : }
     146             : 
     147        3604 : static int nb_node_del_cb(const struct lysc_node *snode, void *arg)
     148             : {
     149        3604 :         struct nb_node *nb_node;
     150             : 
     151        3604 :         nb_node = snode->priv;
     152        3604 :         if (nb_node) {
     153        3604 :                 ((struct lysc_node *)snode)->priv = NULL;
     154        3604 :                 XFREE(MTYPE_NB_NODE, nb_node);
     155             :         }
     156             : 
     157        3604 :         return YANG_ITER_CONTINUE;
     158             : }
     159             : 
     160           0 : void nb_nodes_create(void)
     161             : {
     162           0 :         yang_snodes_iterate(NULL, nb_node_new_cb, 0, NULL);
     163           0 : }
     164             : 
     165           8 : void nb_nodes_delete(void)
     166             : {
     167           8 :         yang_snodes_iterate(NULL, nb_node_del_cb, 0, NULL);
     168           8 : }
     169             : 
     170         598 : struct nb_node *nb_node_find(const char *path)
     171             : {
     172         598 :         const struct lysc_node *snode;
     173             : 
     174             :         /*
     175             :          * Use libyang to find the schema node associated to the path and get
     176             :          * the northbound node from there (snode private pointer).
     177             :          */
     178         598 :         snode = lys_find_path(ly_native_ctx, NULL, path, 0);
     179         598 :         if (!snode)
     180             :                 return NULL;
     181             : 
     182         598 :         return snode->priv;
     183             : }
     184             : 
     185           0 : void nb_node_set_dependency_cbs(const char *dependency_xpath,
     186             :                                 const char *dependant_xpath,
     187             :                                 struct nb_dependency_callbacks *cbs)
     188             : {
     189           0 :         struct nb_node *dependency = nb_node_find(dependency_xpath);
     190           0 :         struct nb_node *dependant = nb_node_find(dependant_xpath);
     191             : 
     192           0 :         if (!dependency || !dependant)
     193             :                 return;
     194             : 
     195           0 :         dependency->dep_cbs.get_dependant_xpath = cbs->get_dependant_xpath;
     196           0 :         dependant->dep_cbs.get_dependency_xpath = cbs->get_dependency_xpath;
     197             : }
     198             : 
     199           0 : bool nb_node_has_dependency(struct nb_node *node)
     200             : {
     201           0 :         return node->dep_cbs.get_dependency_xpath != NULL;
     202             : }
     203             : 
     204       19822 : static int nb_node_validate_cb(const struct nb_node *nb_node,
     205             :                                enum nb_operation operation,
     206             :                                int callback_implemented, bool optional)
     207             : {
     208       19822 :         bool valid;
     209             : 
     210       19822 :         valid = nb_operation_is_valid(operation, nb_node->snode);
     211             : 
     212             :         /*
     213             :          * Add an exception for operational data callbacks. A rw list usually
     214             :          * doesn't need any associated operational data callbacks. But if this
     215             :          * rw list is augmented by another module which adds state nodes under
     216             :          * it, then this list will need to have the 'get_next()', 'get_keys()'
     217             :          * and 'lookup_entry()' callbacks. As such, never log a warning when
     218             :          * these callbacks are implemented when they are not needed, since this
     219             :          * depends on context (e.g. some daemons might augment "frr-interface"
     220             :          * while others don't).
     221             :          */
     222       19822 :         if (!valid && callback_implemented && operation != NB_OP_GET_NEXT
     223           0 :             && operation != NB_OP_GET_KEYS && operation != NB_OP_LOOKUP_ENTRY)
     224           0 :                 flog_warn(EC_LIB_NB_CB_UNNEEDED,
     225             :                           "unneeded '%s' callback for '%s'",
     226             :                           nb_operation_name(operation), nb_node->xpath);
     227             : 
     228       19822 :         if (!optional && valid && !callback_implemented) {
     229           0 :                 flog_err(EC_LIB_NB_CB_MISSING, "missing '%s' callback for '%s'",
     230             :                          nb_operation_name(operation), nb_node->xpath);
     231           0 :                 return 1;
     232             :         }
     233             : 
     234             :         return 0;
     235             : }
     236             : 
     237             : /*
     238             :  * Check if the required callbacks were implemented for the given northbound
     239             :  * node.
     240             :  */
     241        1802 : static unsigned int nb_node_validate_cbs(const struct nb_node *nb_node)
     242             : 
     243             : {
     244        1802 :         unsigned int error = 0;
     245             : 
     246        3604 :         error += nb_node_validate_cb(nb_node, NB_OP_CREATE,
     247        1802 :                                      !!nb_node->cbs.create, false);
     248        3604 :         error += nb_node_validate_cb(nb_node, NB_OP_MODIFY,
     249        1802 :                                      !!nb_node->cbs.modify, false);
     250        3604 :         error += nb_node_validate_cb(nb_node, NB_OP_DESTROY,
     251        1802 :                                      !!nb_node->cbs.destroy, false);
     252        1802 :         error += nb_node_validate_cb(nb_node, NB_OP_MOVE, !!nb_node->cbs.move,
     253             :                                      false);
     254        3604 :         error += nb_node_validate_cb(nb_node, NB_OP_PRE_VALIDATE,
     255        1802 :                                      !!nb_node->cbs.pre_validate, true);
     256        3604 :         error += nb_node_validate_cb(nb_node, NB_OP_APPLY_FINISH,
     257        1802 :                                      !!nb_node->cbs.apply_finish, true);
     258        3604 :         error += nb_node_validate_cb(nb_node, NB_OP_GET_ELEM,
     259        1802 :                                      !!nb_node->cbs.get_elem, false);
     260        3604 :         error += nb_node_validate_cb(nb_node, NB_OP_GET_NEXT,
     261        1802 :                                      !!nb_node->cbs.get_next, false);
     262        3604 :         error += nb_node_validate_cb(nb_node, NB_OP_GET_KEYS,
     263        1802 :                                      !!nb_node->cbs.get_keys, false);
     264        3604 :         error += nb_node_validate_cb(nb_node, NB_OP_LOOKUP_ENTRY,
     265        1802 :                                      !!nb_node->cbs.lookup_entry, false);
     266        1802 :         error += nb_node_validate_cb(nb_node, NB_OP_RPC, !!nb_node->cbs.rpc,
     267             :                                      false);
     268             : 
     269        1802 :         return error;
     270             : }
     271             : 
     272        1802 : static unsigned int nb_node_validate_priority(const struct nb_node *nb_node)
     273             : {
     274             :         /* Top-level nodes can have any priority. */
     275        1802 :         if (!nb_node->parent)
     276             :                 return 0;
     277             : 
     278        1314 :         if (nb_node->priority < nb_node->parent->priority) {
     279           0 :                 flog_err(EC_LIB_NB_CB_INVALID_PRIO,
     280             :                          "node has higher priority than its parent [xpath %s]",
     281             :                          nb_node->xpath);
     282           0 :                 return 1;
     283             :         }
     284             : 
     285             :         return 0;
     286             : }
     287             : 
     288        1802 : static int nb_node_validate(const struct lysc_node *snode, void *arg)
     289             : {
     290        1802 :         struct nb_node *nb_node = snode->priv;
     291        1802 :         unsigned int *errors = arg;
     292             : 
     293             :         /* Validate callbacks and priority. */
     294        1802 :         if (nb_node) {
     295        1802 :                 *errors += nb_node_validate_cbs(nb_node);
     296        1802 :                 *errors += nb_node_validate_priority(nb_node);
     297             :         }
     298             : 
     299        1802 :         return YANG_ITER_CONTINUE;
     300             : }
     301             : 
     302           8 : struct nb_config *nb_config_new(struct lyd_node *dnode)
     303             : {
     304           8 :         struct nb_config *config;
     305             : 
     306           8 :         config = XCALLOC(MTYPE_NB_CONFIG, sizeof(*config));
     307           8 :         if (dnode)
     308           0 :                 config->dnode = dnode;
     309             :         else
     310           8 :                 config->dnode = yang_dnode_new(ly_native_ctx, true);
     311           8 :         config->version = 0;
     312             : 
     313           8 :         return config;
     314             : }
     315             : 
     316          16 : void nb_config_free(struct nb_config *config)
     317             : {
     318          16 :         if (config->dnode)
     319          16 :                 yang_dnode_free(config->dnode);
     320          16 :         XFREE(MTYPE_NB_CONFIG, config);
     321          16 : }
     322             : 
     323           0 : struct nb_config *nb_config_dup(const struct nb_config *config)
     324             : {
     325           0 :         struct nb_config *dup;
     326             : 
     327           0 :         dup = XCALLOC(MTYPE_NB_CONFIG, sizeof(*dup));
     328           0 :         dup->dnode = yang_dnode_dup(config->dnode);
     329           0 :         dup->version = config->version;
     330             : 
     331           0 :         return dup;
     332             : }
     333             : 
     334           0 : int nb_config_merge(struct nb_config *config_dst, struct nb_config *config_src,
     335             :                     bool preserve_source)
     336             : {
     337           0 :         int ret;
     338             : 
     339           0 :         ret = lyd_merge_siblings(&config_dst->dnode, config_src->dnode, 0);
     340           0 :         if (ret != 0)
     341           0 :                 flog_warn(EC_LIB_LIBYANG, "%s: lyd_merge() failed", __func__);
     342             : 
     343           0 :         if (!preserve_source)
     344           0 :                 nb_config_free(config_src);
     345             : 
     346           0 :         return (ret == 0) ? NB_OK : NB_ERR;
     347             : }
     348             : 
     349           2 : void nb_config_replace(struct nb_config *config_dst,
     350             :                        struct nb_config *config_src, bool preserve_source)
     351             : {
     352             :         /* Update version. */
     353           2 :         if (config_src->version != 0)
     354           2 :                 config_dst->version = config_src->version;
     355             : 
     356             :         /* Update dnode. */
     357           2 :         if (config_dst->dnode)
     358           2 :                 yang_dnode_free(config_dst->dnode);
     359           2 :         if (preserve_source) {
     360           2 :                 config_dst->dnode = yang_dnode_dup(config_src->dnode);
     361             :         } else {
     362           0 :                 config_dst->dnode = config_src->dnode;
     363           0 :                 config_src->dnode = NULL;
     364           0 :                 nb_config_free(config_src);
     365             :         }
     366           2 : }
     367             : 
     368             : /* Generate the nb_config_cbs tree. */
     369           0 : static inline int nb_config_cb_compare(const struct nb_config_cb *a,
     370             :                                        const struct nb_config_cb *b)
     371             : {
     372             :         /* Sort by priority first. */
     373           0 :         if (a->nb_node->priority < b->nb_node->priority)
     374             :                 return -1;
     375           0 :         if (a->nb_node->priority > b->nb_node->priority)
     376             :                 return 1;
     377             : 
     378             :         /*
     379             :          * Preserve the order of the configuration changes as told by libyang.
     380             :          */
     381           0 :         if (a->seq < b->seq)
     382             :                 return -1;
     383           0 :         if (a->seq > b->seq)
     384             :                 return 1;
     385             : 
     386             :         /*
     387             :          * All 'apply_finish' callbacks have their sequence number set to zero.
     388             :          * In this case, compare them using their dnode pointers (the order
     389             :          * doesn't matter for callbacks that have the same priority).
     390             :          */
     391           0 :         if (a->dnode < b->dnode)
     392             :                 return -1;
     393           0 :         if (a->dnode > b->dnode)
     394           0 :                 return 1;
     395             : 
     396             :         return 0;
     397             : }
     398           0 : RB_GENERATE(nb_config_cbs, nb_config_cb, entry, nb_config_cb_compare);
     399             : 
     400           2 : static void nb_config_diff_add_change(struct nb_config_cbs *changes,
     401             :                                       enum nb_operation operation,
     402             :                                       uint32_t *seq,
     403             :                                       const struct lyd_node *dnode)
     404             : {
     405           2 :         struct nb_config_change *change;
     406             : 
     407             :         /* Ignore unimplemented nodes. */
     408           2 :         if (!dnode->schema->priv)
     409             :                 return;
     410             : 
     411           2 :         change = XCALLOC(MTYPE_TMP, sizeof(*change));
     412           2 :         change->cb.operation = operation;
     413           2 :         change->cb.seq = *seq;
     414           2 :         *seq = *seq + 1;
     415           2 :         change->cb.nb_node = dnode->schema->priv;
     416           2 :         change->cb.dnode = dnode;
     417             : 
     418           2 :         RB_INSERT(nb_config_cbs, changes, &change->cb);
     419             : }
     420             : 
     421           2 : static void nb_config_diff_del_changes(struct nb_config_cbs *changes)
     422             : {
     423           2 :         while (!RB_EMPTY(nb_config_cbs, changes)) {
     424           2 :                 struct nb_config_change *change;
     425             : 
     426           2 :                 change = (struct nb_config_change *)RB_ROOT(nb_config_cbs,
     427             :                                                             changes);
     428           2 :                 RB_REMOVE(nb_config_cbs, changes, &change->cb);
     429           4 :                 XFREE(MTYPE_TMP, change);
     430             :         }
     431           2 : }
     432             : 
     433             : /*
     434             :  * Helper function used when calculating the delta between two different
     435             :  * configurations. Given a new subtree, calculate all new YANG data nodes,
     436             :  * excluding default leafs and leaf-lists. This is a recursive function.
     437             :  */
     438           6 : static void nb_config_diff_created(const struct lyd_node *dnode, uint32_t *seq,
     439             :                                    struct nb_config_cbs *changes)
     440             : {
     441           6 :         enum nb_operation operation;
     442           6 :         struct lyd_node *child;
     443             : 
     444             :         /* Ignore unimplemented nodes. */
     445           6 :         if (!dnode->schema->priv)
     446             :                 return;
     447             : 
     448           6 :         switch (dnode->schema->nodetype) {
     449           2 :         case LYS_LEAF:
     450             :         case LYS_LEAFLIST:
     451           2 :                 if (lyd_is_default(dnode))
     452             :                         break;
     453             : 
     454           2 :                 if (nb_operation_is_valid(NB_OP_CREATE, dnode->schema))
     455             :                         operation = NB_OP_CREATE;
     456           2 :                 else if (nb_operation_is_valid(NB_OP_MODIFY, dnode->schema))
     457             :                         operation = NB_OP_MODIFY;
     458             :                 else
     459             :                         return;
     460             : 
     461           0 :                 nb_config_diff_add_change(changes, operation, seq, dnode);
     462           0 :                 break;
     463           4 :         case LYS_CONTAINER:
     464             :         case LYS_LIST:
     465           4 :                 if (nb_operation_is_valid(NB_OP_CREATE, dnode->schema))
     466           2 :                         nb_config_diff_add_change(changes, NB_OP_CREATE, seq,
     467             :                                                   dnode);
     468             : 
     469             :                 /* Process child nodes recursively. */
     470           8 :                 LY_LIST_FOR (lyd_child(dnode), child) {
     471           4 :                         nb_config_diff_created(child, seq, changes);
     472             :                 }
     473             :                 break;
     474             :         default:
     475             :                 break;
     476             :         }
     477             : }
     478             : 
     479           0 : static void nb_config_diff_deleted(const struct lyd_node *dnode, uint32_t *seq,
     480             :                                    struct nb_config_cbs *changes)
     481             : {
     482             :         /* Ignore unimplemented nodes. */
     483           0 :         if (!dnode->schema->priv)
     484             :                 return;
     485             : 
     486           0 :         if (nb_operation_is_valid(NB_OP_DESTROY, dnode->schema))
     487           0 :                 nb_config_diff_add_change(changes, NB_OP_DESTROY, seq, dnode);
     488           0 :         else if (CHECK_FLAG(dnode->schema->nodetype, LYS_CONTAINER)) {
     489           0 :                 struct lyd_node *child;
     490             : 
     491             :                 /*
     492             :                  * Non-presence containers need special handling since they
     493             :                  * don't have "destroy" callbacks. In this case, what we need to
     494             :                  * do is to call the "destroy" callbacks of their child nodes
     495             :                  * when applicable (i.e. optional nodes).
     496             :                  */
     497           0 :                 LY_LIST_FOR (lyd_child(dnode), child) {
     498           0 :                         nb_config_diff_deleted(child, seq, changes);
     499             :                 }
     500             :         }
     501             : }
     502             : 
     503           4 : static int nb_lyd_diff_get_op(const struct lyd_node *dnode)
     504             : {
     505           4 :         const struct lyd_meta *meta;
     506           4 :         LY_LIST_FOR (dnode->meta, meta) {
     507           4 :                 if (strcmp(meta->name, "operation")
     508           4 :                     || strcmp(meta->annotation->module->name, "yang"))
     509           0 :                         continue;
     510           4 :                 return lyd_get_meta_value(meta)[0];
     511             :         }
     512             :         return 'n';
     513             : }
     514             : 
     515             : #if 0 /* Used below in nb_config_diff inside normally disabled code */
     516             : static inline void nb_config_diff_dnode_log_path(const char *context,
     517             :                                                  const char *path,
     518             :                                                  const struct lyd_node *dnode)
     519             : {
     520             :         if (dnode->schema->nodetype & LYD_NODE_TERM)
     521             :                 zlog_debug("nb_config_diff: %s: %s: %s", context, path,
     522             :                            lyd_get_value(dnode));
     523             :         else
     524             :                 zlog_debug("nb_config_diff: %s: %s", context, path);
     525             : }
     526             : 
     527             : static inline void nb_config_diff_dnode_log(const char *context,
     528             :                                             const struct lyd_node *dnode)
     529             : {
     530             :         if (!dnode) {
     531             :                 zlog_debug("nb_config_diff: %s: NULL", context);
     532             :                 return;
     533             :         }
     534             : 
     535             :         char *path = lyd_path(dnode, LYD_PATH_STD, NULL, 0);
     536             :         nb_config_diff_dnode_log_path(context, path, dnode);
     537             :         free(path);
     538             : }
     539             : #endif
     540             : 
     541             : /* Calculate the delta between two different configurations. */
     542           2 : static void nb_config_diff(const struct nb_config *config1,
     543             :                            const struct nb_config *config2,
     544             :                            struct nb_config_cbs *changes)
     545             : {
     546           2 :         struct lyd_node *diff = NULL;
     547           2 :         const struct lyd_node *root, *dnode;
     548           2 :         struct lyd_node *target;
     549           2 :         int op;
     550           2 :         LY_ERR err;
     551           2 :         char *path;
     552             : 
     553             : #if 0 /* Useful (noisy) when debugging diff code, and for improving later */
     554             :         if (DEBUG_MODE_CHECK(&nb_dbg_cbs_config, DEBUG_MODE_ALL)) {
     555             :                 LY_LIST_FOR(config1->dnode, root) {
     556             :                         LYD_TREE_DFS_BEGIN(root, dnode) {
     557             :                                 nb_config_diff_dnode_log("from", dnode);
     558             :                                 LYD_TREE_DFS_END(root, dnode);
     559             :                         }
     560             :                 }
     561             :                 LY_LIST_FOR(config2->dnode, root) {
     562             :                         LYD_TREE_DFS_BEGIN(root, dnode) {
     563             :                                 nb_config_diff_dnode_log("to", dnode);
     564             :                                 LYD_TREE_DFS_END(root, dnode);
     565             :                         }
     566             :                 }
     567             :         }
     568             : #endif
     569             : 
     570           2 :         err = lyd_diff_siblings(config1->dnode, config2->dnode,
     571             :                                 LYD_DIFF_DEFAULTS, &diff);
     572           2 :         assert(!err);
     573             : 
     574           2 :         if (diff && DEBUG_MODE_CHECK(&nb_dbg_cbs_config, DEBUG_MODE_ALL)) {
     575           0 :                 char *s;
     576             : 
     577           0 :                 if (!lyd_print_mem(&s, diff, LYD_JSON,
     578             :                                    LYD_PRINT_WITHSIBLINGS | LYD_PRINT_WD_ALL)) {
     579           0 :                         zlog_debug("%s: %s", __func__, s);
     580           0 :                         free(s);
     581             :                 }
     582             :         }
     583             : 
     584           2 :         uint32_t seq = 0;
     585             : 
     586           4 :         LY_LIST_FOR (diff, root) {
     587           6 :                 LYD_TREE_DFS_BEGIN (root, dnode) {
     588           4 :                         op = nb_lyd_diff_get_op(dnode);
     589             : 
     590           4 :                         path = lyd_path(dnode, LYD_PATH_STD, NULL, 0);
     591             : 
     592             : #if 0 /* Useful (noisy) when debugging diff code, and for improving later */
     593             :                         if (DEBUG_MODE_CHECK(&nb_dbg_cbs_config, DEBUG_MODE_ALL)) {
     594             :                                 char context[80];
     595             :                                 snprintf(context, sizeof(context),
     596             :                                          "iterating diff: oper: %c seq: %u", op, seq);
     597             :                                 nb_config_diff_dnode_log_path(context, path, dnode);
     598             :                         }
     599             : #endif
     600           4 :                         switch (op) {
     601           2 :                         case 'c': /* create */
     602             :                                   /*
     603             :                                    * This is rather inefficient, but when we use
     604             :                                    * dnode from the diff instead of the
     605             :                                    * candidate config node we get failures when
     606             :                                    * looking up default values, etc, based on
     607             :                                    * the diff tree.
     608             :                                    */
     609           2 :                                 target = yang_dnode_get(config2->dnode, path);
     610           2 :                                 assert(target);
     611           2 :                                 nb_config_diff_created(target, &seq, changes);
     612             : 
     613             :                                 /* Skip rest of sub-tree, move to next sibling
     614             :                                  */
     615           2 :                                 LYD_TREE_DFS_continue = 1;
     616           2 :                                 break;
     617           0 :                         case 'd': /* delete */
     618           0 :                                 target = yang_dnode_get(config1->dnode, path);
     619           0 :                                 assert(target);
     620           0 :                                 nb_config_diff_deleted(target, &seq, changes);
     621             : 
     622             :                                 /* Skip rest of sub-tree, move to next sibling
     623             :                                  */
     624           0 :                                 LYD_TREE_DFS_continue = 1;
     625           0 :                                 break;
     626           0 :                         case 'r': /* replace */
     627             :                                 /* either moving an entry or changing a value */
     628           0 :                                 target = yang_dnode_get(config2->dnode, path);
     629           0 :                                 assert(target);
     630           0 :                                 nb_config_diff_add_change(changes, NB_OP_MODIFY,
     631             :                                                           &seq, target);
     632           0 :                                 break;
     633             :                         case 'n': /* none */
     634             :                         default:
     635             :                                 break;
     636             :                         }
     637           4 :                         free(path);
     638          10 :                         LYD_TREE_DFS_END(root, dnode);
     639             :                 }
     640             :         }
     641             : 
     642           2 :         lyd_free_all(diff);
     643           2 : }
     644             : 
     645           2 : int nb_candidate_edit(struct nb_config *candidate,
     646             :                       const struct nb_node *nb_node,
     647             :                       enum nb_operation operation, const char *xpath,
     648             :                       const struct yang_data *previous,
     649             :                       const struct yang_data *data)
     650             : {
     651           2 :         struct lyd_node *dnode, *dep_dnode;
     652           2 :         char xpath_edit[XPATH_MAXLEN];
     653           2 :         char dep_xpath[XPATH_MAXLEN];
     654           2 :         LY_ERR err;
     655             : 
     656             :         /* Use special notation for leaf-lists (RFC 6020, section 9.13.5). */
     657           2 :         if (nb_node->snode->nodetype == LYS_LEAFLIST)
     658           0 :                 snprintf(xpath_edit, sizeof(xpath_edit), "%s[.='%s']", xpath,
     659           0 :                          data->value);
     660             :         else
     661           2 :                 strlcpy(xpath_edit, xpath, sizeof(xpath_edit));
     662             : 
     663           2 :         switch (operation) {
     664           2 :         case NB_OP_CREATE:
     665             :         case NB_OP_MODIFY:
     666           4 :                 err = lyd_new_path(candidate->dnode, ly_native_ctx, xpath_edit,
     667           2 :                                    (void *)data->value, LYD_NEW_PATH_UPDATE,
     668             :                                    &dnode);
     669           2 :                 if (err) {
     670           0 :                         flog_warn(EC_LIB_LIBYANG,
     671             :                                   "%s: lyd_new_path(%s) failed: %d", __func__,
     672             :                                   xpath_edit, err);
     673           0 :                         return NB_ERR;
     674           2 :                 } else if (dnode) {
     675             :                         /* Create default nodes */
     676           2 :                         LY_ERR err = lyd_new_implicit_tree(
     677             :                                 dnode, LYD_IMPLICIT_NO_STATE, NULL);
     678           2 :                         if (err) {
     679           0 :                                 flog_warn(EC_LIB_LIBYANG,
     680             :                                           "%s: lyd_new_implicit_all failed: %d",
     681             :                                           __func__, err);
     682             :                         }
     683             :                         /*
     684             :                          * create dependency
     685             :                          *
     686             :                          * dnode returned by the lyd_new_path may be from a
     687             :                          * different schema, so we need to update the nb_node
     688             :                          */
     689           2 :                         nb_node = dnode->schema->priv;
     690           2 :                         if (nb_node->dep_cbs.get_dependency_xpath) {
     691           0 :                                 nb_node->dep_cbs.get_dependency_xpath(
     692             :                                         dnode, dep_xpath);
     693             : 
     694           0 :                                 err = lyd_new_path(candidate->dnode,
     695             :                                                    ly_native_ctx, dep_xpath,
     696             :                                                    NULL, LYD_NEW_PATH_UPDATE,
     697             :                                                    &dep_dnode);
     698             :                                 /* Create default nodes */
     699           0 :                                 if (!err && dep_dnode)
     700           0 :                                         err = lyd_new_implicit_tree(
     701             :                                                 dep_dnode,
     702             :                                                 LYD_IMPLICIT_NO_STATE, NULL);
     703           0 :                                 if (err) {
     704           0 :                                         flog_warn(
     705             :                                                 EC_LIB_LIBYANG,
     706             :                                                 "%s: dependency: lyd_new_path(%s) failed: %d",
     707             :                                                 __func__, dep_xpath, err);
     708           0 :                                         return NB_ERR;
     709             :                                 }
     710             :                         }
     711             :                 }
     712             :                 break;
     713           0 :         case NB_OP_DESTROY:
     714           0 :                 dnode = yang_dnode_get(candidate->dnode, xpath_edit);
     715           0 :                 if (!dnode)
     716             :                         /*
     717             :                          * Return a special error code so the caller can choose
     718             :                          * whether to ignore it or not.
     719             :                          */
     720             :                         return NB_ERR_NOT_FOUND;
     721             :                 /* destroy dependant */
     722           0 :                 if (nb_node->dep_cbs.get_dependant_xpath) {
     723           0 :                         nb_node->dep_cbs.get_dependant_xpath(dnode, dep_xpath);
     724             : 
     725           0 :                         dep_dnode = yang_dnode_get(candidate->dnode, dep_xpath);
     726           0 :                         if (dep_dnode)
     727           0 :                                 lyd_free_tree(dep_dnode);
     728             :                 }
     729           0 :                 lyd_free_tree(dnode);
     730           0 :                 break;
     731             :         case NB_OP_MOVE:
     732             :                 /* TODO: update configuration. */
     733             :                 break;
     734           0 :         case NB_OP_PRE_VALIDATE:
     735             :         case NB_OP_APPLY_FINISH:
     736             :         case NB_OP_GET_ELEM:
     737             :         case NB_OP_GET_NEXT:
     738             :         case NB_OP_GET_KEYS:
     739             :         case NB_OP_LOOKUP_ENTRY:
     740             :         case NB_OP_RPC:
     741           0 :                 flog_warn(EC_LIB_DEVELOPMENT,
     742             :                           "%s: unknown operation (%u) [xpath %s]", __func__,
     743             :                           operation, xpath_edit);
     744           0 :                 return NB_ERR;
     745             :         }
     746             : 
     747             :         return NB_OK;
     748             : }
     749             : 
     750           0 : bool nb_candidate_needs_update(const struct nb_config *candidate)
     751             : {
     752           0 :         if (candidate->version < running_config->version)
     753           0 :                 return true;
     754             : 
     755             :         return false;
     756             : }
     757             : 
     758           0 : int nb_candidate_update(struct nb_config *candidate)
     759             : {
     760           0 :         struct nb_config *updated_config;
     761             : 
     762           0 :         updated_config = nb_config_dup(running_config);
     763           0 :         if (nb_config_merge(updated_config, candidate, true) != NB_OK)
     764             :                 return NB_ERR;
     765             : 
     766           0 :         nb_config_replace(candidate, updated_config, false);
     767             : 
     768           0 :         return NB_OK;
     769             : }
     770             : 
     771             : /*
     772             :  * Perform YANG syntactic and semantic validation.
     773             :  *
     774             :  * WARNING: lyd_validate() can change the configuration as part of the
     775             :  * validation process.
     776             :  */
     777           2 : static int nb_candidate_validate_yang(struct nb_config *candidate, char *errmsg,
     778             :                                       size_t errmsg_len)
     779             : {
     780           2 :         if (lyd_validate_all(&candidate->dnode, ly_native_ctx,
     781             :                              LYD_VALIDATE_NO_STATE, NULL)
     782             :             != 0) {
     783           0 :                 yang_print_errors(ly_native_ctx, errmsg, errmsg_len);
     784           0 :                 return NB_ERR_VALIDATION;
     785             :         }
     786             : 
     787             :         return NB_OK;
     788             : }
     789             : 
     790             : /* Perform code-level validation using the northbound callbacks. */
     791           2 : static int nb_candidate_validate_code(struct nb_context *context,
     792             :                                       struct nb_config *candidate,
     793             :                                       struct nb_config_cbs *changes,
     794             :                                       char *errmsg, size_t errmsg_len)
     795             : {
     796           2 :         struct nb_config_cb *cb;
     797           2 :         struct lyd_node *root, *child;
     798           2 :         int ret;
     799             : 
     800             :         /* First validate the candidate as a whole. */
     801          14 :         LY_LIST_FOR (candidate->dnode, root) {
     802          40 :                 LYD_TREE_DFS_BEGIN (root, child) {
     803          34 :                         struct nb_node *nb_node;
     804             : 
     805          34 :                         nb_node = child->schema->priv;
     806          34 :                         if (!nb_node || !nb_node->cbs.pre_validate)
     807          34 :                                 goto next;
     808             : 
     809           0 :                         ret = nb_callback_pre_validate(context, nb_node, child,
     810             :                                                        errmsg, errmsg_len);
     811           0 :                         if (ret != NB_OK)
     812             :                                 return NB_ERR_VALIDATION;
     813             : 
     814           0 :                 next:
     815          72 :                         LYD_TREE_DFS_END(root, child);
     816             :                 }
     817             :         }
     818             : 
     819             :         /* Now validate the configuration changes. */
     820           6 :         RB_FOREACH (cb, nb_config_cbs, changes) {
     821           2 :                 struct nb_config_change *change = (struct nb_config_change *)cb;
     822             : 
     823           2 :                 ret = nb_callback_configuration(context, NB_EV_VALIDATE, change,
     824             :                                                 errmsg, errmsg_len);
     825           2 :                 if (ret != NB_OK)
     826             :                         return NB_ERR_VALIDATION;
     827             :         }
     828             : 
     829             :         return NB_OK;
     830             : }
     831             : 
     832           0 : int nb_candidate_validate(struct nb_context *context,
     833             :                           struct nb_config *candidate, char *errmsg,
     834             :                           size_t errmsg_len)
     835             : {
     836           0 :         struct nb_config_cbs changes;
     837           0 :         int ret;
     838             : 
     839           0 :         if (nb_candidate_validate_yang(candidate, errmsg, errmsg_len) != NB_OK)
     840             :                 return NB_ERR_VALIDATION;
     841             : 
     842           0 :         RB_INIT(nb_config_cbs, &changes);
     843           0 :         nb_config_diff(running_config, candidate, &changes);
     844           0 :         ret = nb_candidate_validate_code(context, candidate, &changes, errmsg,
     845             :                                          errmsg_len);
     846           0 :         nb_config_diff_del_changes(&changes);
     847             : 
     848           0 :         return ret;
     849             : }
     850             : 
     851           2 : int nb_candidate_commit_prepare(struct nb_context *context,
     852             :                                 struct nb_config *candidate,
     853             :                                 const char *comment,
     854             :                                 struct nb_transaction **transaction,
     855             :                                 char *errmsg, size_t errmsg_len)
     856             : {
     857           2 :         struct nb_config_cbs changes;
     858             : 
     859           2 :         if (nb_candidate_validate_yang(candidate, errmsg, errmsg_len)
     860             :             != NB_OK) {
     861           0 :                 flog_warn(EC_LIB_NB_CANDIDATE_INVALID,
     862             :                           "%s: failed to validate candidate configuration",
     863             :                           __func__);
     864           0 :                 return NB_ERR_VALIDATION;
     865             :         }
     866             : 
     867           2 :         RB_INIT(nb_config_cbs, &changes);
     868           2 :         nb_config_diff(running_config, candidate, &changes);
     869           2 :         if (RB_EMPTY(nb_config_cbs, &changes)) {
     870           0 :                 snprintf(
     871             :                         errmsg, errmsg_len,
     872             :                         "No changes to apply were found during preparation phase");
     873           0 :                 return NB_ERR_NO_CHANGES;
     874             :         }
     875             : 
     876           2 :         if (nb_candidate_validate_code(context, candidate, &changes, errmsg,
     877             :                                        errmsg_len)
     878             :             != NB_OK) {
     879           0 :                 flog_warn(EC_LIB_NB_CANDIDATE_INVALID,
     880             :                           "%s: failed to validate candidate configuration",
     881             :                           __func__);
     882           0 :                 nb_config_diff_del_changes(&changes);
     883           0 :                 return NB_ERR_VALIDATION;
     884             :         }
     885             : 
     886           2 :         *transaction = nb_transaction_new(context, candidate, &changes, comment,
     887             :                                           errmsg, errmsg_len);
     888           2 :         if (*transaction == NULL) {
     889           0 :                 flog_warn(EC_LIB_NB_TRANSACTION_CREATION_FAILED,
     890             :                           "%s: failed to create transaction: %s", __func__,
     891             :                           errmsg);
     892           0 :                 nb_config_diff_del_changes(&changes);
     893           0 :                 return NB_ERR_LOCKED;
     894             :         }
     895             : 
     896           2 :         return nb_transaction_process(NB_EV_PREPARE, *transaction, errmsg,
     897             :                                       errmsg_len);
     898             : }
     899             : 
     900           0 : void nb_candidate_commit_abort(struct nb_transaction *transaction, char *errmsg,
     901             :                                size_t errmsg_len)
     902             : {
     903           0 :         (void)nb_transaction_process(NB_EV_ABORT, transaction, errmsg,
     904             :                                      errmsg_len);
     905           0 :         nb_transaction_free(transaction);
     906           0 : }
     907             : 
     908           2 : void nb_candidate_commit_apply(struct nb_transaction *transaction,
     909             :                                bool save_transaction, uint32_t *transaction_id,
     910             :                                char *errmsg, size_t errmsg_len)
     911             : {
     912           2 :         (void)nb_transaction_process(NB_EV_APPLY, transaction, errmsg,
     913             :                                      errmsg_len);
     914           2 :         nb_transaction_apply_finish(transaction, errmsg, errmsg_len);
     915             : 
     916             :         /* Replace running by candidate. */
     917           2 :         transaction->config->version++;
     918           2 :         nb_config_replace(running_config, transaction->config, true);
     919             : 
     920             :         /* Record transaction. */
     921           2 :         if (save_transaction && nb_db_enabled
     922           2 :             && nb_db_transaction_save(transaction, transaction_id) != NB_OK)
     923           0 :                 flog_warn(EC_LIB_NB_TRANSACTION_RECORD_FAILED,
     924             :                           "%s: failed to record transaction", __func__);
     925             : 
     926           2 :         nb_transaction_free(transaction);
     927           2 : }
     928             : 
     929           2 : int nb_candidate_commit(struct nb_context *context, struct nb_config *candidate,
     930             :                         bool save_transaction, const char *comment,
     931             :                         uint32_t *transaction_id, char *errmsg,
     932             :                         size_t errmsg_len)
     933             : {
     934           2 :         struct nb_transaction *transaction = NULL;
     935           2 :         int ret;
     936             : 
     937           2 :         ret = nb_candidate_commit_prepare(context, candidate, comment,
     938             :                                           &transaction, errmsg, errmsg_len);
     939             :         /*
     940             :          * Apply the changes if the preparation phase succeeded. Otherwise abort
     941             :          * the transaction.
     942             :          */
     943           2 :         if (ret == NB_OK)
     944           2 :                 nb_candidate_commit_apply(transaction, save_transaction,
     945             :                                           transaction_id, errmsg, errmsg_len);
     946           0 :         else if (transaction != NULL)
     947           0 :                 nb_candidate_commit_abort(transaction, errmsg, errmsg_len);
     948             : 
     949           2 :         return ret;
     950             : }
     951             : 
     952           0 : int nb_running_lock(enum nb_client client, const void *user)
     953             : {
     954           0 :         int ret = -1;
     955             : 
     956           0 :         frr_with_mutex (&running_config_mgmt_lock.mtx) {
     957           0 :                 if (!running_config_mgmt_lock.locked) {
     958           0 :                         running_config_mgmt_lock.locked = true;
     959           0 :                         running_config_mgmt_lock.owner_client = client;
     960           0 :                         running_config_mgmt_lock.owner_user = user;
     961           0 :                         ret = 0;
     962             :                 }
     963             :         }
     964             : 
     965           0 :         return ret;
     966             : }
     967             : 
     968           8 : int nb_running_unlock(enum nb_client client, const void *user)
     969             : {
     970           8 :         int ret = -1;
     971             : 
     972          16 :         frr_with_mutex (&running_config_mgmt_lock.mtx) {
     973           8 :                 if (running_config_mgmt_lock.locked
     974           0 :                     && running_config_mgmt_lock.owner_client == client
     975           0 :                     && running_config_mgmt_lock.owner_user == user) {
     976           0 :                         running_config_mgmt_lock.locked = false;
     977           0 :                         running_config_mgmt_lock.owner_client = NB_CLIENT_NONE;
     978           0 :                         running_config_mgmt_lock.owner_user = NULL;
     979           0 :                         ret = 0;
     980             :                 }
     981             :         }
     982             : 
     983           8 :         return ret;
     984             : }
     985             : 
     986           2 : int nb_running_lock_check(enum nb_client client, const void *user)
     987             : {
     988           2 :         int ret = -1;
     989             : 
     990           2 :         frr_with_mutex (&running_config_mgmt_lock.mtx) {
     991           2 :                 if (!running_config_mgmt_lock.locked
     992           0 :                     || (running_config_mgmt_lock.owner_client == client
     993           0 :                         && running_config_mgmt_lock.owner_user == user))
     994           2 :                         ret = 0;
     995             :         }
     996             : 
     997           2 :         return ret;
     998             : }
     999             : 
    1000           6 : static void nb_log_config_callback(const enum nb_event event,
    1001             :                                    enum nb_operation operation,
    1002             :                                    const struct lyd_node *dnode)
    1003             : {
    1004           6 :         const char *value;
    1005           6 :         char xpath[XPATH_MAXLEN];
    1006             : 
    1007           6 :         if (!DEBUG_MODE_CHECK(&nb_dbg_cbs_config, DEBUG_MODE_ALL))
    1008           6 :                 return;
    1009             : 
    1010           0 :         yang_dnode_get_path(dnode, xpath, sizeof(xpath));
    1011           0 :         if (yang_snode_is_typeless_data(dnode->schema))
    1012             :                 value = "(none)";
    1013             :         else
    1014           0 :                 value = yang_dnode_get_string(dnode, NULL);
    1015             : 
    1016           0 :         zlog_debug(
    1017             :                 "northbound callback: event [%s] op [%s] xpath [%s] value [%s]",
    1018             :                 nb_event_name(event), nb_operation_name(operation), xpath,
    1019             :                 value);
    1020             : }
    1021             : 
    1022           6 : static int nb_callback_create(struct nb_context *context,
    1023             :                               const struct nb_node *nb_node,
    1024             :                               enum nb_event event, const struct lyd_node *dnode,
    1025             :                               union nb_resource *resource, char *errmsg,
    1026             :                               size_t errmsg_len)
    1027             : {
    1028           6 :         struct nb_cb_create_args args = {};
    1029           6 :         bool unexpected_error = false;
    1030           6 :         int ret;
    1031             : 
    1032           6 :         nb_log_config_callback(event, NB_OP_CREATE, dnode);
    1033             : 
    1034           6 :         args.context = context;
    1035           6 :         args.event = event;
    1036           6 :         args.dnode = dnode;
    1037           6 :         args.resource = resource;
    1038           6 :         args.errmsg = errmsg;
    1039           6 :         args.errmsg_len = errmsg_len;
    1040           6 :         ret = nb_node->cbs.create(&args);
    1041             : 
    1042             :         /* Detect and log unexpected errors. */
    1043           6 :         switch (ret) {
    1044             :         case NB_OK:
    1045             :         case NB_ERR:
    1046             :                 break;
    1047           0 :         case NB_ERR_VALIDATION:
    1048           0 :                 if (event != NB_EV_VALIDATE)
    1049             :                         unexpected_error = true;
    1050             :                 break;
    1051           0 :         case NB_ERR_RESOURCE:
    1052           0 :                 if (event != NB_EV_PREPARE)
    1053             :                         unexpected_error = true;
    1054             :                 break;
    1055           0 :         case NB_ERR_INCONSISTENCY:
    1056           0 :                 if (event == NB_EV_VALIDATE)
    1057             :                         unexpected_error = true;
    1058             :                 break;
    1059             :         default:
    1060             :                 unexpected_error = true;
    1061             :                 break;
    1062             :         }
    1063             :         if (unexpected_error)
    1064           0 :                 DEBUGD(&nb_dbg_cbs_config,
    1065             :                        "northbound callback: unexpected return value: %s",
    1066             :                        nb_err_name(ret));
    1067             : 
    1068           6 :         return ret;
    1069             : }
    1070             : 
    1071           0 : static int nb_callback_modify(struct nb_context *context,
    1072             :                               const struct nb_node *nb_node,
    1073             :                               enum nb_event event, const struct lyd_node *dnode,
    1074             :                               union nb_resource *resource, char *errmsg,
    1075             :                               size_t errmsg_len)
    1076             : {
    1077           0 :         struct nb_cb_modify_args args = {};
    1078           0 :         bool unexpected_error = false;
    1079           0 :         int ret;
    1080             : 
    1081           0 :         nb_log_config_callback(event, NB_OP_MODIFY, dnode);
    1082             : 
    1083           0 :         args.context = context;
    1084           0 :         args.event = event;
    1085           0 :         args.dnode = dnode;
    1086           0 :         args.resource = resource;
    1087           0 :         args.errmsg = errmsg;
    1088           0 :         args.errmsg_len = errmsg_len;
    1089           0 :         ret = nb_node->cbs.modify(&args);
    1090             : 
    1091             :         /* Detect and log unexpected errors. */
    1092           0 :         switch (ret) {
    1093             :         case NB_OK:
    1094             :         case NB_ERR:
    1095             :                 break;
    1096           0 :         case NB_ERR_VALIDATION:
    1097           0 :                 if (event != NB_EV_VALIDATE)
    1098             :                         unexpected_error = true;
    1099             :                 break;
    1100           0 :         case NB_ERR_RESOURCE:
    1101           0 :                 if (event != NB_EV_PREPARE)
    1102             :                         unexpected_error = true;
    1103             :                 break;
    1104           0 :         case NB_ERR_INCONSISTENCY:
    1105           0 :                 if (event == NB_EV_VALIDATE)
    1106             :                         unexpected_error = true;
    1107             :                 break;
    1108             :         default:
    1109             :                 unexpected_error = true;
    1110             :                 break;
    1111             :         }
    1112             :         if (unexpected_error)
    1113           0 :                 DEBUGD(&nb_dbg_cbs_config,
    1114             :                        "northbound callback: unexpected return value: %s",
    1115             :                        nb_err_name(ret));
    1116             : 
    1117           0 :         return ret;
    1118             : }
    1119             : 
    1120           0 : static int nb_callback_destroy(struct nb_context *context,
    1121             :                                const struct nb_node *nb_node,
    1122             :                                enum nb_event event,
    1123             :                                const struct lyd_node *dnode, char *errmsg,
    1124             :                                size_t errmsg_len)
    1125             : {
    1126           0 :         struct nb_cb_destroy_args args = {};
    1127           0 :         bool unexpected_error = false;
    1128           0 :         int ret;
    1129             : 
    1130           0 :         nb_log_config_callback(event, NB_OP_DESTROY, dnode);
    1131             : 
    1132           0 :         args.context = context;
    1133           0 :         args.event = event;
    1134           0 :         args.dnode = dnode;
    1135           0 :         args.errmsg = errmsg;
    1136           0 :         args.errmsg_len = errmsg_len;
    1137           0 :         ret = nb_node->cbs.destroy(&args);
    1138             : 
    1139             :         /* Detect and log unexpected errors. */
    1140           0 :         switch (ret) {
    1141             :         case NB_OK:
    1142             :         case NB_ERR:
    1143             :                 break;
    1144           0 :         case NB_ERR_VALIDATION:
    1145           0 :                 if (event != NB_EV_VALIDATE)
    1146             :                         unexpected_error = true;
    1147             :                 break;
    1148           0 :         case NB_ERR_INCONSISTENCY:
    1149           0 :                 if (event == NB_EV_VALIDATE)
    1150             :                         unexpected_error = true;
    1151             :                 break;
    1152             :         default:
    1153             :                 unexpected_error = true;
    1154             :                 break;
    1155             :         }
    1156             :         if (unexpected_error)
    1157           0 :                 DEBUGD(&nb_dbg_cbs_config,
    1158             :                        "northbound callback: unexpected return value: %s",
    1159             :                        nb_err_name(ret));
    1160             : 
    1161           0 :         return ret;
    1162             : }
    1163             : 
    1164           0 : static int nb_callback_move(struct nb_context *context,
    1165             :                             const struct nb_node *nb_node, enum nb_event event,
    1166             :                             const struct lyd_node *dnode, char *errmsg,
    1167             :                             size_t errmsg_len)
    1168             : {
    1169           0 :         struct nb_cb_move_args args = {};
    1170           0 :         bool unexpected_error = false;
    1171           0 :         int ret;
    1172             : 
    1173           0 :         nb_log_config_callback(event, NB_OP_MOVE, dnode);
    1174             : 
    1175           0 :         args.context = context;
    1176           0 :         args.event = event;
    1177           0 :         args.dnode = dnode;
    1178           0 :         args.errmsg = errmsg;
    1179           0 :         args.errmsg_len = errmsg_len;
    1180           0 :         ret = nb_node->cbs.move(&args);
    1181             : 
    1182             :         /* Detect and log unexpected errors. */
    1183           0 :         switch (ret) {
    1184             :         case NB_OK:
    1185             :         case NB_ERR:
    1186             :                 break;
    1187           0 :         case NB_ERR_VALIDATION:
    1188           0 :                 if (event != NB_EV_VALIDATE)
    1189             :                         unexpected_error = true;
    1190             :                 break;
    1191           0 :         case NB_ERR_INCONSISTENCY:
    1192           0 :                 if (event == NB_EV_VALIDATE)
    1193             :                         unexpected_error = true;
    1194             :                 break;
    1195             :         default:
    1196             :                 unexpected_error = true;
    1197             :                 break;
    1198             :         }
    1199             :         if (unexpected_error)
    1200           0 :                 DEBUGD(&nb_dbg_cbs_config,
    1201             :                        "northbound callback: unexpected return value: %s",
    1202             :                        nb_err_name(ret));
    1203             : 
    1204           0 :         return ret;
    1205             : }
    1206             : 
    1207           0 : static int nb_callback_pre_validate(struct nb_context *context,
    1208             :                                     const struct nb_node *nb_node,
    1209             :                                     const struct lyd_node *dnode, char *errmsg,
    1210             :                                     size_t errmsg_len)
    1211             : {
    1212           0 :         struct nb_cb_pre_validate_args args = {};
    1213           0 :         bool unexpected_error = false;
    1214           0 :         int ret;
    1215             : 
    1216           0 :         nb_log_config_callback(NB_EV_VALIDATE, NB_OP_PRE_VALIDATE, dnode);
    1217             : 
    1218           0 :         args.dnode = dnode;
    1219           0 :         args.errmsg = errmsg;
    1220           0 :         args.errmsg_len = errmsg_len;
    1221           0 :         ret = nb_node->cbs.pre_validate(&args);
    1222             : 
    1223             :         /* Detect and log unexpected errors. */
    1224           0 :         switch (ret) {
    1225             :         case NB_OK:
    1226             :         case NB_ERR_VALIDATION:
    1227             :                 break;
    1228             :         default:
    1229           0 :                 unexpected_error = true;
    1230           0 :                 break;
    1231             :         }
    1232           0 :         if (unexpected_error)
    1233           0 :                 DEBUGD(&nb_dbg_cbs_config,
    1234             :                        "northbound callback: unexpected return value: %s",
    1235             :                        nb_err_name(ret));
    1236             : 
    1237           0 :         return ret;
    1238             : }
    1239             : 
    1240           0 : static void nb_callback_apply_finish(struct nb_context *context,
    1241             :                                      const struct nb_node *nb_node,
    1242             :                                      const struct lyd_node *dnode, char *errmsg,
    1243             :                                      size_t errmsg_len)
    1244             : {
    1245           0 :         struct nb_cb_apply_finish_args args = {};
    1246             : 
    1247           0 :         nb_log_config_callback(NB_EV_APPLY, NB_OP_APPLY_FINISH, dnode);
    1248             : 
    1249           0 :         args.context = context;
    1250           0 :         args.dnode = dnode;
    1251           0 :         args.errmsg = errmsg;
    1252           0 :         args.errmsg_len = errmsg_len;
    1253           0 :         nb_node->cbs.apply_finish(&args);
    1254           0 : }
    1255             : 
    1256           0 : struct yang_data *nb_callback_get_elem(const struct nb_node *nb_node,
    1257             :                                        const char *xpath,
    1258             :                                        const void *list_entry)
    1259             : {
    1260           0 :         struct nb_cb_get_elem_args args = {};
    1261             : 
    1262           0 :         DEBUGD(&nb_dbg_cbs_state,
    1263             :                "northbound callback (get_elem): xpath [%s] list_entry [%p]",
    1264             :                xpath, list_entry);
    1265             : 
    1266           0 :         args.xpath = xpath;
    1267           0 :         args.list_entry = list_entry;
    1268           0 :         return nb_node->cbs.get_elem(&args);
    1269             : }
    1270             : 
    1271           0 : const void *nb_callback_get_next(const struct nb_node *nb_node,
    1272             :                                  const void *parent_list_entry,
    1273             :                                  const void *list_entry)
    1274             : {
    1275           0 :         struct nb_cb_get_next_args args = {};
    1276             : 
    1277           0 :         DEBUGD(&nb_dbg_cbs_state,
    1278             :                "northbound callback (get_next): node [%s] parent_list_entry [%p] list_entry [%p]",
    1279             :                nb_node->xpath, parent_list_entry, list_entry);
    1280             : 
    1281           0 :         args.parent_list_entry = parent_list_entry;
    1282           0 :         args.list_entry = list_entry;
    1283           0 :         return nb_node->cbs.get_next(&args);
    1284             : }
    1285             : 
    1286           0 : int nb_callback_get_keys(const struct nb_node *nb_node, const void *list_entry,
    1287             :                          struct yang_list_keys *keys)
    1288             : {
    1289           0 :         struct nb_cb_get_keys_args args = {};
    1290             : 
    1291           0 :         DEBUGD(&nb_dbg_cbs_state,
    1292             :                "northbound callback (get_keys): node [%s] list_entry [%p]",
    1293             :                nb_node->xpath, list_entry);
    1294             : 
    1295           0 :         args.list_entry = list_entry;
    1296           0 :         args.keys = keys;
    1297           0 :         return nb_node->cbs.get_keys(&args);
    1298             : }
    1299             : 
    1300           0 : const void *nb_callback_lookup_entry(const struct nb_node *nb_node,
    1301             :                                      const void *parent_list_entry,
    1302             :                                      const struct yang_list_keys *keys)
    1303             : {
    1304           0 :         struct nb_cb_lookup_entry_args args = {};
    1305             : 
    1306           0 :         DEBUGD(&nb_dbg_cbs_state,
    1307             :                "northbound callback (lookup_entry): node [%s] parent_list_entry [%p]",
    1308             :                nb_node->xpath, parent_list_entry);
    1309             : 
    1310           0 :         args.parent_list_entry = parent_list_entry;
    1311           0 :         args.keys = keys;
    1312           0 :         return nb_node->cbs.lookup_entry(&args);
    1313             : }
    1314             : 
    1315           0 : int nb_callback_rpc(const struct nb_node *nb_node, const char *xpath,
    1316             :                     const struct list *input, struct list *output, char *errmsg,
    1317             :                     size_t errmsg_len)
    1318             : {
    1319           0 :         struct nb_cb_rpc_args args = {};
    1320             : 
    1321           0 :         DEBUGD(&nb_dbg_cbs_rpc, "northbound RPC: %s", xpath);
    1322             : 
    1323           0 :         args.xpath = xpath;
    1324           0 :         args.input = input;
    1325           0 :         args.output = output;
    1326           0 :         args.errmsg = errmsg;
    1327           0 :         args.errmsg_len = errmsg_len;
    1328           0 :         return nb_node->cbs.rpc(&args);
    1329             : }
    1330             : 
    1331             : /*
    1332             :  * Call the northbound configuration callback associated to a given
    1333             :  * configuration change.
    1334             :  */
    1335           6 : static int nb_callback_configuration(struct nb_context *context,
    1336             :                                      const enum nb_event event,
    1337             :                                      struct nb_config_change *change,
    1338             :                                      char *errmsg, size_t errmsg_len)
    1339             : {
    1340           6 :         enum nb_operation operation = change->cb.operation;
    1341           6 :         char xpath[XPATH_MAXLEN];
    1342           6 :         const struct nb_node *nb_node = change->cb.nb_node;
    1343           6 :         const struct lyd_node *dnode = change->cb.dnode;
    1344           6 :         union nb_resource *resource;
    1345           6 :         int ret = NB_ERR;
    1346             : 
    1347           6 :         if (event == NB_EV_VALIDATE)
    1348             :                 resource = NULL;
    1349             :         else
    1350           4 :                 resource = &change->resource;
    1351             : 
    1352           6 :         switch (operation) {
    1353           6 :         case NB_OP_CREATE:
    1354           6 :                 ret = nb_callback_create(context, nb_node, event, dnode,
    1355             :                                          resource, errmsg, errmsg_len);
    1356           6 :                 break;
    1357           0 :         case NB_OP_MODIFY:
    1358           0 :                 ret = nb_callback_modify(context, nb_node, event, dnode,
    1359             :                                          resource, errmsg, errmsg_len);
    1360           0 :                 break;
    1361           0 :         case NB_OP_DESTROY:
    1362           0 :                 ret = nb_callback_destroy(context, nb_node, event, dnode,
    1363             :                                           errmsg, errmsg_len);
    1364           0 :                 break;
    1365           0 :         case NB_OP_MOVE:
    1366           0 :                 ret = nb_callback_move(context, nb_node, event, dnode, errmsg,
    1367             :                                        errmsg_len);
    1368           0 :                 break;
    1369           0 :         case NB_OP_PRE_VALIDATE:
    1370             :         case NB_OP_APPLY_FINISH:
    1371             :         case NB_OP_GET_ELEM:
    1372             :         case NB_OP_GET_NEXT:
    1373             :         case NB_OP_GET_KEYS:
    1374             :         case NB_OP_LOOKUP_ENTRY:
    1375             :         case NB_OP_RPC:
    1376           0 :                 yang_dnode_get_path(dnode, xpath, sizeof(xpath));
    1377           0 :                 flog_err(EC_LIB_DEVELOPMENT,
    1378             :                          "%s: unknown operation (%u) [xpath %s]", __func__,
    1379             :                          operation, xpath);
    1380           0 :                 exit(1);
    1381             :         }
    1382             : 
    1383           6 :         if (ret != NB_OK) {
    1384           0 :                 yang_dnode_get_path(dnode, xpath, sizeof(xpath));
    1385             : 
    1386           0 :                 switch (event) {
    1387           0 :                 case NB_EV_VALIDATE:
    1388           0 :                         flog_warn(EC_LIB_NB_CB_CONFIG_VALIDATE,
    1389             :                                   "error processing configuration change: error [%s] event [%s] operation [%s] xpath [%s]%s%s",
    1390             :                                   nb_err_name(ret), nb_event_name(event),
    1391             :                                   nb_operation_name(operation), xpath,
    1392             :                                   errmsg[0] ? " message: " : "", errmsg);
    1393           0 :                         break;
    1394           0 :                 case NB_EV_PREPARE:
    1395           0 :                         flog_warn(EC_LIB_NB_CB_CONFIG_PREPARE,
    1396             :                                   "error processing configuration change: error [%s] event [%s] operation [%s] xpath [%s]%s%s",
    1397             :                                   nb_err_name(ret), nb_event_name(event),
    1398             :                                   nb_operation_name(operation), xpath,
    1399             :                                   errmsg[0] ? " message: " : "", errmsg);
    1400           0 :                         break;
    1401           0 :                 case NB_EV_ABORT:
    1402           0 :                         flog_warn(EC_LIB_NB_CB_CONFIG_ABORT,
    1403             :                                   "error processing configuration change: error [%s] event [%s] operation [%s] xpath [%s]%s%s",
    1404             :                                   nb_err_name(ret), nb_event_name(event),
    1405             :                                   nb_operation_name(operation), xpath,
    1406             :                                   errmsg[0] ? " message: " : "", errmsg);
    1407           0 :                         break;
    1408           0 :                 case NB_EV_APPLY:
    1409           0 :                         flog_err(EC_LIB_NB_CB_CONFIG_APPLY,
    1410             :                                  "error processing configuration change: error [%s] event [%s] operation [%s] xpath [%s]%s%s",
    1411             :                                  nb_err_name(ret), nb_event_name(event),
    1412             :                                  nb_operation_name(operation), xpath,
    1413             :                                  errmsg[0] ? " message: " : "", errmsg);
    1414           0 :                         break;
    1415           0 :                 default:
    1416           0 :                         flog_err(EC_LIB_DEVELOPMENT,
    1417             :                                  "%s: unknown event (%u) [xpath %s]", __func__,
    1418             :                                  event, xpath);
    1419           0 :                         exit(1);
    1420             :                 }
    1421             :         }
    1422             : 
    1423           6 :         return ret;
    1424             : }
    1425             : 
    1426             : static struct nb_transaction *
    1427           2 : nb_transaction_new(struct nb_context *context, struct nb_config *config,
    1428             :                    struct nb_config_cbs *changes, const char *comment,
    1429             :                    char *errmsg, size_t errmsg_len)
    1430             : {
    1431           2 :         struct nb_transaction *transaction;
    1432             : 
    1433           2 :         if (nb_running_lock_check(context->client, context->user)) {
    1434           0 :                 strlcpy(errmsg,
    1435             :                         "running configuration is locked by another client",
    1436             :                         errmsg_len);
    1437           0 :                 return NULL;
    1438             :         }
    1439             : 
    1440           2 :         if (transaction_in_progress) {
    1441           0 :                 strlcpy(errmsg,
    1442             :                         "there's already another transaction in progress",
    1443             :                         errmsg_len);
    1444           0 :                 return NULL;
    1445             :         }
    1446           2 :         transaction_in_progress = true;
    1447             : 
    1448           2 :         transaction = XCALLOC(MTYPE_TMP, sizeof(*transaction));
    1449           2 :         transaction->context = context;
    1450           2 :         if (comment)
    1451           0 :                 strlcpy(transaction->comment, comment,
    1452             :                         sizeof(transaction->comment));
    1453           2 :         transaction->config = config;
    1454           2 :         transaction->changes = *changes;
    1455             : 
    1456           2 :         return transaction;
    1457             : }
    1458             : 
    1459           2 : static void nb_transaction_free(struct nb_transaction *transaction)
    1460             : {
    1461           2 :         nb_config_diff_del_changes(&transaction->changes);
    1462           2 :         XFREE(MTYPE_TMP, transaction);
    1463           2 :         transaction_in_progress = false;
    1464           2 : }
    1465             : 
    1466             : /* Process all configuration changes associated to a transaction. */
    1467           4 : static int nb_transaction_process(enum nb_event event,
    1468             :                                   struct nb_transaction *transaction,
    1469             :                                   char *errmsg, size_t errmsg_len)
    1470             : {
    1471           4 :         struct nb_config_cb *cb;
    1472             : 
    1473          12 :         RB_FOREACH (cb, nb_config_cbs, &transaction->changes) {
    1474           4 :                 struct nb_config_change *change = (struct nb_config_change *)cb;
    1475           4 :                 int ret;
    1476             : 
    1477             :                 /*
    1478             :                  * Only try to release resources that were allocated
    1479             :                  * successfully.
    1480             :                  */
    1481           4 :                 if (event == NB_EV_ABORT && !change->prepare_ok)
    1482             :                         break;
    1483             : 
    1484             :                 /* Call the appropriate callback. */
    1485           4 :                 ret = nb_callback_configuration(transaction->context, event,
    1486             :                                                 change, errmsg, errmsg_len);
    1487           4 :                 switch (event) {
    1488           2 :                 case NB_EV_PREPARE:
    1489           2 :                         if (ret != NB_OK)
    1490           0 :                                 return ret;
    1491           2 :                         change->prepare_ok = true;
    1492           2 :                         break;
    1493             :                 case NB_EV_ABORT:
    1494             :                 case NB_EV_APPLY:
    1495             :                         /*
    1496             :                          * At this point it's not possible to reject the
    1497             :                          * transaction anymore, so any failure here can lead to
    1498             :                          * inconsistencies and should be treated as a bug.
    1499             :                          * Operations prone to errors, like validations and
    1500             :                          * resource allocations, should be performed during the
    1501             :                          * 'prepare' phase.
    1502             :                          */
    1503             :                         break;
    1504             :                 case NB_EV_VALIDATE:
    1505             :                         break;
    1506             :                 }
    1507             :         }
    1508             : 
    1509             :         return NB_OK;
    1510             : }
    1511             : 
    1512             : static struct nb_config_cb *
    1513           0 : nb_apply_finish_cb_new(struct nb_config_cbs *cbs, const struct nb_node *nb_node,
    1514             :                        const struct lyd_node *dnode)
    1515             : {
    1516           0 :         struct nb_config_cb *cb;
    1517             : 
    1518           0 :         cb = XCALLOC(MTYPE_TMP, sizeof(*cb));
    1519           0 :         cb->nb_node = nb_node;
    1520           0 :         cb->dnode = dnode;
    1521           0 :         RB_INSERT(nb_config_cbs, cbs, cb);
    1522             : 
    1523           0 :         return cb;
    1524             : }
    1525             : 
    1526             : static struct nb_config_cb *
    1527           0 : nb_apply_finish_cb_find(struct nb_config_cbs *cbs,
    1528             :                         const struct nb_node *nb_node,
    1529             :                         const struct lyd_node *dnode)
    1530             : {
    1531           0 :         struct nb_config_cb s;
    1532             : 
    1533           0 :         s.seq = 0;
    1534           0 :         s.nb_node = nb_node;
    1535           0 :         s.dnode = dnode;
    1536           0 :         return RB_FIND(nb_config_cbs, cbs, &s);
    1537             : }
    1538             : 
    1539             : /* Call the 'apply_finish' callbacks. */
    1540           2 : static void nb_transaction_apply_finish(struct nb_transaction *transaction,
    1541             :                                         char *errmsg, size_t errmsg_len)
    1542             : {
    1543           2 :         struct nb_config_cbs cbs;
    1544           2 :         struct nb_config_cb *cb;
    1545             : 
    1546             :         /* Initialize tree of 'apply_finish' callbacks. */
    1547           2 :         RB_INIT(nb_config_cbs, &cbs);
    1548             : 
    1549             :         /* Identify the 'apply_finish' callbacks that need to be called. */
    1550           6 :         RB_FOREACH (cb, nb_config_cbs, &transaction->changes) {
    1551           2 :                 struct nb_config_change *change = (struct nb_config_change *)cb;
    1552           2 :                 const struct lyd_node *dnode = change->cb.dnode;
    1553             : 
    1554             :                 /*
    1555             :                  * Iterate up to the root of the data tree. When a node is being
    1556             :                  * deleted, skip its 'apply_finish' callback if one is defined
    1557             :                  * (the 'apply_finish' callbacks from the node ancestors should
    1558             :                  * be called though).
    1559             :                  */
    1560           2 :                 if (change->cb.operation == NB_OP_DESTROY) {
    1561           0 :                         char xpath[XPATH_MAXLEN];
    1562             : 
    1563           0 :                         dnode = lyd_parent(dnode);
    1564           0 :                         if (!dnode)
    1565             :                                 break;
    1566             : 
    1567             :                         /*
    1568             :                          * The dnode from 'delete' callbacks point to elements
    1569             :                          * from the running configuration. Use yang_dnode_get()
    1570             :                          * to get the corresponding dnode from the candidate
    1571             :                          * configuration that is being committed.
    1572             :                          */
    1573           0 :                         yang_dnode_get_path(dnode, xpath, sizeof(xpath));
    1574           0 :                         dnode = yang_dnode_get(transaction->config->dnode,
    1575             :                                                xpath);
    1576             :                 }
    1577           6 :                 while (dnode) {
    1578           4 :                         struct nb_node *nb_node;
    1579             : 
    1580           4 :                         nb_node = dnode->schema->priv;
    1581           4 :                         if (!nb_node || !nb_node->cbs.apply_finish)
    1582           4 :                                 goto next;
    1583             : 
    1584             :                         /*
    1585             :                          * Don't call the callback more than once for the same
    1586             :                          * data node.
    1587             :                          */
    1588           0 :                         if (nb_apply_finish_cb_find(&cbs, nb_node, dnode))
    1589           0 :                                 goto next;
    1590             : 
    1591           0 :                         nb_apply_finish_cb_new(&cbs, nb_node, dnode);
    1592             : 
    1593             :                 next:
    1594          10 :                         dnode = lyd_parent(dnode);
    1595             :                 }
    1596             :         }
    1597             : 
    1598             :         /* Call the 'apply_finish' callbacks, sorted by their priorities. */
    1599           4 :         RB_FOREACH (cb, nb_config_cbs, &cbs)
    1600           0 :                 nb_callback_apply_finish(transaction->context, cb->nb_node,
    1601             :                                          cb->dnode, errmsg, errmsg_len);
    1602             : 
    1603             :         /* Release memory. */
    1604           2 :         while (!RB_EMPTY(nb_config_cbs, &cbs)) {
    1605           0 :                 cb = RB_ROOT(nb_config_cbs, &cbs);
    1606           0 :                 RB_REMOVE(nb_config_cbs, &cbs, cb);
    1607           2 :                 XFREE(MTYPE_TMP, cb);
    1608             :         }
    1609           2 : }
    1610             : 
    1611           0 : static int nb_oper_data_iter_children(const struct lysc_node *snode,
    1612             :                                       const char *xpath, const void *list_entry,
    1613             :                                       const struct yang_list_keys *list_keys,
    1614             :                                       struct yang_translator *translator,
    1615             :                                       bool first, uint32_t flags,
    1616             :                                       nb_oper_data_cb cb, void *arg)
    1617             : {
    1618           0 :         const struct lysc_node *child;
    1619             : 
    1620           0 :         LY_LIST_FOR (lysc_node_child(snode), child) {
    1621           0 :                 int ret;
    1622             : 
    1623           0 :                 ret = nb_oper_data_iter_node(child, xpath, list_entry,
    1624             :                                              list_keys, translator, false,
    1625             :                                              flags, cb, arg);
    1626           0 :                 if (ret != NB_OK)
    1627           0 :                         return ret;
    1628             :         }
    1629             : 
    1630             :         return NB_OK;
    1631             : }
    1632             : 
    1633           0 : static int nb_oper_data_iter_leaf(const struct nb_node *nb_node,
    1634             :                                   const char *xpath, const void *list_entry,
    1635             :                                   const struct yang_list_keys *list_keys,
    1636             :                                   struct yang_translator *translator,
    1637             :                                   uint32_t flags, nb_oper_data_cb cb, void *arg)
    1638             : {
    1639           0 :         struct yang_data *data;
    1640             : 
    1641           0 :         if (CHECK_FLAG(nb_node->snode->flags, LYS_CONFIG_W))
    1642             :                 return NB_OK;
    1643             : 
    1644             :         /* Ignore list keys. */
    1645           0 :         if (lysc_is_key(nb_node->snode))
    1646             :                 return NB_OK;
    1647             : 
    1648           0 :         data = nb_callback_get_elem(nb_node, xpath, list_entry);
    1649           0 :         if (data == NULL)
    1650             :                 /* Leaf of type "empty" is not present. */
    1651             :                 return NB_OK;
    1652             : 
    1653           0 :         return (*cb)(nb_node->snode, translator, data, arg);
    1654             : }
    1655             : 
    1656           0 : static int nb_oper_data_iter_container(const struct nb_node *nb_node,
    1657             :                                        const char *xpath,
    1658             :                                        const void *list_entry,
    1659             :                                        const struct yang_list_keys *list_keys,
    1660             :                                        struct yang_translator *translator,
    1661             :                                        uint32_t flags, nb_oper_data_cb cb,
    1662             :                                        void *arg)
    1663             : {
    1664           0 :         const struct lysc_node *snode = nb_node->snode;
    1665             : 
    1666           0 :         if (CHECK_FLAG(nb_node->flags, F_NB_NODE_CONFIG_ONLY))
    1667             :                 return NB_OK;
    1668             : 
    1669             :         /* Read-only presence containers. */
    1670           0 :         if (nb_node->cbs.get_elem) {
    1671           0 :                 struct yang_data *data;
    1672           0 :                 int ret;
    1673             : 
    1674           0 :                 data = nb_callback_get_elem(nb_node, xpath, list_entry);
    1675           0 :                 if (data == NULL)
    1676             :                         /* Presence container is not present. */
    1677             :                         return NB_OK;
    1678             : 
    1679           0 :                 ret = (*cb)(snode, translator, data, arg);
    1680           0 :                 if (ret != NB_OK)
    1681             :                         return ret;
    1682             :         }
    1683             : 
    1684             :         /* Read-write presence containers. */
    1685           0 :         if (CHECK_FLAG(snode->flags, LYS_CONFIG_W)) {
    1686           0 :                 struct lysc_node_container *scontainer;
    1687             : 
    1688           0 :                 scontainer = (struct lysc_node_container *)snode;
    1689           0 :                 if (CHECK_FLAG(scontainer->flags, LYS_PRESENCE)
    1690           0 :                     && !yang_dnode_get(running_config->dnode, xpath))
    1691             :                         return NB_OK;
    1692             :         }
    1693             : 
    1694             :         /* Iterate over the child nodes. */
    1695           0 :         return nb_oper_data_iter_children(snode, xpath, list_entry, list_keys,
    1696             :                                           translator, false, flags, cb, arg);
    1697             : }
    1698             : 
    1699             : static int
    1700           0 : nb_oper_data_iter_leaflist(const struct nb_node *nb_node, const char *xpath,
    1701             :                            const void *parent_list_entry,
    1702             :                            const struct yang_list_keys *parent_list_keys,
    1703             :                            struct yang_translator *translator, uint32_t flags,
    1704             :                            nb_oper_data_cb cb, void *arg)
    1705             : {
    1706           0 :         const void *list_entry = NULL;
    1707             : 
    1708           0 :         if (CHECK_FLAG(nb_node->snode->flags, LYS_CONFIG_W))
    1709             :                 return NB_OK;
    1710             : 
    1711           0 :         do {
    1712           0 :                 struct yang_data *data;
    1713           0 :                 int ret;
    1714             : 
    1715           0 :                 list_entry = nb_callback_get_next(nb_node, parent_list_entry,
    1716             :                                                   list_entry);
    1717           0 :                 if (!list_entry)
    1718             :                         /* End of the list. */
    1719             :                         break;
    1720             : 
    1721           0 :                 data = nb_callback_get_elem(nb_node, xpath, list_entry);
    1722           0 :                 if (data == NULL)
    1723           0 :                         continue;
    1724             : 
    1725           0 :                 ret = (*cb)(nb_node->snode, translator, data, arg);
    1726           0 :                 if (ret != NB_OK)
    1727           0 :                         return ret;
    1728             :         } while (list_entry);
    1729             : 
    1730             :         return NB_OK;
    1731             : }
    1732             : 
    1733           0 : static int nb_oper_data_iter_list(const struct nb_node *nb_node,
    1734             :                                   const char *xpath_list,
    1735             :                                   const void *parent_list_entry,
    1736             :                                   const struct yang_list_keys *parent_list_keys,
    1737             :                                   struct yang_translator *translator,
    1738             :                                   uint32_t flags, nb_oper_data_cb cb, void *arg)
    1739             : {
    1740           0 :         const struct lysc_node *snode = nb_node->snode;
    1741           0 :         const void *list_entry = NULL;
    1742           0 :         uint32_t position = 1;
    1743             : 
    1744           0 :         if (CHECK_FLAG(nb_node->flags, F_NB_NODE_CONFIG_ONLY))
    1745             :                 return NB_OK;
    1746             : 
    1747             :         /* Iterate over all list entries. */
    1748           0 :         do {
    1749           0 :                 const struct lysc_node_leaf *skey;
    1750           0 :                 struct yang_list_keys list_keys;
    1751           0 :                 char xpath[XPATH_MAXLEN * 2];
    1752           0 :                 int ret;
    1753             : 
    1754             :                 /* Obtain list entry. */
    1755           0 :                 list_entry = nb_callback_get_next(nb_node, parent_list_entry,
    1756             :                                                   list_entry);
    1757           0 :                 if (!list_entry)
    1758             :                         /* End of the list. */
    1759             :                         break;
    1760             : 
    1761           0 :                 if (!CHECK_FLAG(nb_node->flags, F_NB_NODE_KEYLESS_LIST)) {
    1762             :                         /* Obtain the list entry keys. */
    1763           0 :                         if (nb_callback_get_keys(nb_node, list_entry,
    1764             :                                                  &list_keys)
    1765             :                             != NB_OK) {
    1766           0 :                                 flog_warn(EC_LIB_NB_CB_STATE,
    1767             :                                           "%s: failed to get list keys",
    1768             :                                           __func__);
    1769           0 :                                 return NB_ERR;
    1770             :                         }
    1771             : 
    1772             :                         /* Build XPath of the list entry. */
    1773           0 :                         strlcpy(xpath, xpath_list, sizeof(xpath));
    1774           0 :                         unsigned int i = 0;
    1775           0 :                         LY_FOR_KEYS (snode, skey) {
    1776           0 :                                 assert(i < list_keys.num);
    1777           0 :                                 snprintf(xpath + strlen(xpath),
    1778           0 :                                          sizeof(xpath) - strlen(xpath),
    1779           0 :                                          "[%s='%s']", skey->name,
    1780           0 :                                          list_keys.key[i]);
    1781           0 :                                 i++;
    1782             :                         }
    1783           0 :                         assert(i == list_keys.num);
    1784             :                 } else {
    1785             :                         /*
    1786             :                          * Keyless list - build XPath using a positional index.
    1787             :                          */
    1788           0 :                         snprintf(xpath, sizeof(xpath), "%s[%u]", xpath_list,
    1789             :                                  position);
    1790           0 :                         position++;
    1791             :                 }
    1792             : 
    1793             :                 /* Iterate over the child nodes. */
    1794           0 :                 ret = nb_oper_data_iter_children(
    1795           0 :                         nb_node->snode, xpath, list_entry, &list_keys,
    1796             :                         translator, false, flags, cb, arg);
    1797           0 :                 if (ret != NB_OK)
    1798           0 :                         return ret;
    1799           0 :         } while (list_entry);
    1800             : 
    1801           0 :         return NB_OK;
    1802             : }
    1803             : 
    1804           0 : static int nb_oper_data_iter_node(const struct lysc_node *snode,
    1805             :                                   const char *xpath_parent,
    1806             :                                   const void *list_entry,
    1807             :                                   const struct yang_list_keys *list_keys,
    1808             :                                   struct yang_translator *translator,
    1809             :                                   bool first, uint32_t flags,
    1810             :                                   nb_oper_data_cb cb, void *arg)
    1811             : {
    1812           0 :         struct nb_node *nb_node;
    1813           0 :         char xpath[XPATH_MAXLEN];
    1814           0 :         int ret = NB_OK;
    1815             : 
    1816           0 :         if (!first && CHECK_FLAG(flags, NB_OPER_DATA_ITER_NORECURSE)
    1817           0 :             && CHECK_FLAG(snode->nodetype, LYS_CONTAINER | LYS_LIST))
    1818             :                 return NB_OK;
    1819             : 
    1820             :         /* Update XPath. */
    1821           0 :         strlcpy(xpath, xpath_parent, sizeof(xpath));
    1822           0 :         if (!first && snode->nodetype != LYS_USES) {
    1823           0 :                 struct lysc_node *parent;
    1824             : 
    1825             :                 /* Get the real parent. */
    1826           0 :                 parent = snode->parent;
    1827             : 
    1828             :                 /*
    1829             :                  * When necessary, include the namespace of the augmenting
    1830             :                  * module.
    1831             :                  */
    1832           0 :                 if (parent && parent->module != snode->module)
    1833           0 :                         snprintf(xpath + strlen(xpath),
    1834           0 :                                  sizeof(xpath) - strlen(xpath), "/%s:%s",
    1835           0 :                                  snode->module->name, snode->name);
    1836             :                 else
    1837           0 :                         snprintf(xpath + strlen(xpath),
    1838           0 :                                  sizeof(xpath) - strlen(xpath), "/%s",
    1839           0 :                                  snode->name);
    1840             :         }
    1841             : 
    1842           0 :         nb_node = snode->priv;
    1843           0 :         switch (snode->nodetype) {
    1844           0 :         case LYS_CONTAINER:
    1845           0 :                 ret = nb_oper_data_iter_container(nb_node, xpath, list_entry,
    1846             :                                                   list_keys, translator, flags,
    1847             :                                                   cb, arg);
    1848           0 :                 break;
    1849           0 :         case LYS_LEAF:
    1850           0 :                 ret = nb_oper_data_iter_leaf(nb_node, xpath, list_entry,
    1851             :                                              list_keys, translator, flags, cb,
    1852             :                                              arg);
    1853           0 :                 break;
    1854           0 :         case LYS_LEAFLIST:
    1855           0 :                 ret = nb_oper_data_iter_leaflist(nb_node, xpath, list_entry,
    1856             :                                                  list_keys, translator, flags,
    1857             :                                                  cb, arg);
    1858           0 :                 break;
    1859           0 :         case LYS_LIST:
    1860           0 :                 ret = nb_oper_data_iter_list(nb_node, xpath, list_entry,
    1861             :                                              list_keys, translator, flags, cb,
    1862             :                                              arg);
    1863           0 :                 break;
    1864           0 :         case LYS_USES:
    1865           0 :                 ret = nb_oper_data_iter_children(snode, xpath, list_entry,
    1866             :                                                  list_keys, translator, false,
    1867             :                                                  flags, cb, arg);
    1868           0 :                 break;
    1869             :         default:
    1870             :                 break;
    1871             :         }
    1872             : 
    1873             :         return ret;
    1874             : }
    1875             : 
    1876           0 : int nb_oper_data_iterate(const char *xpath, struct yang_translator *translator,
    1877             :                          uint32_t flags, nb_oper_data_cb cb, void *arg)
    1878             : {
    1879           0 :         struct nb_node *nb_node;
    1880           0 :         const void *list_entry = NULL;
    1881           0 :         struct yang_list_keys list_keys;
    1882           0 :         struct list *list_dnodes;
    1883           0 :         struct lyd_node *dnode, *dn;
    1884           0 :         struct listnode *ln;
    1885           0 :         int ret;
    1886             : 
    1887           0 :         nb_node = nb_node_find(xpath);
    1888           0 :         if (!nb_node) {
    1889           0 :                 flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH,
    1890             :                           "%s: unknown data path: %s", __func__, xpath);
    1891           0 :                 return NB_ERR;
    1892             :         }
    1893             : 
    1894             :         /* For now this function works only with containers and lists. */
    1895           0 :         if (!CHECK_FLAG(nb_node->snode->nodetype, LYS_CONTAINER | LYS_LIST)) {
    1896           0 :                 flog_warn(
    1897             :                         EC_LIB_NB_OPERATIONAL_DATA,
    1898             :                         "%s: can't iterate over YANG leaf or leaf-list [xpath %s]",
    1899             :                         __func__, xpath);
    1900           0 :                 return NB_ERR;
    1901             :         }
    1902             : 
    1903             :         /*
    1904             :          * Create a data tree from the XPath so that we can parse the keys of
    1905             :          * all YANG lists (if any).
    1906             :          */
    1907             : 
    1908           0 :         LY_ERR err = lyd_new_path(NULL, ly_native_ctx, xpath, NULL,
    1909             :                                   LYD_NEW_PATH_UPDATE, &dnode);
    1910           0 :         if (err || !dnode) {
    1911           0 :                 const char *errmsg =
    1912           0 :                         err ? ly_errmsg(ly_native_ctx) : "node not found";
    1913           0 :                 flog_warn(EC_LIB_LIBYANG, "%s: lyd_new_path() failed %s",
    1914             :                           __func__, errmsg);
    1915           0 :                 return NB_ERR;
    1916             :         }
    1917             : 
    1918             :         /*
    1919             :          * Create a linked list to sort the data nodes starting from the root.
    1920             :          */
    1921           0 :         list_dnodes = list_new();
    1922           0 :         for (dn = dnode; dn; dn = lyd_parent(dn)) {
    1923           0 :                 if (dn->schema->nodetype != LYS_LIST || !lyd_child(dn))
    1924           0 :                         continue;
    1925           0 :                 listnode_add_head(list_dnodes, dn);
    1926             :         }
    1927             :         /*
    1928             :          * Use the northbound callbacks to find list entry pointer corresponding
    1929             :          * to the given XPath.
    1930             :          */
    1931           0 :         for (ALL_LIST_ELEMENTS_RO(list_dnodes, ln, dn)) {
    1932           0 :                 struct lyd_node *child;
    1933           0 :                 struct nb_node *nn;
    1934           0 :                 unsigned int n = 0;
    1935             : 
    1936             :                 /* Obtain the list entry keys. */
    1937           0 :                 memset(&list_keys, 0, sizeof(list_keys));
    1938           0 :                 LY_LIST_FOR (lyd_child(dn), child) {
    1939           0 :                         if (!lysc_is_key(child->schema))
    1940             :                                 break;
    1941           0 :                         strlcpy(list_keys.key[n],
    1942             :                                 yang_dnode_get_string(child, NULL),
    1943             :                                 sizeof(list_keys.key[n]));
    1944           0 :                         n++;
    1945             :                 }
    1946           0 :                 list_keys.num = n;
    1947           0 :                 if (list_keys.num != yang_snode_num_keys(dn->schema)) {
    1948           0 :                         list_delete(&list_dnodes);
    1949           0 :                         yang_dnode_free(dnode);
    1950           0 :                         return NB_ERR_NOT_FOUND;
    1951             :                 }
    1952             : 
    1953             :                 /* Find the list entry pointer. */
    1954           0 :                 nn = dn->schema->priv;
    1955           0 :                 if (!nn->cbs.lookup_entry) {
    1956           0 :                         flog_warn(
    1957             :                                 EC_LIB_NB_OPERATIONAL_DATA,
    1958             :                                 "%s: data path doesn't support iteration over operational data: %s",
    1959             :                                 __func__, xpath);
    1960           0 :                         list_delete(&list_dnodes);
    1961           0 :                         yang_dnode_free(dnode);
    1962           0 :                         return NB_ERR;
    1963             :                 }
    1964             : 
    1965           0 :                 list_entry =
    1966           0 :                         nb_callback_lookup_entry(nn, list_entry, &list_keys);
    1967           0 :                 if (list_entry == NULL) {
    1968           0 :                         list_delete(&list_dnodes);
    1969           0 :                         yang_dnode_free(dnode);
    1970           0 :                         return NB_ERR_NOT_FOUND;
    1971             :                 }
    1972             :         }
    1973             : 
    1974             :         /* If a list entry was given, iterate over that list entry only. */
    1975           0 :         if (dnode->schema->nodetype == LYS_LIST && lyd_child(dnode))
    1976           0 :                 ret = nb_oper_data_iter_children(
    1977             :                         nb_node->snode, xpath, list_entry, &list_keys,
    1978             :                         translator, true, flags, cb, arg);
    1979             :         else
    1980           0 :                 ret = nb_oper_data_iter_node(nb_node->snode, xpath, list_entry,
    1981             :                                              &list_keys, translator, true,
    1982             :                                              flags, cb, arg);
    1983             : 
    1984           0 :         list_delete(&list_dnodes);
    1985           0 :         yang_dnode_free(dnode);
    1986             : 
    1987           0 :         return ret;
    1988             : }
    1989             : 
    1990       19830 : bool nb_operation_is_valid(enum nb_operation operation,
    1991             :                            const struct lysc_node *snode)
    1992             : {
    1993       19830 :         struct nb_node *nb_node = snode->priv;
    1994       19830 :         struct lysc_node_container *scontainer;
    1995       19830 :         struct lysc_node_leaf *sleaf;
    1996             : 
    1997       19830 :         switch (operation) {
    1998        1808 :         case NB_OP_CREATE:
    1999        1808 :                 if (!CHECK_FLAG(snode->flags, LYS_CONFIG_W))
    2000             :                         return false;
    2001             : 
    2002         836 :                 switch (snode->nodetype) {
    2003         444 :                 case LYS_LEAF:
    2004         444 :                         sleaf = (struct lysc_node_leaf *)snode;
    2005         444 :                         if (sleaf->type->basetype != LY_TYPE_EMPTY)
    2006             :                                 return false;
    2007             :                         break;
    2008          54 :                 case LYS_CONTAINER:
    2009          54 :                         scontainer = (struct lysc_node_container *)snode;
    2010          54 :                         if (!CHECK_FLAG(scontainer->flags, LYS_PRESENCE))
    2011             :                                 return false;
    2012             :                         break;
    2013             :                 case LYS_LIST:
    2014             :                 case LYS_LEAFLIST:
    2015             :                         break;
    2016             :                 default:
    2017             :                         return false;
    2018             :                 }
    2019             :                 return true;
    2020        1804 :         case NB_OP_MODIFY:
    2021        1804 :                 if (!CHECK_FLAG(snode->flags, LYS_CONFIG_W))
    2022             :                         return false;
    2023             : 
    2024         832 :                 switch (snode->nodetype) {
    2025         444 :                 case LYS_LEAF:
    2026         444 :                         sleaf = (struct lysc_node_leaf *)snode;
    2027         444 :                         if (sleaf->type->basetype == LY_TYPE_EMPTY)
    2028             :                                 return false;
    2029             : 
    2030             :                         /* List keys can't be modified. */
    2031         422 :                         if (lysc_is_key(sleaf))
    2032             :                                 return false;
    2033             :                         break;
    2034             :                 default:
    2035             :                         return false;
    2036             :                 }
    2037             :                 return true;
    2038        1802 :         case NB_OP_DESTROY:
    2039        1802 :                 if (!CHECK_FLAG(snode->flags, LYS_CONFIG_W))
    2040             :                         return false;
    2041             : 
    2042         830 :                 switch (snode->nodetype) {
    2043             :                 case LYS_LEAF:
    2044         442 :                         sleaf = (struct lysc_node_leaf *)snode;
    2045             : 
    2046             :                         /* List keys can't be deleted. */
    2047         442 :                         if (lysc_is_key(sleaf))
    2048             :                                 return false;
    2049             : 
    2050             :                         /*
    2051             :                          * Only optional leafs can be deleted, or leafs whose
    2052             :                          * parent is a case statement.
    2053             :                          */
    2054         384 :                         if (snode->parent->nodetype == LYS_CASE)
    2055             :                                 return true;
    2056         146 :                         if (sleaf->when)
    2057             :                                 return true;
    2058         136 :                         if (CHECK_FLAG(sleaf->flags, LYS_MAND_TRUE)
    2059         108 :                             || sleaf->dflt)
    2060             :                                 return false;
    2061             :                         break;
    2062          52 :                 case LYS_CONTAINER:
    2063          52 :                         scontainer = (struct lysc_node_container *)snode;
    2064          52 :                         if (!CHECK_FLAG(scontainer->flags, LYS_PRESENCE))
    2065             :                                 return false;
    2066             :                         break;
    2067             :                 case LYS_LIST:
    2068             :                 case LYS_LEAFLIST:
    2069             :                         break;
    2070             :                 default:
    2071             :                         return false;
    2072             :                 }
    2073             :                 return true;
    2074        1802 :         case NB_OP_MOVE:
    2075        1802 :                 if (!CHECK_FLAG(snode->flags, LYS_CONFIG_W))
    2076             :                         return false;
    2077             : 
    2078         830 :                 switch (snode->nodetype) {
    2079          44 :                 case LYS_LIST:
    2080             :                 case LYS_LEAFLIST:
    2081          44 :                         if (!CHECK_FLAG(snode->flags, LYS_ORDBY_USER))
    2082             :                                 return false;
    2083             :                         break;
    2084             :                 default:
    2085             :                         return false;
    2086             :                 }
    2087             :                 return true;
    2088        3604 :         case NB_OP_PRE_VALIDATE:
    2089             :         case NB_OP_APPLY_FINISH:
    2090        3604 :                 if (!CHECK_FLAG(snode->flags, LYS_CONFIG_W))
    2091             :                         return false;
    2092             :                 return true;
    2093        1802 :         case NB_OP_GET_ELEM:
    2094        1802 :                 if (!CHECK_FLAG(snode->flags, LYS_CONFIG_R))
    2095             :                         return false;
    2096             : 
    2097         152 :                 switch (snode->nodetype) {
    2098             :                 case LYS_LEAF:
    2099             :                 case LYS_LEAFLIST:
    2100             :                         break;
    2101          18 :                 case LYS_CONTAINER:
    2102          18 :                         scontainer = (struct lysc_node_container *)snode;
    2103          18 :                         if (!CHECK_FLAG(scontainer->flags, LYS_PRESENCE))
    2104             :                                 return false;
    2105             :                         break;
    2106             :                 default:
    2107             :                         return false;
    2108             :                 }
    2109             :                 return true;
    2110        1802 :         case NB_OP_GET_NEXT:
    2111        1802 :                 switch (snode->nodetype) {
    2112         102 :                 case LYS_LIST:
    2113         102 :                         if (CHECK_FLAG(nb_node->flags, F_NB_NODE_CONFIG_ONLY))
    2114             :                                 return false;
    2115             :                         break;
    2116           0 :                 case LYS_LEAFLIST:
    2117           0 :                         if (CHECK_FLAG(snode->flags, LYS_CONFIG_W))
    2118             :                                 return false;
    2119             :                         break;
    2120             :                 default:
    2121             :                         return false;
    2122             :                 }
    2123             :                 return true;
    2124        3604 :         case NB_OP_GET_KEYS:
    2125             :         case NB_OP_LOOKUP_ENTRY:
    2126        3604 :                 switch (snode->nodetype) {
    2127         204 :                 case LYS_LIST:
    2128         204 :                         if (CHECK_FLAG(nb_node->flags, F_NB_NODE_CONFIG_ONLY))
    2129             :                                 return false;
    2130          36 :                         if (CHECK_FLAG(nb_node->flags, F_NB_NODE_KEYLESS_LIST))
    2131             :                                 return false;
    2132             :                         break;
    2133             :                 default:
    2134             :                         return false;
    2135             :                 }
    2136             :                 return true;
    2137        1802 :         case NB_OP_RPC:
    2138        1802 :                 if (CHECK_FLAG(snode->flags, LYS_CONFIG_W | LYS_CONFIG_R))
    2139             :                         return false;
    2140             : 
    2141         820 :                 switch (snode->nodetype) {
    2142             :                 case LYS_RPC:
    2143             :                 case LYS_ACTION:
    2144             :                         break;
    2145             :                 default:
    2146             :                         return false;
    2147             :                 }
    2148             :                 return true;
    2149             :         default:
    2150             :                 return false;
    2151             :         }
    2152             : }
    2153             : 
    2154           0 : DEFINE_HOOK(nb_notification_send, (const char *xpath, struct list *arguments),
    2155             :             (xpath, arguments));
    2156             : 
    2157           0 : int nb_notification_send(const char *xpath, struct list *arguments)
    2158             : {
    2159           0 :         int ret;
    2160             : 
    2161           0 :         DEBUGD(&nb_dbg_notif, "northbound notification: %s", xpath);
    2162             : 
    2163           0 :         ret = hook_call(nb_notification_send, xpath, arguments);
    2164           0 :         if (arguments)
    2165           0 :                 list_delete(&arguments);
    2166             : 
    2167           0 :         return ret;
    2168             : }
    2169             : 
    2170             : /* Running configuration user pointers management. */
    2171             : struct nb_config_entry {
    2172             :         char xpath[XPATH_MAXLEN];
    2173             :         void *entry;
    2174             : };
    2175             : 
    2176           0 : static bool running_config_entry_cmp(const void *value1, const void *value2)
    2177             : {
    2178           0 :         const struct nb_config_entry *c1 = value1;
    2179           0 :         const struct nb_config_entry *c2 = value2;
    2180             : 
    2181           0 :         return strmatch(c1->xpath, c2->xpath);
    2182             : }
    2183             : 
    2184           2 : static unsigned int running_config_entry_key_make(const void *value)
    2185             : {
    2186           2 :         return string_hash_make(value);
    2187             : }
    2188             : 
    2189           2 : static void *running_config_entry_alloc(void *p)
    2190             : {
    2191           2 :         struct nb_config_entry *new, *key = p;
    2192             : 
    2193           2 :         new = XCALLOC(MTYPE_NB_CONFIG_ENTRY, sizeof(*new));
    2194           2 :         strlcpy(new->xpath, key->xpath, sizeof(new->xpath));
    2195             : 
    2196           2 :         return new;
    2197             : }
    2198             : 
    2199           2 : static void running_config_entry_free(void *arg)
    2200             : {
    2201           2 :         XFREE(MTYPE_NB_CONFIG_ENTRY, arg);
    2202           0 : }
    2203             : 
    2204           2 : void nb_running_set_entry(const struct lyd_node *dnode, void *entry)
    2205             : {
    2206           2 :         struct nb_config_entry *config, s;
    2207             : 
    2208           2 :         yang_dnode_get_path(dnode, s.xpath, sizeof(s.xpath));
    2209           2 :         config = hash_get(running_config_entries, &s,
    2210             :                           running_config_entry_alloc);
    2211           2 :         config->entry = entry;
    2212           2 : }
    2213             : 
    2214           0 : void nb_running_move_tree(const char *xpath_from, const char *xpath_to)
    2215             : {
    2216           0 :         struct nb_config_entry *entry;
    2217           0 :         struct list *entries = hash_to_list(running_config_entries);
    2218           0 :         struct listnode *ln;
    2219             : 
    2220           0 :         for (ALL_LIST_ELEMENTS_RO(entries, ln, entry)) {
    2221           0 :                 if (!frrstr_startswith(entry->xpath, xpath_from))
    2222           0 :                         continue;
    2223             : 
    2224           0 :                 hash_release(running_config_entries, entry);
    2225             : 
    2226           0 :                 char *newpath =
    2227           0 :                         frrstr_replace(entry->xpath, xpath_from, xpath_to);
    2228           0 :                 strlcpy(entry->xpath, newpath, sizeof(entry->xpath));
    2229           0 :                 XFREE(MTYPE_TMP, newpath);
    2230             : 
    2231           0 :                 (void)hash_get(running_config_entries, entry,
    2232             :                                hash_alloc_intern);
    2233             :         }
    2234             : 
    2235           0 :         list_delete(&entries);
    2236           0 : }
    2237             : 
    2238           0 : static void *nb_running_unset_entry_helper(const struct lyd_node *dnode)
    2239             : {
    2240           0 :         struct nb_config_entry *config, s;
    2241           0 :         struct lyd_node *child;
    2242           0 :         void *entry = NULL;
    2243             : 
    2244           0 :         yang_dnode_get_path(dnode, s.xpath, sizeof(s.xpath));
    2245           0 :         config = hash_release(running_config_entries, &s);
    2246           0 :         if (config) {
    2247           0 :                 entry = config->entry;
    2248           0 :                 running_config_entry_free(config);
    2249             :         }
    2250             : 
    2251             :         /* Unset user pointers from the child nodes. */
    2252           0 :         if (CHECK_FLAG(dnode->schema->nodetype, LYS_LIST | LYS_CONTAINER)) {
    2253           0 :                 LY_LIST_FOR (lyd_child(dnode), child) {
    2254           0 :                         (void)nb_running_unset_entry_helper(child);
    2255             :                 }
    2256             :         }
    2257             : 
    2258           0 :         return entry;
    2259             : }
    2260             : 
    2261           0 : void *nb_running_unset_entry(const struct lyd_node *dnode)
    2262             : {
    2263           0 :         void *entry;
    2264             : 
    2265           0 :         entry = nb_running_unset_entry_helper(dnode);
    2266           0 :         assert(entry);
    2267             : 
    2268           0 :         return entry;
    2269             : }
    2270             : 
    2271           0 : static void *nb_running_get_entry_worker(const struct lyd_node *dnode,
    2272             :                                          const char *xpath,
    2273             :                                          bool abort_if_not_found,
    2274             :                                          bool rec_search)
    2275             : {
    2276           0 :         const struct lyd_node *orig_dnode = dnode;
    2277           0 :         char xpath_buf[XPATH_MAXLEN];
    2278           0 :         bool rec_flag = true;
    2279             : 
    2280           0 :         assert(dnode || xpath);
    2281             : 
    2282           0 :         if (!dnode)
    2283           0 :                 dnode = yang_dnode_get(running_config->dnode, xpath);
    2284             : 
    2285           0 :         while (rec_flag && dnode) {
    2286           0 :                 struct nb_config_entry *config, s;
    2287             : 
    2288           0 :                 yang_dnode_get_path(dnode, s.xpath, sizeof(s.xpath));
    2289           0 :                 config = hash_lookup(running_config_entries, &s);
    2290           0 :                 if (config)
    2291           0 :                         return config->entry;
    2292             : 
    2293           0 :                 rec_flag = rec_search;
    2294             : 
    2295           0 :                 dnode = lyd_parent(dnode);
    2296             :         }
    2297             : 
    2298           0 :         if (!abort_if_not_found)
    2299             :                 return NULL;
    2300             : 
    2301           0 :         yang_dnode_get_path(orig_dnode, xpath_buf, sizeof(xpath_buf));
    2302           0 :         flog_err(EC_LIB_YANG_DNODE_NOT_FOUND,
    2303             :                  "%s: failed to find entry [xpath %s]", __func__, xpath_buf);
    2304           0 :         zlog_backtrace(LOG_ERR);
    2305           0 :         abort();
    2306             : }
    2307             : 
    2308           0 : void *nb_running_get_entry(const struct lyd_node *dnode, const char *xpath,
    2309             :                            bool abort_if_not_found)
    2310             : {
    2311           0 :         return nb_running_get_entry_worker(dnode, xpath, abort_if_not_found,
    2312             :                                            true);
    2313             : }
    2314             : 
    2315           0 : void *nb_running_get_entry_non_rec(const struct lyd_node *dnode,
    2316             :                                    const char *xpath, bool abort_if_not_found)
    2317             : {
    2318           0 :         return nb_running_get_entry_worker(dnode, xpath, abort_if_not_found,
    2319             :                                            false);
    2320             : }
    2321             : 
    2322             : /* Logging functions. */
    2323           0 : const char *nb_event_name(enum nb_event event)
    2324             : {
    2325           0 :         switch (event) {
    2326             :         case NB_EV_VALIDATE:
    2327             :                 return "validate";
    2328           0 :         case NB_EV_PREPARE:
    2329           0 :                 return "prepare";
    2330           0 :         case NB_EV_ABORT:
    2331           0 :                 return "abort";
    2332           0 :         case NB_EV_APPLY:
    2333           0 :                 return "apply";
    2334             :         }
    2335             : 
    2336           0 :         assert(!"Reached end of function we should never hit");
    2337             : }
    2338             : 
    2339           0 : const char *nb_operation_name(enum nb_operation operation)
    2340             : {
    2341           0 :         switch (operation) {
    2342             :         case NB_OP_CREATE:
    2343             :                 return "create";
    2344           0 :         case NB_OP_MODIFY:
    2345           0 :                 return "modify";
    2346           0 :         case NB_OP_DESTROY:
    2347           0 :                 return "destroy";
    2348           0 :         case NB_OP_MOVE:
    2349           0 :                 return "move";
    2350           0 :         case NB_OP_PRE_VALIDATE:
    2351           0 :                 return "pre_validate";
    2352           0 :         case NB_OP_APPLY_FINISH:
    2353           0 :                 return "apply_finish";
    2354           0 :         case NB_OP_GET_ELEM:
    2355           0 :                 return "get_elem";
    2356           0 :         case NB_OP_GET_NEXT:
    2357           0 :                 return "get_next";
    2358           0 :         case NB_OP_GET_KEYS:
    2359           0 :                 return "get_keys";
    2360           0 :         case NB_OP_LOOKUP_ENTRY:
    2361           0 :                 return "lookup_entry";
    2362           0 :         case NB_OP_RPC:
    2363           0 :                 return "rpc";
    2364             :         }
    2365             : 
    2366           0 :         assert(!"Reached end of function we should never hit");
    2367             : }
    2368             : 
    2369           0 : const char *nb_err_name(enum nb_error error)
    2370             : {
    2371           0 :         switch (error) {
    2372             :         case NB_OK:
    2373             :                 return "ok";
    2374           0 :         case NB_ERR:
    2375           0 :                 return "generic error";
    2376           0 :         case NB_ERR_NO_CHANGES:
    2377           0 :                 return "no changes";
    2378           0 :         case NB_ERR_NOT_FOUND:
    2379           0 :                 return "element not found";
    2380           0 :         case NB_ERR_LOCKED:
    2381           0 :                 return "resource is locked";
    2382           0 :         case NB_ERR_VALIDATION:
    2383           0 :                 return "validation";
    2384           0 :         case NB_ERR_RESOURCE:
    2385           0 :                 return "failed to allocate resource";
    2386           0 :         case NB_ERR_INCONSISTENCY:
    2387           0 :                 return "internal inconsistency";
    2388             :         }
    2389             : 
    2390           0 :         assert(!"Reached end of function we should never hit");
    2391             : }
    2392             : 
    2393           0 : const char *nb_client_name(enum nb_client client)
    2394             : {
    2395           0 :         switch (client) {
    2396             :         case NB_CLIENT_CLI:
    2397             :                 return "CLI";
    2398           0 :         case NB_CLIENT_CONFD:
    2399           0 :                 return "ConfD";
    2400           0 :         case NB_CLIENT_SYSREPO:
    2401           0 :                 return "Sysrepo";
    2402           0 :         case NB_CLIENT_GRPC:
    2403           0 :                 return "gRPC";
    2404           0 :         case NB_CLIENT_PCEP:
    2405           0 :                 return "Pcep";
    2406           0 :         case NB_CLIENT_NONE:
    2407           0 :                 return "None";
    2408             :         }
    2409             : 
    2410           0 :         assert(!"Reached end of function we should never hit");
    2411             : }
    2412             : 
    2413          24 : static void nb_load_callbacks(const struct frr_yang_module_info *module)
    2414             : {
    2415         620 :         for (size_t i = 0; module->nodes[i].xpath; i++) {
    2416         596 :                 struct nb_node *nb_node;
    2417         596 :                 uint32_t priority;
    2418             : 
    2419         596 :                 if (i > YANG_MODULE_MAX_NODES) {
    2420           0 :                         zlog_err(
    2421             :                                 "%s: %s.yang has more than %u nodes. Please increase YANG_MODULE_MAX_NODES to fix this problem.",
    2422             :                                 __func__, module->name, YANG_MODULE_MAX_NODES);
    2423           0 :                         exit(1);
    2424             :                 }
    2425             : 
    2426         596 :                 nb_node = nb_node_find(module->nodes[i].xpath);
    2427         596 :                 if (!nb_node) {
    2428           0 :                         flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH,
    2429             :                                   "%s: unknown data path: %s", __func__,
    2430             :                                   module->nodes[i].xpath);
    2431           0 :                         continue;
    2432             :                 }
    2433             : 
    2434         596 :                 nb_node->cbs = module->nodes[i].cbs;
    2435         596 :                 priority = module->nodes[i].priority;
    2436         596 :                 if (priority != 0)
    2437           4 :                         nb_node->priority = priority;
    2438             :         }
    2439          24 : }
    2440             : 
    2441           4 : void nb_validate_callbacks(void)
    2442             : {
    2443           4 :         unsigned int errors = 0;
    2444             : 
    2445           4 :         yang_snodes_iterate(NULL, nb_node_validate, 0, &errors);
    2446           4 :         if (errors > 0) {
    2447           0 :                 flog_err(
    2448             :                         EC_LIB_NB_CBS_VALIDATION,
    2449             :                         "%s: failed to validate northbound callbacks: %u error(s)",
    2450             :                         __func__, errors);
    2451           0 :                 exit(1);
    2452             :         }
    2453           4 : }
    2454             : 
    2455             : 
    2456           4 : void nb_init(struct thread_master *tm,
    2457             :              const struct frr_yang_module_info *const modules[],
    2458             :              size_t nmodules, bool db_enabled)
    2459           4 : {
    2460           4 :         struct yang_module *loaded[nmodules], **loadedp = loaded;
    2461           4 :         bool explicit_compile;
    2462             : 
    2463             :         /*
    2464             :          * Currently using this explicit compile feature in libyang2 leads to
    2465             :          * incorrect behavior in FRR. The functionality suppresses the compiling
    2466             :          * of modules until they have all been loaded into the context. This
    2467             :          * avoids multiple recompiles of the same modules as they are
    2468             :          * imported/augmented etc.
    2469             :          */
    2470           4 :         explicit_compile = false;
    2471             : 
    2472           4 :         nb_db_enabled = db_enabled;
    2473             : 
    2474           4 :         yang_init(true, explicit_compile);
    2475             : 
    2476             :         /* Load YANG modules and their corresponding northbound callbacks. */
    2477          28 :         for (size_t i = 0; i < nmodules; i++) {
    2478          24 :                 DEBUGD(&nb_dbg_events, "northbound: loading %s.yang",
    2479             :                        modules[i]->name);
    2480          24 :                 *loadedp++ = yang_module_load(modules[i]->name);
    2481             :         }
    2482             : 
    2483             :         if (explicit_compile)
    2484             :                 yang_init_loading_complete();
    2485             : 
    2486             :         /* Initialize the compiled nodes with northbound data */
    2487          28 :         for (size_t i = 0; i < nmodules; i++) {
    2488          24 :                 yang_snodes_iterate(loaded[i]->info, nb_node_new_cb, 0, NULL);
    2489          24 :                 nb_load_callbacks(modules[i]);
    2490             :         }
    2491             : 
    2492             :         /* Validate northbound callbacks. */
    2493           4 :         nb_validate_callbacks();
    2494             : 
    2495             :         /* Create an empty running configuration. */
    2496           4 :         running_config = nb_config_new(NULL);
    2497           4 :         running_config_entries = hash_create(running_config_entry_key_make,
    2498             :                                              running_config_entry_cmp,
    2499             :                                              "Running Configuration Entries");
    2500           4 :         pthread_mutex_init(&running_config_mgmt_lock.mtx, NULL);
    2501             : 
    2502             :         /* Initialize the northbound CLI. */
    2503           4 :         nb_cli_init(tm);
    2504           4 : }
    2505             : 
    2506           8 : void nb_terminate(void)
    2507             : {
    2508             :         /* Terminate the northbound CLI. */
    2509           8 :         nb_cli_terminate();
    2510             : 
    2511             :         /* Delete all nb_node's from all YANG modules. */
    2512           8 :         nb_nodes_delete();
    2513             : 
    2514             :         /* Delete the running configuration. */
    2515           8 :         hash_clean(running_config_entries, running_config_entry_free);
    2516           8 :         hash_free(running_config_entries);
    2517           8 :         nb_config_free(running_config);
    2518           8 :         pthread_mutex_destroy(&running_config_mgmt_lock.mtx);
    2519           8 : }

Generated by: LCOV version v1.16-topotato