blob: 2f2905a378eba62adfb724c50cfb6be563b685b5 [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"
30
31int
32parse_schema_path(const char *path, char **dir, char **module)
33{
34 char *p;
35
36 assert(dir);
37 assert(module);
38
39 /* split the path to dirname and basename for further work */
40 *dir = strdup(path);
41 *module = strrchr(*dir, '/');
42 if (!(*module)) {
43 *module = *dir;
44 *dir = strdup("./");
45 } else {
46 *module[0] = '\0'; /* break the dir */
47 *module = strdup((*module) + 1);
48 }
49 /* get the pure module name without suffix or revision part of the filename */
50 if ((p = strchr(*module, '@'))) {
51 /* revision */
52 *p = '\0';
53 } else if ((p = strrchr(*module, '.'))) {
54 /* fileformat suffix */
55 *p = '\0';
56 }
57
58 return 0;
59}
60
61int
62get_input(const char *filepath, LYS_INFORMAT *format_schema, LYD_FORMAT *format_data, struct ly_in **in)
63{
64 struct stat st;
65
66 /* check that the filepath exists and is a regular file */
67 if (stat(filepath, &st) == -1) {
68 YLMSG_E("Unable to use input filepath (%s) - %s.\n", filepath, strerror(errno));
69 return -1;
70 }
71 if (!S_ISREG(st.st_mode)) {
72 YLMSG_E("Provided input file (%s) is not a regular file.\n", filepath);
73 return -1;
74 }
75
Michal Vaskod3b10542021-02-03 11:31:16 +010076 if ((format_schema && !*format_schema) || (format_data && !*format_data)) {
77 /* get the file format */
78 if (get_format(filepath, format_schema, format_data)) {
79 return -1;
80 }
Radek Krejcie9f13b12020-11-09 17:42:04 +010081 }
82
83 if (ly_in_new_filepath(filepath, 0, in)) {
84 YLMSG_E("Unable to process input file.\n");
85 return -1;
86 }
87
88 return 0;
89}
90
91void
92free_features(void *flist)
93{
94 struct schema_features *rec = (struct schema_features *)flist;
95
96 if (rec) {
Michal Vaskoa9a98612021-11-22 10:00:27 +010097 free(rec->mod_name);
Radek Krejcie9f13b12020-11-09 17:42:04 +010098 if (rec->features) {
99 for (uint32_t u = 0; rec->features[u]; ++u) {
100 free(rec->features[u]);
101 }
102 free(rec->features);
103 }
104 free(rec);
105 }
106}
107
108void
109get_features(struct ly_set *fset, const char *module, const char ***features)
110{
111 /* get features list for this module */
112 for (uint32_t u = 0; u < fset->count; ++u) {
113 struct schema_features *sf = (struct schema_features *)fset->objs[u];
Michal Vasko26bbb272022-08-02 14:54:33 +0200114
Michal Vaskoa9a98612021-11-22 10:00:27 +0100115 if (!strcmp(module, sf->mod_name)) {
Radek Krejci8143bb42021-04-07 11:43:50 +0200116 /* matched module - explicitly set features */
Radek Krejcie9f13b12020-11-09 17:42:04 +0100117 *features = (const char **)sf->features;
Michal Vasko686d8fc2021-11-22 10:03:23 +0100118 sf->applied = 1;
Radek Krejcie9f13b12020-11-09 17:42:04 +0100119 return;
120 }
121 }
Radek Krejci8143bb42021-04-07 11:43:50 +0200122
Michal Vasko59740872021-11-22 10:01:34 +0100123 /* features not set so disable all */
124 *features = NULL;
Radek Krejcie9f13b12020-11-09 17:42:04 +0100125}
126
127int
128parse_features(const char *fstring, struct ly_set *fset)
129{
130 struct schema_features *rec;
Michal Vaskoe699f7b2022-05-09 10:49:57 +0200131 uint32_t count;
132 char *p, **fp;
Radek Krejcie9f13b12020-11-09 17:42:04 +0100133
134 rec = calloc(1, sizeof *rec);
135 if (!rec) {
136 YLMSG_E("Unable to allocate features information record (%s).\n", strerror(errno));
137 return -1;
138 }
139 if (ly_set_add(fset, rec, 1, NULL)) {
140 YLMSG_E("Unable to store features information (%s).\n", strerror(errno));
141 free(rec);
142 return -1;
143 }
144
145 /* fill the record */
146 p = strchr(fstring, ':');
147 if (!p) {
Michal Vasko9a5f3dd2021-11-23 08:59:53 +0100148 YLMSG_E("Invalid format of the features specification (%s).\n", fstring);
Radek Krejcie9f13b12020-11-09 17:42:04 +0100149 return -1;
150 }
Michal Vaskoa9a98612021-11-22 10:00:27 +0100151 rec->mod_name = strndup(fstring, p - fstring);
Radek Krejcie9f13b12020-11-09 17:42:04 +0100152
Michal Vaskoe699f7b2022-05-09 10:49:57 +0200153 count = 0;
154 while (p) {
Radek Krejcie9f13b12020-11-09 17:42:04 +0100155 size_t len = 0;
156 char *token = p + 1;
Michal Vaskoe699f7b2022-05-09 10:49:57 +0200157
Radek Krejcie9f13b12020-11-09 17:42:04 +0100158 p = strchr(token, ',');
159 if (!p) {
160 /* the last item, if any */
161 len = strlen(token);
162 } else {
163 len = p - token;
164 }
Michal Vaskoe699f7b2022-05-09 10:49:57 +0200165
Radek Krejcie9f13b12020-11-09 17:42:04 +0100166 if (len) {
Michal Vaskoe699f7b2022-05-09 10:49:57 +0200167 fp = realloc(rec->features, (count + 1) * sizeof *rec->features);
Radek Krejcie9f13b12020-11-09 17:42:04 +0100168 if (!fp) {
169 YLMSG_E("Unable to store features list information (%s).\n", strerror(errno));
170 return -1;
171 }
172 rec->features = fp;
Michal Vaskoe699f7b2022-05-09 10:49:57 +0200173 fp = &rec->features[count++]; /* array item to set */
Radek Krejcie9f13b12020-11-09 17:42:04 +0100174 (*fp) = strndup(token, len);
175 }
176 }
177
Michal Vaskoe699f7b2022-05-09 10:49:57 +0200178 /* terminating NULL */
179 fp = realloc(rec->features, (count + 1) * sizeof *rec->features);
180 if (!fp) {
181 YLMSG_E("Unable to store features list information (%s).\n", strerror(errno));
182 return -1;
183 }
184 rec->features = fp;
185 rec->features[count++] = NULL;
186
Radek Krejcie9f13b12020-11-09 17:42:04 +0100187 return 0;
188}
189
190struct cmdline_file *
191fill_cmdline_file(struct ly_set *set, struct ly_in *in, const char *path, LYD_FORMAT format)
192{
193 struct cmdline_file *rec;
194
195 rec = malloc(sizeof *rec);
196 if (!rec) {
197 YLMSG_E("Allocating memory for data file information failed.\n");
198 return NULL;
199 }
Michal Vasko193dacd2022-10-13 08:43:05 +0200200 rec->in = in;
201 rec->path = path;
202 rec->format = format;
203
Radek Krejcie9f13b12020-11-09 17:42:04 +0100204 if (set && ly_set_add(set, rec, 1, NULL)) {
205 free(rec);
Michal Vasko7edebb42021-01-25 14:18:46 +0100206 YLMSG_E("Storing data file information failed.\n");
Radek Krejcie9f13b12020-11-09 17:42:04 +0100207 return NULL;
208 }
Radek Krejcie9f13b12020-11-09 17:42:04 +0100209
210 return rec;
211}
212
roman300b8782022-08-11 12:49:21 +0200213int
214collect_features(const struct lys_module *mod, struct ly_set *set)
215{
216 struct lysp_feature *f = NULL;
217 uint32_t idx = 0;
218
219 while ((f = lysp_feature_next(f, mod->parsed, &idx))) {
220 if (ly_set_add(set, (void *)f->name, 1, NULL)) {
221 YLMSG_E("Memory allocation failed.\n");
222 ly_set_erase(set, NULL);
223 return 1;
224 }
225 }
226
227 return 0;
228}
229
230void
231print_features(struct ly_out *out, const struct lys_module *mod, const struct ly_set *set)
232{
233 size_t max_len;
234 uint32_t j;
235 const char *name;
236
237 /* header */
238 ly_print(out, "%s:\n", mod->name);
239
240 /* no features */
241 if (!set->count) {
roman8ce25d42022-09-14 08:43:41 +0200242 ly_print(out, "\t(none)\n\n");
roman300b8782022-08-11 12:49:21 +0200243 return;
244 }
245
246 /* get max len, so the statuses of all the features will be aligned */
247 max_len = 0;
248 for (j = 0; j < set->count; ++j) {
249 name = set->objs[j];
250 if (strlen(name) > max_len) {
251 max_len = strlen(name);
252 }
253 }
254
255 /* print features */
256 for (j = 0; j < set->count; ++j) {
257 name = set->objs[j];
258 ly_print(out, "\t%-*s (%s)\n", (int)max_len, name, lys_feature_value(mod, name) ? "off" : "on");
259 }
260
261 ly_print(out, "\n");
262}
263
264int
265generate_features_output(const struct lys_module *mod, const struct ly_set *set, char **features_param)
266{
267 uint32_t j;
268 /*
269 * features_len - length of all the features in the current module
270 * added_len - length of a string to be added, = features_len + extra necessary length
271 * param_len - length of the parameter before appending new string
272 */
273 size_t features_len, added_len, param_len;
274 char *tmp;
275
276 features_len = 0;
277 for (j = 0; j < set->count; j++) {
278 features_len += strlen(set->objs[j]);
279 }
280
281 if (j == 0) {
282 /* no features */
283 added_len = strlen("-F ") + strlen(mod->name) + strlen(":");
284 } else {
285 /* j = comma count, -1 because of trailing comma */
286 added_len = strlen("-F ") + strlen(mod->name) + strlen(":") + features_len + j - 1;
287 }
288
289 /* to avoid strlen(NULL) if this is the first call */
290 param_len = 0;
291 if (*features_param) {
292 param_len = strlen(*features_param);
293 }
294
295 /* +1 because of white space at the beginning */
296 tmp = realloc(*features_param, param_len + added_len + 1 + 1);
297 if (!tmp) {
298 goto error;
299 } else {
300 *features_param = tmp;
301 }
302 sprintf(*features_param + param_len, " -F %s:", mod->name);
303
304 for (j = 0; j < set->count; j++) {
305 strcat(*features_param, set->objs[j]);
306 /* no trailing comma */
307 if (j != (set->count - 1)) {
308 strcat(*features_param, ",");
309 }
310 }
311
312 return 0;
313
314error:
315 YLMSG_E("Memory allocation failed (%s:%d, %s).\n", __FILE__, __LINE__, strerror(errno));
316 return 1;
317}
318
319int
320print_all_features(struct ly_out *out, const struct ly_ctx *ctx, ly_bool generate_features, char **features_param)
321{
322 int ret = 0;
323 uint32_t i = 0;
324 struct lys_module *mod;
325 struct ly_set set = {0};
326
327 while ((mod = ly_ctx_get_module_iter(ctx, &i)) != NULL) {
328 /* only care about implemented modules */
329 if (!mod->implemented) {
330 continue;
331 }
332
333 /* always erase the set, so the previous module's features don't carry over to the next module's features */
334 ly_set_erase(&set, NULL);
335
336 if (collect_features(mod, &set)) {
337 ret = 1;
338 goto cleanup;
339 }
340
341 if (generate_features && generate_features_output(mod, &set, features_param)) {
342 ret = 1;
343 goto cleanup;
344 }
345 print_features(out, mod, &set);
346 }
347
348cleanup:
349 ly_set_erase(&set, NULL);
350 return ret;
351}
352
Radek Krejcie9f13b12020-11-09 17:42:04 +0100353void
354free_cmdline_file(void *cmdline_file)
355{
356 struct cmdline_file *rec = (struct cmdline_file *)cmdline_file;
357
358 if (rec) {
359 ly_in_free(rec->in, 1);
360 free(rec);
361 }
362}
363
364void
365free_cmdline(char *argv[])
366{
367 if (argv) {
368 free(argv[0]);
369 free(argv);
370 }
371}
372
373int
374parse_cmdline(const char *cmdline, int *argc_p, char **argv_p[])
375{
376 int count;
377 char **vector;
378 char *ptr;
379 char qmark = 0;
380
381 assert(cmdline);
382 assert(argc_p);
383 assert(argv_p);
384
385 /* init */
386 optind = 0; /* reinitialize getopt() */
387 count = 1;
388 vector = malloc((count + 1) * sizeof *vector);
389 vector[0] = strdup(cmdline);
390
391 /* command name */
392 strtok(vector[0], " ");
393
394 /* arguments */
395 while ((ptr = strtok(NULL, " "))) {
396 size_t len;
397 void *r;
398
399 len = strlen(ptr);
400
401 if (qmark) {
402 /* still in quotated text */
403 /* remove NULL termination of the previous token since it is not a token,
404 * but a part of the quotation string */
405 ptr[-1] = ' ';
406
407 if ((ptr[len - 1] == qmark) && (ptr[len - 2] != '\\')) {
408 /* end of quotation */
409 qmark = 0;
410 /* shorten the argument by the terminating quotation mark */
411 ptr[len - 1] = '\0';
412 }
413 continue;
414 }
415
416 /* another token in cmdline */
417 ++count;
418 r = realloc(vector, (count + 1) * sizeof *vector);
419 if (!r) {
Michal Vasko9a5f3dd2021-11-23 08:59:53 +0100420 YLMSG_E("Memory allocation failed (%s:%d, %s).\n", __FILE__, __LINE__, strerror(errno));
Radek Krejcie9f13b12020-11-09 17:42:04 +0100421 free(vector);
422 return -1;
423 }
424 vector = r;
425 vector[count - 1] = ptr;
426
427 if ((ptr[0] == '"') || (ptr[0] == '\'')) {
428 /* remember the quotation mark to identify end of quotation */
429 qmark = ptr[0];
430
431 /* move the remembered argument after the quotation mark */
432 ++vector[count - 1];
433
434 /* check if the quotation is terminated within this token */
435 if ((ptr[len - 1] == qmark) && (ptr[len - 2] != '\\')) {
436 /* end of quotation */
437 qmark = 0;
438 /* shorten the argument by the terminating quotation mark */
439 ptr[len - 1] = '\0';
440 }
441 }
442 }
443 vector[count] = NULL;
444
445 *argc_p = count;
446 *argv_p = vector;
447
448 return 0;
449}
450
451int
452get_format(const char *filename, LYS_INFORMAT *schema, LYD_FORMAT *data)
453{
454 char *ptr;
455 LYS_INFORMAT informat_s;
456 LYD_FORMAT informat_d;
457
458 /* get the file format */
459 if ((ptr = strrchr(filename, '.')) != NULL) {
460 ++ptr;
461 if (!strcmp(ptr, "yang")) {
462 informat_s = LYS_IN_YANG;
463 informat_d = 0;
464 } else if (!strcmp(ptr, "yin")) {
465 informat_s = LYS_IN_YIN;
466 informat_d = 0;
467 } else if (!strcmp(ptr, "xml")) {
468 informat_s = 0;
469 informat_d = LYD_XML;
470 } else if (!strcmp(ptr, "json")) {
471 informat_s = 0;
472 informat_d = LYD_JSON;
Michal Vaskod3b10542021-02-03 11:31:16 +0100473 } else if (!strcmp(ptr, "lyb")) {
474 informat_s = 0;
475 informat_d = LYD_LYB;
Radek Krejcie9f13b12020-11-09 17:42:04 +0100476 } else {
477 YLMSG_E("Input file \"%s\" in an unknown format \"%s\".\n", filename, ptr);
478 return 0;
479 }
480 } else {
481 YLMSG_E("Input file \"%s\" without file extension - unknown format.\n", filename);
482 return 1;
483 }
484
485 if (informat_d) {
486 if (!data) {
487 YLMSG_E("Input file \"%s\" not expected to contain data instances (unexpected format).\n", filename);
488 return 2;
489 }
490 (*data) = informat_d;
491 } else if (informat_s) {
492 if (!schema) {
493 YLMSG_E("Input file \"%s\" not expected to contain schema definition (unexpected format).\n", filename);
494 return 3;
495 }
496 (*schema) = informat_s;
497 }
498
499 return 0;
500}
501
502int
503print_list(struct ly_out *out, struct ly_ctx *ctx, LYD_FORMAT outformat)
504{
505 struct lyd_node *ylib;
506 uint32_t idx = 0, has_modules = 0;
507 const struct lys_module *mod;
508
509 if (outformat != LYD_UNKNOWN) {
Michal Vasko794ab4b2021-03-31 09:42:19 +0200510 if (ly_ctx_get_yanglib_data(ctx, &ylib, "%u", ly_ctx_get_change_count(ctx))) {
Michal Vaskoc431a0a2021-01-25 14:31:58 +0100511 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 +0100512 return 1;
513 }
514
515 lyd_print_all(out, ylib, outformat, 0);
516 lyd_free_all(ylib);
517 return 0;
518 }
519
520 /* iterate schemas in context and provide just the basic info */
521 ly_print(out, "List of the loaded models:\n");
522 while ((mod = ly_ctx_get_module_iter(ctx, &idx))) {
523 has_modules++;
524
525 /* conformance print */
526 if (mod->implemented) {
527 ly_print(out, " I");
528 } else {
529 ly_print(out, " i");
530 }
531
532 /* module print */
533 ly_print(out, " %s", mod->name);
534 if (mod->revision) {
535 ly_print(out, "@%s", mod->revision);
536 }
537
538 /* submodules print */
539 if (mod->parsed && mod->parsed->includes) {
540 uint64_t u = 0;
Michal Vasko26bbb272022-08-02 14:54:33 +0200541
Radek Krejcie9f13b12020-11-09 17:42:04 +0100542 ly_print(out, " (");
543 LY_ARRAY_FOR(mod->parsed->includes, u) {
544 ly_print(out, "%s%s", !u ? "" : ",", mod->parsed->includes[u].name);
545 if (mod->parsed->includes[u].rev[0]) {
546 ly_print(out, "@%s", mod->parsed->includes[u].rev);
547 }
548 }
549 ly_print(out, ")");
550 }
551
552 /* finish the line */
553 ly_print(out, "\n");
554 }
555
556 if (!has_modules) {
557 ly_print(out, "\t(none)\n");
558 }
559
560 ly_print_flush(out);
561 return 0;
562}
563
564int
Radek Krejcie9f13b12020-11-09 17:42:04 +0100565evaluate_xpath(const struct lyd_node *tree, const char *xpath)
566{
Radek Krejci89757c12020-11-26 12:07:47 +0100567 struct ly_set *set = NULL;
Radek Krejcie9f13b12020-11-09 17:42:04 +0100568
569 if (lyd_find_xpath(tree, xpath, &set)) {
570 return -1;
571 }
572
573 /* print result */
574 printf("XPath \"%s\" evaluation result:\n", xpath);
575 if (!set->count) {
576 printf("\tEmpty\n");
577 } else {
578 for (uint32_t u = 0; u < set->count; ++u) {
579 struct lyd_node *node = (struct lyd_node *)set->objs[u];
Michal Vasko26bbb272022-08-02 14:54:33 +0200580
Radek Krejcie9f13b12020-11-09 17:42:04 +0100581 printf(" %s \"%s\"", lys_nodetype2str(node->schema->nodetype), node->schema->name);
582 if (node->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST)) {
Radek Krejci6d5ba0c2021-04-26 07:49:59 +0200583 printf(" (value: \"%s\")\n", lyd_get_value(node));
Radek Krejcie9f13b12020-11-09 17:42:04 +0100584 } else if (node->schema->nodetype == LYS_LIST) {
585 printf(" (");
586 for (struct lyd_node *key = ((struct lyd_node_inner *)node)->child; key && lysc_is_key(key->schema); key = key->next) {
587 printf("%s\"%s\": \"%s\";", (key != ((struct lyd_node_inner *)node)->child) ? " " : "",
Radek Krejci6d5ba0c2021-04-26 07:49:59 +0200588 key->schema->name, lyd_get_value(key));
Radek Krejcie9f13b12020-11-09 17:42:04 +0100589 }
590 printf(")\n");
aPiecek1ad408a2023-04-12 10:45:23 +0200591 } else {
592 printf("\n");
Radek Krejcie9f13b12020-11-09 17:42:04 +0100593 }
594 }
595 }
596
597 ly_set_free(set, NULL);
598 return 0;
599}
600
aPiecekf9b59812023-04-27 14:40:38 +0200601/**
602 * @brief Checking that a parent data node exists in the datastore for the nested-notification and action.
603 *
604 * @param[in] op Operation to check.
605 * @param[in] oper_tree Data from datastore.
606 * @param[in] operational_f Operational datastore file information.
607 * @return LY_ERR value.
608 */
609static LY_ERR
610check_operation_parent(struct lyd_node *op, struct lyd_node *oper_tree, struct cmdline_file *operational_f)
611{
612 LY_ERR ret;
613 struct ly_set *set = NULL;
614 char *path = NULL;
615
616 if (!op || !lyd_parent(op)) {
617 /* The function is defined only for nested-notification and action. */
618 return LY_SUCCESS;
619 }
620
621 if (!operational_f || (operational_f && !operational_f->in)) {
622 YLMSG_E("The --operational parameter needed to validate operation \"%s\" is missing.\n", LYD_NAME(op));
623 ret = LY_EVALID;
624 goto cleanup;
625 }
626
627 path = lyd_path(lyd_parent(op), LYD_PATH_STD, NULL, 0);
628 if (!path) {
629 ret = LY_EMEM;
630 goto cleanup;
631 }
632
633 if (!oper_tree) {
634 YLMSG_W("Operational datastore is empty or contains unknown data.\n");
635 YLMSG_E("Operation \"%s\" parent \"%s\" not found in the operational data.\n", LYD_NAME(op), path);
636 ret = LY_EVALID;
637 goto cleanup;
638 }
639 if ((ret = lyd_find_xpath(oper_tree, path, &set))) {
640 goto cleanup;
641 }
642 if (!set->count) {
643 YLMSG_E("Operation \"%s\" parent \"%s\" not found in the operational data.\n", LYD_NAME(op), path);
644 ret = LY_EVALID;
645 goto cleanup;
646 }
647
648cleanup:
649 ly_set_free(set, NULL);
650 free(path);
651
652 return ret;
653}
654
Radek Krejcie9f13b12020-11-09 17:42:04 +0100655LY_ERR
Michal Vaskoe0665742021-02-11 11:08:44 +0100656process_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 +0200657 uint32_t options_parse, uint32_t options_validate, uint32_t options_print, struct cmdline_file *operational_f,
658 struct cmdline_file *rpc_f, struct ly_set *inputs, struct ly_set *xpaths)
Radek Krejcie9f13b12020-11-09 17:42:04 +0100659{
Radek Krejci89757c12020-11-26 12:07:47 +0100660 LY_ERR ret = LY_SUCCESS;
Michal Vaskoff141b02022-05-02 09:22:36 +0200661 struct lyd_node *tree = NULL, *op = NULL, *envp = NULL, *merged_tree = NULL, *oper_tree = NULL;
aPieceka549c502023-04-12 11:28:31 +0200662 const char *xpath;
Michal Vasko9e81ce52022-04-29 10:16:21 +0200663 struct ly_set *set = NULL;
Radek Krejcie9f13b12020-11-09 17:42:04 +0100664
665 /* additional operational datastore */
666 if (operational_f && operational_f->in) {
Michal Vasko3f08fb92022-04-21 09:52:35 +0200667 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 +0100668 if (ret) {
669 YLMSG_E("Failed to parse operational datastore file \"%s\".\n", operational_f->path);
670 goto cleanup;
671 }
672 }
673
674 for (uint32_t u = 0; u < inputs->count; ++u) {
675 struct cmdline_file *input_f = (struct cmdline_file *)inputs->objs[u];
Michal Vasko26bbb272022-08-02 14:54:33 +0200676
Radek Krejcie9f13b12020-11-09 17:42:04 +0100677 switch (data_type) {
Michal Vasko1e4c68e2021-02-18 15:03:01 +0100678 case LYD_TYPE_DATA_YANG:
Michal Vaskoe0665742021-02-11 11:08:44 +0100679 ret = lyd_parse_data(ctx, NULL, input_f->in, input_f->format, options_parse, options_validate, &tree);
Radek Krejcie9f13b12020-11-09 17:42:04 +0100680 break;
Michal Vasko1e4c68e2021-02-18 15:03:01 +0100681 case LYD_TYPE_RPC_YANG:
682 case LYD_TYPE_REPLY_YANG:
683 case LYD_TYPE_NOTIF_YANG:
Michal Vaskoff141b02022-05-02 09:22:36 +0200684 ret = lyd_parse_op(ctx, NULL, input_f->in, input_f->format, data_type, &tree, &op);
Radek Krejcie9f13b12020-11-09 17:42:04 +0100685 break;
Michal Vasko3f08fb92022-04-21 09:52:35 +0200686 case LYD_TYPE_RPC_NETCONF:
687 case LYD_TYPE_NOTIF_NETCONF:
Michal Vaskoff141b02022-05-02 09:22:36 +0200688 ret = lyd_parse_op(ctx, NULL, input_f->in, input_f->format, data_type, &envp, &op);
689
690 /* adjust pointers */
691 for (tree = op; lyd_parent(tree); tree = lyd_parent(tree)) {}
Michal Vasko3f08fb92022-04-21 09:52:35 +0200692 break;
693 case LYD_TYPE_REPLY_NETCONF:
694 /* parse source RPC operation */
695 assert(rpc_f && rpc_f->in);
Michal Vaskoff141b02022-05-02 09:22:36 +0200696 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 +0200697 if (ret) {
698 YLMSG_E("Failed to parse source NETCONF RPC operation file \"%s\".\n", rpc_f->path);
699 goto cleanup;
700 }
701
Michal Vaskoff141b02022-05-02 09:22:36 +0200702 /* adjust pointers */
703 for (tree = op; lyd_parent(tree); tree = lyd_parent(tree)) {}
704
Michal Vasko3f08fb92022-04-21 09:52:35 +0200705 /* free input */
Michal Vaskoff141b02022-05-02 09:22:36 +0200706 lyd_free_siblings(lyd_child(op));
Michal Vasko3f08fb92022-04-21 09:52:35 +0200707
708 /* we do not care */
709 lyd_free_all(envp);
710 envp = NULL;
711
Michal Vaskoff141b02022-05-02 09:22:36 +0200712 ret = lyd_parse_op(ctx, op, input_f->in, input_f->format, data_type, &envp, NULL);
Michal Vasko3f08fb92022-04-21 09:52:35 +0200713 break;
Radek Krejci89757c12020-11-26 12:07:47 +0100714 default:
715 YLMSG_E("Internal error (%s:%d).\n", __FILE__, __LINE__);
716 goto cleanup;
Radek Krejcie9f13b12020-11-09 17:42:04 +0100717 }
718
719 if (ret) {
720 YLMSG_E("Failed to parse input data file \"%s\".\n", input_f->path);
721 goto cleanup;
722 }
723
724 if (merge) {
725 /* merge the data so far parsed for later validation and print */
726 if (!merged_tree) {
727 merged_tree = tree;
728 } else {
729 ret = lyd_merge_siblings(&merged_tree, tree, LYD_MERGE_DESTRUCT);
730 if (ret) {
731 YLMSG_E("Merging %s with previous data failed.\n", input_f->path);
732 goto cleanup;
733 }
734 }
735 tree = NULL;
736 } else if (format) {
Michal Vasko3f08fb92022-04-21 09:52:35 +0200737 /* print */
738 switch (data_type) {
739 case LYD_TYPE_DATA_YANG:
740 lyd_print_all(out, tree, format, options_print);
741 break;
742 case LYD_TYPE_RPC_YANG:
743 case LYD_TYPE_REPLY_YANG:
744 case LYD_TYPE_NOTIF_YANG:
745 case LYD_TYPE_RPC_NETCONF:
746 case LYD_TYPE_NOTIF_NETCONF:
747 lyd_print_tree(out, tree, format, options_print);
748 break;
749 case LYD_TYPE_REPLY_NETCONF:
750 /* just the output */
751 lyd_print_tree(out, lyd_child(tree), format, options_print);
752 break;
753 default:
754 assert(0);
755 }
Michal Vasko0bec3222022-04-22 07:46:00 +0200756 } else {
757 /* validation of the RPC/Action/reply/Notification with the operational datastore, if any */
758 switch (data_type) {
759 case LYD_TYPE_DATA_YANG:
760 /* already validated */
761 break;
762 case LYD_TYPE_RPC_YANG:
763 case LYD_TYPE_RPC_NETCONF:
764 ret = lyd_validate_op(tree, oper_tree, LYD_TYPE_RPC_YANG, NULL);
765 break;
766 case LYD_TYPE_REPLY_YANG:
767 case LYD_TYPE_REPLY_NETCONF:
768 ret = lyd_validate_op(tree, oper_tree, LYD_TYPE_REPLY_YANG, NULL);
769 break;
770 case LYD_TYPE_NOTIF_YANG:
771 case LYD_TYPE_NOTIF_NETCONF:
772 ret = lyd_validate_op(tree, oper_tree, LYD_TYPE_NOTIF_YANG, NULL);
773 break;
774 default:
775 assert(0);
776 }
Radek Krejcie9f13b12020-11-09 17:42:04 +0100777 if (ret) {
Michal Vasko0bec3222022-04-22 07:46:00 +0200778 if (operational_f->path) {
779 YLMSG_E("Failed to validate input data file \"%s\" with operational datastore \"%s\".\n",
780 input_f->path, operational_f->path);
781 } else {
782 YLMSG_E("Failed to validate input data file \"%s\".\n", input_f->path);
783 }
Radek Krejcie9f13b12020-11-09 17:42:04 +0100784 goto cleanup;
785 }
Michal Vasko9e81ce52022-04-29 10:16:21 +0200786
aPiecekf9b59812023-04-27 14:40:38 +0200787 if ((ret = check_operation_parent(op, oper_tree, operational_f))) {
788 goto cleanup;
Michal Vasko9e81ce52022-04-29 10:16:21 +0200789 }
Radek Krejcie9f13b12020-11-09 17:42:04 +0100790 }
Michal Vasko3f08fb92022-04-21 09:52:35 +0200791
792 /* next iter */
Radek Krejcie9f13b12020-11-09 17:42:04 +0100793 lyd_free_all(tree);
794 tree = NULL;
Michal Vasko3f08fb92022-04-21 09:52:35 +0200795 lyd_free_all(envp);
796 envp = NULL;
Radek Krejcie9f13b12020-11-09 17:42:04 +0100797 }
798
799 if (merge) {
800 /* validate the merged result */
aPiecek65de6d12023-04-05 13:38:53 +0200801 ret = lyd_validate_all(&merged_tree, ctx, options_validate, NULL);
Radek Krejcie9f13b12020-11-09 17:42:04 +0100802 if (ret) {
803 YLMSG_E("Merged data are not valid.\n");
804 goto cleanup;
805 }
806
807 if (format) {
808 /* and print it */
809 lyd_print_all(out, merged_tree, format, options_print);
810 }
811
Michal Vaskof8e71f72022-03-29 12:12:58 +0200812 for (uint32_t u = 0; xpaths && (u < xpaths->count); ++u) {
aPieceka549c502023-04-12 11:28:31 +0200813 xpath = (const char *)xpaths->objs[u];
814 ly_set_free(set, NULL);
815 ret = lys_find_xpath(ctx, NULL, xpath, LYS_FIND_NO_MATCH_ERROR, &set);
816 if (ret || !set->count) {
817 ret = (ret == LY_SUCCESS) ? LY_EINVAL : ret;
818 YLMSG_E("The requested xpath failed.\n");
819 goto cleanup;
820 }
821 if (evaluate_xpath(merged_tree, xpath)) {
Radek Krejcie9f13b12020-11-09 17:42:04 +0100822 goto cleanup;
823 }
824 }
825 }
826
827cleanup:
Radek Krejcie9f13b12020-11-09 17:42:04 +0100828 lyd_free_all(tree);
Michal Vasko3f08fb92022-04-21 09:52:35 +0200829 lyd_free_all(envp);
Michal Vaskoe1f495b2022-04-20 08:32:41 +0200830 lyd_free_all(merged_tree);
Michal Vasko3f08fb92022-04-21 09:52:35 +0200831 lyd_free_all(oper_tree);
Michal Vasko9e81ce52022-04-29 10:16:21 +0200832 ly_set_free(set, NULL);
Radek Krejcie9f13b12020-11-09 17:42:04 +0100833 return ret;
834}
romanf00089c2022-10-06 16:01:31 +0200835
836const struct lysc_node *
837find_schema_path(const struct ly_ctx *ctx, const char *schema_path)
838{
839 const char *end, *module_name_end;
840 char *module_name = NULL;
Michal Vaskob10c93b2022-12-14 12:16:27 +0100841 const struct lysc_node *node = NULL, *parent_node = NULL, *parent_node_tmp = NULL;
romanf00089c2022-10-06 16:01:31 +0200842 const struct lys_module *module;
843 size_t node_name_len;
844 ly_bool found_exact_match = 0;
845
846 /* iterate over each '/' in the path */
847 while (schema_path) {
848 /* example: schema_path = /listen/endpoint
849 * end == NULL for endpoint, end exists for listen */
850 end = strchr(schema_path + 1, '/');
851 if (end) {
852 node_name_len = end - schema_path - 1;
853 } else {
854 node_name_len = strlen(schema_path + 1);
855 }
856
857 /* ex: schema_path = /ietf-interfaces:interfaces/interface/ietf-ip:ipv4 */
858 module_name_end = strchr(schema_path, ':');
romanba1421b2022-10-07 11:02:36 +0200859 if (module_name_end && (!end || (module_name_end < end))) {
860 /* only get module's name, if it is in the current scope */
861 free(module_name);
862 /* - 1 because module_name_end points to ':' */
863 module_name = strndup(schema_path + 1, module_name_end - schema_path - 1);
864 if (!module_name) {
865 YLMSG_E("Memory allocation failed (%s:%d, %s)", __FILE__, __LINE__, strerror(errno));
866 parent_node = NULL;
867 goto cleanup;
romanf00089c2022-10-06 16:01:31 +0200868 }
romanba1421b2022-10-07 11:02:36 +0200869 /* move the pointer to the beginning of the node's name - 1 */
870 schema_path = module_name_end;
romanf00089c2022-10-06 16:01:31 +0200871
romanba1421b2022-10-07 11:02:36 +0200872 /* recalculate the length of the node's name, because the module prefix mustn't be compared later */
romanf00089c2022-10-06 16:01:31 +0200873 if (module_name_end < end) {
romanba1421b2022-10-07 11:02:36 +0200874 node_name_len = end - module_name_end - 1;
875 } else if (!end) {
876 node_name_len = strlen(module_name_end + 1);
romanf00089c2022-10-06 16:01:31 +0200877 }
878 }
879
880 module = ly_ctx_get_module_implemented(ctx, module_name);
881 if (!module) {
882 /* unknown module name */
883 parent_node = NULL;
884 goto cleanup;
885 }
886
887 /* iterate over the node's siblings / module's top level containers */
888 while ((node = lys_getnext(node, parent_node, module->compiled, LYS_GETNEXT_WITHCASE | LYS_GETNEXT_WITHCHOICE))) {
889 if (end && !strncmp(node->name, schema_path + 1, node_name_len) && (node->name[node_name_len] == '\0')) {
890 /* check if the whole node's name matches and it's not just a common prefix */
891 parent_node = node;
892 break;
893 } else if (!strncmp(node->name, schema_path + 1, node_name_len)) {
894 /* do the same here, however if there is no exact match, use the last node with the same prefix */
895 if (strlen(node->name) == node_name_len) {
896 parent_node = node;
897 found_exact_match = 1;
898 break;
899 } else {
900 parent_node_tmp = node;
901 }
902 }
903 }
904
905 if (!end && !found_exact_match) {
906 /* no exact match */
907 parent_node = parent_node_tmp;
908 }
909 found_exact_match = 0;
910
911 /* next iter */
912 schema_path = strchr(schema_path + 1, '/');
913 }
914
915cleanup:
916 free(module_name);
917 return parent_node;
918}