diff options
Diffstat (limited to 'src/log.c')
-rw-r--r-- | src/log.c | 483 |
1 files changed, 366 insertions, 117 deletions
@@ -4,7 +4,7 @@ * @author Michal Vasko <mvasko@cesnet.cz> * @brief Logger routines implementations * - * Copyright (c) 2015 - 2022 CESNET, z.s.p.o. + * Copyright (c) 2015 - 2023 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. @@ -41,15 +41,13 @@ ATOMIC_T ly_log_opts = (uint_fast32_t)(LY_LOLOG | LY_LOSTORE_LAST); THREAD_LOCAL uint32_t *temp_ly_log_opts; static ly_log_clb log_clb; static ATOMIC_T path_flag = 1; +THREAD_LOCAL char last_msg[LY_LAST_MSG_SIZE]; #ifndef NDEBUG ATOMIC_T ly_ldbg_groups = 0; #endif THREAD_LOCAL struct ly_log_location_s log_location = {0}; -/* how many bytes add when enlarging buffers */ -#define LY_BUF_STEP 128 - LIBYANG_API_DEF LY_ERR ly_errcode(const struct ly_ctx *ctx) { @@ -63,6 +61,47 @@ ly_errcode(const struct ly_ctx *ctx) return LY_SUCCESS; } +LIBYANG_API_DEF const char * +ly_strerrcode(LY_ERR err) +{ + /* ignore plugin flag */ + err &= ~LY_EPLUGIN; + + switch (err) { + case LY_SUCCESS: + return "Success"; + case LY_EMEM: + return "Out of memory"; + case LY_ESYS: + return "System call failed"; + case LY_EINVAL: + return "Invalid value"; + case LY_EEXIST: + return "Already exists"; + case LY_ENOTFOUND: + return "Not found"; + case LY_EINT: + return "Internal error"; + case LY_EVALID: + return "Validation failed"; + case LY_EDENIED: + return "Operation denied"; + case LY_EINCOMPLETE: + return "Operation incomplete"; + case LY_ERECOMPILE: + return "Recompilation required"; + case LY_ENOT: + return "Negative result"; + case LY_EOTHER: + return "Another failure reason"; + case LY_EPLUGIN: + break; + } + + /* unreachable */ + return "Unknown"; +} + LIBYANG_API_DEF LY_VECODE ly_vecode(const struct ly_ctx *ctx) { @@ -77,6 +116,38 @@ ly_vecode(const struct ly_ctx *ctx) } LIBYANG_API_DEF const char * +ly_strvecode(LY_VECODE vecode) +{ + switch (vecode) { + case LYVE_SUCCESS: + return "Success"; + case LYVE_SYNTAX: + return "General syntax error"; + case LYVE_SYNTAX_YANG: + return "YANG syntax error"; + case LYVE_SYNTAX_YIN: + return "YIN syntax error"; + case LYVE_REFERENCE: + return "Reference error"; + case LYVE_XPATH: + return "XPath error"; + case LYVE_SEMANTICS: + return "Semantic error"; + case LYVE_SYNTAX_XML: + return "XML syntax error"; + case LYVE_SYNTAX_JSON: + return "JSON syntax error"; + case LYVE_DATA: + return "YANG data error"; + case LYVE_OTHER: + return "Another error"; + } + + /* unreachable */ + return "Unknown"; +} + +LIBYANG_API_DEF const char * ly_errmsg(const struct ly_ctx *ctx) { struct ly_err_item *i; @@ -92,6 +163,12 @@ ly_errmsg(const struct ly_ctx *ctx) } LIBYANG_API_DEF const char * +ly_last_errmsg(void) +{ + return last_msg; +} + +LIBYANG_API_DEF const char * ly_errpath(const struct ly_ctx *ctx) { struct ly_err_item *i; @@ -169,61 +246,148 @@ ly_err_new(struct ly_err_item **err, LY_ERR ecode, LY_VECODE vecode, char *path, return e->no; } +/** + * @brief Get error record from error hash table of a context for the current thread. + * + * @param[in] ctx Context to use. + * @return Thread error record, if any. + */ +static struct ly_ctx_err_rec * +ly_err_get_rec(const struct ly_ctx *ctx) +{ + struct ly_ctx_err_rec rec, *match; + + /* prepare record */ + rec.tid = pthread_self(); + + /* get the pointer to the matching record */ + if (lyht_find(ctx->err_ht, &rec, lyht_hash((void *)&rec.tid, sizeof rec.tid), (void **)&match)) { + return NULL; + } + + return match; +} + +/** + * @brief Insert new error record to error hash table of a context for the current thread. + * + * @param[in] ctx Context to use. + * @return Thread error record. + */ +static struct ly_ctx_err_rec * +ly_err_new_rec(const struct ly_ctx *ctx) +{ + struct ly_ctx_err_rec new, *rec; + LY_ERR r; + + /* insert a new record */ + new.err = NULL; + new.tid = pthread_self(); + + /* reuse lock */ + /* LOCK */ + pthread_mutex_lock((pthread_mutex_t *)&ctx->lyb_hash_lock); + + r = lyht_insert(ctx->err_ht, &new, lyht_hash((void *)&new.tid, sizeof new.tid), (void **)&rec); + + /* UNLOCK */ + pthread_mutex_unlock((pthread_mutex_t *)&ctx->lyb_hash_lock); + + return r ? NULL : rec; +} + LIBYANG_API_DEF struct ly_err_item * ly_err_first(const struct ly_ctx *ctx) { + struct ly_ctx_err_rec *rec; + LY_CHECK_ARG_RET(NULL, ctx, NULL); - return pthread_getspecific(ctx->errlist_key); + /* get the pointer to the matching record */ + rec = ly_err_get_rec(ctx); + + return rec ? rec->err : NULL; } LIBYANG_API_DEF struct ly_err_item * ly_err_last(const struct ly_ctx *ctx) { - const struct ly_err_item *e; + struct ly_ctx_err_rec *rec; LY_CHECK_ARG_RET(NULL, ctx, NULL); - e = pthread_getspecific(ctx->errlist_key); - return e ? e->prev : NULL; + /* get the pointer to the matching record */ + if (!(rec = ly_err_get_rec(ctx))) { + return NULL; + } + + return rec->err ? rec->err->prev : NULL; +} + +void +ly_err_move(struct ly_ctx *src_ctx, struct ly_ctx *trg_ctx) +{ + struct ly_ctx_err_rec *rec; + struct ly_err_item *err = NULL; + + /* get and remove the errors from src */ + rec = ly_err_get_rec(src_ctx); + if (rec) { + err = rec->err; + rec->err = NULL; + } + + /* set them for trg */ + if (!(rec = ly_err_get_rec(trg_ctx))) { + if (!(rec = ly_err_new_rec(trg_ctx))) { + LOGINT(NULL); + ly_err_free(err); + return; + } + } + ly_err_free(rec->err); + rec->err = err; } LIBYANG_API_DEF void ly_err_free(void *ptr) { - struct ly_err_item *i, *next; + struct ly_err_item *e, *next; /* clean the error list */ - for (i = (struct ly_err_item *)ptr; i; i = next) { - next = i->next; - free(i->msg); - free(i->path); - free(i->apptag); - free(i); + LY_LIST_FOR_SAFE(ptr, next, e) { + free(e->msg); + free(e->path); + free(e->apptag); + free(e); } } LIBYANG_API_DEF void ly_err_clean(struct ly_ctx *ctx, struct ly_err_item *eitem) { - struct ly_err_item *i, *first; + struct ly_ctx_err_rec *rec; + struct ly_err_item *e; - first = ly_err_first(ctx); - if (first == eitem) { + if (!(rec = ly_err_get_rec(ctx))) { + return; + } + if (rec->err == eitem) { eitem = NULL; } - if (eitem) { + + if (!eitem) { + /* free all err */ + ly_err_free(rec->err); + rec->err = NULL; + } else { /* disconnect the error */ - for (i = first; i && (i->next != eitem); i = i->next) {} - assert(i); - i->next = NULL; - first->prev = i; + for (e = rec->err; e && (e->next != eitem); e = e->next) {} + assert(e); + e->next = NULL; + rec->err->prev = e; /* free this err and newer */ ly_err_free(eitem); - } else { - /* free all err */ - ly_err_free(first); - pthread_setspecific(ctx->errlist_key, NULL); } } @@ -336,64 +500,99 @@ ly_log_location_revert(uint32_t scnode_steps, uint32_t dnode_steps, uint32_t pat } } +const struct lyd_node * +ly_log_location_dnode(uint32_t idx) +{ + if (idx < log_location.dnodes.count) { + return log_location.dnodes.dnodes[idx]; + } + + return NULL; +} + +uint32_t +ly_log_location_dnode_count(void) +{ + return log_location.dnodes.count; +} + +/** + * @brief Store generated error in a context. + * + * @param[in] ctx Context to use. + * @param[in] level Message log level. + * @param[in] no Error number. + * @param[in] vecode Error validation error code. + * @param[in] msg Error message, always spent. + * @param[in] path Error path, always spent. + * @param[in] apptag Error app tag, always spent. + * @return LY_ERR value. + */ static LY_ERR 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; + struct ly_ctx_err_rec *rec; + struct ly_err_item *e, *last; assert(ctx && (level < LY_LLVRB)); - eitem = pthread_getspecific(ctx->errlist_key); - if (!eitem) { + if (!(rec = ly_err_get_rec(ctx))) { + if (!(rec = ly_err_new_rec(ctx))) { + goto mem_fail; + } + } + + e = rec->err; + if (!e) { /* if we are only to fill in path, there must have been an error stored */ assert(msg); - eitem = malloc(sizeof *eitem); - LY_CHECK_GOTO(!eitem, mem_fail); - eitem->prev = eitem; - eitem->next = NULL; + e = malloc(sizeof *e); + LY_CHECK_GOTO(!e, mem_fail); + e->prev = e; + e->next = NULL; - pthread_setspecific(ctx->errlist_key, eitem); + rec->err = e; } else if (!msg) { /* only filling the path */ assert(path); /* find last error */ - eitem = eitem->prev; + e = e->prev; do { - if (eitem->level == LY_LLERR) { + if (e->level == LY_LLERR) { /* fill the path */ - free(eitem->path); - eitem->path = path; + free(e->path); + e->path = path; return LY_SUCCESS; } - eitem = eitem->prev; - } while (eitem->prev->next); + e = e->prev; + } while (e->prev->next); /* last error was not found */ assert(0); } else if ((temp_ly_log_opts && ((*temp_ly_log_opts & LY_LOSTORE_LAST) == LY_LOSTORE_LAST)) || (!temp_ly_log_opts && ((ATOMIC_LOAD_RELAXED(ly_log_opts) & LY_LOSTORE_LAST) == LY_LOSTORE_LAST))) { /* overwrite last message */ - free(eitem->msg); - free(eitem->path); - free(eitem->apptag); + free(e->msg); + free(e->path); + free(e->apptag); } else { /* store new message */ - last = eitem->prev; - eitem->prev = malloc(sizeof *eitem); - LY_CHECK_GOTO(!eitem->prev, mem_fail); - eitem = eitem->prev; - eitem->prev = last; - eitem->next = NULL; - last->next = eitem; + last = e->prev; + e->prev = malloc(sizeof *e); + LY_CHECK_GOTO(!e->prev, mem_fail); + e = e->prev; + e->prev = last; + e->next = NULL; + last->next = e; } /* fill in the information */ - eitem->level = level; - eitem->no = no; - eitem->vecode = vecode; - eitem->msg = msg; - eitem->path = path; - eitem->apptag = apptag; + e->level = level; + e->no = no; + e->vecode = vecode; + e->msg = msg; + e->path = path; + e->apptag = apptag; return LY_SUCCESS; mem_fail: @@ -445,14 +644,19 @@ log_vprintf(const struct ly_ctx *ctx, LY_LOG_LEVEL level, LY_ERR no, LY_VECODE v return; } - /* store the error/warning (if we need to store errors internally, it does not matter what are the user log options) */ + /* print into a single message */ + if (vasprintf(&msg, format, args) == -1) { + LOGMEM(ctx); + free(path); + return; + } + + /* store as the last message */ + strncpy(last_msg, msg, LY_LAST_MSG_SIZE - 1); + + /* store the error/warning in the context (if we need to store errors internally, it does not matter what are + * the user log options) */ if ((level < LY_LLVRB) && ctx && lostore) { - assert(format); - if (vasprintf(&msg, format, args) == -1) { - LOGMEM(ctx); - free(path); - return; - } if (((no & ~LY_EPLUGIN) == LY_EVALID) && (vecode == LYVE_SUCCESS)) { /* assume we are inheriting the error, so inherit vecode as well */ vecode = ly_vecode(ctx); @@ -462,11 +666,6 @@ log_vprintf(const struct ly_ctx *ctx, LY_LOG_LEVEL level, LY_ERR no, LY_VECODE v } free_strs = 0; } else { - if (vasprintf(&msg, format, args) == -1) { - LOGMEM(ctx); - free(path); - return; - } free_strs = 1; } @@ -524,6 +723,8 @@ ly_log_dbg(uint32_t group, const char *format, ...) va_start(ap, format); log_vprintf(NULL, LY_LLDBG, 0, 0, NULL, NULL, dbg_format, ap); va_end(ap); + + free(dbg_format); } #endif @@ -556,20 +757,20 @@ ly_vlog_build_path_append(char **str, const struct lysc_node *snode, const struc if (snode->nodetype & (LYS_CHOICE | LYS_CASE)) { /* schema-only node */ return LY_SUCCESS; - } else if (lysc_data_parent(snode) != parent->schema) { + } else if (lysc_data_parent(snode) != lyd_node_schema(parent)) { /* not a direct descendant node */ return LY_SUCCESS; } /* get module to print, if any */ mod = snode->module; - prev_mod = (parent->schema) ? parent->schema->module : lyd_owner_module(parent); + prev_mod = lyd_node_module(parent); if (prev_mod == mod) { mod = NULL; } /* realloc string */ - len = strlen(*str); + len = *str ? strlen(*str) : 0; new_len = len + 1 + (mod ? strlen(mod->name) + 1 : 0) + strlen(snode->name); mem = realloc(*str, new_len + 1); LY_CHECK_ERR_RET(!mem, LOGMEM(LYD_CTX(parent)), LY_EMEM); @@ -580,6 +781,41 @@ ly_vlog_build_path_append(char **str, const struct lysc_node *snode, const struc return LY_SUCCESS; } +LY_ERR +ly_vlog_build_data_path(const struct ly_ctx *ctx, char **path) +{ + LY_ERR rc = LY_SUCCESS; + const struct lyd_node *dnode = NULL; + + *path = NULL; + + if (log_location.dnodes.count) { + dnode = log_location.dnodes.objs[log_location.dnodes.count - 1]; + if (dnode->parent || !lysc_data_parent(dnode->schema)) { + /* data node with all of its parents */ + *path = lyd_path(log_location.dnodes.objs[log_location.dnodes.count - 1], LYD_PATH_STD, NULL, 0); + LY_CHECK_ERR_GOTO(!*path, LOGMEM(ctx); rc = LY_EMEM, cleanup); + } else { + /* data parsers put all the parent nodes in the set, but they are not connected */ + *path = lyd_path_set(&log_location.dnodes, LYD_PATH_STD); + LY_CHECK_ERR_GOTO(!*path, LOGMEM(ctx); rc = LY_EMEM, cleanup); + } + } + + /* sometimes the last node is not created yet and we only have the schema node */ + if (log_location.scnodes.count) { + rc = ly_vlog_build_path_append(path, log_location.scnodes.objs[log_location.scnodes.count - 1], dnode); + LY_CHECK_GOTO(rc, cleanup); + } + +cleanup: + if (rc) { + free(*path); + *path = NULL; + } + return rc; +} + /** * @brief Build log path from the stored log location information. * @@ -592,32 +828,17 @@ ly_vlog_build_path(const struct ly_ctx *ctx, char **path) { int r; char *str = NULL, *prev = NULL; - const struct lyd_node *dnode; *path = NULL; if (log_location.paths.count && ((const char *)(log_location.paths.objs[log_location.paths.count - 1]))[0]) { /* simply get what is in the provided path string */ - *path = strdup((const char *)log_location.paths.objs[log_location.paths.count - 1]); - LY_CHECK_ERR_RET(!(*path), LOGMEM(ctx), LY_EMEM); + r = asprintf(path, "Path \"%s\"", (const char *)log_location.paths.objs[log_location.paths.count - 1]); + LY_CHECK_ERR_RET(r == -1, LOGMEM(ctx), LY_EMEM); } else { /* data/schema node */ if (log_location.dnodes.count) { - dnode = log_location.dnodes.objs[log_location.dnodes.count - 1]; - if (dnode->parent || !lysc_data_parent(dnode->schema)) { - /* data node with all of its parents */ - str = lyd_path(log_location.dnodes.objs[log_location.dnodes.count - 1], LYD_PATH_STD, NULL, 0); - LY_CHECK_ERR_RET(!str, LOGMEM(ctx), LY_EMEM); - } else { - /* data parsers put all the parent nodes in the set, but they are not connected */ - str = lyd_path_set(&log_location.dnodes, LYD_PATH_STD); - LY_CHECK_ERR_RET(!str, LOGMEM(ctx), LY_EMEM); - } - - /* sometimes the last node is not created yet and we only have the schema node */ - if (log_location.scnodes.count) { - ly_vlog_build_path_append(&str, log_location.scnodes.objs[log_location.scnodes.count - 1], dnode); - } + LY_CHECK_RET(ly_vlog_build_data_path(ctx, &str)); r = asprintf(path, "Data location \"%s\"", str); free(str); @@ -630,28 +851,28 @@ ly_vlog_build_path(const struct ly_ctx *ctx, char **path) free(str); LY_CHECK_ERR_RET(r == -1, LOGMEM(ctx), LY_EMEM); } + } - /* line */ - prev = *path; - if (log_location.line) { - r = asprintf(path, "%s%sine number %" PRIu64, prev ? prev : "", prev ? ", l" : "L", log_location.line); - free(prev); - LY_CHECK_ERR_RET(r == -1, LOGMEM(ctx), LY_EMEM); + /* line */ + prev = *path; + if (log_location.line) { + r = asprintf(path, "%s%sine number %" PRIu64, prev ? prev : "", prev ? ", l" : "L", log_location.line); + free(prev); + LY_CHECK_ERR_RET(r == -1, LOGMEM(ctx), LY_EMEM); - log_location.line = 0; - } else if (log_location.inputs.count) { - r = asprintf(path, "%s%sine number %" PRIu64, prev ? prev : "", prev ? ", l" : "L", - ((struct ly_in *)log_location.inputs.objs[log_location.inputs.count - 1])->line); - free(prev); - LY_CHECK_ERR_RET(r == -1, LOGMEM(ctx), LY_EMEM); - } + log_location.line = 0; + } else if (log_location.inputs.count) { + r = asprintf(path, "%s%sine number %" PRIu64, prev ? prev : "", prev ? ", l" : "L", + ((struct ly_in *)log_location.inputs.objs[log_location.inputs.count - 1])->line); + free(prev); + LY_CHECK_ERR_RET(r == -1, LOGMEM(ctx), LY_EMEM); + } - if (*path) { - prev = *path; - r = asprintf(path, "%s.", prev); - free(prev); - LY_CHECK_ERR_RET(r == -1, LOGMEM(ctx), LY_EMEM); - } + if (*path) { + prev = *path; + r = asprintf(path, "%s.", prev); + free(prev); + LY_CHECK_ERR_RET(r == -1, LOGMEM(ctx), LY_EMEM); } return LY_SUCCESS; @@ -680,12 +901,12 @@ ly_vlog(const struct ly_ctx *ctx, const char *apptag, LY_VECODE code, const char * @param[in] plugin_name Name of the plugin generating the message. * @param[in] level Log message level (error, warning, etc.) * @param[in] err_no Error type code. - * @param[in] path Optional path of the error. + * @param[in] path Optional path of the error, used if set. * @param[in] format Format string to print. * @param[in] ap Var arg list for @p format. */ static void -ly_ext_log(const struct ly_ctx *ctx, const char *plugin_name, LY_LOG_LEVEL level, LY_ERR err_no, const char *path, +ly_ext_log(const struct ly_ctx *ctx, const char *plugin_name, LY_LOG_LEVEL level, LY_ERR err_no, char *path, const char *format, va_list ap) { char *plugin_msg; @@ -698,8 +919,7 @@ ly_ext_log(const struct ly_ctx *ctx, const char *plugin_name, LY_LOG_LEVEL level return; } - log_vprintf(ctx, level, (level == LY_LLERR ? LY_EPLUGIN : 0) | err_no, LYVE_OTHER, path ? strdup(path) : NULL, NULL, - plugin_msg, ap); + log_vprintf(ctx, level, (level == LY_LLERR ? LY_EPLUGIN : 0) | err_no, LYVE_OTHER, path, NULL, plugin_msg, ap); free(plugin_msg); } @@ -717,8 +937,6 @@ lyplg_ext_parse_log(const struct lysp_ctx *pctx, const struct lysp_ext_instance va_start(ap, format); ly_ext_log(PARSER_CTX(pctx), ext->record->plugin.id, level, err_no, path, format, ap); va_end(ap); - - free(path); } LIBYANG_API_DEF void @@ -726,9 +944,15 @@ lyplg_ext_compile_log(const struct lysc_ctx *cctx, const struct lysc_ext_instanc const char *format, ...) { va_list ap; + char *path = NULL; + + if (cctx && (asprintf(&path, "Path \"%s\".", cctx->path) == -1)) { + LOGMEM(cctx->ctx); + return; + } va_start(ap, format); - ly_ext_log(ext->module->ctx, ext->def->plugin->id, level, err_no, cctx ? cctx->path : NULL, format, ap); + ly_ext_log(ext->module->ctx, ext->def->plugin->id, level, err_no, path, format, ap); va_end(ap); } @@ -737,12 +961,37 @@ lyplg_ext_compile_log_path(const char *path, const struct lysc_ext_instance *ext const char *format, ...) { va_list ap; + char *log_path = NULL; + + if (path && (asprintf(&log_path, "Path \"%s\".", path) == -1)) { + LOGMEM(ext->module->ctx); + return; + } va_start(ap, format); - ly_ext_log(ext->module->ctx, ext->def->plugin->id, level, err_no, path, format, ap); + ly_ext_log(ext->module->ctx, ext->def->plugin->id, level, err_no, log_path, format, ap); + va_end(ap); +} + +/** + * @brief Serves only for creating ap. + */ +static void +_lyplg_ext_compile_log_err(const struct ly_err_item *err, const struct lysc_ext_instance *ext, ...) +{ + va_list ap; + + va_start(ap, ext); + ly_ext_log(ext->module->ctx, ext->def->plugin->id, err->level, err->no, err->path ? strdup(err->path) : NULL, "%s", ap); va_end(ap); } +LIBYANG_API_DEF void +lyplg_ext_compile_log_err(const struct ly_err_item *err, const struct lysc_ext_instance *ext) +{ + _lyplg_ext_compile_log_err(err, ext, err->msg); +} + /** * @brief Exact same functionality as ::ly_err_print() but has variable arguments so log_vprintf() can be called. */ |