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