blob: 36c79281b0b7b00697590536928dcaeac2e02339 [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),
Michal Vasko69730152020-10-09 16:30:07 +0200193 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 Vasko69730152020-10-09 16:30:07 +0200246
Michal Vasko5aa44c02020-06-29 11:47:02 +0200247#ifndef __APPLE__
Radek Krejcif0e1ba52020-05-22 15:14:35 +0200248 char proc_path[32];
249 int len;
Michal Vasko5aa44c02020-06-29 11:47:02 +0200250#endif
Radek Krejcif0e1ba52020-05-22 15:14:35 +0200251
252 LY_CHECK_ARG_RET(NULL, ctx, in, filepath, );
253 if (*filepath) {
254 /* filepath already set */
255 return;
256 }
257
258 switch (in->type) {
259 case LY_IN_FILEPATH:
260 if (realpath(in->method.fpath.filepath, path) != NULL) {
Radek Krejci011e4aa2020-09-04 15:22:31 +0200261 lydict_insert(ctx, path, 0, filepath);
Radek Krejcif0e1ba52020-05-22 15:14:35 +0200262 } else {
Radek Krejci011e4aa2020-09-04 15:22:31 +0200263 lydict_insert(ctx, in->method.fpath.filepath, 0, filepath);
Radek Krejcif0e1ba52020-05-22 15:14:35 +0200264 }
265
266 break;
267 case LY_IN_FD:
268#ifdef __APPLE__
269 if (fcntl(in->method.fd, F_GETPATH, path) != -1) {
Radek Krejci011e4aa2020-09-04 15:22:31 +0200270 lydict_insert(ctx, path, 0, filepath);
Radek Krejcif0e1ba52020-05-22 15:14:35 +0200271 }
272#else
273 /* get URI if there is /proc */
274 sprintf(proc_path, "/proc/self/fd/%d", in->method.fd);
275 if ((len = readlink(proc_path, path, PATH_MAX - 1)) > 0) {
Radek Krejci011e4aa2020-09-04 15:22:31 +0200276 lydict_insert(ctx, path, len, filepath);
Radek Krejcif0e1ba52020-05-22 15:14:35 +0200277 }
278#endif
279 break;
280 case LY_IN_MEMORY:
281 case LY_IN_FILE:
282 /* nothing to do */
283 break;
284 default:
285 LOGINT(ctx);
286 break;
287 }
288
289}
290
291API void
Radek Krejci857189e2020-09-01 13:26:36 +0200292ly_in_free(struct ly_in *in, ly_bool destroy)
Radek Krejcif0e1ba52020-05-22 15:14:35 +0200293{
294 if (!in) {
295 return;
296 } else if (in->type == LY_IN_ERROR) {
297 LOGINT(NULL);
298 return;
299 }
300
301 if (destroy) {
302 if (in->type == LY_IN_MEMORY) {
Michal Vasko22df3f02020-08-24 13:29:22 +0200303 free((char *)in->start);
Radek Krejcif0e1ba52020-05-22 15:14:35 +0200304 } else {
Michal Vasko22df3f02020-08-24 13:29:22 +0200305 ly_munmap((char *)in->start, in->length);
Radek Krejcif0e1ba52020-05-22 15:14:35 +0200306
307 if (in->type == LY_IN_FILE) {
308 fclose(in->method.f);
309 } else {
310 close(in->method.fd);
311
312 if (in->type == LY_IN_FILEPATH) {
313 free(in->method.fpath.filepath);
314 }
315 }
316 }
317 } else if (in->type != LY_IN_MEMORY) {
Michal Vasko22df3f02020-08-24 13:29:22 +0200318 ly_munmap((char *)in->start, in->length);
Radek Krejcif0e1ba52020-05-22 15:14:35 +0200319
320 if (in->type == LY_IN_FILEPATH) {
321 close(in->method.fpath.fd);
322 free(in->method.fpath.filepath);
323 }
324 }
325
326 free(in);
327}
Michal Vasko63f3d842020-07-08 10:10:14 +0200328
329LY_ERR
330ly_in_read(struct ly_in *in, void *buf, size_t count)
331{
332 if (in->length && (in->length - (in->current - in->start) < count)) {
333 /* EOF */
334 return LY_EDENIED;
335 }
336
337 memcpy(buf, in->current, count);
338 in->current += count;
339 return LY_SUCCESS;
340}
341
342API size_t
343ly_in_parsed(const struct ly_in *in)
344{
345 return in->current - in->func_start;
346}
347
348LY_ERR
349ly_in_skip(struct ly_in *in, size_t count)
350{
351 if (in->length && (in->length - (in->current - in->start) < count)) {
352 /* EOF */
353 return LY_EDENIED;
354 }
355
356 in->current += count;
357 return LY_SUCCESS;
358}
Radek Krejci1798aae2020-07-14 13:26:06 +0200359
360void
361lyd_ctx_free(struct lyd_ctx *lydctx)
362{
363 ly_set_erase(&lydctx->unres_node_type, NULL);
364 ly_set_erase(&lydctx->unres_meta_type, NULL);
365 ly_set_erase(&lydctx->when_check, NULL);
366}
367
368LY_ERR
369lyd_parser_check_schema(struct lyd_ctx *lydctx, const struct lysc_node *snode)
370{
371 /* alternatively, we could provide line for the error messages, but it doesn't work for the LYB format */
372
373 if ((lydctx->parse_options & LYD_PARSE_NO_STATE) && (snode->flags & LYS_CONFIG_R)) {
374 LOGVAL(lydctx->data_ctx->ctx, LY_VLOG_LYSC, snode, LY_VCODE_INNODE, "state", snode->name);
375 return LY_EVALID;
376 }
377
378 if (snode->nodetype & (LYS_RPC | LYS_ACTION)) {
379 if (lydctx->int_opts & LYD_INTOPT_RPC) {
380 if (lydctx->op_node) {
381 LOGVAL(lydctx->data_ctx->ctx, LY_VLOG_LYSC, snode, LYVE_DATA, "Unexpected %s element \"%s\", %s \"%s\" already parsed.",
Michal Vasko69730152020-10-09 16:30:07 +0200382 lys_nodetype2str(snode->nodetype), snode->name,
383 lys_nodetype2str(lydctx->op_node->schema->nodetype), lydctx->op_node->schema->name);
Radek Krejci1798aae2020-07-14 13:26:06 +0200384 return LY_EVALID;
385 }
386 } else {
387 LOGVAL(lydctx->data_ctx->ctx, LY_VLOG_LYSC, snode, LYVE_DATA, "Unexpected %s element \"%s\".",
Michal Vasko69730152020-10-09 16:30:07 +0200388 lys_nodetype2str(snode->nodetype), snode->name);
Radek Krejci1798aae2020-07-14 13:26:06 +0200389 return LY_EVALID;
390 }
391 } else if (snode->nodetype == LYS_NOTIF) {
392 if (lydctx->int_opts & LYD_INTOPT_NOTIF) {
393 if (lydctx->op_node) {
394 LOGVAL(lydctx->data_ctx->ctx, LY_VLOG_LYSC, snode, LYVE_DATA, "Unexpected %s element \"%s\", %s \"%s\" already parsed.",
Michal Vasko69730152020-10-09 16:30:07 +0200395 lys_nodetype2str(snode->nodetype), snode->name,
396 lys_nodetype2str(lydctx->op_node->schema->nodetype), lydctx->op_node->schema->name);
Radek Krejci1798aae2020-07-14 13:26:06 +0200397 return LY_EVALID;
398 }
399 } else {
400 LOGVAL(lydctx->data_ctx->ctx, LY_VLOG_LYSC, snode, LYVE_DATA, "Unexpected %s element \"%s\".",
Michal Vasko69730152020-10-09 16:30:07 +0200401 lys_nodetype2str(snode->nodetype), snode->name);
Radek Krejci1798aae2020-07-14 13:26:06 +0200402 return LY_EVALID;
403 }
404 }
405
406 return LY_SUCCESS;
407}
408
409LY_ERR
410lyd_parser_create_term(struct lyd_ctx *lydctx, const struct lysc_node *schema, const char *value, size_t value_len,
Michal Vaskofeca4fb2020-10-05 08:58:40 +0200411 ly_bool *dynamic, LY_PREFIX_FORMAT format, void *prefix_data, uint32_t hints, struct lyd_node **node)
Radek Krejci1798aae2020-07-14 13:26:06 +0200412{
Michal Vaskofeca4fb2020-10-05 08:58:40 +0200413 ly_bool incomplete;
Radek Krejci1798aae2020-07-14 13:26:06 +0200414
Michal Vaskofeca4fb2020-10-05 08:58:40 +0200415 LY_CHECK_RET(lyd_create_term(schema, value, value_len, dynamic, format, prefix_data, hints, &incomplete, node));
416
417 if (incomplete && !(lydctx->parse_options & LYD_PARSE_ONLY)) {
Radek Krejci3d92e442020-10-12 12:48:13 +0200418 LY_CHECK_RET(ly_set_add(&lydctx->unres_node_type, *node, 1, NULL));
Radek Krejci1798aae2020-07-14 13:26:06 +0200419 }
Michal Vaskofeca4fb2020-10-05 08:58:40 +0200420 return LY_SUCCESS;
Radek Krejci1798aae2020-07-14 13:26:06 +0200421}
422
423LY_ERR
424lyd_parser_create_meta(struct lyd_ctx *lydctx, struct lyd_node *parent, struct lyd_meta **meta, const struct lys_module *mod,
Michal Vaskofeca4fb2020-10-05 08:58:40 +0200425 const char *name, size_t name_len, const char *value, size_t value_len, ly_bool *dynamic, LY_PREFIX_FORMAT format,
426 void *prefix_data, uint32_t hints)
Radek Krejci1798aae2020-07-14 13:26:06 +0200427{
Michal Vaskofeca4fb2020-10-05 08:58:40 +0200428 ly_bool incomplete;
429
430 LY_CHECK_RET(lyd_create_meta(parent, meta, mod, name, name_len, value, value_len, dynamic, format, prefix_data,
431 hints, &incomplete));
432
433 if (incomplete && !(lydctx->parse_options & LYD_PARSE_ONLY)) {
Radek Krejci3d92e442020-10-12 12:48:13 +0200434 LY_CHECK_RET(ly_set_add(&lydctx->unres_meta_type, *meta, 1, NULL));
Radek Krejci1798aae2020-07-14 13:26:06 +0200435 }
Michal Vaskofeca4fb2020-10-05 08:58:40 +0200436 return LY_SUCCESS;
Radek Krejci1798aae2020-07-14 13:26:06 +0200437}