blob: d1a6dd39ff80ec302e29a960e16063b60c5cc788 [file] [log] [blame]
Radek Krejci5aeea3a2018-09-05 13:29:36 +02001/**
2 * @file log.c
3 * @author Radek Krejci <rkrejci@cesnet.cz>
4 * @brief Logger routines implementations
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#include <assert.h>
17#include <stdarg.h>
18#include <stdio.h>
19
20#include "libyang.h"
21#include "common.h"
22#include "context.h"
23
24THREAD_LOCAL enum int_log_opts log_opt;
25volatile uint8_t ly_log_level = LY_LLWRN;
26volatile uint8_t ly_log_opts = LY_LOLOG | LY_LOSTORE_LAST;
27static void (*ly_log_clb)(LY_LOG_LEVEL level, const char *msg, const char *path);
28static volatile int path_flag = 1;
29#ifndef NDEBUG
30volatile int ly_log_dbg_groups = 0;
31#endif
32
Radek Krejci94aa9942018-09-07 17:12:17 +020033/* how many bytes add when enlarging buffers */
34#define LY_BUF_STEP 128
35
Radek Krejci5aeea3a2018-09-05 13:29:36 +020036API LY_VECODE
37ly_vecode(const struct ly_ctx *ctx)
38{
39 struct ly_err_item *i;
40
41 i = ly_err_first(ctx);
42 if (i) {
43 return i->prev->vecode;
44 }
45
46 return LYVE_SUCCESS;
47}
48
49API const char *
50ly_errmsg(const struct ly_ctx *ctx)
51{
52 struct ly_err_item *i;
53
Michal Vaskob3d0d6b2018-09-07 10:17:33 +020054 LY_CHECK_ARG_RET(NULL, ctx, NULL);
Radek Krejci5aeea3a2018-09-05 13:29:36 +020055
56 i = ly_err_first(ctx);
57 if (i) {
58 return i->prev->msg;
59 }
60
61 return NULL;
62}
63
64API const char *
65ly_errpath(const struct ly_ctx *ctx)
66{
67 struct ly_err_item *i;
68
Michal Vaskob3d0d6b2018-09-07 10:17:33 +020069 LY_CHECK_ARG_RET(NULL, ctx, NULL);
Radek Krejci5aeea3a2018-09-05 13:29:36 +020070
71 i = ly_err_first(ctx);
72 if (i) {
73 return i->prev->path;
74 }
75
76 return NULL;
77}
78
79API const char *
80ly_errapptag(const struct ly_ctx *ctx)
81{
82 struct ly_err_item *i;
83
Michal Vaskob3d0d6b2018-09-07 10:17:33 +020084 LY_CHECK_ARG_RET(NULL, ctx, NULL);
Radek Krejci5aeea3a2018-09-05 13:29:36 +020085
86 i = ly_err_first(ctx);
87 if (i) {
88 return i->prev->apptag;
89 }
90
91 return NULL;
92}
93
94API struct ly_err_item *
95ly_err_first(const struct ly_ctx *ctx)
96{
Michal Vaskob3d0d6b2018-09-07 10:17:33 +020097 LY_CHECK_ARG_RET(NULL, ctx, NULL);
Radek Krejci5aeea3a2018-09-05 13:29:36 +020098
99 return pthread_getspecific(ctx->errlist_key);
100}
101
102void
103ly_err_free(void *ptr)
104{
105 struct ly_err_item *i, *next;
106
107 /* clean the error list */
108 for (i = (struct ly_err_item *)ptr; i; i = next) {
109 next = i->next;
110 free(i->msg);
111 free(i->path);
112 free(i->apptag);
113 free(i);
114 }
115}
116
117API void
118ly_err_clean(struct ly_ctx *ctx, struct ly_err_item *eitem)
119{
120 struct ly_err_item *i, *first;
121
122 first = ly_err_first(ctx);
123 if (first == eitem) {
124 eitem = NULL;
125 }
126 if (eitem) {
127 /* disconnect the error */
128 for (i = first; i && (i->next != eitem); i = i->next);
129 assert(i);
130 i->next = NULL;
131 first->prev = i;
132 /* free this err and newer */
133 ly_err_free(eitem);
134 } else {
135 /* free all err */
136 ly_err_free(first);
137 pthread_setspecific(ctx->errlist_key, NULL);
138 }
139}
140
141API LY_LOG_LEVEL
142ly_verb(LY_LOG_LEVEL level)
143{
144 LY_LOG_LEVEL prev = ly_log_level;
145
146 ly_log_level = level;
147 return prev;
148}
149
150API int
151ly_log_options(int opts)
152{
153 uint8_t prev = ly_log_opts;
154
155 ly_log_opts = opts;
156 return prev;
157}
158
159API void
160ly_verb_dbg(int dbg_groups)
161{
162#ifndef NDEBUG
163 ly_log_dbg_groups = dbg_groups;
164#else
165 (void)dbg_groups;
166#endif
167}
168
169API void
170ly_set_log_clb(void (*clb)(LY_LOG_LEVEL level, const char *msg, const char *path), int path)
171{
172 ly_log_clb = clb;
173 path_flag = path;
174}
175
176API void
177(*ly_get_log_clb(void))(LY_LOG_LEVEL, const char *, const char *)
178{
179 return ly_log_clb;
180}
181
182static LY_ERR
183log_store(const struct ly_ctx *ctx, LY_LOG_LEVEL level, LY_ERR no, LY_VECODE vecode, char *msg, char *path, char *apptag)
184{
185 struct ly_err_item *eitem, *last;
186
187 assert(ctx && (level < LY_LLVRB));
188
189 eitem = pthread_getspecific(ctx->errlist_key);
190 if (!eitem) {
191 /* if we are only to fill in path, there must have been an error stored */
192 assert(msg);
193 eitem = malloc(sizeof *eitem);
194 LY_CHECK_GOTO(!eitem, mem_fail);
195 eitem->prev = eitem;
196 eitem->next = NULL;
197
198 pthread_setspecific(ctx->errlist_key, eitem);
199 } else if (!msg) {
200 /* only filling the path */
201 assert(path);
202
203 /* find last error */
204 eitem = eitem->prev;
205 do {
206 if (eitem->level == LY_LLERR) {
207 /* fill the path */
208 free(eitem->path);
209 eitem->path = path;
210 return LY_SUCCESS;
211 }
212 eitem = eitem->prev;
213 } while (eitem->prev->next);
214 /* last error was not found */
215 assert(0);
216 } else if ((log_opt != ILO_STORE) && ((ly_log_opts & LY_LOSTORE_LAST) == LY_LOSTORE_LAST)) {
217 /* overwrite last message */
218 free(eitem->msg);
219 free(eitem->path);
220 free(eitem->apptag);
221 } else {
222 /* store new message */
223 last = eitem->prev;
224 eitem->prev = malloc(sizeof *eitem);
225 LY_CHECK_GOTO(!eitem->prev, mem_fail);
226 eitem = eitem->prev;
227 eitem->prev = last;
228 eitem->next = NULL;
229 last->next = eitem;
230 }
231
232 /* fill in the information */
233 eitem->level = level;
234 eitem->no = no;
235 eitem->vecode = vecode;
236 eitem->msg = msg;
237 eitem->path = path;
238 eitem->apptag = apptag;
239 return LY_SUCCESS;
240
241mem_fail:
242 LOGMEM(NULL);
243 free(msg);
244 free(path);
245 free(apptag);
246 return LY_EMEM;
247}
248
249static void
250log_vprintf(const struct ly_ctx *ctx, LY_LOG_LEVEL level, LY_ERR no, LY_VECODE vecode, char *path,
251 const char *format, va_list args)
252{
253 char *msg = NULL;
254 int free_strs;
255
256 if ((log_opt == ILO_ERR2WRN) && (level == LY_LLERR)) {
257 /* change error to warning */
258 level = LY_LLWRN;
259 }
260
261 if ((log_opt == ILO_IGNORE) || (level > ly_log_level)) {
262 /* do not print or store the message */
263 free(path);
264 return;
265 }
266
267 if ((no == LY_EVALID) && (vecode == LYVE_SUCCESS)) {
268 /* assume we are inheriting the error, so inherit vecode as well */
269 vecode = ly_vecode(ctx);
270 }
271
272 /* store the error/warning (if we need to store errors internally, it does not matter what are the user log options) */
273 if ((level < LY_LLVRB) && ctx && ((ly_log_opts & LY_LOSTORE) || (log_opt == ILO_STORE))) {
274 if (!format) {
275 assert(path);
276 /* postponed print of path related to the previous error, do not rewrite stored original message */
277 if (log_store(ctx, level, no, vecode, NULL, path, NULL)) {
278 return;
279 }
280 msg = "Path is related to the previous error message.";
281 } else {
282 if (vasprintf(&msg, format, args) == -1) {
283 LOGMEM(ctx);
284 free(path);
285 return;
286 }
287 if (log_store(ctx, level, no, vecode, msg, path, NULL)) {
288 return;
289 }
290 }
291 free_strs = 0;
292 } else {
293 if (vasprintf(&msg, format, args) == -1) {
294 LOGMEM(ctx);
295 free(path);
296 return;
297 }
298 free_strs = 1;
299 }
300
301 /* if we are only storing errors internally, never print the message (yet) */
302 if ((ly_log_opts & LY_LOLOG) && (log_opt != ILO_STORE)) {
303 if (ly_log_clb) {
304 ly_log_clb(level, msg, path);
305 } else {
306 fprintf(stderr, "libyang[%d]: %s%s", level, msg, path ? " " : "\n");
307 if (path) {
308 fprintf(stderr, "(path: %s)\n", path);
309 }
310 }
311 }
312
313 if (free_strs) {
314 free(path);
315 free(msg);
316 }
317}
318
Radek Krejci4ab61562018-09-05 15:00:37 +0200319#ifndef NDEBUG
320
321void
322ly_log_dbg(int group, const char *format, ...)
323{
324 char *dbg_format;
325 const char *str_group;
326 va_list ap;
327
328 if (!(ly_log_dbg_groups & group)) {
329 return;
330 }
331
332 switch (group) {
333 case LY_LDGDICT:
334 str_group = "DICT";
335 break;
336 case LY_LDGYANG:
337 str_group = "YANG";
338 break;
339 case LY_LDGYIN:
340 str_group = "YIN";
341 break;
342 case LY_LDGXPATH:
343 str_group = "XPATH";
344 break;
345 case LY_LDGDIFF:
346 str_group = "DIFF";
347 break;
348 default:
349 LOGINT(NULL);
350 return;
351 }
352
353 if (asprintf(&dbg_format, "%s: %s", str_group, format) == -1) {
354 LOGMEM(NULL);
355 return;
356 }
357
358 va_start(ap, format);
359 log_vprintf(NULL, LY_LLDBG, 0, 0, NULL, dbg_format, ap);
360 va_end(ap);
361}
362
363#endif
364
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200365void
366ly_log(const struct ly_ctx *ctx, LY_LOG_LEVEL level, LY_ERR no, const char *format, ...)
367{
368 va_list ap;
369
370 va_start(ap, format);
371 log_vprintf(ctx, level, no, 0, NULL, format, ap);
372 va_end(ap);
373}
374
Radek Krejci94aa9942018-09-07 17:12:17 +0200375static LY_ERR
376ly_vlog_build_path_print(const struct ly_ctx *ctx, char **path, uint16_t *index, const char *str, uint16_t str_len, uint16_t *length)
377{
378 void *mem;
379 uint16_t step;
380
381 if ((*index) < str_len) {
382 /* enlarge buffer */
383 step = (str_len < LY_BUF_STEP) ? LY_BUF_STEP : str_len;
384 mem = realloc(*path, *length + *index + step + 1);
385 LY_CHECK_ERR_RET(!mem, LOGMEM(ctx), LY_EMEM);
386 *path = mem;
387
388 /* move data, lengths */
389 memmove(&(*path)[*index + step], &(*path)[*index], *length);
390 (*index) += step;
391 }
392
393 (*index) -= str_len;
394 memcpy(&(*path)[*index], str, str_len);
395 *length += str_len;
396
397 return 0;
398}
399
400LY_ERR
401ly_vlog_build_path(const struct ly_ctx *ctx, enum LY_VLOG_ELEM elem_type, const void *elem, char **path, int UNUSED(schema_all_prefixes))
402{
403 uint16_t length, index;
404 size_t len;
405 LY_ERR rc = LY_SUCCESS;
406
407 length = 0;
408 *path = malloc(1);
409 LY_CHECK_ERR_RET(!(*path), LOGMEM(ctx), LY_EMEM);
410 index = 0;
411
412 while (elem) {
413 switch (elem_type) {
414 case LY_VLOG_LYS:
415 /* TODO */
416 break;
417 case LY_VLOG_LYD:
418#if 0 /* TODO when data tree present */
419 name = ((struct lyd_node *)elem)->schema->name;
420 if (!((struct lyd_node *)elem)->parent ||
421 lyd_node_module((struct lyd_node *)elem) != lyd_node_module(((struct lyd_node *)elem)->parent)) {
422 prefix = lyd_node_module((struct lyd_node *)elem)->name;
423 } else {
424 prefix = NULL;
425 }
426
427 /* handle predicates (keys) in case of lists */
428 if (((struct lyd_node *)elem)->schema->nodetype == LYS_LIST) {
429 dlist = (struct lyd_node *)elem;
430 slist = (struct lys_node_list *)((struct lyd_node *)elem)->schema;
431 if (slist->keys_size) {
432 /* schema list with keys - use key values in predicates */
433 for (i = slist->keys_size - 1; i > -1; i--) {
434 LY_TREE_FOR(dlist->child, diter) {
435 if (diter->schema == (struct lys_node *)slist->keys[i]) {
436 break;
437 }
438 }
439 if (diter && ((struct lyd_node_leaf_list *)diter)->value_str) {
440 if (strchr(((struct lyd_node_leaf_list *)diter)->value_str, '\'')) {
441 val_start = "=\"";
442 val_end = "\"]";
443 } else {
444 val_start = "='";
445 val_end = "']";
446 }
447
448 /* print value */
449 if (ly_vlog_build_path_print(path, &index, val_end, 2, &length)) {
450 return -1;
451 }
452 len = strlen(((struct lyd_node_leaf_list *)diter)->value_str);
453 if (ly_vlog_build_path_print(path, &index,
454 ((struct lyd_node_leaf_list *)diter)->value_str, len, &length)) {
455 return -1;
456 }
457
458 /* print schema name */
459 if (ly_vlog_build_path_print(path, &index, val_start, 2, &length)) {
460 return -1;
461 }
462 len = strlen(diter->schema->name);
463 if (ly_vlog_build_path_print(path, &index, diter->schema->name, len, &length)) {
464 return -1;
465 }
466
467 if (lyd_node_module(dlist) != lyd_node_module(diter)) {
468 if (ly_vlog_build_path_print(path, &index, ":", 1, &length)) {
469 return -1;
470 }
471 len = strlen(lyd_node_module(diter)->name);
472 if (ly_vlog_build_path_print(path, &index, lyd_node_module(diter)->name, len, &length)) {
473 return -1;
474 }
475 }
476
477 if (ly_vlog_build_path_print(path, &index, "[", 1, &length)) {
478 return -1;
479 }
480 }
481 }
482 } else {
483 /* schema list without keys - use instance position */
484 i = j = lyd_list_pos(dlist);
485 len = 1;
486 while (j > 9) {
487 ++len;
488 j /= 10;
489 }
490
491 if (ly_vlog_build_path_print(path, &index, "]", 1, &length)) {
492 return -1;
493 }
494
495 str = malloc(len + 1);
496 LY_CHECK_ERR_RETURN(!str, LOGMEM(NULL), -1);
497 sprintf(str, "%d", i);
498
499 if (ly_vlog_build_path_print(path, &index, str, len, &length)) {
500 free(str);
501 return -1;
502 }
503 free(str);
504
505 if (ly_vlog_build_path_print(path, &index, "[", 1, &length)) {
506 return -1;
507 }
508 }
509 } else if (((struct lyd_node *)elem)->schema->nodetype == LYS_LEAFLIST &&
510 ((struct lyd_node_leaf_list *)elem)->value_str) {
511 if (strchr(((struct lyd_node_leaf_list *)elem)->value_str, '\'')) {
512 val_start = "[.=\"";
513 val_end = "\"]";
514 } else {
515 val_start = "[.='";
516 val_end = "']";
517 }
518
519 if (ly_vlog_build_path_print(path, &index, val_end, 2, &length)) {
520 return -1;
521 }
522 len = strlen(((struct lyd_node_leaf_list *)elem)->value_str);
523 if (ly_vlog_build_path_print(path, &index, ((struct lyd_node_leaf_list *)elem)->value_str, len, &length)) {
524 return -1;
525 }
526 if (ly_vlog_build_path_print(path, &index, val_start, 4, &length)) {
527 return -1;
528 }
529 }
530
531 /* check if it is yang-data top element */
532 if (!((struct lyd_node *)elem)->parent) {
533 ext_name = lyp_get_yang_data_template_name(elem);
534 if (ext_name) {
535 if (ly_vlog_build_path_print(path, &index, name, strlen(name), &length)) {
536 return -1;
537 }
538 if (ly_vlog_build_path_print(path, &index, "/", 1, &length)) {
539 return -1;
540 }
541 yang_data_extension = 1;
542 name = ext_name;
543 }
544 }
545
546 elem = ((struct lyd_node *)elem)->parent;
547 break;
548#endif
549 case LY_VLOG_STR:
550 len = strlen((const char *)elem);
551 rc = ly_vlog_build_path_print(ctx, path, &index, (const char *)elem, len, &length);
552 LY_CHECK_RET(rc != LY_SUCCESS, rc);
553 goto success;
554 case LY_VLOG_LINE:
555
556 goto success;
557 default:
558 /* shouldn't be here */
559 LOGINT_RET(ctx);
560 }
561
562#if 0 /* TODO when data/schema tree present */
563 if (name) {
564 if (ly_vlog_build_path_print(ctx, path, &index, name, strlen(name), &length)) {
565 return -1;
566 }
567 if (prefix) {
568 if (yang_data_extension && ly_vlog_build_path_print(path, &index, "#", 1, &length)) {
569 return -1;
570 }
571 if (ly_vlog_build_path_print(ctx, path, &index, ":", 1, &length)) {
572 return -1;
573 }
574 if (ly_vlog_build_path_print(ctx, path, &index, prefix, strlen(prefix), &length)) {
575 return -1;
576 }
577 }
578 }
579 if (ly_vlog_build_path_print(ctx, path, &index, "/", 1, &length)) {
580 return -1;
581 }
582 if ((elem_type == LY_VLOG_LYS) && !elem && sparent && (sparent->nodetype == LYS_AUGMENT)) {
583 len = strlen(((struct lys_node_augment *)sparent)->target_name);
584 if (ly_vlog_build_path_print(ctx, path, &index, ((struct lys_node_augment *)sparent)->target_name, len, &length)) {
585 return -1;
586 }
587 }
588#endif
589 }
590
591success:
592 memmove(*path, (*path) + index, length);
593 (*path)[length] = '\0';
594 return LY_SUCCESS;
595}
596
597void
598ly_vlog(const struct ly_ctx *ctx, enum LY_VLOG_ELEM elem_type, const void *elem, LY_VECODE code, const char *format, ...)
599{
600 va_list ap;
601 char* path = NULL;
602 const struct ly_err_item *first;
603
604 if (path_flag && (elem_type != LY_VLOG_NONE)) {
605 if (elem_type == LY_VLOG_PREV) {
606 /* use previous path */
607 first = ly_err_first(ctx);
608 if (first && first->prev->path) {
609 path = strdup(first->prev->path);
610 }
611 } else {
612 /* print path */
613 if (!elem) {
614 /* top-level */
615 path = strdup("/");
616 } else {
617 ly_vlog_build_path(ctx, elem_type, elem, &path, 0);
618 }
619 }
620 }
621
622 va_start(ap, format);
623 log_vprintf(ctx, LY_LLERR, LY_EVALID, code, path, format, ap);
624 /* path is spent and should not be freed! */
625 va_end(ap);
626}
627
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200628API void
629ly_err_print(struct ly_err_item *eitem)
630{
631 if (ly_log_opts & LY_LOLOG) {
632 if (ly_log_clb) {
633 ly_log_clb(eitem->level, eitem->msg, eitem->path);
634 } else {
635 fprintf(stderr, "libyang[%d]: %s%s", eitem->level, eitem->msg, eitem->path ? " " : "\n");
636 if (eitem->path) {
637 fprintf(stderr, "(path: %s)\n", eitem->path);
638 }
639 }
640 }
641}
642