/** * @file log.c * @author Radek Krejci * @brief libyang logger implementation * * Copyright (c) 2015 - 2018 CESNET, z.s.p.o. * * This source code is licensed under BSD 3-Clause License (the "License"). * You may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://opensource.org/licenses/BSD-3-Clause */ #define _GNU_SOURCE #define _BSD_SOURCE #define _DEFAULT_SOURCE #include #include #include #include #include #include #include "common.h" #include "parser.h" #include "context.h" #include "tree_internal.h" volatile uint8_t ly_log_level = LY_LLWRN; volatile uint8_t ly_log_opts = LY_LOLOG | LY_LOSTORE_LAST; static void (*ly_log_clb)(LY_LOG_LEVEL level, const char *msg, const char *path); static volatile int path_flag = 1; #ifndef NDEBUG volatile int ly_log_dbg_groups = 0; #endif API LY_LOG_LEVEL ly_verb(LY_LOG_LEVEL level) { LY_LOG_LEVEL prev = ly_log_level; ly_log_level = level; return prev; } API int ly_log_options(int opts) { uint8_t prev = ly_log_opts; ly_log_opts = opts; return prev; } API void ly_verb_dbg(int dbg_groups) { #ifndef NDEBUG ly_log_dbg_groups = dbg_groups; #else (void)dbg_groups; #endif } API void ly_set_log_clb(void (*clb)(LY_LOG_LEVEL level, const char *msg, const char *path), int path) { ly_log_clb = clb; path_flag = path; } API void (*ly_get_log_clb(void))(LY_LOG_LEVEL, const char *, const char *) { return ly_log_clb; } /* !! spends all string parameters !! */ 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) { struct ly_err_item *eitem, *last; assert(ctx && (level < LY_LLVRB)); eitem = pthread_getspecific(ctx->errlist_key); if (!eitem) { /* if we are only to fill in path, there must have been an error stored */ assert(msg); eitem = malloc(sizeof *eitem); if (!eitem) { goto mem_fail; } eitem->prev = eitem; eitem->next = NULL; pthread_setspecific(ctx->errlist_key, eitem); } else if (!msg) { /* only filling the path */ assert(path); /* find last error */ eitem = eitem->prev; do { if (eitem->level == LY_LLERR) { /* fill the path */ free(eitem->path); eitem->path = path; return 0; } eitem = eitem->prev; } while (eitem->prev->next); /* last error was not found */ assert(0); } else if ((log_opt != ILO_STORE) && ((ly_log_opts & LY_LOSTORE_LAST) == LY_LOSTORE_LAST)) { /* overwrite last message */ free(eitem->msg); free(eitem->path); free(eitem->apptag); } else { /* store new message */ last = eitem->prev; eitem->prev = malloc(sizeof *eitem); if (!eitem->prev) { goto mem_fail; } eitem = eitem->prev; eitem->prev = last; eitem->next = NULL; last->next = eitem; } /* fill in the information */ eitem->level = level; eitem->no = no; eitem->vecode = vecode; eitem->msg = msg; eitem->path = path; eitem->apptag = apptag; return 0; mem_fail: LOGMEM(NULL); free(msg); free(path); free(apptag); return -1; } /* !! spends path !! */ 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) { char *msg = NULL; int free_strs; if ((log_opt == ILO_ERR2WRN) && (level == LY_LLERR)) { /* change error to warning */ level = LY_LLWRN; } if ((log_opt == ILO_IGNORE) || (level > ly_log_level)) { /* do not print or store the message */ free(path); return; } /* set global errno on normal logging, but do not erase */ if ((log_opt != ILO_STORE) && no) { ly_errno = no; } if ((no == LY_EVALID) && (vecode == LYVE_SUCCESS)) { /* assume we are inheriting the error, so inherit vecode as well */ vecode = ly_vecode(ctx); } /* store the error/warning (if we need to store errors internally, it does not matter what are the user log options) */ if ((level < LY_LLVRB) && ctx && ((ly_log_opts & LY_LOSTORE) || (log_opt == ILO_STORE))) { if (!format) { assert(path); /* postponed print of path related to the previous error, do not rewrite stored original message */ if (log_store(ctx, level, no, vecode, NULL, path, NULL)) { return; } msg = "Path is related to the previous error message."; } else { if (vasprintf(&msg, format, args) == -1) { LOGMEM(ctx); free(path); return; } if (log_store(ctx, level, no, vecode, msg, path, NULL)) { return; } } free_strs = 0; } else { if (vasprintf(&msg, format, args) == -1) { LOGMEM(ctx); free(path); return; } free_strs = 1; } /* if we are only storing errors internally, never print the message (yet) */ if ((ly_log_opts & LY_LOLOG) && (log_opt != ILO_STORE)) { if (ly_log_clb) { ly_log_clb(level, msg, path); } else { fprintf(stderr, "libyang[%d]: %s%s", level, msg, path ? " " : "\n"); if (path) { fprintf(stderr, "(path: %s)\n", path); } } } if (free_strs) { free(path); free(msg); } } void ly_log(const struct ly_ctx *ctx, LY_LOG_LEVEL level, LY_ERR no, const char *format, ...) { va_list ap; va_start(ap, format); log_vprintf(ctx, level, no, 0, NULL, format, ap); va_end(ap); } #ifndef NDEBUG void ly_log_dbg(int group, const char *format, ...) { char *dbg_format; const char *str_group; va_list ap; if (!(ly_log_dbg_groups & group)) { return; } switch (group) { case LY_LDGDICT: str_group = "DICT"; break; case LY_LDGYANG: str_group = "YANG"; break; case LY_LDGYIN: str_group = "YIN"; break; case LY_LDGXPATH: str_group = "XPATH"; break; case LY_LDGDIFF: str_group = "DIFF"; break; default: LOGINT(NULL); return; } if (asprintf(&dbg_format, "%s: %s", str_group, format) == -1) { LOGMEM(NULL); return; } va_start(ap, format); log_vprintf(NULL, LY_LLDBG, 0, 0, NULL, dbg_format, ap); va_end(ap); } #endif API void lyext_log(const struct ly_ctx *ctx, LY_LOG_LEVEL level, const char *plugin, const char *function, const char *format, ...) { va_list ap; char *plugin_msg; if (ly_log_level < level) { return; } if (asprintf(&plugin_msg, "%s (reported by plugin %s, %s())", format, plugin, function) == -1) { LOGMEM(ctx); return; } va_start(ap, format); log_vprintf(ctx, level, (level == LY_LLERR ? LY_EPLUGIN : 0), 0, NULL, plugin_msg, ap); va_end(ap); free(plugin_msg); } const char *ly_errs[] = { /* LYE_SUCCESS */ "", /* LYE_XML_MISS */ "Missing %s \"%s\".", /* LYE_XML_INVAL */ "Invalid %s.", /* LYE_XML_INCHAR */ "Encountered invalid character sequence \"%.10s\".", /* LYE_EOF */ "Unexpected end of input data.", /* LYE_INSTMT */ "Invalid keyword \"%s\".", /* LYE_INCHILDSTMT */ "Invalid keyword \"%s\" as a child to \"%s\".", /* LYE_INPAR */ "Invalid ancestor \"%s\" of \"%s\".", /* LYE_INID */ "Invalid identifier \"%s\" (%s).", /* LYE_INDATE */ "Invalid date \"%s\", valid date in format \"YYYY-MM-DD\" expected.", /* LYE_INARG */ "Invalid value \"%s\" of \"%s\".", /* LYE_MISSSTMT */ "Missing keyword \"%s\".", /* LYE_MISSCHILDSTMT */ "Missing keyword \"%s\" as a child to \"%s\".", /* LYE_MISSARG */ "Missing argument \"%s\" to keyword \"%s\".", /* LYE_TOOMANY */ "Too many instances of \"%s\" in \"%s\".", /* LYE_DUPID */ "Duplicated %s identifier \"%s\".", /* LYE_DUPLEAFLIST */ "Duplicated instance of \"%s\" leaf-list (\"%s\").", /* LYE_DUPLIST */ "Duplicated instance of \"%s\" list.", /* LYE_NOUNIQ */ "Unique data leaf(s) \"%s\" not satisfied in \"%s\" and \"%s\".", /* LYE_ENUM_INVAL */ "Invalid value \"%d\" of \"%s\" enum, restricted enum value does not match the base type value \"%d\".", /* LYE_ENUM_INNAME */ "Adding new enum name \"%s\" in restricted enumeration type is not allowed.", /* LYE_ENUM_DUPVAL */ "The value \"%d\" of \"%s\" enum has already been assigned to \"%s\" enum.", /* LYE_ENUM_DUPNAME */ "The enum name \"%s\" has already been assigned to another enum.", /* LYE_ENUM_WS */ "The enum name \"%s\" includes invalid leading or trailing whitespaces.", /* LYE_BITS_INVAL */ "Invalid position \"%d\" of \"%s\" bit, restricted bits position does not match the base type position \"%d\".", /* LYE_BITS_INNAME */ "Adding new bit name \"%s\" in restricted bits type is not allowed.", /* LYE_BITS_DUPVAL */ "The position \"%d\" of \"%s\" bit has already been assigned to \"%s\" bit.", /* LYE_BITS_DUPNAME */ "The bit name \"%s\" has already been assigned to another bit.", /* LYE_INMOD */ "Module name \"%s\" refers to an unknown module.", /* LYE_INMOD_LEN */ "Module name \"%.*s\" refers to an unknown module.", /* LYE_KEY_NLEAF */ "Key \"%s\" is not a leaf.", /* LYE_KEY_TYPE */ "Key \"%s\" must not be the built-in type \"empty\".", /* LYE_KEY_CONFIG */ "The \"config\" value of the \"%s\" key differs from its list config value.", /* LYE_KEY_MISS */ "Leaf \"%s\" defined as key in a list not found.", /* LYE_KEY_DUP */ "Key identifier \"%s\" is not unique.", /* LYE_INREGEX */ "Regular expression \"%s\" is not valid (\"%s\": %s).", /* LYE_INRESOLV */ "Failed to resolve %s \"%s\".", /* LYE_INSTATUS */ "A %s definition \"%s\" %s %s definition \"%s\".", /* LYE_CIRC_LEAFREFS */"A circular chain of leafrefs detected.", /* LYE_CIRC_FEATURES */"A circular chain features detected in \"%s\" feature.", /* LYE_CIRC_IMPORTS */ "A circular dependency (import) for module \"%s\".", /* LYE_CIRC_INCLUDES */"A circular dependency (include) for submodule \"%s\".", /* LYE_INVER */ "Different YANG versions of a submodule and its main module.", /* LYE_SUBMODULE */ "Unable to parse submodule, parse the main module instead.", /* LYE_OBSDATA */ "Obsolete data \"%s\" instantiated.", /* LYE_OBSTYPE */ "Data node \"%s\" with obsolete type \"%s\" instantiated.", /* LYE_NORESOLV */ "No resolvents found for %s \"%s\".", /* LYE_INELEM */ "Unknown element \"%s\".", /* LYE_INELEM_LEN */ "Unknown element \"%.*s\".", /* LYE_MISSELEM */ "Missing required element \"%s\" in \"%s\".", /* LYE_INVAL */ "Invalid value \"%s\" in \"%s\" element.", /* LYE_INMETA */ "Invalid \"%s:%s\" metadata with value \"%s\".", /* LYE_INATTR */ "Invalid attribute \"%s\".", /* LYE_MISSATTR */ "Missing attribute \"%s\" in \"%s\" element.", /* LYE_NOCONSTR */ "Value \"%s\" does not satisfy the constraint \"%s\" (range, length, or pattern).", /* LYE_INCHAR */ "Unexpected character(s) '%c' (%.15s).", /* LYE_INPRED */ "Predicate resolution failed on \"%s\".", /* LYE_MCASEDATA */ "Data for more than one case branch of \"%s\" choice present.", /* LYE_NOMUST */ "Must condition \"%s\" not satisfied.", /* LYE_NOWHEN */ "When condition \"%s\" not satisfied.", /* LYE_INORDER */ "Invalid order of elements \"%s\" and \"%s\".", /* LYE_INWHEN */ "Irresolvable when condition \"%s\".", /* LYE_NOMIN */ "Too few \"%s\" elements.", /* LYE_NOMAX */ "Too many \"%s\" elements.", /* LYE_NOREQINS */ "Required instance of \"%s\" does not exists.", /* LYE_NOLEAFREF */ "Leafref \"%s\" of value \"%s\" points to a non-existing leaf.", /* LYE_NOMANDCHOICE */ "Mandatory choice \"%s\" missing a case branch.", /* LYE_XPATH_INTOK */ "Unexpected XPath token %s (%.15s).", /* LYE_XPATH_EOF */ "Unexpected XPath expression end.", /* LYE_XPATH_INOP_1 */ "Cannot apply XPath operation %s on %s.", /* LYE_XPATH_INOP_2 */ "Cannot apply XPath operation %s on %s and %s.", /* LYE_XPATH_INCTX */ "Invalid context type %s in %s.", /* LYE_XPATH_INMOD */ "Unknown module \"%.*s\".", /* LYE_XPATH_INFUNC */ "Unknown XPath function \"%.*s\".", /* LYE_XPATH_INARGCOUNT */ "Invalid number of arguments (%d) for the XPath function %.*s.", /* LYE_XPATH_INARGTYPE */ "Wrong type of argument #%d (%s) for the XPath function %s.", /* LYE_XPATH_DUMMY */ "Accessing the value of the dummy node \"%s\".", /* LYE_XPATH_NOEND */ "Unterminated string delimited with %c (%.15s).", /* LYE_PATH_INCHAR */ "Unexpected character(s) '%c' (\"%s\").", /* LYE_PATH_INMOD */ "Module not found or not implemented.", /* LYE_PATH_MISSMOD */ "Missing module name.", /* LYE_PATH_INNODE */ "Schema node not found.", /* LYE_PATH_INKEY */ "List key not found or on incorrect position (\"%s\").", /* LYE_PATH_MISSKEY */ "List keys or position missing (\"%s\").", /* LYE_PATH_INIDENTREF */ "Identityref predicate value \"%.*s\" missing module name.", /* LYE_PATH_EXISTS */ "Node already exists.", /* LYE_PATH_MISSPAR */ "Parent does not exist.", /* LYE_PATH_PREDTOOMANY */ "Too many predicates.", }; static const LY_VECODE ecode2vecode[] = { LYVE_SUCCESS, /* LYE_SUCCESS */ LYVE_XML_MISS, /* LYE_XML_MISS */ LYVE_XML_INVAL, /* LYE_XML_INVAL */ LYVE_XML_INCHAR, /* LYE_XML_INCHAR */ LYVE_EOF, /* LYE_EOF */ LYVE_INSTMT, /* LYE_INSTMT */ LYVE_INSTMT, /* LYE_INCHILDSTMT */ LYVE_INPAR, /* LYE_INPAR */ LYVE_INID, /* LYE_INID */ LYVE_INDATE, /* LYE_INDATE */ LYVE_INARG, /* LYE_INARG */ LYVE_MISSSTMT, /* LYE_MISSCHILDSTMT */ LYVE_MISSSTMT, /* LYE_MISSSTMT */ LYVE_MISSARG, /* LYE_MISSARG */ LYVE_TOOMANY, /* LYE_TOOMANY */ LYVE_DUPID, /* LYE_DUPID */ LYVE_DUPLEAFLIST, /* LYE_DUPLEAFLIST */ LYVE_DUPLIST, /* LYE_DUPLIST */ LYVE_NOUNIQ, /* LYE_NOUNIQ */ LYVE_ENUM_INVAL, /* LYE_ENUM_INVAL */ LYVE_ENUM_INNAME, /* LYE_ENUM_INNAME */ LYVE_ENUM_INVAL, /* LYE_ENUM_DUPVAL */ LYVE_ENUM_INNAME, /* LYE_ENUM_DUPNAME */ LYVE_ENUM_WS, /* LYE_ENUM_WS */ LYVE_BITS_INVAL, /* LYE_BITS_INVAL */ LYVE_BITS_INNAME, /* LYE_BITS_INNAME */ LYVE_BITS_INVAL, /* LYE_BITS_DUPVAL */ LYVE_BITS_INNAME, /* LYE_BITS_DUPNAME */ LYVE_INMOD, /* LYE_INMOD */ LYVE_INMOD, /* LYE_INMOD_LEN */ LYVE_KEY_NLEAF, /* LYE_KEY_NLEAF */ LYVE_KEY_TYPE, /* LYE_KEY_TYPE */ LYVE_KEY_CONFIG, /* LYE_KEY_CONFIG */ LYVE_KEY_MISS, /* LYE_KEY_MISS */ LYVE_KEY_DUP, /* LYE_KEY_DUP */ LYVE_INREGEX, /* LYE_INREGEX */ LYVE_INRESOLV, /* LYE_INRESOLV */ LYVE_INSTATUS, /* LYE_INSTATUS */ LYVE_CIRC_LEAFREFS,/* LYE_CIRC_LEAFREFS */ LYVE_CIRC_FEATURES,/* LYE_CIRC_FEATURES */ LYVE_CIRC_IMPORTS, /* LYE_CIRC_IMPORTS */ LYVE_CIRC_INCLUDES,/* LYE_CIRC_INCLUDES */ LYVE_INVER, /* LYE_INVER */ LYVE_SUBMODULE, /* LYE_SUBMODULE */ LYVE_OBSDATA, /* LYE_OBSDATA */ LYVE_OBSDATA, /* LYE_OBSTYPE */ LYVE_NORESOLV, /* LYE_NORESOLV */ LYVE_INELEM, /* LYE_INELEM */ LYVE_INELEM, /* LYE_INELEM_LEN */ LYVE_MISSELEM, /* LYE_MISSELEM */ LYVE_INVAL, /* LYE_INVAL */ LYVE_INMETA, /* LYE_INMETA */ LYVE_INATTR, /* LYE_INATTR */ LYVE_MISSATTR, /* LYE_MISSATTR */ LYVE_NOCONSTR, /* LYE_NOCONSTR */ LYVE_INCHAR, /* LYE_INCHAR */ LYVE_INPRED, /* LYE_INPRED */ LYVE_MCASEDATA, /* LYE_MCASEDATA */ LYVE_NOMUST, /* LYE_NOMUST */ LYVE_NOWHEN, /* LYE_NOWHEN */ LYVE_INORDER, /* LYE_INORDER */ LYVE_INWHEN, /* LYE_INWHEN */ LYVE_NOMIN, /* LYE_NOMIN */ LYVE_NOMAX, /* LYE_NOMAX */ LYVE_NOREQINS, /* LYE_NOREQINS */ LYVE_NOLEAFREF, /* LYE_NOLEAFREF */ LYVE_NOMANDCHOICE, /* LYE_NOMANDCHOICE */ LYVE_XPATH_INTOK, /* LYE_XPATH_INTOK */ LYVE_XPATH_EOF, /* LYE_XPATH_EOF */ LYVE_XPATH_INOP, /* LYE_XPATH_INOP_1 */ LYVE_XPATH_INOP, /* LYE_XPATH_INOP_2 */ LYVE_XPATH_INCTX, /* LYE_XPATH_INCTX */ LYVE_XPATH_INMOD, /* LYE_XPATH_INMOD */ LYVE_XPATH_INFUNC, /* LYE_XPATH_INFUNC */ LYVE_XPATH_INARGCOUNT, /* LYE_XPATH_INARGCOUNT */ LYVE_XPATH_INARGTYPE, /* LYE_XPATH_INARGTYPE */ LYVE_XPATH_DUMMY, /* LYE_XPATH_DUMMY */ LYVE_XPATH_NOEND, /* LYE_XPATH_NOEND */ LYVE_PATH_INCHAR, /* LYE_PATH_INCHAR */ LYVE_PATH_INMOD, /* LYE_PATH_INMOD */ LYVE_PATH_MISSMOD, /* LYE_PATH_MISSMOD */ LYVE_PATH_INNODE, /* LYE_PATH_INNODE */ LYVE_PATH_INKEY, /* LYE_PATH_INKEY */ LYVE_PATH_MISSKEY, /* LYE_PATH_MISSKEY */ LYVE_PATH_INIDENTREF, /* LYE_PATH_INIDENTREF */ LYVE_PATH_EXISTS, /* LYE_PATH_EXISTS */ LYVE_PATH_MISSPAR, /* LYE_PATH_MISSPAR */ LYVE_PATH_PREDTOOMANY, /* LYE_PATH_PREDTOOMANY */ }; static int ly_vlog_build_path_print(char **path, uint16_t *index, const char *str, uint16_t str_len, uint16_t *length) { void *mem; uint16_t step; if ((*index) < str_len) { /* enlarge buffer */ step = (str_len < LY_BUF_STEP) ? LY_BUF_STEP : str_len; mem = realloc(*path, *length + *index + step + 1); LY_CHECK_ERR_RETURN(!mem, LOGMEM(NULL), -1); *path = mem; /* move data, lengths */ memmove(&(*path)[*index + step], &(*path)[*index], *length); (*index) += step; } (*index) -= str_len; memcpy(&(*path)[*index], str, str_len); *length += str_len; return 0; } int ly_vlog_build_path(enum LY_VLOG_ELEM elem_type, const void *elem, char **path, int schema_all_prefixes, int data_no_predicates) { int i, j, yang_data_extension = 0; struct lys_node_list *slist; struct lys_node *sparent; struct lyd_node *dlist, *diter; const struct lys_module *top_smodule = NULL; const char *name, *prefix = NULL, *val_end, *val_start, *ext_name; char *str; uint16_t length, index; size_t len; length = 0; *path = malloc(1); LY_CHECK_ERR_RETURN(!(*path), LOGMEM(NULL), -1); index = 0; while (elem) { switch (elem_type) { case LY_VLOG_XML: name = ((struct lyxml_elem *)elem)->name; prefix = ((struct lyxml_elem *)elem)->ns ? ((struct lyxml_elem *)elem)->ns->prefix : NULL; elem = ((struct lyxml_elem *)elem)->parent; break; case LY_VLOG_LYS: if (!top_smodule) { /* remember the top module, it will act as the current module */ for (sparent = (struct lys_node *)elem; lys_parent(sparent); sparent = lys_parent(sparent)); top_smodule = lys_node_module(sparent); } if (!((struct lys_node *)elem)->parent || (lys_node_module((struct lys_node *)elem) != top_smodule) || schema_all_prefixes) { prefix = lys_node_module((struct lys_node *)elem)->name; } else { prefix = NULL; } if (((struct lys_node *)elem)->nodetype & (LYS_AUGMENT | LYS_GROUPING)) { if (ly_vlog_build_path_print(path, &index, "]", 1, &length)) { return -1; } name = ((struct lys_node *)elem)->name; if (ly_vlog_build_path_print(path, &index, name, strlen(name), &length)) { return -1; } if (((struct lys_node *)elem)->nodetype == LYS_GROUPING) { name = "{grouping}["; } else { /* augment */ name = "{augment}["; } } else if (((struct lys_node *)elem)->nodetype == LYS_EXT) { name = ((struct lys_ext_instance *)elem)->def->name; if (!strcmp(name, "yang-data")) { yang_data_extension = 1; name = ((struct lys_ext_instance *)elem)->arg_value; prefix = lys_node_module((struct lys_node *)elem)->name; } } else { name = ((struct lys_node *)elem)->name; } if (((struct lys_node *)elem)->nodetype == LYS_EXT) { if (((struct lys_ext_instance*)elem)->parent_type == LYEXT_PAR_NODE) { elem = (struct lys_node*)((struct lys_ext_instance*)elem)->parent; } else { sparent = NULL; elem = NULL; } break; } /* need to find the parent again because we don't want to skip augments */ do { sparent = ((struct lys_node *)elem)->parent; elem = lys_parent((struct lys_node *)elem); } while (elem && (((struct lys_node *)elem)->nodetype == LYS_USES)); break; case LY_VLOG_LYD: name = ((struct lyd_node *)elem)->schema->name; if (!((struct lyd_node *)elem)->parent || lyd_node_module((struct lyd_node *)elem) != lyd_node_module(((struct lyd_node *)elem)->parent)) { prefix = lyd_node_module((struct lyd_node *)elem)->name; } else { prefix = NULL; } /* handle predicates (keys) in case of lists */ if (!data_no_predicates) { if (((struct lyd_node *)elem)->schema->nodetype == LYS_LIST) { dlist = (struct lyd_node *)elem; slist = (struct lys_node_list *)((struct lyd_node *)elem)->schema; if (slist->keys_size) { /* schema list with keys - use key values in predicates */ for (i = slist->keys_size - 1; i > -1; i--) { LY_TREE_FOR(dlist->child, diter) { if (diter->schema == (struct lys_node *)slist->keys[i]) { break; } } if (diter && ((struct lyd_node_leaf_list *)diter)->value_str) { if (strchr(((struct lyd_node_leaf_list *)diter)->value_str, '\'')) { val_start = "=\""; val_end = "\"]"; } else { val_start = "='"; val_end = "']"; } /* print value */ if (ly_vlog_build_path_print(path, &index, val_end, 2, &length)) { return -1; } len = strlen(((struct lyd_node_leaf_list *)diter)->value_str); if (ly_vlog_build_path_print(path, &index, ((struct lyd_node_leaf_list *)diter)->value_str, len, &length)) { return -1; } /* print schema name */ if (ly_vlog_build_path_print(path, &index, val_start, 2, &length)) { return -1; } len = strlen(diter->schema->name); if (ly_vlog_build_path_print(path, &index, diter->schema->name, len, &length)) { return -1; } if (lyd_node_module(dlist) != lyd_node_module(diter)) { if (ly_vlog_build_path_print(path, &index, ":", 1, &length)) { return -1; } len = strlen(lyd_node_module(diter)->name); if (ly_vlog_build_path_print(path, &index, lyd_node_module(diter)->name, len, &length)) { return -1; } } if (ly_vlog_build_path_print(path, &index, "[", 1, &length)) { return -1; } } } } else { /* schema list without keys - use instance position */ i = j = lyd_list_pos(dlist); len = 1; while (j > 9) { ++len; j /= 10; } if (ly_vlog_build_path_print(path, &index, "]", 1, &length)) { return -1; } str = malloc(len + 1); LY_CHECK_ERR_RETURN(!str, LOGMEM(NULL), -1); sprintf(str, "%d", i); if (ly_vlog_build_path_print(path, &index, str, len, &length)) { free(str); return -1; } free(str); if (ly_vlog_build_path_print(path, &index, "[", 1, &length)) { return -1; } } } else if (((struct lyd_node *)elem)->schema->nodetype == LYS_LEAFLIST && ((struct lyd_node_leaf_list *)elem)->value_str) { if (strchr(((struct lyd_node_leaf_list *)elem)->value_str, '\'')) { val_start = "[.=\""; val_end = "\"]"; } else { val_start = "[.='"; val_end = "']"; } if (ly_vlog_build_path_print(path, &index, val_end, 2, &length)) { return -1; } len = strlen(((struct lyd_node_leaf_list *)elem)->value_str); if (ly_vlog_build_path_print(path, &index, ((struct lyd_node_leaf_list *)elem)->value_str, len, &length)) { return -1; } if (ly_vlog_build_path_print(path, &index, val_start, 4, &length)) { return -1; } } } /* check if it is yang-data top element */ if (!((struct lyd_node *)elem)->parent) { ext_name = lyp_get_yang_data_template_name(elem); if (ext_name) { if (ly_vlog_build_path_print(path, &index, name, strlen(name), &length)) { return -1; } if (ly_vlog_build_path_print(path, &index, "/", 1, &length)) { return -1; } yang_data_extension = 1; name = ext_name; } } elem = ((struct lyd_node *)elem)->parent; break; case LY_VLOG_STR: len = strlen((const char *)elem); if (ly_vlog_build_path_print(path, &index, (const char *)elem, len, &length)) { return -1; } goto success; default: /* shouldn't be here */ LOGINT(NULL); return -1; } if (name) { if (ly_vlog_build_path_print(path, &index, name, strlen(name), &length)) { return -1; } if (prefix) { if (yang_data_extension && ly_vlog_build_path_print(path, &index, "#", 1, &length)) { return -1; } if (ly_vlog_build_path_print(path, &index, ":", 1, &length)) { return -1; } if (ly_vlog_build_path_print(path, &index, prefix, strlen(prefix), &length)) { return -1; } } } if (ly_vlog_build_path_print(path, &index, "/", 1, &length)) { return -1; } if ((elem_type == LY_VLOG_LYS) && !elem && sparent && (sparent->nodetype == LYS_AUGMENT)) { len = strlen(((struct lys_node_augment *)sparent)->target_name); if (ly_vlog_build_path_print(path, &index, ((struct lys_node_augment *)sparent)->target_name, len, &length)) { return -1; } } } success: memmove(*path, (*path) + index, length); (*path)[length] = '\0'; return 0; } void ly_vlog(const struct ly_ctx *ctx, LY_ECODE ecode, enum LY_VLOG_ELEM elem_type, const void *elem, ...) { va_list ap; const char *fmt; char* path = NULL; const struct ly_err_item *first; if ((ecode == LYE_PATH) && !path_flag) { return; } if (path_flag && (elem_type != LY_VLOG_NONE)) { if (elem_type == LY_VLOG_PREV) { /* use previous path */ first = ly_err_first(ctx); if (first && first->prev->path) { path = strdup(first->prev->path); } } else { /* print path */ if (!elem) { /* top-level */ path = strdup("/"); } else { ly_vlog_build_path(elem_type, elem, &path, 0, 0); } } } va_start(ap, elem); /* path is spent and should not be freed! */ switch (ecode) { case LYE_SPEC: fmt = va_arg(ap, char *); log_vprintf(ctx, LY_LLERR, LY_EVALID, LYVE_SUCCESS, path, fmt, ap); break; case LYE_PATH: assert(path); log_vprintf(ctx, LY_LLERR, LY_EVALID, LYVE_SUCCESS, path, NULL, ap); break; default: log_vprintf(ctx, LY_LLERR, LY_EVALID, ecode2vecode[ecode], path, ly_errs[ecode], ap); break; } va_end(ap); } void ly_vlog_str(const struct ly_ctx *ctx, enum LY_VLOG_ELEM elem_type, const char *str, ...) { va_list ap; char *path = NULL, *fmt, *ptr; const struct ly_err_item *first; assert((elem_type == LY_VLOG_NONE) || (elem_type == LY_VLOG_PREV)); if (elem_type == LY_VLOG_PREV) { /* use previous path */ first = ly_err_first(ctx); if (first && first->prev->path) { path = strdup(first->prev->path); } } if (strchr(str, '%')) { /* must be enough */ fmt = malloc(2 * strlen(str) + 1); strcpy(fmt, str); for (ptr = strchr(fmt, '%'); ptr; ptr = strchr(ptr + 2, '%')) { memmove(ptr + 1, ptr, strlen(ptr) + 1); ptr[0] = '%'; } } else { fmt = strdup(str); } va_start(ap, str); /* path is spent and should not be freed! */ log_vprintf(ctx, LY_LLERR, LY_EVALID, LYVE_SUCCESS, path, fmt, ap); va_end(ap); free(fmt); } API void ly_err_print(struct ly_err_item *eitem) { if (ly_log_opts & LY_LOLOG) { if (ly_log_clb) { ly_log_clb(eitem->level, eitem->msg, eitem->path); } else { fprintf(stderr, "libyang[%d]: %s%s", eitem->level, eitem->msg, eitem->path ? " " : "\n"); if (eitem->path) { fprintf(stderr, "(path: %s)\n", eitem->path); } } } } static void err_print(struct ly_ctx *ctx, struct ly_err_item *last_eitem) { if (!last_eitem) { last_eitem = pthread_getspecific(ctx->errlist_key); } else { /* this last was already stored before, do not write it again */ last_eitem = last_eitem->next; } if ((log_opt != ILO_STORE) && (log_opt != ILO_IGNORE)) { for (; last_eitem; last_eitem = last_eitem->next) { ly_err_print(last_eitem); /* also properly update ly_errno */ if (last_eitem->level == LY_LLERR) { ly_errno = last_eitem->no; } } } } /** * @brief Make \p last_eitem the last error item ignoring any logging options. */ void ly_err_free_next(struct ly_ctx *ctx, struct ly_err_item *last_eitem) { if (!last_eitem) { ly_err_clean(ctx, NULL); } else if (last_eitem->next) { ly_err_clean(ctx, last_eitem->next); } } /** * @brief Properly clean errors from \p ctx based on the user and internal logging options * after resolving schema/data unres. * * @param[in] ctx Context used. * @param[in] prev_eitem Most recent error item before resolving data unres. * @param[in] keep Whether to keep the stored errors. */ static void err_clean(struct ly_ctx *ctx, struct ly_err_item *prev_eitem, int keep) { struct ly_err_item *first; /* internal options take precedence */ if (log_opt == ILO_STORE) { /* keep all the new errors */ } else if ((log_opt == ILO_IGNORE) || !keep || !(ly_log_opts & LY_LOSTORE)) { /* throw away all the new errors */ ly_err_free_next(ctx, prev_eitem); } else if ((ly_log_opts & LY_LOSTORE_LAST) == LY_LOSTORE_LAST) { /* keep only the most recent error */ first = pthread_getspecific(ctx->errlist_key); if (!first) { /* no errors whatsoever */ return; } prev_eitem = first->prev; /* put the context errlist in order */ pthread_setspecific(ctx->errlist_key, prev_eitem); assert(!prev_eitem->prev->next || (prev_eitem->prev->next == prev_eitem)); prev_eitem->prev->next = NULL; prev_eitem->prev = prev_eitem; /* free all the errlist items except the last one, do not free any if there is only one */ if (prev_eitem != first) { ly_err_free(first); } } } 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) { assert(prev_ilo); *prev_ilo = log_opt; if (new_ilo == ILO_STORE) { /* only in this case the errors are only temporarily stored */ assert(ctx && prev_last_eitem); *prev_last_eitem = (struct ly_err_item *)ly_err_first(ctx); if (*prev_last_eitem) { *prev_last_eitem = (*prev_last_eitem)->prev; } } if (log_opt != ILO_IGNORE) { log_opt = new_ilo; } /* else we can just keep it, useless to change it */ } 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) { assert(log_opt != ILO_LOG); if (log_opt != ILO_STORE) { /* nothing to print or free */ assert(log_opt == prev_ilo || (!ctx && !prev_last_eitem && !keep_and_print)); log_opt = prev_ilo; return; } assert(ctx); log_opt = prev_ilo; if (keep_and_print) { err_print(ctx, prev_last_eitem); } err_clean(ctx, prev_last_eitem, keep_and_print); } void ly_err_last_set_apptag(const struct ly_ctx *ctx, const char *apptag) { struct ly_err_item *i; if (log_opt != ILO_IGNORE) { i = ly_err_first(ctx); if (i) { i = i->prev; i->apptag = strdup(apptag); } } }