blob: 243d030f7e1ac9a60c7e7a8c9b7b8512b1991c88 [file] [log] [blame]
Radek Krejci5aeea3a2018-09-05 13:29:36 +02001/**
2 * @file log.c
3 * @author Radek Krejci <rkrejci@cesnet.cz>
Michal Vasko193dacd2022-10-13 08:43:05 +02004 * @author Michal Vasko <mvasko@cesnet.cz>
Radek Krejci5aeea3a2018-09-05 13:29:36 +02005 * @brief Logger routines implementations
6 *
Michal Vasko193dacd2022-10-13 08:43:05 +02007 * Copyright (c) 2015 - 2022 CESNET, z.s.p.o.
Radek Krejci5aeea3a2018-09-05 13:29:36 +02008 *
9 * This source code is licensed under BSD 3-Clause License (the "License").
10 * You may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
12 *
13 * https://opensource.org/licenses/BSD-3-Clause
14 */
15
Christian Hopps32874e12021-05-01 09:43:54 -040016#define _GNU_SOURCE /* asprintf, strdup */
Radek Krejci535ea9f2020-05-29 16:01:05 +020017
18#include "log.h"
Radek Krejcib7db73a2018-10-24 14:18:40 +020019
Radek Krejci5aeea3a2018-09-05 13:29:36 +020020#include <assert.h>
Radek Krejcic04f0a22018-09-21 15:49:45 +020021#include <inttypes.h>
Radek Krejcie7b95092019-05-15 11:03:07 +020022#include <pthread.h>
Radek Krejci5aeea3a2018-09-05 13:29:36 +020023#include <stdarg.h>
Radek Krejcie7b95092019-05-15 11:03:07 +020024#include <stdint.h>
Radek Krejci5aeea3a2018-09-05 13:29:36 +020025#include <stdio.h>
Radek Krejcie7b95092019-05-15 11:03:07 +020026#include <stdlib.h>
27#include <string.h>
Radek Krejci5aeea3a2018-09-05 13:29:36 +020028
Radek Krejci535ea9f2020-05-29 16:01:05 +020029#include "common.h"
Radek Krejciaa45bda2020-07-20 07:43:38 +020030#include "compat.h"
Radek Krejciaddfc9a2020-12-17 20:46:35 +010031#include "in_internal.h"
Radek Krejci0935f412019-08-20 16:15:18 +020032#include "plugins_exts.h"
Radek Krejci77114102021-03-10 15:21:57 +010033#include "set.h"
Radek Krejci535ea9f2020-05-29 16:01:05 +020034#include "tree_data.h"
Michal Vaskodbf3e652022-10-21 08:46:25 +020035#include "tree_data_internal.h"
Radek Krejci535ea9f2020-05-29 16:01:05 +020036#include "tree_schema.h"
Michal Vasko193dacd2022-10-13 08:43:05 +020037#include "tree_schema_internal.h"
Radek Krejci5aeea3a2018-09-05 13:29:36 +020038
Václav Kubernátd367ad92021-11-29 09:28:56 +010039ATOMIC_T ly_ll = (uint_fast32_t)LY_LLWRN;
40ATOMIC_T ly_log_opts = (uint_fast32_t)(LY_LOLOG | LY_LOSTORE_LAST);
Michal Vaskod8085612020-08-21 12:55:23 +020041static ly_log_clb log_clb;
Václav Kubernátd367ad92021-11-29 09:28:56 +010042static ATOMIC_T path_flag = 1;
Radek Krejci5aeea3a2018-09-05 13:29:36 +020043#ifndef NDEBUG
Václav Kubernátd367ad92021-11-29 09:28:56 +010044ATOMIC_T ly_ldbg_groups = 0;
Radek Krejci5aeea3a2018-09-05 13:29:36 +020045#endif
46
Radek Krejciddace2c2021-01-08 11:30:56 +010047THREAD_LOCAL struct ly_log_location_s log_location = {0};
48
Radek Krejci94aa9942018-09-07 17:12:17 +020049/* how many bytes add when enlarging buffers */
50#define LY_BUF_STEP 128
51
Jan Kundrátc53a7ec2021-12-09 16:01:19 +010052LIBYANG_API_DEF LY_ERR
Radek Krejcid33273d2018-10-25 14:55:52 +020053ly_errcode(const struct ly_ctx *ctx)
54{
55 struct ly_err_item *i;
56
Radek Krejci572ee602020-09-16 14:35:08 +020057 i = ly_err_last(ctx);
Radek Krejcid33273d2018-10-25 14:55:52 +020058 if (i) {
Radek Krejci572ee602020-09-16 14:35:08 +020059 return i->no;
Radek Krejcid33273d2018-10-25 14:55:52 +020060 }
61
62 return LY_SUCCESS;
63}
64
Jan Kundrátc53a7ec2021-12-09 16:01:19 +010065LIBYANG_API_DEF LY_VECODE
Radek Krejci5aeea3a2018-09-05 13:29:36 +020066ly_vecode(const struct ly_ctx *ctx)
67{
68 struct ly_err_item *i;
69
Radek Krejci572ee602020-09-16 14:35:08 +020070 i = ly_err_last(ctx);
Radek Krejci5aeea3a2018-09-05 13:29:36 +020071 if (i) {
Radek Krejci572ee602020-09-16 14:35:08 +020072 return i->vecode;
Radek Krejci5aeea3a2018-09-05 13:29:36 +020073 }
74
75 return LYVE_SUCCESS;
76}
77
Jan Kundrátc53a7ec2021-12-09 16:01:19 +010078LIBYANG_API_DEF const char *
Radek Krejci5aeea3a2018-09-05 13:29:36 +020079ly_errmsg(const struct ly_ctx *ctx)
80{
81 struct ly_err_item *i;
82
Michal Vaskob3d0d6b2018-09-07 10:17:33 +020083 LY_CHECK_ARG_RET(NULL, ctx, NULL);
Radek Krejci5aeea3a2018-09-05 13:29:36 +020084
Radek Krejci572ee602020-09-16 14:35:08 +020085 i = ly_err_last(ctx);
Radek Krejci5aeea3a2018-09-05 13:29:36 +020086 if (i) {
Radek Krejci572ee602020-09-16 14:35:08 +020087 return i->msg;
Radek Krejci5aeea3a2018-09-05 13:29:36 +020088 }
89
90 return NULL;
91}
92
Jan Kundrátc53a7ec2021-12-09 16:01:19 +010093LIBYANG_API_DEF const char *
Radek Krejci5aeea3a2018-09-05 13:29:36 +020094ly_errpath(const struct ly_ctx *ctx)
95{
96 struct ly_err_item *i;
97
Michal Vaskob3d0d6b2018-09-07 10:17:33 +020098 LY_CHECK_ARG_RET(NULL, ctx, NULL);
Radek Krejci5aeea3a2018-09-05 13:29:36 +020099
Radek Krejci572ee602020-09-16 14:35:08 +0200100 i = ly_err_last(ctx);
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200101 if (i) {
Radek Krejci572ee602020-09-16 14:35:08 +0200102 return i->path;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200103 }
104
105 return NULL;
106}
107
Jan Kundrátc53a7ec2021-12-09 16:01:19 +0100108LIBYANG_API_DEF const char *
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200109ly_errapptag(const struct ly_ctx *ctx)
110{
111 struct ly_err_item *i;
112
Michal Vaskob3d0d6b2018-09-07 10:17:33 +0200113 LY_CHECK_ARG_RET(NULL, ctx, NULL);
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200114
Radek Krejci572ee602020-09-16 14:35:08 +0200115 i = ly_err_last(ctx);
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200116 if (i) {
Radek Krejci572ee602020-09-16 14:35:08 +0200117 return i->apptag;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200118 }
119
120 return NULL;
121}
122
Jan Kundrátc53a7ec2021-12-09 16:01:19 +0100123LIBYANG_API_DEF LY_ERR
aPiecek6d618552021-06-18 10:02:59 +0200124ly_err_new(struct ly_err_item **err, LY_ERR ecode, LY_VECODE vecode, char *path, char *apptag, const char *err_format, ...)
Radek Krejcie7b95092019-05-15 11:03:07 +0200125{
Radek Krejcidb0ee022021-03-15 16:53:44 +0100126 char *msg = NULL;
127 struct ly_err_item *e;
Radek Krejcie7b95092019-05-15 11:03:07 +0200128
Radek Krejcid43298b2021-03-25 16:17:15 +0100129 if (!err || (ecode == LY_SUCCESS)) {
Radek Krejcidb0ee022021-03-15 16:53:44 +0100130 /* nothing to do */
131 return ecode;
132 }
133
134 e = malloc(sizeof *e);
135 LY_CHECK_ERR_RET(!e, LOGMEM(NULL), LY_EMEM);
136 e->prev = (*err) ? (*err)->prev : e;
137 e->next = NULL;
138 if (*err) {
139 (*err)->prev->next = e;
140 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200141
142 /* fill in the information */
Radek Krejcidb0ee022021-03-15 16:53:44 +0100143 e->level = LY_LLERR;
144 e->no = ecode;
145 e->vecode = vecode;
146 e->path = path;
147 e->apptag = apptag;
Radek Krejcie7b95092019-05-15 11:03:07 +0200148
aPiecek6d618552021-06-18 10:02:59 +0200149 if (err_format) {
Radek Krejcidb0ee022021-03-15 16:53:44 +0100150 va_list print_args;
151
aPiecek6d618552021-06-18 10:02:59 +0200152 va_start(print_args, err_format);
Radek Krejcidb0ee022021-03-15 16:53:44 +0100153
aPiecek6d618552021-06-18 10:02:59 +0200154 if (vasprintf(&msg, err_format, print_args) == -1) {
155 /* we don't have anything more to do, just set msg to NULL to avoid undefined content,
Radek Krejcidb0ee022021-03-15 16:53:44 +0100156 * still keep the information about the original error instead of LY_EMEM or other printf's error */
157 msg = NULL;
158 }
159
160 va_end(print_args);
161 }
162 e->msg = msg;
163
164 if (!(*err)) {
165 *err = e;
166 }
167
168 return e->no;
Radek Krejcie7b95092019-05-15 11:03:07 +0200169}
170
Jan Kundrátc53a7ec2021-12-09 16:01:19 +0100171LIBYANG_API_DEF struct ly_err_item *
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200172ly_err_first(const struct ly_ctx *ctx)
173{
Michal Vaskob3d0d6b2018-09-07 10:17:33 +0200174 LY_CHECK_ARG_RET(NULL, ctx, NULL);
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200175
176 return pthread_getspecific(ctx->errlist_key);
177}
178
Jan Kundrátc53a7ec2021-12-09 16:01:19 +0100179LIBYANG_API_DEF struct ly_err_item *
Radek Krejci572ee602020-09-16 14:35:08 +0200180ly_err_last(const struct ly_ctx *ctx)
181{
182 const struct ly_err_item *e;
183
184 LY_CHECK_ARG_RET(NULL, ctx, NULL);
185
186 e = pthread_getspecific(ctx->errlist_key);
187 return e ? e->prev : NULL;
188}
189
Jan Kundrátc53a7ec2021-12-09 16:01:19 +0100190LIBYANG_API_DEF void
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200191ly_err_free(void *ptr)
192{
193 struct ly_err_item *i, *next;
194
195 /* clean the error list */
196 for (i = (struct ly_err_item *)ptr; i; i = next) {
197 next = i->next;
Radek Krejcidb0ee022021-03-15 16:53:44 +0100198 free(i->msg);
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200199 free(i->path);
200 free(i->apptag);
201 free(i);
202 }
203}
204
Jan Kundrátc53a7ec2021-12-09 16:01:19 +0100205LIBYANG_API_DEF void
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200206ly_err_clean(struct ly_ctx *ctx, struct ly_err_item *eitem)
207{
208 struct ly_err_item *i, *first;
209
210 first = ly_err_first(ctx);
211 if (first == eitem) {
212 eitem = NULL;
213 }
214 if (eitem) {
215 /* disconnect the error */
Radek Krejci1e008d22020-08-17 11:37:37 +0200216 for (i = first; i && (i->next != eitem); i = i->next) {}
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200217 assert(i);
218 i->next = NULL;
219 first->prev = i;
220 /* free this err and newer */
221 ly_err_free(eitem);
222 } else {
223 /* free all err */
224 ly_err_free(first);
225 pthread_setspecific(ctx->errlist_key, NULL);
226 }
227}
228
Jan Kundrátc53a7ec2021-12-09 16:01:19 +0100229LIBYANG_API_DEF LY_LOG_LEVEL
Radek Krejci52b6d512020-10-12 12:33:17 +0200230ly_log_level(LY_LOG_LEVEL level)
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200231{
Václav Kubernátd367ad92021-11-29 09:28:56 +0100232 LY_LOG_LEVEL prev = ATOMIC_LOAD_RELAXED(ly_ll);
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200233
Václav Kubernátd367ad92021-11-29 09:28:56 +0100234 ATOMIC_STORE_RELAXED(ly_ll, level);
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200235 return prev;
236}
237
Jan Kundrátc53a7ec2021-12-09 16:01:19 +0100238LIBYANG_API_DEF uint32_t
Radek Krejci1deb5be2020-08-26 16:43:36 +0200239ly_log_options(uint32_t opts)
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200240{
Václav Kubernátd367ad92021-11-29 09:28:56 +0100241 uint32_t prev = ATOMIC_LOAD_RELAXED(ly_log_opts);
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200242
Václav Kubernátd367ad92021-11-29 09:28:56 +0100243 ATOMIC_STORE_RELAXED(ly_log_opts, opts);
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200244 return prev;
245}
246
Jan Kundrátc53a7ec2021-12-09 16:01:19 +0100247LIBYANG_API_DEF uint32_t
Radek Krejci68433c92020-10-12 17:03:55 +0200248ly_log_dbg_groups(uint32_t dbg_groups)
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200249{
250#ifndef NDEBUG
Václav Kubernátd367ad92021-11-29 09:28:56 +0100251 uint32_t prev = ATOMIC_LOAD_RELAXED(ly_ldbg_groups);
Radek Krejciebdaed02020-11-09 13:05:06 +0100252
Václav Kubernátd367ad92021-11-29 09:28:56 +0100253 ATOMIC_STORE_RELAXED(ly_ldbg_groups, dbg_groups);
Radek Krejciebdaed02020-11-09 13:05:06 +0100254 return prev;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200255#else
256 (void)dbg_groups;
Radek Krejciebdaed02020-11-09 13:05:06 +0100257 return 0;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200258#endif
259}
260
Jan Kundrátc53a7ec2021-12-09 16:01:19 +0100261LIBYANG_API_DEF void
Radek Krejci857189e2020-09-01 13:26:36 +0200262ly_set_log_clb(ly_log_clb clb, ly_bool path)
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200263{
Michal Vaskod8085612020-08-21 12:55:23 +0200264 log_clb = clb;
Václav Kubernátd367ad92021-11-29 09:28:56 +0100265 ATOMIC_STORE_RELAXED(path_flag, path);
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200266}
267
Jan Kundrátc53a7ec2021-12-09 16:01:19 +0100268LIBYANG_API_DEF ly_log_clb
Michal Vaskod8085612020-08-21 12:55:23 +0200269ly_get_log_clb(void)
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200270{
Michal Vaskod8085612020-08-21 12:55:23 +0200271 return log_clb;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200272}
273
Radek Krejciaddfc9a2020-12-17 20:46:35 +0100274void
Michal Vasko59e69e72022-02-18 09:18:21 +0100275ly_log_location(const struct lysc_node *scnode, const struct lyd_node *dnode, const char *path, const struct ly_in *in,
Michal Vaskof8ebf132022-11-21 14:06:48 +0100276 uint64_t line)
Radek Krejciaddfc9a2020-12-17 20:46:35 +0100277{
Radek Krejciaddfc9a2020-12-17 20:46:35 +0100278 if (scnode) {
Radek Krejciddace2c2021-01-08 11:30:56 +0100279 ly_set_add(&log_location.scnodes, (void *)scnode, 1, NULL);
Radek Krejciaddfc9a2020-12-17 20:46:35 +0100280 }
Radek Krejciaddfc9a2020-12-17 20:46:35 +0100281 if (dnode) {
Radek Krejciddace2c2021-01-08 11:30:56 +0100282 ly_set_add(&log_location.dnodes, (void *)dnode, 1, NULL);
Radek Krejciaddfc9a2020-12-17 20:46:35 +0100283 }
Radek Krejciaddfc9a2020-12-17 20:46:35 +0100284 if (path) {
285 char *s = strdup(path);
Michal Vasko26bbb272022-08-02 14:54:33 +0200286
Radek Krejciddace2c2021-01-08 11:30:56 +0100287 LY_CHECK_ERR_RET(!s, LOGMEM(NULL), );
288 ly_set_add(&log_location.paths, s, 1, NULL);
Radek Krejciaddfc9a2020-12-17 20:46:35 +0100289 }
Radek Krejciaddfc9a2020-12-17 20:46:35 +0100290 if (in) {
Radek Krejciddace2c2021-01-08 11:30:56 +0100291 ly_set_add(&log_location.inputs, (void *)in, 1, NULL);
Radek Krejciaddfc9a2020-12-17 20:46:35 +0100292 }
Radek Krejciaddfc9a2020-12-17 20:46:35 +0100293 if (line) {
Radek Krejciddace2c2021-01-08 11:30:56 +0100294 log_location.line = line;
Radek Krejciaddfc9a2020-12-17 20:46:35 +0100295 }
296}
297
298void
Michal Vasko59e69e72022-02-18 09:18:21 +0100299ly_log_location_revert(uint32_t scnode_steps, uint32_t dnode_steps, uint32_t path_steps, uint32_t in_steps)
Radek Krejciaddfc9a2020-12-17 20:46:35 +0100300{
Radek Krejciddace2c2021-01-08 11:30:56 +0100301 for (uint32_t i = scnode_steps; i && log_location.scnodes.count; i--) {
302 log_location.scnodes.count--;
Radek Krejciaddfc9a2020-12-17 20:46:35 +0100303 }
304
Radek Krejciddace2c2021-01-08 11:30:56 +0100305 for (uint32_t i = dnode_steps; i && log_location.dnodes.count; i--) {
306 log_location.dnodes.count--;
Radek Krejciaddfc9a2020-12-17 20:46:35 +0100307 }
308
Radek Krejciddace2c2021-01-08 11:30:56 +0100309 for (uint32_t i = path_steps; i && log_location.paths.count; i--) {
310 ly_set_rm_index(&log_location.paths, log_location.paths.count - 1, free);
Radek Krejciaddfc9a2020-12-17 20:46:35 +0100311 }
312
Radek Krejciddace2c2021-01-08 11:30:56 +0100313 for (uint32_t i = in_steps; i && log_location.inputs.count; i--) {
314 log_location.inputs.count--;
Radek Krejciaddfc9a2020-12-17 20:46:35 +0100315 }
316
Radek Krejciddace2c2021-01-08 11:30:56 +0100317 /* deallocate the empty sets */
318 if (scnode_steps && !log_location.scnodes.count) {
319 ly_set_erase(&log_location.scnodes, NULL);
Radek Krejciaddfc9a2020-12-17 20:46:35 +0100320 }
Radek Krejciddace2c2021-01-08 11:30:56 +0100321 if (dnode_steps && !log_location.dnodes.count) {
322 ly_set_erase(&log_location.dnodes, NULL);
323 }
324 if (path_steps && !log_location.paths.count) {
325 ly_set_erase(&log_location.paths, free);
326 }
327 if (in_steps && !log_location.inputs.count) {
328 ly_set_erase(&log_location.inputs, NULL);
Radek Krejciaddfc9a2020-12-17 20:46:35 +0100329 }
330}
331
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200332static LY_ERR
333log_store(const struct ly_ctx *ctx, LY_LOG_LEVEL level, LY_ERR no, LY_VECODE vecode, char *msg, char *path, char *apptag)
334{
335 struct ly_err_item *eitem, *last;
336
337 assert(ctx && (level < LY_LLVRB));
338
339 eitem = pthread_getspecific(ctx->errlist_key);
340 if (!eitem) {
341 /* if we are only to fill in path, there must have been an error stored */
342 assert(msg);
343 eitem = malloc(sizeof *eitem);
344 LY_CHECK_GOTO(!eitem, mem_fail);
345 eitem->prev = eitem;
346 eitem->next = NULL;
347
348 pthread_setspecific(ctx->errlist_key, eitem);
349 } else if (!msg) {
350 /* only filling the path */
351 assert(path);
352
353 /* find last error */
354 eitem = eitem->prev;
355 do {
356 if (eitem->level == LY_LLERR) {
357 /* fill the path */
358 free(eitem->path);
359 eitem->path = path;
360 return LY_SUCCESS;
361 }
362 eitem = eitem->prev;
363 } while (eitem->prev->next);
364 /* last error was not found */
365 assert(0);
Václav Kubernátd367ad92021-11-29 09:28:56 +0100366 } else if ((ATOMIC_LOAD_RELAXED(ly_log_opts) & LY_LOSTORE_LAST) == LY_LOSTORE_LAST) {
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200367 /* overwrite last message */
368 free(eitem->msg);
369 free(eitem->path);
370 free(eitem->apptag);
371 } else {
372 /* store new message */
373 last = eitem->prev;
374 eitem->prev = malloc(sizeof *eitem);
375 LY_CHECK_GOTO(!eitem->prev, mem_fail);
376 eitem = eitem->prev;
377 eitem->prev = last;
378 eitem->next = NULL;
379 last->next = eitem;
380 }
381
382 /* fill in the information */
383 eitem->level = level;
384 eitem->no = no;
385 eitem->vecode = vecode;
386 eitem->msg = msg;
387 eitem->path = path;
388 eitem->apptag = apptag;
389 return LY_SUCCESS;
390
391mem_fail:
392 LOGMEM(NULL);
393 free(msg);
394 free(path);
395 free(apptag);
396 return LY_EMEM;
397}
398
399static void
Michal Vaskoe9391c72021-10-05 10:04:56 +0200400log_vprintf(const struct ly_ctx *ctx, LY_LOG_LEVEL level, LY_ERR no, LY_VECODE vecode, char *path, const char *apptag,
Radek Krejci0f969882020-08-21 16:56:47 +0200401 const char *format, va_list args)
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200402{
403 char *msg = NULL;
Radek Krejci857189e2020-09-01 13:26:36 +0200404 ly_bool free_strs;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200405
Václav Kubernátd367ad92021-11-29 09:28:56 +0100406 if (level > ATOMIC_LOAD_RELAXED(ly_ll)) {
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200407 /* do not print or store the message */
408 free(path);
409 return;
410 }
411
Michal Vasko5a016922021-05-07 08:19:15 +0200412 if (no == LY_EMEM) {
413 /* just print it, anything else would most likely fail anyway */
Václav Kubernátd367ad92021-11-29 09:28:56 +0100414 if (ATOMIC_LOAD_RELAXED(ly_log_opts) & LY_LOLOG) {
Michal Vasko5a016922021-05-07 08:19:15 +0200415 if (log_clb) {
416 log_clb(level, LY_EMEM_MSG, path);
417 } else {
418 fprintf(stderr, "libyang[%d]: ", level);
419 vfprintf(stderr, format, args);
420 if (path) {
421 fprintf(stderr, " (path: %s)\n", path);
422 } else {
423 fprintf(stderr, "\n");
424 }
425 }
426 }
427 free(path);
428 return;
429 }
430
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200431 /* store the error/warning (if we need to store errors internally, it does not matter what are the user log options) */
Václav Kubernátd367ad92021-11-29 09:28:56 +0100432 if ((level < LY_LLVRB) && ctx && (ATOMIC_LOAD_RELAXED(ly_log_opts) & LY_LOSTORE)) {
Michal Vasko004d3152020-06-11 19:59:22 +0200433 assert(format);
434 if (vasprintf(&msg, format, args) == -1) {
435 LOGMEM(ctx);
436 free(path);
437 return;
438 }
Radek Krejcic9e64a62020-09-18 20:08:12 +0200439 if (((no & ~LY_EPLUGIN) == LY_EVALID) && (vecode == LYVE_SUCCESS)) {
440 /* assume we are inheriting the error, so inherit vecode as well */
441 vecode = ly_vecode(ctx);
442 }
Michal Vaskoe9391c72021-10-05 10:04:56 +0200443 if (log_store(ctx, level, no, vecode, msg, path, apptag ? strdup(apptag) : NULL)) {
Michal Vasko004d3152020-06-11 19:59:22 +0200444 return;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200445 }
446 free_strs = 0;
447 } else {
448 if (vasprintf(&msg, format, args) == -1) {
449 LOGMEM(ctx);
450 free(path);
451 return;
452 }
453 free_strs = 1;
454 }
455
456 /* if we are only storing errors internally, never print the message (yet) */
Václav Kubernátd367ad92021-11-29 09:28:56 +0100457 if (ATOMIC_LOAD_RELAXED(ly_log_opts) & LY_LOLOG) {
Michal Vaskod8085612020-08-21 12:55:23 +0200458 if (log_clb) {
459 log_clb(level, msg, path);
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200460 } else {
461 fprintf(stderr, "libyang[%d]: %s%s", level, msg, path ? " " : "\n");
462 if (path) {
463 fprintf(stderr, "(path: %s)\n", path);
464 }
465 }
466 }
467
468 if (free_strs) {
469 free(path);
470 free(msg);
471 }
472}
473
Radek Krejci4ab61562018-09-05 15:00:37 +0200474#ifndef NDEBUG
475
476void
Radek Krejci1deb5be2020-08-26 16:43:36 +0200477ly_log_dbg(uint32_t group, const char *format, ...)
Radek Krejci4ab61562018-09-05 15:00:37 +0200478{
479 char *dbg_format;
480 const char *str_group;
481 va_list ap;
482
Václav Kubernátd367ad92021-11-29 09:28:56 +0100483 if (!(ATOMIC_LOAD_RELAXED(ly_ldbg_groups) & group)) {
Radek Krejci4ab61562018-09-05 15:00:37 +0200484 return;
485 }
486
487 switch (group) {
488 case LY_LDGDICT:
489 str_group = "DICT";
490 break;
Radek Krejci4ab61562018-09-05 15:00:37 +0200491 case LY_LDGXPATH:
492 str_group = "XPATH";
493 break;
Michal Vaskoe558f792021-07-28 08:20:15 +0200494 case LY_LDGDEPSETS:
495 str_group = "DEPSETS";
496 break;
Radek Krejci4ab61562018-09-05 15:00:37 +0200497 default:
498 LOGINT(NULL);
499 return;
500 }
501
502 if (asprintf(&dbg_format, "%s: %s", str_group, format) == -1) {
503 LOGMEM(NULL);
504 return;
505 }
506
507 va_start(ap, format);
Michal Vaskoe9391c72021-10-05 10:04:56 +0200508 log_vprintf(NULL, LY_LLDBG, 0, 0, NULL, NULL, dbg_format, ap);
Radek Krejci4ab61562018-09-05 15:00:37 +0200509 va_end(ap);
510}
511
512#endif
513
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200514void
515ly_log(const struct ly_ctx *ctx, LY_LOG_LEVEL level, LY_ERR no, const char *format, ...)
516{
517 va_list ap;
518
519 va_start(ap, format);
Michal Vaskoe9391c72021-10-05 10:04:56 +0200520 log_vprintf(ctx, level, no, 0, NULL, NULL, format, ap);
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200521 va_end(ap);
522}
523
Michal Vaskodbf3e652022-10-21 08:46:25 +0200524/**
525 * @brief Append a schema node name to a generated data path, only if it fits.
526 *
527 * @param[in,out] str Generated path to update.
528 * @param[in] snode Schema node to append.
529 * @param[in] parent Last printed data node.
530 * @return LY_ERR value.
531 */
532static LY_ERR
533ly_vlog_build_path_append(char **str, const struct lysc_node *snode, const struct lyd_node *parent)
534{
535 const struct lys_module *mod, *prev_mod;
536 uint32_t len, new_len;
537 void *mem;
538
539 if (snode->nodetype & (LYS_CHOICE | LYS_CASE)) {
540 /* schema-only node */
541 return LY_SUCCESS;
542 } else if (lysc_data_parent(snode) != parent->schema) {
543 /* not a direct descendant node */
544 return LY_SUCCESS;
545 }
546
547 /* get module to print, if any */
548 mod = snode->module;
549 prev_mod = (parent->schema) ? parent->schema->module : lyd_owner_module(parent);
550 if (prev_mod == mod) {
551 mod = NULL;
552 }
553
554 /* realloc string */
555 len = strlen(*str);
556 new_len = len + 1 + (mod ? strlen(mod->name) + 1 : 0) + strlen(snode->name);
557 mem = realloc(*str, new_len + 1);
558 LY_CHECK_ERR_RET(!mem, LOGMEM(LYD_CTX(parent)), LY_EMEM);
559 *str = mem;
560
561 /* print the last schema node */
562 sprintf(*str + len, "/%s%s%s", mod ? mod->name : "", mod ? ":" : "", snode->name);
563 return LY_SUCCESS;
564}
565
566/**
567 * @brief Build log path from the stored log location information.
568 *
569 * @param[in] ctx Context to use.
570 * @param[out] path Generated log path.
571 * @return LY_ERR value.
572 */
Radek Krejci94aa9942018-09-07 17:12:17 +0200573static LY_ERR
Radek Krejciddace2c2021-01-08 11:30:56 +0100574ly_vlog_build_path(const struct ly_ctx *ctx, char **path)
Radek Krejci94aa9942018-09-07 17:12:17 +0200575{
Michal Vaskodbf3e652022-10-21 08:46:25 +0200576 int r;
Radek Krejci2efc45b2020-12-22 16:25:44 +0100577 char *str = NULL, *prev = NULL;
Michal Vaskodbf3e652022-10-21 08:46:25 +0200578 const struct lyd_node *dnode;
Radek Krejcicb3e6472021-01-06 08:19:01 +0100579
Radek Krejci2efc45b2020-12-22 16:25:44 +0100580 *path = NULL;
Radek Krejci94aa9942018-09-07 17:12:17 +0200581
Radek Krejciddace2c2021-01-08 11:30:56 +0100582 if (log_location.paths.count && ((const char *)(log_location.paths.objs[log_location.paths.count - 1]))[0]) {
Radek Krejci2efc45b2020-12-22 16:25:44 +0100583 /* simply get what is in the provided path string */
Radek Krejciddace2c2021-01-08 11:30:56 +0100584 *path = strdup((const char *)log_location.paths.objs[log_location.paths.count - 1]);
Radek Krejcic04f0a22018-09-21 15:49:45 +0200585 LY_CHECK_ERR_RET(!(*path), LOGMEM(ctx), LY_EMEM);
Radek Krejci2efc45b2020-12-22 16:25:44 +0100586 } else {
Michal Vaskodbf3e652022-10-21 08:46:25 +0200587 /* data/schema node */
588 if (log_location.dnodes.count) {
589 dnode = log_location.dnodes.objs[log_location.dnodes.count - 1];
Michal Vaskoa4dfb3c2022-10-25 14:59:31 +0200590 if (dnode->parent || !lysc_data_parent(dnode->schema)) {
591 /* data node with all of its parents */
592 str = lyd_path(log_location.dnodes.objs[log_location.dnodes.count - 1], LYD_PATH_STD, NULL, 0);
593 LY_CHECK_ERR_RET(!str, LOGMEM(ctx), LY_EMEM);
594 } else {
Michal Vaskodbf3e652022-10-21 08:46:25 +0200595 /* data parsers put all the parent nodes in the set, but they are not connected */
596 str = lyd_path_set(&log_location.dnodes, LYD_PATH_STD);
Michal Vasko3e65ee32022-10-21 10:09:51 +0200597 LY_CHECK_ERR_RET(!str, LOGMEM(ctx), LY_EMEM);
Michal Vaskoa4dfb3c2022-10-25 14:59:31 +0200598 }
Michal Vaskodbf3e652022-10-21 08:46:25 +0200599
Michal Vaskoa4dfb3c2022-10-25 14:59:31 +0200600 /* sometimes the last node is not created yet and we only have the schema node */
601 if (log_location.scnodes.count) {
602 ly_vlog_build_path_append(&str, log_location.scnodes.objs[log_location.scnodes.count - 1], dnode);
Michal Vaskodbf3e652022-10-21 08:46:25 +0200603 }
Michal Vaskodbf3e652022-10-21 08:46:25 +0200604
605 r = asprintf(path, "Data location \"%s\"", str);
606 free(str);
607 LY_CHECK_ERR_RET(r == -1, LOGMEM(ctx), LY_EMEM);
608 } else if (log_location.scnodes.count) {
Radek Krejciddace2c2021-01-08 11:30:56 +0100609 str = lysc_path(log_location.scnodes.objs[log_location.scnodes.count - 1], LYSC_PATH_LOG, NULL, 0);
Radek Krejci2efc45b2020-12-22 16:25:44 +0100610 LY_CHECK_ERR_RET(!str, LOGMEM(ctx), LY_EMEM);
611
Michal Vaskodbf3e652022-10-21 08:46:25 +0200612 r = asprintf(path, "Schema location \"%s\"", str);
Radek Krejci2efc45b2020-12-22 16:25:44 +0100613 free(str);
Michal Vaskodbf3e652022-10-21 08:46:25 +0200614 LY_CHECK_ERR_RET(r == -1, LOGMEM(ctx), LY_EMEM);
Radek Krejci2efc45b2020-12-22 16:25:44 +0100615 }
Radek Krejci2efc45b2020-12-22 16:25:44 +0100616
Michal Vaskodbf3e652022-10-21 08:46:25 +0200617 /* line */
618 prev = *path;
Radek Krejciddace2c2021-01-08 11:30:56 +0100619 if (log_location.line) {
Michal Vaskodbf3e652022-10-21 08:46:25 +0200620 r = asprintf(path, "%s%sine number %" PRIu64, prev ? prev : "", prev ? ", l" : "L", log_location.line);
Radek Krejci2efc45b2020-12-22 16:25:44 +0100621 free(prev);
Michal Vaskodbf3e652022-10-21 08:46:25 +0200622 LY_CHECK_ERR_RET(r == -1, LOGMEM(ctx), LY_EMEM);
Radek Krejci2efc45b2020-12-22 16:25:44 +0100623
Radek Krejciddace2c2021-01-08 11:30:56 +0100624 log_location.line = 0;
625 } else if (log_location.inputs.count) {
Michal Vaskodbf3e652022-10-21 08:46:25 +0200626 r = asprintf(path, "%s%sine number %" PRIu64, prev ? prev : "", prev ? ", l" : "L",
Radek Krejciddace2c2021-01-08 11:30:56 +0100627 ((struct ly_in *)log_location.inputs.objs[log_location.inputs.count - 1])->line);
Radek Krejci2efc45b2020-12-22 16:25:44 +0100628 free(prev);
Michal Vaskodbf3e652022-10-21 08:46:25 +0200629 LY_CHECK_ERR_RET(r == -1, LOGMEM(ctx), LY_EMEM);
Radek Krejci2efc45b2020-12-22 16:25:44 +0100630 }
631
632 if (*path) {
633 prev = *path;
Michal Vaskodbf3e652022-10-21 08:46:25 +0200634 r = asprintf(path, "%s.", prev);
Radek Krejci2efc45b2020-12-22 16:25:44 +0100635 free(prev);
Michal Vaskodbf3e652022-10-21 08:46:25 +0200636 LY_CHECK_ERR_RET(r == -1, LOGMEM(ctx), LY_EMEM);
Radek Krejci2efc45b2020-12-22 16:25:44 +0100637 }
Radek Krejci94aa9942018-09-07 17:12:17 +0200638 }
639
Radek Krejci94aa9942018-09-07 17:12:17 +0200640 return LY_SUCCESS;
641}
642
643void
Michal Vaskoe9391c72021-10-05 10:04:56 +0200644ly_vlog(const struct ly_ctx *ctx, const char *apptag, LY_VECODE code, const char *format, ...)
Radek Krejci94aa9942018-09-07 17:12:17 +0200645{
646 va_list ap;
Michal Vasko22df3f02020-08-24 13:29:22 +0200647 char *path = NULL;
Radek Krejci94aa9942018-09-07 17:12:17 +0200648
Václav Kubernátd367ad92021-11-29 09:28:56 +0100649 if (ATOMIC_LOAD_RELAXED(path_flag) && ctx) {
Radek Krejciddace2c2021-01-08 11:30:56 +0100650 ly_vlog_build_path(ctx, &path);
Radek Krejci94aa9942018-09-07 17:12:17 +0200651 }
652
653 va_start(ap, format);
Michal Vaskoe9391c72021-10-05 10:04:56 +0200654 log_vprintf(ctx, LY_LLERR, LY_EVALID, code, path, apptag, format, ap);
Radek Krejci94aa9942018-09-07 17:12:17 +0200655 /* path is spent and should not be freed! */
656 va_end(ap);
657}
658
Michal Vasko193dacd2022-10-13 08:43:05 +0200659/**
660 * @brief Print a log message from an extension plugin callback.
661 *
662 * @param[in] ctx libyang context to store the error record. If not provided, the error is just printed.
663 * @param[in] plugin_name Name of the plugin generating the message.
664 * @param[in] level Log message level (error, warning, etc.)
665 * @param[in] err_no Error type code.
666 * @param[in] path Optional path of the error.
667 * @param[in] format Format string to print.
668 * @param[in] ap Var arg list for @p format.
669 */
670static void
671ly_ext_log(const struct ly_ctx *ctx, const char *plugin_name, LY_LOG_LEVEL level, LY_ERR err_no, const char *path,
672 const char *format, va_list ap)
Radek Krejci0935f412019-08-20 16:15:18 +0200673{
Radek Krejci0935f412019-08-20 16:15:18 +0200674 char *plugin_msg;
Radek Krejci0935f412019-08-20 16:15:18 +0200675
Václav Kubernátd367ad92021-11-29 09:28:56 +0100676 if (ATOMIC_LOAD_RELAXED(ly_ll) < level) {
Radek Krejci0935f412019-08-20 16:15:18 +0200677 return;
678 }
Michal Vasko193dacd2022-10-13 08:43:05 +0200679 if (asprintf(&plugin_msg, "Ext plugin \"%s\": %s", plugin_name, format) == -1) {
680 LOGMEM(ctx);
Radek Krejci0935f412019-08-20 16:15:18 +0200681 return;
682 }
683
Michal Vasko193dacd2022-10-13 08:43:05 +0200684 log_vprintf(ctx, level, (level == LY_LLERR ? LY_EPLUGIN : 0) | err_no, LYVE_OTHER, path ? strdup(path) : NULL, NULL,
685 plugin_msg, ap);
686 free(plugin_msg);
687}
688
689LIBYANG_API_DEF void
690lyplg_ext_parse_log(const struct lysp_ctx *pctx, const struct lysp_ext_instance *ext, LY_LOG_LEVEL level, LY_ERR err_no,
691 const char *format, ...)
692{
693 va_list ap;
694 char *path = NULL;
695
696 if (ATOMIC_LOAD_RELAXED(path_flag)) {
697 ly_vlog_build_path(PARSER_CTX(pctx), &path);
698 }
699
Radek Krejci0935f412019-08-20 16:15:18 +0200700 va_start(ap, format);
Michal Vasko193dacd2022-10-13 08:43:05 +0200701 ly_ext_log(PARSER_CTX(pctx), ext->record->plugin.id, level, err_no, path, format, ap);
Radek Krejci0935f412019-08-20 16:15:18 +0200702 va_end(ap);
703
Michal Vasko193dacd2022-10-13 08:43:05 +0200704 free(path);
705}
706
707LIBYANG_API_DEF void
708lyplg_ext_compile_log(const struct lysc_ctx *cctx, const struct lysc_ext_instance *ext, LY_LOG_LEVEL level, LY_ERR err_no,
709 const char *format, ...)
710{
711 va_list ap;
712
713 va_start(ap, format);
714 ly_ext_log(ext->module->ctx, ext->def->plugin->id, level, err_no, cctx ? cctx->path : NULL, format, ap);
715 va_end(ap);
716}
717
718LIBYANG_API_DEF void
719lyplg_ext_compile_log_path(const char *path, const struct lysc_ext_instance *ext, LY_LOG_LEVEL level, LY_ERR err_no,
720 const char *format, ...)
721{
722 va_list ap;
723
724 va_start(ap, format);
725 ly_ext_log(ext->module->ctx, ext->def->plugin->id, level, err_no, path, format, ap);
726 va_end(ap);
Radek Krejci0935f412019-08-20 16:15:18 +0200727}
728
Michal Vasko177d0ed2020-11-23 16:43:03 +0100729/**
Michal Vaskoc78a6092021-05-07 15:27:35 +0200730 * @brief Exact same functionality as ::ly_err_print() but has variable arguments so log_vprintf() can be called.
Michal Vasko177d0ed2020-11-23 16:43:03 +0100731 */
732static void
Michal Vaskoc78a6092021-05-07 15:27:35 +0200733_ly_err_print(const struct ly_ctx *ctx, struct ly_err_item *eitem, const char *format, ...)
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200734{
Michal Vasko177d0ed2020-11-23 16:43:03 +0100735 va_list ap;
736 char *path_dup = NULL;
737
738 LY_CHECK_ARG_RET(ctx, eitem, );
739
740 if (eitem->path) {
741 /* duplicate path because it will be freed */
742 path_dup = strdup(eitem->path);
743 LY_CHECK_ERR_RET(!path_dup, LOGMEM(ctx), );
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200744 }
Michal Vasko177d0ed2020-11-23 16:43:03 +0100745
Michal Vaskoc78a6092021-05-07 15:27:35 +0200746 va_start(ap, format);
Michal Vaskoe9391c72021-10-05 10:04:56 +0200747 log_vprintf(ctx, eitem->level, eitem->no, eitem->vecode, path_dup, eitem->apptag, format, ap);
Michal Vasko177d0ed2020-11-23 16:43:03 +0100748 va_end(ap);
Michal Vasko177d0ed2020-11-23 16:43:03 +0100749}
750
Jan Kundrátc53a7ec2021-12-09 16:01:19 +0100751LIBYANG_API_DEF void
Michal Vasko177d0ed2020-11-23 16:43:03 +0100752ly_err_print(const struct ly_ctx *ctx, struct ly_err_item *eitem)
753{
Michal Vaskoc78a6092021-05-07 15:27:35 +0200754 /* String ::ly_err_item.msg cannot be used directly because it may contain the % character */
755 _ly_err_print(ctx, eitem, "%s", eitem->msg);
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200756}