blob: 6b6ab749724477d936f8875bd06a7956c6a63b2d [file] [log] [blame]
Radek Krejcif0e1ba52020-05-22 15:14:35 +02001/**
2 * @file printer.c
3 * @author Radek Krejci <rkrejci@cesnet.cz>
4 * @brief Generic libyang printers functions.
5 *
6 * Copyright (c) 2015 - 2019 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
16
Radek Krejcica376bd2020-06-11 16:04:06 +020017#include "parser.h"
18
Radek Krejcif0e1ba52020-05-22 15:14:35 +020019#include <errno.h>
20#include <fcntl.h>
21#include <limits.h>
22#include <stdio.h>
Radek Krejcica376bd2020-06-11 16:04:06 +020023#include <stdlib.h>
Radek Krejcif0e1ba52020-05-22 15:14:35 +020024#include <string.h>
Radek Krejcica376bd2020-06-11 16:04:06 +020025#include <unistd.h>
Radek Krejcif0e1ba52020-05-22 15:14:35 +020026
Radek Krejcica376bd2020-06-11 16:04:06 +020027#include "common.h"
Michal Vasko5aa44c02020-06-29 11:47:02 +020028#include "compat.h"
Radek Krejcica376bd2020-06-11 16:04:06 +020029#include "dict.h"
30#include "log.h"
Radek Krejci1798aae2020-07-14 13:26:06 +020031#include "parser_data.h"
Radek Krejcif0e1ba52020-05-22 15:14:35 +020032#include "parser_internal.h"
Radek Krejci1798aae2020-07-14 13:26:06 +020033#include "tree_data_internal.h"
Radek Krejcica376bd2020-06-11 16:04:06 +020034#include "tree_schema_internal.h"
Radek Krejcif0e1ba52020-05-22 15:14:35 +020035
36API LY_IN_TYPE
37ly_in_type(const struct ly_in *in)
38{
39 LY_CHECK_ARG_RET(NULL, in, LY_IN_ERROR);
40 return in->type;
41}
42
43API LY_ERR
44ly_in_new_fd(int fd, struct ly_in **in)
45{
46 size_t length;
47 char *addr;
48
49 LY_CHECK_ARG_RET(NULL, fd >= 0, in, LY_EINVAL);
50
51 LY_CHECK_RET(ly_mmap(NULL, fd, &length, (void **)&addr));
52 if (!addr) {
53 LOGERR(NULL, LY_EINVAL, "Empty input file.");
54 return LY_EINVAL;
55 }
56
57 *in = calloc(1, sizeof **in);
58 LY_CHECK_ERR_RET(!*in, LOGMEM(NULL); ly_munmap(addr, length), LY_EMEM);
59
60 (*in)->type = LY_IN_FD;
61 (*in)->method.fd = fd;
Michal Vasko63f3d842020-07-08 10:10:14 +020062 (*in)->current = (*in)->start = (*in)->func_start = addr;
Radek Krejcif0e1ba52020-05-22 15:14:35 +020063 (*in)->length = length;
64
65 return LY_SUCCESS;
66}
67
68API int
69ly_in_fd(struct ly_in *in, int fd)
70{
71 int prev_fd;
72 size_t length;
73 const char *addr;
74
75 LY_CHECK_ARG_RET(NULL, in, in->type == LY_IN_FD, -1);
76
77 prev_fd = in->method.fd;
78
79 if (fd != -1) {
80 LY_CHECK_RET(ly_mmap(NULL, fd, &length, (void **)&addr), -1);
81 if (!addr) {
82 LOGERR(NULL, LY_EINVAL, "Empty input file.");
83 return -1;
84 }
85
Michal Vasko22df3f02020-08-24 13:29:22 +020086 ly_munmap((char *)in->start, in->length);
Radek Krejcif0e1ba52020-05-22 15:14:35 +020087
88 in->method.fd = fd;
89 in->current = in->start = addr;
90 in->length = length;
91 }
92
93 return prev_fd;
94}
95
96API LY_ERR
97ly_in_new_file(FILE *f, struct ly_in **in)
98{
99 LY_CHECK_ARG_RET(NULL, f, in, LY_EINVAL);
100
101 LY_CHECK_RET(ly_in_new_fd(fileno(f), in));
102
103 /* convert the LY_IN_FD input handler into the LY_IN_FILE */
104 (*in)->type = LY_IN_FILE;
105 (*in)->method.f = f;
106
107 return LY_SUCCESS;
108}
109
110API FILE *
111ly_in_file(struct ly_in *in, FILE *f)
112{
113 FILE *prev_f;
114
115 LY_CHECK_ARG_RET(NULL, in, in->type == LY_IN_FILE, NULL);
116
117 prev_f = in->method.f;
118
119 if (f) {
120 /* convert LY_IN_FILE handler into LY_IN_FD to be able to update it via ly_in_fd() */
121 in->type = LY_IN_FD;
122 in->method.fd = fileno(prev_f);
123 if (ly_in_fd(in, fileno(f)) == -1) {
124 in->type = LY_IN_FILE;
125 in->method.f = prev_f;
126 return NULL;
127 }
128
129 /* if success, convert the result back */
130 in->type = LY_IN_FILE;
131 in->method.f = f;
132 }
133
134 return prev_f;
135}
136
137API LY_ERR
138ly_in_new_memory(const char *str, struct ly_in **in)
139{
140 LY_CHECK_ARG_RET(NULL, str, in, LY_EINVAL);
141
142 *in = calloc(1, sizeof **in);
143 LY_CHECK_ERR_RET(!*in, LOGMEM(NULL), LY_EMEM);
144
145 (*in)->type = LY_IN_MEMORY;
Michal Vasko63f3d842020-07-08 10:10:14 +0200146 (*in)->start = (*in)->current = (*in)->func_start = str;
Radek Krejcif0e1ba52020-05-22 15:14:35 +0200147
148 return LY_SUCCESS;
149}
150
Michal Vasko63f3d842020-07-08 10:10:14 +0200151API const char *
Radek Krejcif0e1ba52020-05-22 15:14:35 +0200152ly_in_memory(struct ly_in *in, const char *str)
153{
154 const char *data;
155
156 LY_CHECK_ARG_RET(NULL, in, in->type == LY_IN_MEMORY, NULL);
157
158 data = in->current;
159
160 if (str) {
161 in->start = in->current = str;
162 }
163
164 return data;
165}
166
167API LY_ERR
168ly_in_reset(struct ly_in *in)
169{
170 LY_CHECK_ARG_RET(NULL, in, LY_EINVAL);
171
Michal Vasko63f3d842020-07-08 10:10:14 +0200172 in->current = in->func_start = in->start;
Radek Krejcif0e1ba52020-05-22 15:14:35 +0200173 return LY_SUCCESS;
174}
175
176API LY_ERR
177ly_in_new_filepath(const char *filepath, size_t len, struct ly_in **in)
178{
Radek Krejci0f969882020-08-21 16:56:47 +0200179 LY_ERR ret;
Radek Krejcif0e1ba52020-05-22 15:14:35 +0200180 char *fp;
181 int fd;
182
183 LY_CHECK_ARG_RET(NULL, filepath, in, LY_EINVAL);
184
185 if (len) {
186 fp = strndup(filepath, len);
187 } else {
188 fp = strdup(filepath);
189 }
190
191 fd = open(fp, O_RDONLY);
Michal Vaskof2eb8af2020-07-14 12:22:40 +0200192 LY_CHECK_ERR_RET(fd == -1, LOGERR(NULL, LY_ESYS, "Failed to open file \"%s\" (%s).", fp, strerror(errno)); free(fp),
193 LY_ESYS);
Radek Krejcif0e1ba52020-05-22 15:14:35 +0200194
195 LY_CHECK_ERR_RET(ret = ly_in_new_fd(fd, in), free(fp), ret);
196
197 /* convert the LY_IN_FD input handler into the LY_IN_FILE */
198 (*in)->type = LY_IN_FILEPATH;
199 (*in)->method.fpath.fd = fd;
200 (*in)->method.fpath.filepath = fp;
201
202 return LY_SUCCESS;
203}
204
205API const char *
206ly_in_filepath(struct ly_in *in, const char *filepath, size_t len)
207{
208 int fd, prev_fd;
209 char *fp = NULL;
210
211 LY_CHECK_ARG_RET(NULL, in, in->type == LY_IN_FILEPATH, filepath ? NULL : ((void *)-1));
212
213 if (!filepath) {
214 return in->method.fpath.filepath;
215 }
216
217 if (len) {
218 fp = strndup(filepath, len);
219 } else {
220 fp = strdup(filepath);
221 }
222
223 /* replace filepath */
224 fd = open(fp, O_RDONLY);
225 LY_CHECK_ERR_RET(!fd, LOGERR(NULL, LY_ESYS, "Failed to open file \"%s\" (%s).", fp, strerror(errno)); free(fp), NULL);
226
227 /* convert LY_IN_FILEPATH handler into LY_IN_FD to be able to update it via ly_in_fd() */
228 in->type = LY_IN_FD;
229 prev_fd = ly_in_fd(in, fd);
230 LY_CHECK_ERR_RET(prev_fd == -1, in->type = LY_IN_FILEPATH; free(fp), NULL);
231
232 /* and convert the result back */
233 in->type = LY_IN_FILEPATH;
234 close(prev_fd);
235 free(in->method.fpath.filepath);
236 in->method.fpath.fd = fd;
237 in->method.fpath.filepath = fp;
238
239 return NULL;
240}
241
242void
243lys_parser_fill_filepath(struct ly_ctx *ctx, struct ly_in *in, const char **filepath)
244{
245 char path[PATH_MAX];
Michal Vasko5aa44c02020-06-29 11:47:02 +0200246#ifndef __APPLE__
Radek Krejcif0e1ba52020-05-22 15:14:35 +0200247 char proc_path[32];
248 int len;
Michal Vasko5aa44c02020-06-29 11:47:02 +0200249#endif
Radek Krejcif0e1ba52020-05-22 15:14:35 +0200250
251 LY_CHECK_ARG_RET(NULL, ctx, in, filepath, );
252 if (*filepath) {
253 /* filepath already set */
254 return;
255 }
256
257 switch (in->type) {
258 case LY_IN_FILEPATH:
259 if (realpath(in->method.fpath.filepath, path) != NULL) {
Radek Krejci011e4aa2020-09-04 15:22:31 +0200260 lydict_insert(ctx, path, 0, filepath);
Radek Krejcif0e1ba52020-05-22 15:14:35 +0200261 } else {
Radek Krejci011e4aa2020-09-04 15:22:31 +0200262 lydict_insert(ctx, in->method.fpath.filepath, 0, filepath);
Radek Krejcif0e1ba52020-05-22 15:14:35 +0200263 }
264
265 break;
266 case LY_IN_FD:
267#ifdef __APPLE__
268 if (fcntl(in->method.fd, F_GETPATH, path) != -1) {
Radek Krejci011e4aa2020-09-04 15:22:31 +0200269 lydict_insert(ctx, path, 0, filepath);
Radek Krejcif0e1ba52020-05-22 15:14:35 +0200270 }
271#else
272 /* get URI if there is /proc */
273 sprintf(proc_path, "/proc/self/fd/%d", in->method.fd);
274 if ((len = readlink(proc_path, path, PATH_MAX - 1)) > 0) {
Radek Krejci011e4aa2020-09-04 15:22:31 +0200275 lydict_insert(ctx, path, len, filepath);
Radek Krejcif0e1ba52020-05-22 15:14:35 +0200276 }
277#endif
278 break;
279 case LY_IN_MEMORY:
280 case LY_IN_FILE:
281 /* nothing to do */
282 break;
283 default:
284 LOGINT(ctx);
285 break;
286 }
287
288}
289
290API void
Radek Krejci857189e2020-09-01 13:26:36 +0200291ly_in_free(struct ly_in *in, ly_bool destroy)
Radek Krejcif0e1ba52020-05-22 15:14:35 +0200292{
293 if (!in) {
294 return;
295 } else if (in->type == LY_IN_ERROR) {
296 LOGINT(NULL);
297 return;
298 }
299
300 if (destroy) {
301 if (in->type == LY_IN_MEMORY) {
Michal Vasko22df3f02020-08-24 13:29:22 +0200302 free((char *)in->start);
Radek Krejcif0e1ba52020-05-22 15:14:35 +0200303 } else {
Michal Vasko22df3f02020-08-24 13:29:22 +0200304 ly_munmap((char *)in->start, in->length);
Radek Krejcif0e1ba52020-05-22 15:14:35 +0200305
306 if (in->type == LY_IN_FILE) {
307 fclose(in->method.f);
308 } else {
309 close(in->method.fd);
310
311 if (in->type == LY_IN_FILEPATH) {
312 free(in->method.fpath.filepath);
313 }
314 }
315 }
316 } else if (in->type != LY_IN_MEMORY) {
Michal Vasko22df3f02020-08-24 13:29:22 +0200317 ly_munmap((char *)in->start, in->length);
Radek Krejcif0e1ba52020-05-22 15:14:35 +0200318
319 if (in->type == LY_IN_FILEPATH) {
320 close(in->method.fpath.fd);
321 free(in->method.fpath.filepath);
322 }
323 }
324
325 free(in);
326}
Michal Vasko63f3d842020-07-08 10:10:14 +0200327
328LY_ERR
329ly_in_read(struct ly_in *in, void *buf, size_t count)
330{
331 if (in->length && (in->length - (in->current - in->start) < count)) {
332 /* EOF */
333 return LY_EDENIED;
334 }
335
336 memcpy(buf, in->current, count);
337 in->current += count;
338 return LY_SUCCESS;
339}
340
341API size_t
342ly_in_parsed(const struct ly_in *in)
343{
344 return in->current - in->func_start;
345}
346
347LY_ERR
348ly_in_skip(struct ly_in *in, size_t count)
349{
350 if (in->length && (in->length - (in->current - in->start) < count)) {
351 /* EOF */
352 return LY_EDENIED;
353 }
354
355 in->current += count;
356 return LY_SUCCESS;
357}
Radek Krejci1798aae2020-07-14 13:26:06 +0200358
359void
360lyd_ctx_free(struct lyd_ctx *lydctx)
361{
362 ly_set_erase(&lydctx->unres_node_type, NULL);
363 ly_set_erase(&lydctx->unres_meta_type, NULL);
364 ly_set_erase(&lydctx->when_check, NULL);
365}
366
367LY_ERR
368lyd_parser_check_schema(struct lyd_ctx *lydctx, const struct lysc_node *snode)
369{
370 /* alternatively, we could provide line for the error messages, but it doesn't work for the LYB format */
371
372 if ((lydctx->parse_options & LYD_PARSE_NO_STATE) && (snode->flags & LYS_CONFIG_R)) {
373 LOGVAL(lydctx->data_ctx->ctx, LY_VLOG_LYSC, snode, LY_VCODE_INNODE, "state", snode->name);
374 return LY_EVALID;
375 }
376
377 if (snode->nodetype & (LYS_RPC | LYS_ACTION)) {
378 if (lydctx->int_opts & LYD_INTOPT_RPC) {
379 if (lydctx->op_node) {
380 LOGVAL(lydctx->data_ctx->ctx, LY_VLOG_LYSC, snode, LYVE_DATA, "Unexpected %s element \"%s\", %s \"%s\" already parsed.",
381 lys_nodetype2str(snode->nodetype), snode->name,
382 lys_nodetype2str(lydctx->op_node->schema->nodetype), lydctx->op_node->schema->name);
383 return LY_EVALID;
384 }
385 } else {
386 LOGVAL(lydctx->data_ctx->ctx, LY_VLOG_LYSC, snode, LYVE_DATA, "Unexpected %s element \"%s\".",
387 lys_nodetype2str(snode->nodetype), snode->name);
388 return LY_EVALID;
389 }
390 } else if (snode->nodetype == LYS_NOTIF) {
391 if (lydctx->int_opts & LYD_INTOPT_NOTIF) {
392 if (lydctx->op_node) {
393 LOGVAL(lydctx->data_ctx->ctx, LY_VLOG_LYSC, snode, LYVE_DATA, "Unexpected %s element \"%s\", %s \"%s\" already parsed.",
394 lys_nodetype2str(snode->nodetype), snode->name,
395 lys_nodetype2str(lydctx->op_node->schema->nodetype), lydctx->op_node->schema->name);
396 return LY_EVALID;
397 }
398 } else {
399 LOGVAL(lydctx->data_ctx->ctx, LY_VLOG_LYSC, snode, LYVE_DATA, "Unexpected %s element \"%s\".",
400 lys_nodetype2str(snode->nodetype), snode->name);
401 return LY_EVALID;
402 }
403 }
404
405 return LY_SUCCESS;
406}
407
408LY_ERR
409lyd_parser_create_term(struct lyd_ctx *lydctx, const struct lysc_node *schema, const char *value, size_t value_len,
Radek Krejci857189e2020-09-01 13:26:36 +0200410 ly_bool *dynamic, uint32_t value_hints, LY_PREFIX_FORMAT format, void *prefix_data, struct lyd_node **node)
Radek Krejci1798aae2020-07-14 13:26:06 +0200411{
412 LY_ERR ret;
413
Michal Vaskoc8a230d2020-08-14 12:17:10 +0200414 ret = lyd_create_term(schema, value, value_len, dynamic, value_hints, format, prefix_data, node);
Radek Krejci1798aae2020-07-14 13:26:06 +0200415 if (ret == LY_EINCOMPLETE) {
416 if (!(lydctx->parse_options & LYD_PARSE_ONLY)) {
Radek Krejciba03a5a2020-08-27 14:40:41 +0200417 LY_CHECK_RET(ly_set_add(&lydctx->unres_node_type, *node, LY_SET_OPT_USEASLIST, NULL));
Radek Krejci1798aae2020-07-14 13:26:06 +0200418 }
419 ret = LY_SUCCESS;
420 }
421 return ret;
422}
423
424LY_ERR
425lyd_parser_create_meta(struct lyd_ctx *lydctx, struct lyd_node *parent, struct lyd_meta **meta, const struct lys_module *mod,
Radek Krejci857189e2020-09-01 13:26:36 +0200426 const char *name, size_t name_len, const char *value, size_t value_len, ly_bool *dynamic, uint32_t value_hints,
Radek Krejci0f969882020-08-21 16:56:47 +0200427 LY_PREFIX_FORMAT format, void *prefix_data, const struct lysc_node *ctx_snode)
Radek Krejci1798aae2020-07-14 13:26:06 +0200428{
429 LY_ERR ret;
Michal Vaskoc8a230d2020-08-14 12:17:10 +0200430 ret = lyd_create_meta(parent, meta, mod, name, name_len, value, value_len, dynamic, value_hints, format, prefix_data,
431 ctx_snode);
Radek Krejci1798aae2020-07-14 13:26:06 +0200432 if (ret == LY_EINCOMPLETE) {
Radek Krejciba03a5a2020-08-27 14:40:41 +0200433 LY_CHECK_RET(ly_set_add(&lydctx->unres_meta_type, *meta, LY_SET_OPT_USEASLIST, NULL));
Radek Krejci1798aae2020-07-14 13:26:06 +0200434 ret = LY_SUCCESS;
435 }
436 return ret;
437}