blob: f2ff464f0d756d39f13aa7fcf4d043e2625051dc [file] [log] [blame]
Radek Krejcie9f13b12020-11-09 17:42:04 +01001/**
2 * @file common.c
3 * @author Radek Krejci <rkrejci@cesnet.cz>
4 * @brief libyang's yanglint tool - common functions for both interactive and non-interactive mode.
5 *
6 * Copyright (c) 2020 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#define _GNU_SOURCE
Radek Krejcif8dc59a2020-11-25 13:47:44 +010016#define _POSIX_C_SOURCE 200809L /* strdup, strndup */
Radek Krejcie9f13b12020-11-09 17:42:04 +010017
18#include "common.h"
19
20#include <assert.h>
21#include <errno.h>
22#include <getopt.h>
23#include <stdio.h>
24#include <stdlib.h>
25#include <string.h>
26#include <sys/stat.h>
27
28#include "compat.h"
29#include "libyang.h"
aPiecek647f62e2023-05-18 10:55:58 +020030#include "plugins_exts.h"
Radek Krejcie9f13b12020-11-09 17:42:04 +010031
32int
33parse_schema_path(const char *path, char **dir, char **module)
34{
35 char *p;
36
37 assert(dir);
38 assert(module);
39
40 /* split the path to dirname and basename for further work */
41 *dir = strdup(path);
42 *module = strrchr(*dir, '/');
43 if (!(*module)) {
44 *module = *dir;
45 *dir = strdup("./");
46 } else {
47 *module[0] = '\0'; /* break the dir */
48 *module = strdup((*module) + 1);
49 }
50 /* get the pure module name without suffix or revision part of the filename */
51 if ((p = strchr(*module, '@'))) {
52 /* revision */
53 *p = '\0';
54 } else if ((p = strrchr(*module, '.'))) {
55 /* fileformat suffix */
56 *p = '\0';
57 }
58
59 return 0;
60}
61
62int
63get_input(const char *filepath, LYS_INFORMAT *format_schema, LYD_FORMAT *format_data, struct ly_in **in)
64{
65 struct stat st;
66
67 /* check that the filepath exists and is a regular file */
68 if (stat(filepath, &st) == -1) {
69 YLMSG_E("Unable to use input filepath (%s) - %s.\n", filepath, strerror(errno));
70 return -1;
71 }
72 if (!S_ISREG(st.st_mode)) {
73 YLMSG_E("Provided input file (%s) is not a regular file.\n", filepath);
74 return -1;
75 }
76
aPiecekef0a3392023-05-16 10:34:32 +020077 if (get_format(filepath, format_schema, format_data)) {
78 return -1;
Radek Krejcie9f13b12020-11-09 17:42:04 +010079 }
80
81 if (ly_in_new_filepath(filepath, 0, in)) {
82 YLMSG_E("Unable to process input file.\n");
83 return -1;
84 }
85
86 return 0;
87}
88
89void
90free_features(void *flist)
91{
92 struct schema_features *rec = (struct schema_features *)flist;
93
94 if (rec) {
Michal Vaskoa9a98612021-11-22 10:00:27 +010095 free(rec->mod_name);
Radek Krejcie9f13b12020-11-09 17:42:04 +010096 if (rec->features) {
97 for (uint32_t u = 0; rec->features[u]; ++u) {
98 free(rec->features[u]);
99 }
100 free(rec->features);
101 }
102 free(rec);
103 }
104}
105
106void
107get_features(struct ly_set *fset, const char *module, const char ***features)
108{
109 /* get features list for this module */
110 for (uint32_t u = 0; u < fset->count; ++u) {
111 struct schema_features *sf = (struct schema_features *)fset->objs[u];
Michal Vasko26bbb272022-08-02 14:54:33 +0200112
Michal Vaskoa9a98612021-11-22 10:00:27 +0100113 if (!strcmp(module, sf->mod_name)) {
Radek Krejci8143bb42021-04-07 11:43:50 +0200114 /* matched module - explicitly set features */
Radek Krejcie9f13b12020-11-09 17:42:04 +0100115 *features = (const char **)sf->features;
Michal Vasko686d8fc2021-11-22 10:03:23 +0100116 sf->applied = 1;
Radek Krejcie9f13b12020-11-09 17:42:04 +0100117 return;
118 }
119 }
Radek Krejci8143bb42021-04-07 11:43:50 +0200120
Michal Vasko59740872021-11-22 10:01:34 +0100121 /* features not set so disable all */
122 *features = NULL;
Radek Krejcie9f13b12020-11-09 17:42:04 +0100123}
124
125int
126parse_features(const char *fstring, struct ly_set *fset)
127{
128 struct schema_features *rec;
Michal Vaskoe699f7b2022-05-09 10:49:57 +0200129 uint32_t count;
130 char *p, **fp;
Radek Krejcie9f13b12020-11-09 17:42:04 +0100131
132 rec = calloc(1, sizeof *rec);
133 if (!rec) {
134 YLMSG_E("Unable to allocate features information record (%s).\n", strerror(errno));
135 return -1;
136 }
137 if (ly_set_add(fset, rec, 1, NULL)) {
138 YLMSG_E("Unable to store features information (%s).\n", strerror(errno));
139 free(rec);
140 return -1;
141 }
142
143 /* fill the record */
144 p = strchr(fstring, ':');
145 if (!p) {
Michal Vasko9a5f3dd2021-11-23 08:59:53 +0100146 YLMSG_E("Invalid format of the features specification (%s).\n", fstring);
Radek Krejcie9f13b12020-11-09 17:42:04 +0100147 return -1;
148 }
Michal Vaskoa9a98612021-11-22 10:00:27 +0100149 rec->mod_name = strndup(fstring, p - fstring);
Radek Krejcie9f13b12020-11-09 17:42:04 +0100150
Michal Vaskoe699f7b2022-05-09 10:49:57 +0200151 count = 0;
152 while (p) {
Radek Krejcie9f13b12020-11-09 17:42:04 +0100153 size_t len = 0;
154 char *token = p + 1;
Michal Vaskoe699f7b2022-05-09 10:49:57 +0200155
Radek Krejcie9f13b12020-11-09 17:42:04 +0100156 p = strchr(token, ',');
157 if (!p) {
158 /* the last item, if any */
159 len = strlen(token);
160 } else {
161 len = p - token;
162 }
Michal Vaskoe699f7b2022-05-09 10:49:57 +0200163
Radek Krejcie9f13b12020-11-09 17:42:04 +0100164 if (len) {
Michal Vaskoe699f7b2022-05-09 10:49:57 +0200165 fp = realloc(rec->features, (count + 1) * sizeof *rec->features);
Radek Krejcie9f13b12020-11-09 17:42:04 +0100166 if (!fp) {
167 YLMSG_E("Unable to store features list information (%s).\n", strerror(errno));
168 return -1;
169 }
170 rec->features = fp;
Michal Vaskoe699f7b2022-05-09 10:49:57 +0200171 fp = &rec->features[count++]; /* array item to set */
Radek Krejcie9f13b12020-11-09 17:42:04 +0100172 (*fp) = strndup(token, len);
173 }
174 }
175
Michal Vaskoe699f7b2022-05-09 10:49:57 +0200176 /* terminating NULL */
177 fp = realloc(rec->features, (count + 1) * sizeof *rec->features);
178 if (!fp) {
179 YLMSG_E("Unable to store features list information (%s).\n", strerror(errno));
180 return -1;
181 }
182 rec->features = fp;
183 rec->features[count++] = NULL;
184
Radek Krejcie9f13b12020-11-09 17:42:04 +0100185 return 0;
186}
187
188struct cmdline_file *
189fill_cmdline_file(struct ly_set *set, struct ly_in *in, const char *path, LYD_FORMAT format)
190{
191 struct cmdline_file *rec;
192
193 rec = malloc(sizeof *rec);
194 if (!rec) {
195 YLMSG_E("Allocating memory for data file information failed.\n");
196 return NULL;
197 }
Michal Vasko193dacd2022-10-13 08:43:05 +0200198 rec->in = in;
199 rec->path = path;
200 rec->format = format;
201
Radek Krejcie9f13b12020-11-09 17:42:04 +0100202 if (set && ly_set_add(set, rec, 1, NULL)) {
203 free(rec);
Michal Vasko7edebb42021-01-25 14:18:46 +0100204 YLMSG_E("Storing data file information failed.\n");
Radek Krejcie9f13b12020-11-09 17:42:04 +0100205 return NULL;
206 }
Radek Krejcie9f13b12020-11-09 17:42:04 +0100207
208 return rec;
209}
210
roman300b8782022-08-11 12:49:21 +0200211int
212collect_features(const struct lys_module *mod, struct ly_set *set)
213{
214 struct lysp_feature *f = NULL;
215 uint32_t idx = 0;
216
217 while ((f = lysp_feature_next(f, mod->parsed, &idx))) {
218 if (ly_set_add(set, (void *)f->name, 1, NULL)) {
219 YLMSG_E("Memory allocation failed.\n");
220 ly_set_erase(set, NULL);
221 return 1;
222 }
223 }
224
225 return 0;
226}
227
228void
229print_features(struct ly_out *out, const struct lys_module *mod, const struct ly_set *set)
230{
231 size_t max_len;
232 uint32_t j;
233 const char *name;
234
235 /* header */
236 ly_print(out, "%s:\n", mod->name);
237
238 /* no features */
239 if (!set->count) {
roman8ce25d42022-09-14 08:43:41 +0200240 ly_print(out, "\t(none)\n\n");
roman300b8782022-08-11 12:49:21 +0200241 return;
242 }
243
244 /* get max len, so the statuses of all the features will be aligned */
245 max_len = 0;
246 for (j = 0; j < set->count; ++j) {
247 name = set->objs[j];
248 if (strlen(name) > max_len) {
249 max_len = strlen(name);
250 }
251 }
252
253 /* print features */
254 for (j = 0; j < set->count; ++j) {
255 name = set->objs[j];
256 ly_print(out, "\t%-*s (%s)\n", (int)max_len, name, lys_feature_value(mod, name) ? "off" : "on");
257 }
258
259 ly_print(out, "\n");
260}
261
262int
263generate_features_output(const struct lys_module *mod, const struct ly_set *set, char **features_param)
264{
265 uint32_t j;
266 /*
267 * features_len - length of all the features in the current module
268 * added_len - length of a string to be added, = features_len + extra necessary length
269 * param_len - length of the parameter before appending new string
270 */
271 size_t features_len, added_len, param_len;
272 char *tmp;
273
274 features_len = 0;
275 for (j = 0; j < set->count; j++) {
276 features_len += strlen(set->objs[j]);
277 }
278
279 if (j == 0) {
280 /* no features */
281 added_len = strlen("-F ") + strlen(mod->name) + strlen(":");
282 } else {
283 /* j = comma count, -1 because of trailing comma */
284 added_len = strlen("-F ") + strlen(mod->name) + strlen(":") + features_len + j - 1;
285 }
286
287 /* to avoid strlen(NULL) if this is the first call */
288 param_len = 0;
289 if (*features_param) {
290 param_len = strlen(*features_param);
291 }
292
293 /* +1 because of white space at the beginning */
294 tmp = realloc(*features_param, param_len + added_len + 1 + 1);
295 if (!tmp) {
296 goto error;
297 } else {
298 *features_param = tmp;
299 }
300 sprintf(*features_param + param_len, " -F %s:", mod->name);
301
302 for (j = 0; j < set->count; j++) {
303 strcat(*features_param, set->objs[j]);
304 /* no trailing comma */
305 if (j != (set->count - 1)) {
306 strcat(*features_param, ",");
307 }
308 }
309
310 return 0;
311
312error:
313 YLMSG_E("Memory allocation failed (%s:%d, %s).\n", __FILE__, __LINE__, strerror(errno));
314 return 1;
315}
316
317int
318print_all_features(struct ly_out *out, const struct ly_ctx *ctx, ly_bool generate_features, char **features_param)
319{
320 int ret = 0;
321 uint32_t i = 0;
322 struct lys_module *mod;
323 struct ly_set set = {0};
324
325 while ((mod = ly_ctx_get_module_iter(ctx, &i)) != NULL) {
326 /* only care about implemented modules */
327 if (!mod->implemented) {
328 continue;
329 }
330
331 /* always erase the set, so the previous module's features don't carry over to the next module's features */
332 ly_set_erase(&set, NULL);
333
334 if (collect_features(mod, &set)) {
335 ret = 1;
336 goto cleanup;
337 }
338
339 if (generate_features && generate_features_output(mod, &set, features_param)) {
340 ret = 1;
341 goto cleanup;
342 }
343 print_features(out, mod, &set);
344 }
345
346cleanup:
347 ly_set_erase(&set, NULL);
348 return ret;
349}
350
Radek Krejcie9f13b12020-11-09 17:42:04 +0100351void
352free_cmdline_file(void *cmdline_file)
353{
354 struct cmdline_file *rec = (struct cmdline_file *)cmdline_file;
355
356 if (rec) {
357 ly_in_free(rec->in, 1);
358 free(rec);
359 }
360}
361
362void
363free_cmdline(char *argv[])
364{
365 if (argv) {
366 free(argv[0]);
367 free(argv);
368 }
369}
370
371int
372parse_cmdline(const char *cmdline, int *argc_p, char **argv_p[])
373{
374 int count;
375 char **vector;
376 char *ptr;
377 char qmark = 0;
378
379 assert(cmdline);
380 assert(argc_p);
381 assert(argv_p);
382
383 /* init */
384 optind = 0; /* reinitialize getopt() */
385 count = 1;
386 vector = malloc((count + 1) * sizeof *vector);
387 vector[0] = strdup(cmdline);
388
389 /* command name */
390 strtok(vector[0], " ");
391
392 /* arguments */
393 while ((ptr = strtok(NULL, " "))) {
394 size_t len;
395 void *r;
396
397 len = strlen(ptr);
398
399 if (qmark) {
400 /* still in quotated text */
401 /* remove NULL termination of the previous token since it is not a token,
402 * but a part of the quotation string */
403 ptr[-1] = ' ';
404
405 if ((ptr[len - 1] == qmark) && (ptr[len - 2] != '\\')) {
406 /* end of quotation */
407 qmark = 0;
408 /* shorten the argument by the terminating quotation mark */
409 ptr[len - 1] = '\0';
410 }
411 continue;
412 }
413
414 /* another token in cmdline */
415 ++count;
416 r = realloc(vector, (count + 1) * sizeof *vector);
417 if (!r) {
Michal Vasko9a5f3dd2021-11-23 08:59:53 +0100418 YLMSG_E("Memory allocation failed (%s:%d, %s).\n", __FILE__, __LINE__, strerror(errno));
Radek Krejcie9f13b12020-11-09 17:42:04 +0100419 free(vector);
420 return -1;
421 }
422 vector = r;
423 vector[count - 1] = ptr;
424
425 if ((ptr[0] == '"') || (ptr[0] == '\'')) {
426 /* remember the quotation mark to identify end of quotation */
427 qmark = ptr[0];
428
429 /* move the remembered argument after the quotation mark */
430 ++vector[count - 1];
431
432 /* check if the quotation is terminated within this token */
433 if ((ptr[len - 1] == qmark) && (ptr[len - 2] != '\\')) {
434 /* end of quotation */
435 qmark = 0;
436 /* shorten the argument by the terminating quotation mark */
437 ptr[len - 1] = '\0';
438 }
439 }
440 }
441 vector[count] = NULL;
442
443 *argc_p = count;
444 *argv_p = vector;
445
446 return 0;
447}
448
aPiecekef0a3392023-05-16 10:34:32 +0200449LYS_INFORMAT
450get_schema_format(const char *filename)
Radek Krejcie9f13b12020-11-09 17:42:04 +0100451{
452 char *ptr;
Radek Krejcie9f13b12020-11-09 17:42:04 +0100453
Radek Krejcie9f13b12020-11-09 17:42:04 +0100454 if ((ptr = strrchr(filename, '.')) != NULL) {
455 ++ptr;
456 if (!strcmp(ptr, "yang")) {
aPiecekef0a3392023-05-16 10:34:32 +0200457 return LYS_IN_YANG;
Radek Krejcie9f13b12020-11-09 17:42:04 +0100458 } else if (!strcmp(ptr, "yin")) {
aPiecekef0a3392023-05-16 10:34:32 +0200459 return LYS_IN_YIN;
Radek Krejcie9f13b12020-11-09 17:42:04 +0100460 } else {
aPiecekef0a3392023-05-16 10:34:32 +0200461 return LYS_IN_UNKNOWN;
Radek Krejcie9f13b12020-11-09 17:42:04 +0100462 }
463 } else {
aPiecekef0a3392023-05-16 10:34:32 +0200464 return LYS_IN_UNKNOWN;
465 }
466}
467
468LYD_FORMAT
469get_data_format(const char *filename)
470{
471 char *ptr;
472
473 if ((ptr = strrchr(filename, '.')) != NULL) {
474 ++ptr;
475 if (!strcmp(ptr, "xml")) {
476 return LYD_XML;
477 } else if (!strcmp(ptr, "json")) {
478 return LYD_JSON;
479 } else if (!strcmp(ptr, "lyb")) {
480 return LYD_LYB;
481 } else {
482 return LYD_UNKNOWN;
483 }
484 } else {
485 return LYD_UNKNOWN;
486 }
487}
488
489int
490get_format(const char *filepath, LYS_INFORMAT *schema_form, LYD_FORMAT *data_form)
491{
492 LYS_INFORMAT schema;
493 LYD_FORMAT data;
494
495 schema = !schema_form || !*schema_form ? LYS_IN_UNKNOWN : *schema_form;
496 data = !data_form || !*data_form ? LYD_UNKNOWN : *data_form;
497
498 if (!schema) {
499 schema = get_schema_format(filepath);
500 }
501 if (!data) {
502 data = get_data_format(filepath);
Radek Krejcie9f13b12020-11-09 17:42:04 +0100503 }
504
aPiecekef0a3392023-05-16 10:34:32 +0200505 if (!schema && !data) {
506 YLMSG_E("Input schema format for %s file not recognized.", filepath);
507 return -1;
508 } else if (!data && !schema) {
509 YLMSG_E("Input data format for %s file not recognized.", filepath);
510 return -1;
511 }
512 assert(schema || data);
513
514 if (schema_form) {
515 *schema_form = schema;
516 }
517 if (data_form) {
518 *data_form = data;
Radek Krejcie9f13b12020-11-09 17:42:04 +0100519 }
520
521 return 0;
522}
523
524int
525print_list(struct ly_out *out, struct ly_ctx *ctx, LYD_FORMAT outformat)
526{
527 struct lyd_node *ylib;
528 uint32_t idx = 0, has_modules = 0;
529 const struct lys_module *mod;
530
531 if (outformat != LYD_UNKNOWN) {
Michal Vasko794ab4b2021-03-31 09:42:19 +0200532 if (ly_ctx_get_yanglib_data(ctx, &ylib, "%u", ly_ctx_get_change_count(ctx))) {
Michal Vaskoc431a0a2021-01-25 14:31:58 +0100533 YLMSG_E("Getting context info (ietf-yang-library data) failed. If the YANG module is missing or not implemented, use an option to add it internally.\n");
Radek Krejcie9f13b12020-11-09 17:42:04 +0100534 return 1;
535 }
536
537 lyd_print_all(out, ylib, outformat, 0);
538 lyd_free_all(ylib);
539 return 0;
540 }
541
542 /* iterate schemas in context and provide just the basic info */
543 ly_print(out, "List of the loaded models:\n");
544 while ((mod = ly_ctx_get_module_iter(ctx, &idx))) {
545 has_modules++;
546
547 /* conformance print */
548 if (mod->implemented) {
549 ly_print(out, " I");
550 } else {
551 ly_print(out, " i");
552 }
553
554 /* module print */
555 ly_print(out, " %s", mod->name);
556 if (mod->revision) {
557 ly_print(out, "@%s", mod->revision);
558 }
559
560 /* submodules print */
561 if (mod->parsed && mod->parsed->includes) {
562 uint64_t u = 0;
Michal Vasko26bbb272022-08-02 14:54:33 +0200563
Radek Krejcie9f13b12020-11-09 17:42:04 +0100564 ly_print(out, " (");
565 LY_ARRAY_FOR(mod->parsed->includes, u) {
566 ly_print(out, "%s%s", !u ? "" : ",", mod->parsed->includes[u].name);
567 if (mod->parsed->includes[u].rev[0]) {
568 ly_print(out, "@%s", mod->parsed->includes[u].rev);
569 }
570 }
571 ly_print(out, ")");
572 }
573
574 /* finish the line */
575 ly_print(out, "\n");
576 }
577
578 if (!has_modules) {
579 ly_print(out, "\t(none)\n");
580 }
581
582 ly_print_flush(out);
583 return 0;
584}
585
586int
Radek Krejcie9f13b12020-11-09 17:42:04 +0100587evaluate_xpath(const struct lyd_node *tree, const char *xpath)
588{
Radek Krejci89757c12020-11-26 12:07:47 +0100589 struct ly_set *set = NULL;
Radek Krejcie9f13b12020-11-09 17:42:04 +0100590
591 if (lyd_find_xpath(tree, xpath, &set)) {
592 return -1;
593 }
594
595 /* print result */
596 printf("XPath \"%s\" evaluation result:\n", xpath);
597 if (!set->count) {
598 printf("\tEmpty\n");
599 } else {
600 for (uint32_t u = 0; u < set->count; ++u) {
601 struct lyd_node *node = (struct lyd_node *)set->objs[u];
Michal Vasko26bbb272022-08-02 14:54:33 +0200602
Radek Krejcie9f13b12020-11-09 17:42:04 +0100603 printf(" %s \"%s\"", lys_nodetype2str(node->schema->nodetype), node->schema->name);
604 if (node->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST)) {
Radek Krejci6d5ba0c2021-04-26 07:49:59 +0200605 printf(" (value: \"%s\")\n", lyd_get_value(node));
Radek Krejcie9f13b12020-11-09 17:42:04 +0100606 } else if (node->schema->nodetype == LYS_LIST) {
607 printf(" (");
608 for (struct lyd_node *key = ((struct lyd_node_inner *)node)->child; key && lysc_is_key(key->schema); key = key->next) {
609 printf("%s\"%s\": \"%s\";", (key != ((struct lyd_node_inner *)node)->child) ? " " : "",
Radek Krejci6d5ba0c2021-04-26 07:49:59 +0200610 key->schema->name, lyd_get_value(key));
Radek Krejcie9f13b12020-11-09 17:42:04 +0100611 }
612 printf(")\n");
aPiecek1ad408a2023-04-12 10:45:23 +0200613 } else {
614 printf("\n");
Radek Krejcie9f13b12020-11-09 17:42:04 +0100615 }
616 }
617 }
618
619 ly_set_free(set, NULL);
620 return 0;
621}
622
aPiecekf9b59812023-04-27 14:40:38 +0200623/**
624 * @brief Checking that a parent data node exists in the datastore for the nested-notification and action.
625 *
626 * @param[in] op Operation to check.
627 * @param[in] oper_tree Data from datastore.
628 * @param[in] operational_f Operational datastore file information.
629 * @return LY_ERR value.
630 */
631static LY_ERR
632check_operation_parent(struct lyd_node *op, struct lyd_node *oper_tree, struct cmdline_file *operational_f)
633{
634 LY_ERR ret;
635 struct ly_set *set = NULL;
636 char *path = NULL;
637
638 if (!op || !lyd_parent(op)) {
639 /* The function is defined only for nested-notification and action. */
640 return LY_SUCCESS;
641 }
642
643 if (!operational_f || (operational_f && !operational_f->in)) {
644 YLMSG_E("The --operational parameter needed to validate operation \"%s\" is missing.\n", LYD_NAME(op));
645 ret = LY_EVALID;
646 goto cleanup;
647 }
648
649 path = lyd_path(lyd_parent(op), LYD_PATH_STD, NULL, 0);
650 if (!path) {
651 ret = LY_EMEM;
652 goto cleanup;
653 }
654
655 if (!oper_tree) {
656 YLMSG_W("Operational datastore is empty or contains unknown data.\n");
657 YLMSG_E("Operation \"%s\" parent \"%s\" not found in the operational data.\n", LYD_NAME(op), path);
658 ret = LY_EVALID;
659 goto cleanup;
660 }
661 if ((ret = lyd_find_xpath(oper_tree, path, &set))) {
662 goto cleanup;
663 }
664 if (!set->count) {
665 YLMSG_E("Operation \"%s\" parent \"%s\" not found in the operational data.\n", LYD_NAME(op), path);
666 ret = LY_EVALID;
667 goto cleanup;
668 }
669
670cleanup:
671 ly_set_free(set, NULL);
672 free(path);
673
674 return ret;
675}
676
Radek Krejcie9f13b12020-11-09 17:42:04 +0100677LY_ERR
Michal Vaskoe0665742021-02-11 11:08:44 +0100678process_data(struct ly_ctx *ctx, enum lyd_type data_type, uint8_t merge, LYD_FORMAT format, struct ly_out *out,
Michal Vasko3f08fb92022-04-21 09:52:35 +0200679 uint32_t options_parse, uint32_t options_validate, uint32_t options_print, struct cmdline_file *operational_f,
680 struct cmdline_file *rpc_f, struct ly_set *inputs, struct ly_set *xpaths)
Radek Krejcie9f13b12020-11-09 17:42:04 +0100681{
Radek Krejci89757c12020-11-26 12:07:47 +0100682 LY_ERR ret = LY_SUCCESS;
Michal Vaskoff141b02022-05-02 09:22:36 +0200683 struct lyd_node *tree = NULL, *op = NULL, *envp = NULL, *merged_tree = NULL, *oper_tree = NULL;
aPieceka549c502023-04-12 11:28:31 +0200684 const char *xpath;
Michal Vasko9e81ce52022-04-29 10:16:21 +0200685 struct ly_set *set = NULL;
Radek Krejcie9f13b12020-11-09 17:42:04 +0100686
687 /* additional operational datastore */
688 if (operational_f && operational_f->in) {
Michal Vasko3f08fb92022-04-21 09:52:35 +0200689 ret = lyd_parse_data(ctx, NULL, operational_f->in, operational_f->format, LYD_PARSE_ONLY, 0, &oper_tree);
Radek Krejcie9f13b12020-11-09 17:42:04 +0100690 if (ret) {
691 YLMSG_E("Failed to parse operational datastore file \"%s\".\n", operational_f->path);
692 goto cleanup;
693 }
694 }
695
696 for (uint32_t u = 0; u < inputs->count; ++u) {
697 struct cmdline_file *input_f = (struct cmdline_file *)inputs->objs[u];
Michal Vasko26bbb272022-08-02 14:54:33 +0200698
Radek Krejcie9f13b12020-11-09 17:42:04 +0100699 switch (data_type) {
Michal Vasko1e4c68e2021-02-18 15:03:01 +0100700 case LYD_TYPE_DATA_YANG:
Michal Vaskoe0665742021-02-11 11:08:44 +0100701 ret = lyd_parse_data(ctx, NULL, input_f->in, input_f->format, options_parse, options_validate, &tree);
Radek Krejcie9f13b12020-11-09 17:42:04 +0100702 break;
Michal Vasko1e4c68e2021-02-18 15:03:01 +0100703 case LYD_TYPE_RPC_YANG:
704 case LYD_TYPE_REPLY_YANG:
705 case LYD_TYPE_NOTIF_YANG:
Michal Vaskoff141b02022-05-02 09:22:36 +0200706 ret = lyd_parse_op(ctx, NULL, input_f->in, input_f->format, data_type, &tree, &op);
Radek Krejcie9f13b12020-11-09 17:42:04 +0100707 break;
Michal Vasko3f08fb92022-04-21 09:52:35 +0200708 case LYD_TYPE_RPC_NETCONF:
709 case LYD_TYPE_NOTIF_NETCONF:
Michal Vaskoff141b02022-05-02 09:22:36 +0200710 ret = lyd_parse_op(ctx, NULL, input_f->in, input_f->format, data_type, &envp, &op);
711
712 /* adjust pointers */
713 for (tree = op; lyd_parent(tree); tree = lyd_parent(tree)) {}
Michal Vasko3f08fb92022-04-21 09:52:35 +0200714 break;
715 case LYD_TYPE_REPLY_NETCONF:
716 /* parse source RPC operation */
717 assert(rpc_f && rpc_f->in);
Michal Vaskoff141b02022-05-02 09:22:36 +0200718 ret = lyd_parse_op(ctx, NULL, rpc_f->in, rpc_f->format, LYD_TYPE_RPC_NETCONF, &envp, &op);
Michal Vasko3f08fb92022-04-21 09:52:35 +0200719 if (ret) {
720 YLMSG_E("Failed to parse source NETCONF RPC operation file \"%s\".\n", rpc_f->path);
721 goto cleanup;
722 }
723
Michal Vaskoff141b02022-05-02 09:22:36 +0200724 /* adjust pointers */
725 for (tree = op; lyd_parent(tree); tree = lyd_parent(tree)) {}
726
Michal Vasko3f08fb92022-04-21 09:52:35 +0200727 /* free input */
Michal Vaskoff141b02022-05-02 09:22:36 +0200728 lyd_free_siblings(lyd_child(op));
Michal Vasko3f08fb92022-04-21 09:52:35 +0200729
730 /* we do not care */
731 lyd_free_all(envp);
732 envp = NULL;
733
Michal Vaskoff141b02022-05-02 09:22:36 +0200734 ret = lyd_parse_op(ctx, op, input_f->in, input_f->format, data_type, &envp, NULL);
Michal Vasko3f08fb92022-04-21 09:52:35 +0200735 break;
Radek Krejci89757c12020-11-26 12:07:47 +0100736 default:
737 YLMSG_E("Internal error (%s:%d).\n", __FILE__, __LINE__);
738 goto cleanup;
Radek Krejcie9f13b12020-11-09 17:42:04 +0100739 }
740
741 if (ret) {
742 YLMSG_E("Failed to parse input data file \"%s\".\n", input_f->path);
743 goto cleanup;
744 }
745
746 if (merge) {
747 /* merge the data so far parsed for later validation and print */
748 if (!merged_tree) {
749 merged_tree = tree;
750 } else {
751 ret = lyd_merge_siblings(&merged_tree, tree, LYD_MERGE_DESTRUCT);
752 if (ret) {
753 YLMSG_E("Merging %s with previous data failed.\n", input_f->path);
754 goto cleanup;
755 }
756 }
757 tree = NULL;
758 } else if (format) {
Michal Vasko3f08fb92022-04-21 09:52:35 +0200759 /* print */
760 switch (data_type) {
761 case LYD_TYPE_DATA_YANG:
762 lyd_print_all(out, tree, format, options_print);
763 break;
764 case LYD_TYPE_RPC_YANG:
765 case LYD_TYPE_REPLY_YANG:
766 case LYD_TYPE_NOTIF_YANG:
767 case LYD_TYPE_RPC_NETCONF:
768 case LYD_TYPE_NOTIF_NETCONF:
769 lyd_print_tree(out, tree, format, options_print);
770 break;
771 case LYD_TYPE_REPLY_NETCONF:
772 /* just the output */
773 lyd_print_tree(out, lyd_child(tree), format, options_print);
774 break;
775 default:
776 assert(0);
777 }
Michal Vasko0bec3222022-04-22 07:46:00 +0200778 } else {
779 /* validation of the RPC/Action/reply/Notification with the operational datastore, if any */
780 switch (data_type) {
781 case LYD_TYPE_DATA_YANG:
782 /* already validated */
783 break;
784 case LYD_TYPE_RPC_YANG:
785 case LYD_TYPE_RPC_NETCONF:
786 ret = lyd_validate_op(tree, oper_tree, LYD_TYPE_RPC_YANG, NULL);
787 break;
788 case LYD_TYPE_REPLY_YANG:
789 case LYD_TYPE_REPLY_NETCONF:
790 ret = lyd_validate_op(tree, oper_tree, LYD_TYPE_REPLY_YANG, NULL);
791 break;
792 case LYD_TYPE_NOTIF_YANG:
793 case LYD_TYPE_NOTIF_NETCONF:
794 ret = lyd_validate_op(tree, oper_tree, LYD_TYPE_NOTIF_YANG, NULL);
795 break;
796 default:
797 assert(0);
798 }
Radek Krejcie9f13b12020-11-09 17:42:04 +0100799 if (ret) {
Michal Vasko0bec3222022-04-22 07:46:00 +0200800 if (operational_f->path) {
801 YLMSG_E("Failed to validate input data file \"%s\" with operational datastore \"%s\".\n",
802 input_f->path, operational_f->path);
803 } else {
804 YLMSG_E("Failed to validate input data file \"%s\".\n", input_f->path);
805 }
Radek Krejcie9f13b12020-11-09 17:42:04 +0100806 goto cleanup;
807 }
Michal Vasko9e81ce52022-04-29 10:16:21 +0200808
aPiecekf9b59812023-04-27 14:40:38 +0200809 if ((ret = check_operation_parent(op, oper_tree, operational_f))) {
810 goto cleanup;
Michal Vasko9e81ce52022-04-29 10:16:21 +0200811 }
Radek Krejcie9f13b12020-11-09 17:42:04 +0100812 }
Michal Vasko3f08fb92022-04-21 09:52:35 +0200813
814 /* next iter */
Radek Krejcie9f13b12020-11-09 17:42:04 +0100815 lyd_free_all(tree);
816 tree = NULL;
Michal Vasko3f08fb92022-04-21 09:52:35 +0200817 lyd_free_all(envp);
818 envp = NULL;
Radek Krejcie9f13b12020-11-09 17:42:04 +0100819 }
820
821 if (merge) {
822 /* validate the merged result */
aPiecek65de6d12023-04-05 13:38:53 +0200823 ret = lyd_validate_all(&merged_tree, ctx, options_validate, NULL);
Radek Krejcie9f13b12020-11-09 17:42:04 +0100824 if (ret) {
825 YLMSG_E("Merged data are not valid.\n");
826 goto cleanup;
827 }
828
829 if (format) {
830 /* and print it */
831 lyd_print_all(out, merged_tree, format, options_print);
832 }
833
Michal Vaskof8e71f72022-03-29 12:12:58 +0200834 for (uint32_t u = 0; xpaths && (u < xpaths->count); ++u) {
aPieceka549c502023-04-12 11:28:31 +0200835 xpath = (const char *)xpaths->objs[u];
836 ly_set_free(set, NULL);
837 ret = lys_find_xpath(ctx, NULL, xpath, LYS_FIND_NO_MATCH_ERROR, &set);
838 if (ret || !set->count) {
839 ret = (ret == LY_SUCCESS) ? LY_EINVAL : ret;
840 YLMSG_E("The requested xpath failed.\n");
841 goto cleanup;
842 }
843 if (evaluate_xpath(merged_tree, xpath)) {
Radek Krejcie9f13b12020-11-09 17:42:04 +0100844 goto cleanup;
845 }
846 }
847 }
848
849cleanup:
Radek Krejcie9f13b12020-11-09 17:42:04 +0100850 lyd_free_all(tree);
Michal Vasko3f08fb92022-04-21 09:52:35 +0200851 lyd_free_all(envp);
Michal Vaskoe1f495b2022-04-20 08:32:41 +0200852 lyd_free_all(merged_tree);
Michal Vasko3f08fb92022-04-21 09:52:35 +0200853 lyd_free_all(oper_tree);
Michal Vasko9e81ce52022-04-29 10:16:21 +0200854 ly_set_free(set, NULL);
Radek Krejcie9f13b12020-11-09 17:42:04 +0100855 return ret;
856}
romanf00089c2022-10-06 16:01:31 +0200857
858const struct lysc_node *
859find_schema_path(const struct ly_ctx *ctx, const char *schema_path)
860{
861 const char *end, *module_name_end;
862 char *module_name = NULL;
Michal Vaskob10c93b2022-12-14 12:16:27 +0100863 const struct lysc_node *node = NULL, *parent_node = NULL, *parent_node_tmp = NULL;
romanf00089c2022-10-06 16:01:31 +0200864 const struct lys_module *module;
865 size_t node_name_len;
866 ly_bool found_exact_match = 0;
867
868 /* iterate over each '/' in the path */
869 while (schema_path) {
870 /* example: schema_path = /listen/endpoint
871 * end == NULL for endpoint, end exists for listen */
872 end = strchr(schema_path + 1, '/');
873 if (end) {
874 node_name_len = end - schema_path - 1;
875 } else {
876 node_name_len = strlen(schema_path + 1);
877 }
878
879 /* ex: schema_path = /ietf-interfaces:interfaces/interface/ietf-ip:ipv4 */
880 module_name_end = strchr(schema_path, ':');
romanba1421b2022-10-07 11:02:36 +0200881 if (module_name_end && (!end || (module_name_end < end))) {
882 /* only get module's name, if it is in the current scope */
883 free(module_name);
884 /* - 1 because module_name_end points to ':' */
885 module_name = strndup(schema_path + 1, module_name_end - schema_path - 1);
886 if (!module_name) {
887 YLMSG_E("Memory allocation failed (%s:%d, %s)", __FILE__, __LINE__, strerror(errno));
888 parent_node = NULL;
889 goto cleanup;
romanf00089c2022-10-06 16:01:31 +0200890 }
romanba1421b2022-10-07 11:02:36 +0200891 /* move the pointer to the beginning of the node's name - 1 */
892 schema_path = module_name_end;
romanf00089c2022-10-06 16:01:31 +0200893
romanba1421b2022-10-07 11:02:36 +0200894 /* recalculate the length of the node's name, because the module prefix mustn't be compared later */
romanf00089c2022-10-06 16:01:31 +0200895 if (module_name_end < end) {
romanba1421b2022-10-07 11:02:36 +0200896 node_name_len = end - module_name_end - 1;
897 } else if (!end) {
898 node_name_len = strlen(module_name_end + 1);
romanf00089c2022-10-06 16:01:31 +0200899 }
900 }
901
902 module = ly_ctx_get_module_implemented(ctx, module_name);
903 if (!module) {
904 /* unknown module name */
905 parent_node = NULL;
906 goto cleanup;
907 }
908
909 /* iterate over the node's siblings / module's top level containers */
910 while ((node = lys_getnext(node, parent_node, module->compiled, LYS_GETNEXT_WITHCASE | LYS_GETNEXT_WITHCHOICE))) {
911 if (end && !strncmp(node->name, schema_path + 1, node_name_len) && (node->name[node_name_len] == '\0')) {
912 /* check if the whole node's name matches and it's not just a common prefix */
913 parent_node = node;
914 break;
915 } else if (!strncmp(node->name, schema_path + 1, node_name_len)) {
916 /* do the same here, however if there is no exact match, use the last node with the same prefix */
917 if (strlen(node->name) == node_name_len) {
918 parent_node = node;
919 found_exact_match = 1;
920 break;
921 } else {
922 parent_node_tmp = node;
923 }
924 }
925 }
926
927 if (!end && !found_exact_match) {
928 /* no exact match */
929 parent_node = parent_node_tmp;
930 }
931 found_exact_match = 0;
932
933 /* next iter */
934 schema_path = strchr(schema_path + 1, '/');
935 }
936
937cleanup:
938 free(module_name);
939 return parent_node;
940}
aPiecek647f62e2023-05-18 10:55:58 +0200941
942LY_ERR
943ext_data_clb(const struct lysc_ext_instance *ext, void *user_data, void **ext_data, ly_bool *ext_data_free)
944{
945 struct ly_ctx *ctx;
946 struct lyd_node *data = NULL;
947
948 ctx = ext->module->ctx;
949 if (user_data) {
950 lyd_parse_data_path(ctx, user_data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &data);
951 }
952
953 *ext_data = data;
954 *ext_data_free = 1;
955 return LY_SUCCESS;
956}