Coverage Report

Created: 2025-08-26 06:20

/src/libyang/src/plugins_exts/yangdata.c
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * @file yangdata.c
3
 * @author Radek Krejci <rkrejci@cesnet.cz>
4
 * @brief libyang extension plugin - yang-data (RFC 8040)
5
 *
6
 * Copyright (c) 2021 CESNET, z.s.p.o.
7
 *
8
 * This source code is licensed under BSD 3-Clause License (the "License").
9
 * You may not use this file except in compliance with the License.
10
 * You may obtain a copy of the License at
11
 *
12
 *     https://opensource.org/licenses/BSD-3-Clause
13
 */
14
15
#include <stdint.h>
16
#include <stdlib.h>
17
#include <string.h>
18
19
#include "libyang.h"
20
#include "plugins_exts.h"
21
22
/**
23
 * @brief Free yang-data extension instances' data.
24
 *
25
 * Implementation of ::lyplg_clb_free_clb callback set as lyext_plugin::free.
26
 */
27
static void
28
yangdata_free(struct ly_ctx *ctx, struct lysc_ext_instance *ext)
29
0
{
30
0
    lyplg_ext_instance_substatements_free(ctx, ext->substmts);
31
0
}
32
33
/**
34
 * @brief Compile yang-data extension instances.
35
 *
36
 * Implementation of ::lyplg_ext_compile_clb callback set as lyext_plugin::compile.
37
 */
38
static LY_ERR
39
yangdata_compile(struct lysc_ctx *cctx, const struct lysp_ext_instance *p_ext, struct lysc_ext_instance *c_ext)
40
0
{
41
0
    LY_ERR ret;
42
0
    LY_ARRAY_COUNT_TYPE u;
43
0
    struct lysc_module *mod_c;
44
0
    const struct lysc_node *child;
45
0
    ly_bool valid = 1;
46
0
    uint32_t prev_options = *lysc_ctx_get_options(cctx);
47
48
    /* yang-data can appear only at the top level of a YANG module or submodule */
49
0
    if ((c_ext->parent_stmt != LY_STMT_MODULE) && (c_ext->parent_stmt != LY_STMT_SUBMODULE)) {
50
0
        lyplg_ext_log(c_ext, LY_LLWRN, 0, lysc_ctx_get_path(cctx),
51
0
                "Extension %s is ignored since it appears as a non top-level statement in \"%s\" statement.",
52
0
                p_ext->name, ly_stmt2str(c_ext->parent_stmt));
53
0
        return LY_ENOT;
54
0
    }
55
56
0
    mod_c = (struct lysc_module *)c_ext->parent;
57
58
    /* check for duplication */
59
0
    LY_ARRAY_FOR(mod_c->exts, u) {
60
0
        if ((&mod_c->exts[u] != c_ext) && (mod_c->exts[u].def == c_ext->def) && !strcmp(mod_c->exts[u].argument, c_ext->argument)) {
61
            /* duplication of the same yang-data extension in a single module */
62
0
            lyplg_ext_log(c_ext, LY_LLERR, LY_EVALID, lysc_ctx_get_path(cctx), "Extension %s is instantiated multiple times.", p_ext->name);
63
0
            return LY_EVALID;
64
0
        }
65
0
    }
66
67
    /* compile annotation substatements
68
     * To let the compilation accept different statements possibly leading to the container top-level node, there are 3
69
     * allowed substatements pointing to a single storage. But when compiled, the substaments list is compressed just to
70
     * a single item providing the schema tree. */
71
0
    LY_ARRAY_CREATE_GOTO(cctx->ctx, c_ext->substmts, 3, ret, emem);
72
0
    LY_ARRAY_INCREMENT(c_ext->substmts);
73
0
    c_ext->substmts[0].stmt = LY_STMT_CONTAINER;
74
0
    c_ext->substmts[0].cardinality = LY_STMT_CARD_OPT;
75
0
    c_ext->substmts[0].storage = &c_ext->data;
76
77
0
    LY_ARRAY_INCREMENT(c_ext->substmts);
78
0
    c_ext->substmts[1].stmt = LY_STMT_CHOICE;
79
0
    c_ext->substmts[1].cardinality = LY_STMT_CARD_OPT;
80
0
    c_ext->substmts[1].storage = &c_ext->data;
81
82
0
    LY_ARRAY_INCREMENT(c_ext->substmts);
83
0
    c_ext->substmts[2].stmt = LY_STMT_USES;
84
0
    c_ext->substmts[2].cardinality = LY_STMT_CARD_OPT;
85
0
    c_ext->substmts[2].storage = &c_ext->data;
86
87
0
    *lysc_ctx_get_options(cctx) |= LYS_COMPILE_NO_CONFIG | LYS_COMPILE_NO_DISABLED;
88
0
    ret = lys_compile_extension_instance(cctx, p_ext, c_ext);
89
0
    *lysc_ctx_get_options(cctx) = prev_options;
90
0
    LY_ARRAY_DECREMENT(c_ext->substmts);
91
0
    LY_ARRAY_DECREMENT(c_ext->substmts);
92
0
    if (ret) {
93
0
        return ret;
94
0
    }
95
96
    /* check that we have really just a single container data definition in the top */
97
0
    child = *(struct lysc_node **)c_ext->substmts[0].storage;
98
0
    if (!child) {
99
0
        valid = 0;
100
0
        lyplg_ext_log(c_ext, LY_LLERR, LY_EVALID, lysc_ctx_get_path(cctx),
101
0
                "Extension %s is instantiated without any top level data node, but exactly one container data node is expected.",
102
0
                p_ext->name);
103
0
    } else if (child->next) {
104
0
        valid = 0;
105
0
        lyplg_ext_log(c_ext, LY_LLERR, LY_EVALID, lysc_ctx_get_path(cctx),
106
0
                "Extension %s is instantiated with multiple top level data nodes, but only a single container data node is allowed.",
107
0
                p_ext->name);
108
0
    } else if (child->nodetype == LYS_CHOICE) {
109
        /* all the choice's case are expected to result to a single container node */
110
0
        const struct lysc_node *snode = NULL;
111
112
0
        while ((snode = lys_getnext(snode, child, mod_c, 0))) {
113
0
            if (snode->next) {
114
0
                valid = 0;
115
0
                lyplg_ext_log(c_ext, LY_LLERR, LY_EVALID, lysc_ctx_get_path(cctx),
116
0
                        "Extension %s is instantiated with multiple top level data nodes (inside a single choice's case), "
117
0
                        "but only a single container data node is allowed.", p_ext->name);
118
0
                break;
119
0
            } else if (snode->nodetype != LYS_CONTAINER) {
120
0
                valid = 0;
121
0
                lyplg_ext_log(c_ext, LY_LLERR, LY_EVALID, lysc_ctx_get_path(cctx),
122
0
                        "Extension %s is instantiated with %s top level data node (inside a choice), "
123
0
                        "but only a single container data node is allowed.", p_ext->name, lys_nodetype2str(snode->nodetype));
124
0
                break;
125
0
            }
126
0
        }
127
0
    } else if (child->nodetype != LYS_CONTAINER) {
128
        /* via uses */
129
0
        valid = 0;
130
0
        lyplg_ext_log(c_ext, LY_LLERR, LY_EVALID, lysc_ctx_get_path(cctx),
131
0
                "Extension %s is instantiated with %s top level data node, but only a single container data node is allowed.",
132
0
                p_ext->name, lys_nodetype2str(child->nodetype));
133
0
    }
134
135
0
    if (!valid) {
136
0
        yangdata_free(lysc_ctx_get_ctx(cctx), c_ext);
137
0
        c_ext->data = c_ext->substmts = NULL;
138
0
        return LY_EVALID;
139
0
    }
140
141
0
    return LY_SUCCESS;
142
143
0
emem:
144
0
    lyplg_ext_log(c_ext, LY_LLERR, LY_EMEM, lysc_ctx_get_path(cctx), "Memory allocation failed (%s()).", __func__);
145
0
    return LY_EMEM;
146
0
}
147
148
/**
149
 * @brief INFO printer
150
 *
151
 * Implementation of ::lyplg_ext_schema_printer_clb set as ::lyext_plugin::sprinter
152
 */
153
static LY_ERR
154
yangdata_schema_printer(struct lyspr_ctx *ctx, struct lysc_ext_instance *ext, ly_bool *flag)
155
0
{
156
0
    lysc_print_extension_instance(ctx, ext, flag);
157
0
    return LY_SUCCESS;
158
0
}
159
160
/**
161
 * @brief Plugin descriptions for the yang-data extension
162
 *
163
 * Note that external plugins are supposed to use:
164
 *
165
 *   LYPLG_EXTENSIONS = {
166
 */
167
const struct lyplg_ext_record plugins_yangdata[] = {
168
    {
169
        .module = "ietf-restconf",
170
        .revision = "2017-01-26",
171
        .name = "yang-data",
172
173
        .plugin.id = "libyang 2 - yang-data, version 1",
174
        .plugin.compile = &yangdata_compile,
175
        .plugin.validate = NULL,
176
        .plugin.sprinter = &yangdata_schema_printer,
177
        .plugin.free = yangdata_free
178
    },
179
    {0}     /* terminating zeroed record */
180
};