1 /**
2 * @file log.c
3 * @author Radek Krejci <rkrejci@cesnet.cz>
4 * @brief libyang logger implementation
5 *
6 * Copyright (c) 2015 - 2018 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 #define _BSD_SOURCE
17 #define _DEFAULT_SOURCE
18 #include <stdarg.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <limits.h>
22 #include <string.h>
23 #include <assert.h>
24
25 #include "common.h"
26 #include "parser.h"
27 #include "context.h"
28 #include "tree_internal.h"
29
30 volatile uint8_t ly_log_level = LY_LLWRN;
31 volatile uint8_t ly_log_opts = LY_LOLOG | LY_LOSTORE_LAST;
32 static void (*ly_log_clb)(LY_LOG_LEVEL level, const char *msg, const char *path);
33 static volatile int path_flag = 1;
34 volatile int ly_log_dbg_groups = 0;
35
36 API LY_LOG_LEVEL
ly_verb(LY_LOG_LEVEL level)37 ly_verb(LY_LOG_LEVEL level)
38 {
39 LY_LOG_LEVEL prev = ly_log_level;
40
41 ly_log_level = level;
42 return prev;
43 }
44
45 API int
ly_log_options(int opts)46 ly_log_options(int opts)
47 {
48 uint8_t prev = ly_log_opts;
49
50 ly_log_opts = opts;
51 return prev;
52 }
53
54 API void
ly_verb_dbg(int dbg_groups)55 ly_verb_dbg(int dbg_groups)
56 {
57 #ifndef NDEBUG
58 ly_log_dbg_groups = dbg_groups;
59 #else
60 (void)dbg_groups;
61 #endif
62 }
63
64 API void
ly_set_log_clb(void (* clb)(LY_LOG_LEVEL level,const char * msg,const char * path),int path)65 ly_set_log_clb(void (*clb)(LY_LOG_LEVEL level, const char *msg, const char *path), int path)
66 {
67 ly_log_clb = clb;
68 path_flag = path;
69 }
70
71 API void
ly_get_log_clb(void)72 (*ly_get_log_clb(void))(LY_LOG_LEVEL, const char *, const char *)
73 {
74 return ly_log_clb;
75 }
76
77 /* !! spends all string parameters !! */
78 static int
log_store(const struct ly_ctx * ctx,LY_LOG_LEVEL level,LY_ERR no,LY_VECODE vecode,char * msg,char * path,char * apptag)79 log_store(const struct ly_ctx *ctx, LY_LOG_LEVEL level, LY_ERR no, LY_VECODE vecode, char *msg, char *path, char *apptag)
80 {
81 struct ly_err_item *eitem, *last;
82
83 assert(ctx && (level < LY_LLVRB));
84
85 eitem = pthread_getspecific(ctx->errlist_key);
86 if (!eitem) {
87 /* if we are only to fill in path, there must have been an error stored */
88 assert(msg);
89 eitem = malloc(sizeof *eitem);
90 if (!eitem) {
91 goto mem_fail;
92 }
93 eitem->prev = eitem;
94 eitem->next = NULL;
95
96 pthread_setspecific(ctx->errlist_key, eitem);
97 } else if (!msg) {
98 /* only filling the path */
99 assert(path);
100
101 /* find last error */
102 eitem = eitem->prev;
103 do {
104 if (eitem->level == LY_LLERR) {
105 /* fill the path */
106 free(eitem->path);
107 eitem->path = path;
108 return 0;
109 }
110 eitem = eitem->prev;
111 } while (eitem->prev->next);
112 /* last error was not found */
113 assert(0);
114 } else if ((log_opt != ILO_STORE) && ((ly_log_opts & LY_LOSTORE_LAST) == LY_LOSTORE_LAST)) {
115 /* overwrite last message */
116 free(eitem->msg);
117 free(eitem->path);
118 free(eitem->apptag);
119 } else {
120 /* store new message */
121 last = eitem->prev;
122 eitem->prev = malloc(sizeof *eitem);
123 if (!eitem->prev) {
124 goto mem_fail;
125 }
126 eitem = eitem->prev;
127 eitem->prev = last;
128 eitem->next = NULL;
129 last->next = eitem;
130 }
131
132 /* fill in the information */
133 eitem->level = level;
134 eitem->no = no;
135 eitem->vecode = vecode;
136 eitem->msg = msg;
137 eitem->path = path;
138 eitem->apptag = apptag;
139 return 0;
140
141 mem_fail:
142 LOGMEM(NULL);
143 free(msg);
144 free(path);
145 free(apptag);
146 return -1;
147 }
148
149 /* !! spends path !! */
150 static void
log_vprintf(const struct ly_ctx * ctx,LY_LOG_LEVEL level,LY_ERR no,LY_VECODE vecode,char * path,const char * format,va_list args)151 log_vprintf(const struct ly_ctx *ctx, LY_LOG_LEVEL level, LY_ERR no, LY_VECODE vecode, char *path,
152 const char *format, va_list args)
153 {
154 char *msg = NULL;
155 int free_strs;
156
157 if ((log_opt == ILO_ERR2WRN) && (level == LY_LLERR)) {
158 /* change error to warning */
159 level = LY_LLWRN;
160 }
161
162 if ((log_opt == ILO_IGNORE) || (level > ly_log_level)) {
163 /* do not print or store the message */
164 free(path);
165 return;
166 }
167
168 /* set global errno on normal logging, but do not erase */
169 if ((log_opt != ILO_STORE) && no) {
170 ly_errno = no;
171 }
172
173 if ((no == LY_EVALID) && (vecode == LYVE_SUCCESS)) {
174 /* assume we are inheriting the error, so inherit vecode as well */
175 vecode = ly_vecode(ctx);
176 }
177
178 /* store the error/warning (if we need to store errors internally, it does not matter what are the user log options) */
179 if ((level < LY_LLVRB) && ctx && ((ly_log_opts & LY_LOSTORE) || (log_opt == ILO_STORE))) {
180 if (!format) {
181 assert(path);
182 /* postponed print of path related to the previous error, do not rewrite stored original message */
183 if (log_store(ctx, level, no, vecode, NULL, path, NULL)) {
184 return;
185 }
186 msg = "Path is related to the previous error message.";
187 } else {
188 if (vasprintf(&msg, format, args) == -1) {
189 LOGMEM(ctx);
190 free(path);
191 return;
192 }
193 if (log_store(ctx, level, no, vecode, msg, path, NULL)) {
194 return;
195 }
196 }
197 free_strs = 0;
198 } else {
199 if (vasprintf(&msg, format, args) == -1) {
200 LOGMEM(ctx);
201 free(path);
202 return;
203 }
204 free_strs = 1;
205 }
206
207 /* if we are only storing errors internally, never print the message (yet) */
208 if ((ly_log_opts & LY_LOLOG) && (log_opt != ILO_STORE)) {
209 if (ly_log_clb) {
210 ly_log_clb(level, msg, path);
211 } else {
212 fprintf(stderr, "libyang[%d]: %s%s", level, msg, path ? " " : "\n");
213 if (path) {
214 fprintf(stderr, "(path: %s)\n", path);
215 }
216 }
217 }
218
219 if (free_strs) {
220 free(path);
221 free(msg);
222 }
223 }
224
225 void
ly_log(const struct ly_ctx * ctx,LY_LOG_LEVEL level,LY_ERR no,const char * format,...)226 ly_log(const struct ly_ctx *ctx, LY_LOG_LEVEL level, LY_ERR no, const char *format, ...)
227 {
228 va_list ap;
229
230 va_start(ap, format);
231 log_vprintf(ctx, level, no, 0, NULL, format, ap);
232 va_end(ap);
233 }
234
235 #ifndef NDEBUG
236
237 void
ly_log_dbg(int group,const char * format,...)238 ly_log_dbg(int group, const char *format, ...)
239 {
240 char *dbg_format;
241 const char *str_group;
242 va_list ap;
243
244 if (!(ly_log_dbg_groups & group)) {
245 return;
246 }
247
248 switch (group) {
249 case LY_LDGDICT:
250 str_group = "DICT";
251 break;
252 case LY_LDGYANG:
253 str_group = "YANG";
254 break;
255 case LY_LDGYIN:
256 str_group = "YIN";
257 break;
258 case LY_LDGXPATH:
259 str_group = "XPATH";
260 break;
261 case LY_LDGDIFF:
262 str_group = "DIFF";
263 break;
264 case LY_LDGAPI:
265 str_group = "API";
266 break;
267 case LY_LDGHASH:
268 str_group = "HASH";
269 break;
270 default:
271 LOGINT(NULL);
272 return;
273 }
274
275 if (asprintf(&dbg_format, "%s: %s", str_group, format) == -1) {
276 LOGMEM(NULL);
277 return;
278 }
279
280 va_start(ap, format);
281 log_vprintf(NULL, LY_LLDBG, 0, 0, NULL, dbg_format, ap);
282 va_end(ap);
283 free(dbg_format);
284 }
285
286 #endif
287
288 API void
lyext_log(const struct ly_ctx * ctx,LY_LOG_LEVEL level,const char * plugin,const char * function,const char * format,...)289 lyext_log(const struct ly_ctx *ctx, LY_LOG_LEVEL level, const char *plugin, const char *function, const char *format, ...)
290 {
291 va_list ap;
292 char *plugin_msg;
293 int ret;
294
295 if (ly_log_level < level) {
296 return;
297 }
298
299 if (plugin)
300 ret = asprintf(&plugin_msg, "%s (reported by plugin %s, %s())", format, plugin, function);
301 else
302 ret = asprintf(&plugin_msg, "%s", format);
303
304 if (ret == -1) {
305 LOGMEM(ctx);
306 return;
307 }
308
309 va_start(ap, format);
310 log_vprintf(ctx, level, (level == LY_LLERR ? LY_EPLUGIN : 0), 0, NULL, plugin_msg, ap);
311 va_end(ap);
312
313 free(plugin_msg);
314 }
315
316 static enum LY_VLOG_ELEM extvelog2velog[] = {
317 LY_VLOG_NONE, /* LYEXT_VLOG_NONE */
318 LY_VLOG_XML, /* LYEXT_VLOG_XML */
319 LY_VLOG_LYS, /* LYEXT_VLOG_LYS */
320 LY_VLOG_LYD, /* LYEXT_VLOG_LYD */
321 LY_VLOG_STR, /* LYEXT_VLOG_STR */
322 LY_VLOG_PREV, /* LYEXT_VLOG_PREV */
323 };
324
325 API void
lyext_vlog(const struct ly_ctx * ctx,LY_VECODE vecode,const char * plugin,const char * function,LYEXT_VLOG_ELEM elem_type,const void * elem,const char * format,...)326 lyext_vlog(const struct ly_ctx *ctx, LY_VECODE vecode, const char *plugin, const char *function,
327 LYEXT_VLOG_ELEM elem_type, const void *elem, const char *format, ...)
328 {
329 enum LY_VLOG_ELEM etype = extvelog2velog[elem_type];
330 char *plugin_msg, *path = NULL;
331 va_list ap;
332 int ret;
333
334 if (path_flag && (etype != LY_VLOG_NONE)) {
335 if (etype == LY_VLOG_PREV) {
336 /* use previous path */
337 const struct ly_err_item *first = ly_err_first(ctx);
338 if (first && first->prev->path) {
339 path = strdup(first->prev->path);
340 }
341 } else {
342 /* print path */
343 if (!elem) {
344 /* top-level */
345 path = strdup("/");
346 } else {
347 ly_vlog_build_path(etype, elem, &path, 0, 0);
348 }
349 }
350 }
351
352 if (plugin)
353 ret = asprintf(&plugin_msg, "%s (reported by plugin %s, %s())", format, plugin, function);
354 else
355 ret = asprintf(&plugin_msg, "%s", format);
356
357 if (ret == -1) {
358 LOGMEM(ctx);
359 free(path);
360 return;
361 }
362
363 va_start(ap, format);
364 /* path is spent and should not be freed! */
365 log_vprintf(ctx, LY_LLERR, LY_EVALID, vecode, path, plugin_msg, ap);
366 va_end(ap);
367
368 free(plugin_msg);
369 }
370
371 const char *ly_errs[] = {
372 /* LYE_SUCCESS */ "",
373 /* LYE_XML_MISS */ "Missing %s \"%s\".",
374 /* LYE_XML_INVAL */ "Invalid %s.",
375 /* LYE_XML_INCHAR */ "Encountered invalid character sequence \"%.10s\".",
376
377 /* LYE_EOF */ "Unexpected end of input data.",
378 /* LYE_INSTMT */ "Invalid keyword \"%s\".",
379 /* LYE_INCHILDSTMT */ "Invalid keyword \"%s\" as a child to \"%s\".",
380 /* LYE_INPAR */ "Invalid ancestor \"%s\" of \"%s\".",
381 /* LYE_INID */ "Invalid identifier \"%s\" (%s).",
382 /* LYE_INDATE */ "Invalid date \"%s\", valid date in format \"YYYY-MM-DD\" expected.",
383 /* LYE_INARG */ "Invalid value \"%s\" of \"%s\".",
384 /* LYE_MISSSTMT */ "Missing keyword \"%s\".",
385 /* LYE_MISSCHILDSTMT */ "Missing keyword \"%s\" as a child to \"%s\".",
386 /* LYE_MISSARG */ "Missing argument \"%s\" to keyword \"%s\".",
387 /* LYE_TOOMANY */ "Too many instances of \"%s\" in \"%s\".",
388 /* LYE_DUPID */ "Duplicated %s identifier \"%s\".",
389 /* LYE_DUPLEAFLIST */ "Duplicated instance of \"%s\" leaf-list (\"%s\").",
390 /* LYE_DUPLIST */ "Duplicated instance of \"%s\" list.",
391 /* LYE_NOUNIQ */ "Unique data leaf(s) \"%s\" not satisfied in \"%s\" and \"%s\".",
392 /* LYE_ENUM_INVAL */ "Invalid value \"%d\" of \"%s\" enum, restricted enum value does not match the base type value \"%d\".",
393 /* LYE_ENUM_INNAME */ "Adding new enum name \"%s\" in restricted enumeration type is not allowed.",
394 /* LYE_ENUM_DUPVAL */ "The value \"%d\" of \"%s\" enum has already been assigned to \"%s\" enum.",
395 /* LYE_ENUM_DUPNAME */ "The enum name \"%s\" has already been assigned to another enum.",
396 /* LYE_ENUM_WS */ "The enum name \"%s\" includes invalid leading or trailing whitespaces.",
397 /* LYE_BITS_INVAL */ "Invalid position \"%d\" of \"%s\" bit, restricted bits position does not match the base type position \"%d\".",
398 /* LYE_BITS_INNAME */ "Adding new bit name \"%s\" in restricted bits type is not allowed.",
399 /* LYE_BITS_DUPVAL */ "The position \"%d\" of \"%s\" bit has already been assigned to \"%s\" bit.",
400 /* LYE_BITS_DUPNAME */ "The bit name \"%s\" has already been assigned to another bit.",
401 /* LYE_INMOD */ "Module name \"%s\" refers to an unknown module.",
402 /* LYE_INMOD_LEN */ "Module name \"%.*s\" refers to an unknown module.",
403 /* LYE_KEY_NLEAF */ "Key \"%s\" is not a leaf.",
404 /* LYE_KEY_TYPE */ "Key \"%s\" must not be the built-in type \"empty\".",
405 /* LYE_KEY_CONFIG */ "The \"config\" value of the \"%s\" key differs from its list config value.",
406 /* LYE_KEY_MISS */ "Leaf \"%s\" defined as key in a list not found.",
407 /* LYE_KEY_DUP */ "Key identifier \"%s\" is not unique.",
408 /* LYE_INREGEX */ "Regular expression \"%s\" is not valid (\"%s\": %s).",
409 /* LYE_INRESOLV */ "Failed to resolve %s \"%s\".",
410 /* LYE_INSTATUS */ "A %s definition \"%s\" %s %s definition \"%s\".",
411 /* LYE_CIRC_LEAFREFS */"A circular chain of leafrefs detected.",
412 /* LYE_CIRC_FEATURES */"A circular chain features detected in \"%s\" feature.",
413 /* LYE_CIRC_IMPORTS */ "A circular dependency (import) for module \"%s\".",
414 /* LYE_CIRC_INCLUDES */"A circular dependency (include) for submodule \"%s\".",
415 /* LYE_INVER */ "Different YANG versions of a submodule and its main module.",
416 /* LYE_SUBMODULE */ "Unable to parse submodule, parse the main module instead.",
417
418 /* LYE_OBSDATA */ "Obsolete data \"%s\" instantiated.",
419 /* LYE_OBSTYPE */ "Data node \"%s\" with obsolete type \"%s\" instantiated.",
420 /* LYE_NORESOLV */ "No resolvents found for %s \"%s\".",
421 /* LYE_INELEM */ "Unknown element \"%s\".",
422 /* LYE_INELEM_LEN */ "Unknown element \"%.*s\".",
423 /* LYE_MISSELEM */ "Missing required element \"%s\" in \"%s\".",
424 /* LYE_INVAL */ "Invalid value \"%s\" in \"%s\" element.",
425 /* LYE_INMETA */ "Invalid \"%s:%s\" metadata with value \"%s\".",
426 /* LYE_INATTR */ "Invalid attribute \"%s\".",
427 /* LYE_MISSATTR */ "Missing attribute \"%s\" in \"%s\" element.",
428 /* LYE_NOCONSTR */ "Value \"%s\" does not satisfy the constraint \"%s\" (range, length, or pattern).",
429 /* LYE_INCHAR */ "Unexpected character(s) '%c' (%.15s).",
430 /* LYE_INPRED */ "Predicate resolution failed on \"%s\".",
431 /* LYE_MCASEDATA */ "Data for more than one case branch of \"%s\" choice present.",
432 /* LYE_NOMUST */ "Must condition \"%s\" not satisfied.",
433 /* LYE_NOWHEN */ "When condition \"%s\" not satisfied.",
434 /* LYE_INORDER */ "Invalid order of elements \"%s\" and \"%s\".",
435 /* LYE_INWHEN */ "Irresolvable when condition \"%s\".",
436 /* LYE_NOMIN */ "Too few \"%s\" elements.",
437 /* LYE_NOMAX */ "Too many \"%s\" elements.",
438 /* LYE_NOREQINS */ "Required instance of \"%s\" does not exist.",
439 /* LYE_NOLEAFREF */ "Leafref \"%s\" of value \"%s\" points to a non-existing leaf.",
440 /* LYE_NOMANDCHOICE */ "Mandatory choice \"%s\" missing a case branch.",
441
442 /* LYE_XPATH_INTOK */ "Unexpected XPath token %s (%.15s).",
443 /* LYE_XPATH_EOF */ "Unexpected XPath expression end.",
444 /* LYE_XPATH_INOP_1 */ "Cannot apply XPath operation %s on %s.",
445 /* LYE_XPATH_INOP_2 */ "Cannot apply XPath operation %s on %s and %s.",
446 /* LYE_XPATH_INCTX */ "Invalid context type %s in %s.",
447 /* LYE_XPATH_INMOD */ "Unknown module \"%.*s\".",
448 /* LYE_XPATH_INFUNC */ "Unknown XPath function \"%.*s\".",
449 /* LYE_XPATH_INARGCOUNT */ "Invalid number of arguments (%d) for the XPath function %.*s.",
450 /* LYE_XPATH_INARGTYPE */ "Wrong type of argument #%d (%s) for the XPath function %s.",
451 /* LYE_XPATH_DUMMY */ "Accessing the value of the dummy node \"%s\".",
452 /* LYE_XPATH_NOEND */ "Unterminated string delimited with %c (%.15s).",
453
454 /* LYE_PATH_INCHAR */ "Unexpected character(s) '%c' (\"%s\").",
455 /* LYE_PATH_INMOD */ "Module not found or not implemented.",
456 /* LYE_PATH_MISSMOD */ "Missing module name.",
457 /* LYE_PATH_INNODE */ "Schema node not found.",
458 /* LYE_PATH_INKEY */ "List key not found or on incorrect position (\"%s\").",
459 /* LYE_PATH_MISSKEY */ "List keys or position missing (\"%s\").",
460 /* LYE_PATH_INIDENTREF */ "Identityref predicate value \"%.*s\" missing module name.",
461 /* LYE_PATH_EXISTS */ "Node already exists.",
462 /* LYE_PATH_MISSPAR */ "Parent does not exist.",
463 /* LYE_PATH_PREDTOOMANY */ "Too many predicates.",
464 };
465
466 static const LY_VECODE ecode2vecode[] = {
467 LYVE_SUCCESS, /* LYE_SUCCESS */
468
469 LYVE_XML_MISS, /* LYE_XML_MISS */
470 LYVE_XML_INVAL, /* LYE_XML_INVAL */
471 LYVE_XML_INCHAR, /* LYE_XML_INCHAR */
472
473 LYVE_EOF, /* LYE_EOF */
474 LYVE_INSTMT, /* LYE_INSTMT */
475 LYVE_INSTMT, /* LYE_INCHILDSTMT */
476 LYVE_INPAR, /* LYE_INPAR */
477 LYVE_INID, /* LYE_INID */
478 LYVE_INDATE, /* LYE_INDATE */
479 LYVE_INARG, /* LYE_INARG */
480 LYVE_MISSSTMT, /* LYE_MISSCHILDSTMT */
481 LYVE_MISSSTMT, /* LYE_MISSSTMT */
482 LYVE_MISSARG, /* LYE_MISSARG */
483 LYVE_TOOMANY, /* LYE_TOOMANY */
484 LYVE_DUPID, /* LYE_DUPID */
485 LYVE_DUPLEAFLIST, /* LYE_DUPLEAFLIST */
486 LYVE_DUPLIST, /* LYE_DUPLIST */
487 LYVE_NOUNIQ, /* LYE_NOUNIQ */
488 LYVE_ENUM_INVAL, /* LYE_ENUM_INVAL */
489 LYVE_ENUM_INNAME, /* LYE_ENUM_INNAME */
490 LYVE_ENUM_INVAL, /* LYE_ENUM_DUPVAL */
491 LYVE_ENUM_INNAME, /* LYE_ENUM_DUPNAME */
492 LYVE_ENUM_WS, /* LYE_ENUM_WS */
493 LYVE_BITS_INVAL, /* LYE_BITS_INVAL */
494 LYVE_BITS_INNAME, /* LYE_BITS_INNAME */
495 LYVE_BITS_INVAL, /* LYE_BITS_DUPVAL */
496 LYVE_BITS_INNAME, /* LYE_BITS_DUPNAME */
497 LYVE_INMOD, /* LYE_INMOD */
498 LYVE_INMOD, /* LYE_INMOD_LEN */
499 LYVE_KEY_NLEAF, /* LYE_KEY_NLEAF */
500 LYVE_KEY_TYPE, /* LYE_KEY_TYPE */
501 LYVE_KEY_CONFIG, /* LYE_KEY_CONFIG */
502 LYVE_KEY_MISS, /* LYE_KEY_MISS */
503 LYVE_KEY_DUP, /* LYE_KEY_DUP */
504 LYVE_INREGEX, /* LYE_INREGEX */
505 LYVE_INRESOLV, /* LYE_INRESOLV */
506 LYVE_INSTATUS, /* LYE_INSTATUS */
507 LYVE_CIRC_LEAFREFS,/* LYE_CIRC_LEAFREFS */
508 LYVE_CIRC_FEATURES,/* LYE_CIRC_FEATURES */
509 LYVE_CIRC_IMPORTS, /* LYE_CIRC_IMPORTS */
510 LYVE_CIRC_INCLUDES,/* LYE_CIRC_INCLUDES */
511 LYVE_INVER, /* LYE_INVER */
512 LYVE_SUBMODULE, /* LYE_SUBMODULE */
513
514 LYVE_OBSDATA, /* LYE_OBSDATA */
515 LYVE_OBSDATA, /* LYE_OBSTYPE */
516 LYVE_NORESOLV, /* LYE_NORESOLV */
517 LYVE_INELEM, /* LYE_INELEM */
518 LYVE_INELEM, /* LYE_INELEM_LEN */
519 LYVE_MISSELEM, /* LYE_MISSELEM */
520 LYVE_INVAL, /* LYE_INVAL */
521 LYVE_INMETA, /* LYE_INMETA */
522 LYVE_INATTR, /* LYE_INATTR */
523 LYVE_MISSATTR, /* LYE_MISSATTR */
524 LYVE_NOCONSTR, /* LYE_NOCONSTR */
525 LYVE_INCHAR, /* LYE_INCHAR */
526 LYVE_INPRED, /* LYE_INPRED */
527 LYVE_MCASEDATA, /* LYE_MCASEDATA */
528 LYVE_NOMUST, /* LYE_NOMUST */
529 LYVE_NOWHEN, /* LYE_NOWHEN */
530 LYVE_INORDER, /* LYE_INORDER */
531 LYVE_INWHEN, /* LYE_INWHEN */
532 LYVE_NOMIN, /* LYE_NOMIN */
533 LYVE_NOMAX, /* LYE_NOMAX */
534 LYVE_NOREQINS, /* LYE_NOREQINS */
535 LYVE_NOLEAFREF, /* LYE_NOLEAFREF */
536 LYVE_NOMANDCHOICE, /* LYE_NOMANDCHOICE */
537
538 LYVE_XPATH_INTOK, /* LYE_XPATH_INTOK */
539 LYVE_XPATH_EOF, /* LYE_XPATH_EOF */
540 LYVE_XPATH_INOP, /* LYE_XPATH_INOP_1 */
541 LYVE_XPATH_INOP, /* LYE_XPATH_INOP_2 */
542 LYVE_XPATH_INCTX, /* LYE_XPATH_INCTX */
543 LYVE_XPATH_INMOD, /* LYE_XPATH_INMOD */
544 LYVE_XPATH_INFUNC, /* LYE_XPATH_INFUNC */
545 LYVE_XPATH_INARGCOUNT, /* LYE_XPATH_INARGCOUNT */
546 LYVE_XPATH_INARGTYPE, /* LYE_XPATH_INARGTYPE */
547 LYVE_XPATH_DUMMY, /* LYE_XPATH_DUMMY */
548 LYVE_XPATH_NOEND, /* LYE_XPATH_NOEND */
549
550 LYVE_PATH_INCHAR, /* LYE_PATH_INCHAR */
551 LYVE_PATH_INMOD, /* LYE_PATH_INMOD */
552 LYVE_PATH_MISSMOD, /* LYE_PATH_MISSMOD */
553 LYVE_PATH_INNODE, /* LYE_PATH_INNODE */
554 LYVE_PATH_INKEY, /* LYE_PATH_INKEY */
555 LYVE_PATH_MISSKEY, /* LYE_PATH_MISSKEY */
556 LYVE_PATH_INIDENTREF, /* LYE_PATH_INIDENTREF */
557 LYVE_PATH_EXISTS, /* LYE_PATH_EXISTS */
558 LYVE_PATH_MISSPAR, /* LYE_PATH_MISSPAR */
559 LYVE_PATH_PREDTOOMANY, /* LYE_PATH_PREDTOOMANY */
560 };
561
562 static int
ly_vlog_build_path_print(char ** path,uint16_t * index,const char * str,uint16_t str_len,uint16_t * length)563 ly_vlog_build_path_print(char **path, uint16_t *index, const char *str, uint16_t str_len, uint16_t *length)
564 {
565 void *mem;
566 uint16_t step;
567
568 if ((*index) < str_len) {
569 /* enlarge buffer */
570 step = (str_len < LY_BUF_STEP) ? LY_BUF_STEP : str_len;
571 mem = realloc(*path, *length + *index + step + 1);
572 LY_CHECK_ERR_RETURN(!mem, LOGMEM(NULL), -1);
573 *path = mem;
574
575 /* move data, lengths */
576 memmove(&(*path)[*index + step], &(*path)[*index], *length);
577 (*index) += step;
578 }
579
580 (*index) -= str_len;
581 memcpy(&(*path)[*index], str, str_len);
582 *length += str_len;
583
584 return 0;
585 }
586
587 int
ly_vlog_build_path(enum LY_VLOG_ELEM elem_type,const void * elem,char ** path,int schema_all_prefixes,int data_no_last_predicate)588 ly_vlog_build_path(enum LY_VLOG_ELEM elem_type, const void *elem, char **path, int schema_all_prefixes, int data_no_last_predicate)
589 {
590 int i, j, yang_data_extension = 0;
591 struct lys_node_list *slist;
592 struct lys_node *sparent = NULL;
593 struct lyd_node *dlist, *diter;
594 const struct lys_module *top_smodule = NULL;
595 const char *name, *prefix = NULL, *val_end, *val_start, *ext_name;
596 char *str;
597 uint16_t length, index;
598 size_t len;
599
600 length = 0;
601 *path = malloc(1);
602 LY_CHECK_ERR_RETURN(!(*path), LOGMEM(NULL), -1);
603 index = 0;
604
605 while (elem) {
606 switch (elem_type) {
607 case LY_VLOG_XML:
608 name = ((struct lyxml_elem *)elem)->name;
609 prefix = ((struct lyxml_elem *)elem)->ns ? ((struct lyxml_elem *)elem)->ns->prefix : NULL;
610 elem = ((struct lyxml_elem *)elem)->parent;
611 break;
612 case LY_VLOG_LYS:
613 if (!top_smodule) {
614 /* remember the top module, it will act as the current module */
615 for (sparent = (struct lys_node *)elem; lys_parent(sparent); sparent = lys_parent(sparent));
616 top_smodule = lys_node_module(sparent);
617 }
618
619 /* skip uses */
620 sparent = lys_parent((struct lys_node *)elem);
621 while (sparent && (sparent->nodetype == LYS_USES)) {
622 sparent = lys_parent(sparent);
623 }
624 if (!sparent || (lys_node_module((struct lys_node *)elem) != top_smodule) || schema_all_prefixes) {
625 prefix = lys_node_module((struct lys_node *)elem)->name;
626 } else {
627 prefix = NULL;
628 }
629
630 if (((struct lys_node *)elem)->nodetype & (LYS_AUGMENT | LYS_GROUPING)) {
631 if (ly_vlog_build_path_print(path, &index, "]", 1, &length)) {
632 return -1;
633 }
634
635 name = ((struct lys_node *)elem)->name;
636 if (ly_vlog_build_path_print(path, &index, name, strlen(name), &length)) {
637 return -1;
638 }
639
640 if (((struct lys_node *)elem)->nodetype == LYS_GROUPING) {
641 name = "{grouping}[";
642 } else { /* augment */
643 name = "{augment}[";
644 }
645 } else if (((struct lys_node *)elem)->nodetype == LYS_EXT) {
646 name = ((struct lys_ext_instance *)elem)->def->name;
647 if (!strcmp(name, "yang-data")) {
648 yang_data_extension = 1;
649 name = ((struct lys_ext_instance *)elem)->arg_value;
650 prefix = lys_node_module((struct lys_node *)elem)->name;
651 }
652 } else {
653 name = ((struct lys_node *)elem)->name;
654 }
655
656 if (((struct lys_node *)elem)->nodetype == LYS_EXT) {
657 if (((struct lys_ext_instance*)elem)->parent_type == LYEXT_PAR_NODE) {
658 elem = (struct lys_node*)((struct lys_ext_instance*)elem)->parent;
659 } else {
660 sparent = NULL;
661 elem = NULL;
662 }
663 break;
664 }
665
666 /* need to find the parent again because we don't want to skip augments */
667 do {
668 sparent = ((struct lys_node *)elem)->parent;
669 elem = lys_parent((struct lys_node *)elem);
670 } while (elem && (((struct lys_node *)elem)->nodetype == LYS_USES));
671 break;
672 case LY_VLOG_LYD:
673 name = ((struct lyd_node *)elem)->schema->name;
674 if (!((struct lyd_node *)elem)->parent ||
675 lyd_node_module((struct lyd_node *)elem) != lyd_node_module(((struct lyd_node *)elem)->parent)) {
676 prefix = lyd_node_module((struct lyd_node *)elem)->name;
677 } else {
678 prefix = NULL;
679 }
680
681 /* handle predicates (keys) in case of lists */
682 if (!data_no_last_predicate || index) {
683 if (((struct lyd_node *)elem)->schema->nodetype == LYS_LIST) {
684 dlist = (struct lyd_node *)elem;
685 slist = (struct lys_node_list *)((struct lyd_node *)elem)->schema;
686 if (slist->keys_size) {
687 /* schema list with keys - use key values in predicates */
688 for (i = slist->keys_size - 1; i > -1; i--) {
689 LY_TREE_FOR(dlist->child, diter) {
690 if (diter->schema == (struct lys_node *)slist->keys[i]) {
691 break;
692 }
693 }
694 if (diter && ((struct lyd_node_leaf_list *)diter)->value_str) {
695 if (strchr(((struct lyd_node_leaf_list *)diter)->value_str, '\'')) {
696 val_start = "=\"";
697 val_end = "\"]";
698 } else {
699 val_start = "='";
700 val_end = "']";
701 }
702
703 /* print value */
704 if (ly_vlog_build_path_print(path, &index, val_end, 2, &length)) {
705 return -1;
706 }
707 len = strlen(((struct lyd_node_leaf_list *)diter)->value_str);
708 if (ly_vlog_build_path_print(path, &index,
709 ((struct lyd_node_leaf_list *)diter)->value_str, len, &length)) {
710 return -1;
711 }
712
713 /* print schema name */
714 if (ly_vlog_build_path_print(path, &index, val_start, 2, &length)) {
715 return -1;
716 }
717 len = strlen(diter->schema->name);
718 if (ly_vlog_build_path_print(path, &index, diter->schema->name, len, &length)) {
719 return -1;
720 }
721
722 if (lyd_node_module(dlist) != lyd_node_module(diter)) {
723 if (ly_vlog_build_path_print(path, &index, ":", 1, &length)) {
724 return -1;
725 }
726 len = strlen(lyd_node_module(diter)->name);
727 if (ly_vlog_build_path_print(path, &index, lyd_node_module(diter)->name, len, &length)) {
728 return -1;
729 }
730 }
731
732 if (ly_vlog_build_path_print(path, &index, "[", 1, &length)) {
733 return -1;
734 }
735 }
736 }
737 } else {
738 /* schema list without keys - use instance position */
739 i = j = lyd_list_pos(dlist);
740 len = 1;
741 while (j > 9) {
742 ++len;
743 j /= 10;
744 }
745
746 if (ly_vlog_build_path_print(path, &index, "]", 1, &length)) {
747 return -1;
748 }
749
750 str = malloc(len + 1);
751 LY_CHECK_ERR_RETURN(!str, LOGMEM(NULL), -1);
752 sprintf(str, "%d", i);
753
754 if (ly_vlog_build_path_print(path, &index, str, len, &length)) {
755 free(str);
756 return -1;
757 }
758 free(str);
759
760 if (ly_vlog_build_path_print(path, &index, "[", 1, &length)) {
761 return -1;
762 }
763 }
764 } else if (((struct lyd_node *)elem)->schema->nodetype == LYS_LEAFLIST &&
765 ((struct lyd_node_leaf_list *)elem)->value_str) {
766 if (strchr(((struct lyd_node_leaf_list *)elem)->value_str, '\'')) {
767 val_start = "[.=\"";
768 val_end = "\"]";
769 } else {
770 val_start = "[.='";
771 val_end = "']";
772 }
773
774 if (ly_vlog_build_path_print(path, &index, val_end, 2, &length)) {
775 return -1;
776 }
777 len = strlen(((struct lyd_node_leaf_list *)elem)->value_str);
778 if (ly_vlog_build_path_print(path, &index, ((struct lyd_node_leaf_list *)elem)->value_str, len, &length)) {
779 return -1;
780 }
781 if (ly_vlog_build_path_print(path, &index, val_start, 4, &length)) {
782 return -1;
783 }
784 }
785 }
786
787 /* check if it is yang-data top element */
788 if (!((struct lyd_node *)elem)->parent) {
789 ext_name = lyp_get_yang_data_template_name(elem);
790 if (ext_name) {
791 if (ly_vlog_build_path_print(path, &index, name, strlen(name), &length)) {
792 return -1;
793 }
794 if (ly_vlog_build_path_print(path, &index, "/", 1, &length)) {
795 return -1;
796 }
797 yang_data_extension = 1;
798 name = ext_name;
799 }
800 }
801
802 elem = ((struct lyd_node *)elem)->parent;
803 break;
804 case LY_VLOG_STR:
805 len = strlen((const char *)elem);
806 if (ly_vlog_build_path_print(path, &index, (const char *)elem, len, &length)) {
807 return -1;
808 }
809 goto success;
810 default:
811 /* shouldn't be here */
812 LOGINT(NULL);
813 return -1;
814 }
815 if (name) {
816 if (ly_vlog_build_path_print(path, &index, name, strlen(name), &length)) {
817 return -1;
818 }
819 if (prefix) {
820 if (yang_data_extension && ly_vlog_build_path_print(path, &index, "#", 1, &length)) {
821 return -1;
822 }
823 if (ly_vlog_build_path_print(path, &index, ":", 1, &length)) {
824 return -1;
825 }
826 if (ly_vlog_build_path_print(path, &index, prefix, strlen(prefix), &length)) {
827 return -1;
828 }
829 }
830 }
831 if (ly_vlog_build_path_print(path, &index, "/", 1, &length)) {
832 return -1;
833 }
834 if ((elem_type == LY_VLOG_LYS) && !elem && sparent && (sparent->nodetype == LYS_AUGMENT)) {
835 len = strlen(((struct lys_node_augment *)sparent)->target_name);
836 if (ly_vlog_build_path_print(path, &index, ((struct lys_node_augment *)sparent)->target_name, len, &length)) {
837 return -1;
838 }
839 }
840 }
841
842 success:
843 memmove(*path, (*path) + index, length);
844 (*path)[length] = '\0';
845 return 0;
846 }
847
848 void
ly_vlog(const struct ly_ctx * ctx,LY_ECODE ecode,enum LY_VLOG_ELEM elem_type,const void * elem,...)849 ly_vlog(const struct ly_ctx *ctx, LY_ECODE ecode, enum LY_VLOG_ELEM elem_type, const void *elem, ...)
850 {
851 va_list ap;
852 const char *fmt;
853 char* path = NULL;
854 const struct ly_err_item *first;
855
856 if ((ecode == LYE_PATH) && !path_flag) {
857 return;
858 }
859
860 if (path_flag && (elem_type != LY_VLOG_NONE)) {
861 if (elem_type == LY_VLOG_PREV) {
862 /* use previous path */
863 first = ly_err_first(ctx);
864 if (first && first->prev->path) {
865 path = strdup(first->prev->path);
866 }
867 } else {
868 /* print path */
869 if (!elem) {
870 /* top-level */
871 path = strdup("/");
872 } else {
873 ly_vlog_build_path(elem_type, elem, &path, 0, 0);
874 }
875 }
876 }
877
878 va_start(ap, elem);
879 /* path is spent and should not be freed! */
880 switch (ecode) {
881 case LYE_SPEC:
882 fmt = va_arg(ap, char *);
883 log_vprintf(ctx, LY_LLERR, LY_EVALID, LYVE_SUCCESS, path, fmt, ap);
884 break;
885 case LYE_PATH:
886 assert(path);
887 log_vprintf(ctx, LY_LLERR, LY_EVALID, LYVE_SUCCESS, path, NULL, ap);
888 break;
889 default:
890 log_vprintf(ctx, LY_LLERR, LY_EVALID, ecode2vecode[ecode], path, ly_errs[ecode], ap);
891 break;
892 }
893 va_end(ap);
894 }
895
896 void
ly_vlog_str(const struct ly_ctx * ctx,enum LY_VLOG_ELEM elem_type,const char * str,...)897 ly_vlog_str(const struct ly_ctx *ctx, enum LY_VLOG_ELEM elem_type, const char *str, ...)
898 {
899 va_list ap;
900 char *path = NULL, *fmt, *ptr;
901 const struct ly_err_item *first;
902
903 assert((elem_type == LY_VLOG_NONE) || (elem_type == LY_VLOG_PREV));
904
905 if (elem_type == LY_VLOG_PREV) {
906 /* use previous path */
907 first = ly_err_first(ctx);
908 if (first && first->prev->path) {
909 path = strdup(first->prev->path);
910 }
911 }
912
913 if (strchr(str, '%')) {
914 /* must be enough */
915 fmt = malloc(2 * strlen(str) + 1);
916 strcpy(fmt, str);
917 for (ptr = strchr(fmt, '%'); ptr; ptr = strchr(ptr + 2, '%')) {
918 memmove(ptr + 1, ptr, strlen(ptr) + 1);
919 ptr[0] = '%';
920 }
921 } else {
922 fmt = strdup(str);
923 }
924
925 va_start(ap, str);
926 /* path is spent and should not be freed! */
927 log_vprintf(ctx, LY_LLERR, LY_EVALID, LYVE_SUCCESS, path, fmt, ap);
928 va_end(ap);
929
930 free(fmt);
931 }
932
933 API void
ly_err_print(struct ly_err_item * eitem)934 ly_err_print(struct ly_err_item *eitem)
935 {
936 if (ly_log_opts & LY_LOLOG) {
937 if (ly_log_clb) {
938 ly_log_clb(eitem->level, eitem->msg, eitem->path);
939 } else {
940 fprintf(stderr, "libyang[%d]: %s%s", eitem->level, eitem->msg, eitem->path ? " " : "\n");
941 if (eitem->path) {
942 fprintf(stderr, "(path: %s)\n", eitem->path);
943 }
944 }
945 }
946 }
947
948 static void
err_print(struct ly_ctx * ctx,struct ly_err_item * last_eitem)949 err_print(struct ly_ctx *ctx, struct ly_err_item *last_eitem)
950 {
951 if (!last_eitem) {
952 last_eitem = pthread_getspecific(ctx->errlist_key);
953 } else {
954 /* this last was already stored before, do not write it again */
955 last_eitem = last_eitem->next;
956 }
957
958 if ((log_opt != ILO_STORE) && (log_opt != ILO_IGNORE)) {
959 for (; last_eitem; last_eitem = last_eitem->next) {
960 ly_err_print(last_eitem);
961
962 /* also properly update ly_errno */
963 if (last_eitem->level == LY_LLERR) {
964 ly_errno = last_eitem->no;
965 }
966 }
967 }
968 }
969
970 /**
971 * @brief Make \p last_eitem the last error item ignoring any logging options.
972 */
973 void
ly_err_free_next(struct ly_ctx * ctx,struct ly_err_item * last_eitem)974 ly_err_free_next(struct ly_ctx *ctx, struct ly_err_item *last_eitem)
975 {
976 if (!last_eitem) {
977 ly_err_clean(ctx, NULL);
978 } else if (last_eitem->next) {
979 ly_err_clean(ctx, last_eitem->next);
980 }
981 }
982
983 /**
984 * @brief Properly clean errors from \p ctx based on the user and internal logging options
985 * after resolving schema/data unres.
986 *
987 * @param[in] ctx Context used.
988 * @param[in] prev_eitem Most recent error item before resolving data unres.
989 * @param[in] keep Whether to keep the stored errors.
990 */
991 static void
err_clean(struct ly_ctx * ctx,struct ly_err_item * prev_eitem,int keep)992 err_clean(struct ly_ctx *ctx, struct ly_err_item *prev_eitem, int keep)
993 {
994 struct ly_err_item *first;
995
996 /* internal options take precedence */
997 if (log_opt == ILO_STORE) {
998 /* keep all the new errors */
999 } else if ((log_opt == ILO_IGNORE) || !keep || !(ly_log_opts & LY_LOSTORE)) {
1000 /* throw away all the new errors */
1001 ly_err_free_next(ctx, prev_eitem);
1002 } else if ((ly_log_opts & LY_LOSTORE_LAST) == LY_LOSTORE_LAST) {
1003 /* keep only the most recent error */
1004 first = pthread_getspecific(ctx->errlist_key);
1005 if (!first) {
1006 /* no errors whatsoever */
1007 return;
1008 }
1009 prev_eitem = first->prev;
1010
1011 /* put the context errlist in order */
1012 pthread_setspecific(ctx->errlist_key, prev_eitem);
1013 assert(!prev_eitem->prev->next || (prev_eitem->prev->next == prev_eitem));
1014 prev_eitem->prev->next = NULL;
1015 prev_eitem->prev = prev_eitem;
1016
1017 /* free all the errlist items except the last one, do not free any if there is only one */
1018 if (prev_eitem != first) {
1019 ly_err_free(first);
1020 }
1021 }
1022 }
1023
1024 void
ly_ilo_change(struct ly_ctx * ctx,enum int_log_opts new_ilo,enum int_log_opts * prev_ilo,struct ly_err_item ** prev_last_eitem)1025 ly_ilo_change(struct ly_ctx *ctx, enum int_log_opts new_ilo, enum int_log_opts *prev_ilo, struct ly_err_item **prev_last_eitem)
1026 {
1027 assert(prev_ilo);
1028
1029 *prev_ilo = log_opt;
1030 if (new_ilo == ILO_STORE) {
1031 /* only in this case the errors are only temporarily stored */
1032 assert(ctx && prev_last_eitem);
1033 *prev_last_eitem = (struct ly_err_item *)ly_err_first(ctx);
1034 if (*prev_last_eitem) {
1035 *prev_last_eitem = (*prev_last_eitem)->prev;
1036 }
1037 }
1038
1039 if (log_opt != ILO_IGNORE) {
1040 log_opt = new_ilo;
1041 } /* else we can just keep it, useless to change it */
1042 }
1043
1044 void
ly_ilo_restore(struct ly_ctx * ctx,enum int_log_opts prev_ilo,struct ly_err_item * prev_last_eitem,int keep_and_print)1045 ly_ilo_restore(struct ly_ctx *ctx, enum int_log_opts prev_ilo, struct ly_err_item *prev_last_eitem, int keep_and_print)
1046 {
1047 assert(log_opt != ILO_LOG);
1048 if (log_opt != ILO_STORE) {
1049 /* nothing to print or free */
1050 assert(log_opt == prev_ilo || (!ctx && !prev_last_eitem && !keep_and_print));
1051 log_opt = prev_ilo;
1052 return;
1053 }
1054
1055 assert(ctx);
1056
1057 log_opt = prev_ilo;
1058 if (keep_and_print) {
1059 err_print(ctx, prev_last_eitem);
1060 }
1061 err_clean(ctx, prev_last_eitem, keep_and_print);
1062 }
1063
1064 void
ly_err_last_set_apptag(const struct ly_ctx * ctx,const char * apptag)1065 ly_err_last_set_apptag(const struct ly_ctx *ctx, const char *apptag)
1066 {
1067 struct ly_err_item *i;
1068
1069 if (log_opt != ILO_IGNORE) {
1070 i = ly_err_first(ctx);
1071 if (i) {
1072 i = i->prev;
1073 i->apptag = strdup(apptag);
1074 }
1075 }
1076 }
1077
1078 void
ly_err_last_set_msg(const struct ly_ctx * ctx,const char * msg)1079 ly_err_last_set_msg(const struct ly_ctx *ctx, const char *msg)
1080 {
1081 struct ly_err_item *i;
1082
1083 if (log_opt != ILO_IGNORE) {
1084 i = ly_err_first(ctx);
1085 if (i) {
1086 i = i->prev;
1087 free(i->msg);
1088 i->msg = strdup(msg);
1089 }
1090 }
1091 }
1092