Coverage Report

Created: 2025-10-23 06:55

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libyang/src/log.c
Line
Count
Source
1
/**
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 /* asprintf, strdup */
16
#include <sys/cdefs.h>
17
18
#include "log.h"
19
20
#include <assert.h>
21
#include <inttypes.h>
22
#include <pthread.h>
23
#include <stdarg.h>
24
#include <stdint.h>
25
#include <stdio.h>
26
#include <stdlib.h>
27
#include <string.h>
28
29
#include "common.h"
30
#include "compat.h"
31
#include "in_internal.h"
32
#include "plugins_exts.h"
33
#include "set.h"
34
#include "tree_data.h"
35
#include "tree_schema.h"
36
37
volatile LY_LOG_LEVEL ly_ll = LY_LLWRN;
38
volatile uint32_t ly_log_opts = LY_LOLOG | LY_LOSTORE_LAST;
39
static ly_log_clb log_clb;
40
static volatile ly_bool path_flag = 1;
41
#ifndef NDEBUG
42
volatile uint32_t ly_ldbg_groups = 0;
43
#endif
44
45
THREAD_LOCAL struct ly_log_location_s log_location = {0};
46
47
/* how many bytes add when enlarging buffers */
48
#define LY_BUF_STEP 128
49
50
API LY_ERR
51
ly_errcode(const struct ly_ctx *ctx)
52
0
{
53
0
    struct ly_err_item *i;
54
55
0
    i = ly_err_last(ctx);
56
0
    if (i) {
57
0
        return i->no;
58
0
    }
59
60
0
    return LY_SUCCESS;
61
0
}
62
63
API LY_VECODE
64
ly_vecode(const struct ly_ctx *ctx)
65
0
{
66
0
    struct ly_err_item *i;
67
68
0
    i = ly_err_last(ctx);
69
0
    if (i) {
70
0
        return i->vecode;
71
0
    }
72
73
0
    return LYVE_SUCCESS;
74
0
}
75
76
API const char *
77
ly_errmsg(const struct ly_ctx *ctx)
78
0
{
79
0
    struct ly_err_item *i;
80
81
0
    LY_CHECK_ARG_RET(NULL, ctx, NULL);
82
83
0
    i = ly_err_last(ctx);
84
0
    if (i) {
85
0
        return i->msg;
86
0
    }
87
88
0
    return NULL;
89
0
}
90
91
API const char *
92
ly_errpath(const struct ly_ctx *ctx)
93
0
{
94
0
    struct ly_err_item *i;
95
96
0
    LY_CHECK_ARG_RET(NULL, ctx, NULL);
97
98
0
    i = ly_err_last(ctx);
99
0
    if (i) {
100
0
        return i->path;
101
0
    }
102
103
0
    return NULL;
104
0
}
105
106
API const char *
107
ly_errapptag(const struct ly_ctx *ctx)
108
0
{
109
0
    struct ly_err_item *i;
110
111
0
    LY_CHECK_ARG_RET(NULL, ctx, NULL);
112
113
0
    i = ly_err_last(ctx);
114
0
    if (i) {
115
0
        return i->apptag;
116
0
    }
117
118
0
    return NULL;
119
0
}
120
121
API LY_ERR
122
ly_err_new(struct ly_err_item **err, LY_ERR ecode, LY_VECODE vecode, char *path, char *apptag, const char *err_msg, ...)
123
0
{
124
0
    char *msg = NULL;
125
0
    struct ly_err_item *e;
126
127
0
    if (!err || (ecode == LY_SUCCESS)) {
128
        /* nothing to do */
129
0
        return ecode;
130
0
    }
131
132
0
    e = malloc(sizeof *e);
133
0
    LY_CHECK_ERR_RET(!e, LOGMEM(NULL), LY_EMEM);
134
0
    e->prev = (*err) ? (*err)->prev : e;
135
0
    e->next = NULL;
136
0
    if (*err) {
137
0
        (*err)->prev->next = e;
138
0
    }
139
140
    /* fill in the information */
141
0
    e->level = LY_LLERR;
142
0
    e->no = ecode;
143
0
    e->vecode = vecode;
144
0
    e->path = path;
145
0
    e->apptag = apptag;
146
147
0
    if (err_msg) {
148
0
        va_list print_args;
149
150
0
        va_start(print_args, err_msg);
151
152
0
        if (vasprintf(&msg, err_msg, print_args) == -1) {
153
            /* we don't have anything more to do, just set err_msg to NULL to avoid undefined content,
154
             * still keep the information about the original error instead of LY_EMEM or other printf's error */
155
0
            msg = NULL;
156
0
        }
157
158
0
        va_end(print_args);
159
0
    }
160
0
    e->msg = msg;
161
162
0
    if (!(*err)) {
163
0
        *err = e;
164
0
    }
165
166
0
    return e->no;
167
0
}
168
169
API struct ly_err_item *
170
ly_err_first(const struct ly_ctx *ctx)
171
0
{
172
0
    LY_CHECK_ARG_RET(NULL, ctx, NULL);
173
174
0
    return pthread_getspecific(ctx->errlist_key);
175
0
}
176
177
API struct ly_err_item *
178
ly_err_last(const struct ly_ctx *ctx)
179
0
{
180
0
    const struct ly_err_item *e;
181
182
0
    LY_CHECK_ARG_RET(NULL, ctx, NULL);
183
184
0
    e = pthread_getspecific(ctx->errlist_key);
185
0
    return e ? e->prev : NULL;
186
0
}
187
188
API void
189
ly_err_free(void *ptr)
190
0
{
191
0
    struct ly_err_item *i, *next;
192
193
    /* clean the error list */
194
0
    for (i = (struct ly_err_item *)ptr; i; i = next) {
195
0
        next = i->next;
196
0
        free(i->msg);
197
0
        free(i->path);
198
0
        free(i->apptag);
199
0
        free(i);
200
0
    }
201
0
}
202
203
API void
204
ly_err_clean(struct ly_ctx *ctx, struct ly_err_item *eitem)
205
0
{
206
0
    struct ly_err_item *i, *first;
207
208
0
    first = ly_err_first(ctx);
209
0
    if (first == eitem) {
210
0
        eitem = NULL;
211
0
    }
212
0
    if (eitem) {
213
        /* disconnect the error */
214
0
        for (i = first; i && (i->next != eitem); i = i->next) {}
215
0
        assert(i);
216
0
        i->next = NULL;
217
0
        first->prev = i;
218
        /* free this err and newer */
219
0
        ly_err_free(eitem);
220
0
    } else {
221
        /* free all err */
222
0
        ly_err_free(first);
223
0
        pthread_setspecific(ctx->errlist_key, NULL);
224
0
    }
225
0
}
226
227
API LY_LOG_LEVEL
228
ly_log_level(LY_LOG_LEVEL level)
229
0
{
230
0
    LY_LOG_LEVEL prev = ly_ll;
231
232
0
    ly_ll = level;
233
0
    return prev;
234
0
}
235
236
API uint32_t
237
ly_log_options(uint32_t opts)
238
0
{
239
0
    uint32_t prev = ly_log_opts;
240
241
0
    ly_log_opts = opts;
242
0
    return prev;
243
0
}
244
245
API uint32_t
246
ly_log_dbg_groups(uint32_t dbg_groups)
247
0
{
248
#ifndef NDEBUG
249
    uint32_t prev = ly_ldbg_groups;
250
251
    ly_ldbg_groups = dbg_groups;
252
    return prev;
253
#else
254
0
    (void)dbg_groups;
255
0
    return 0;
256
0
#endif
257
0
}
258
259
API void
260
ly_set_log_clb(ly_log_clb clb, ly_bool path)
261
0
{
262
0
    log_clb = clb;
263
0
    path_flag = path;
264
0
}
265
266
API ly_log_clb
267
ly_get_log_clb(void)
268
0
{
269
0
    return log_clb;
270
0
}
271
272
void
273
ly_log_location(const struct lysc_node *scnode, const struct lyd_node *dnode,
274
        const char *path, const struct ly_in *in, uint64_t line, ly_bool reset)
275
0
{
276
0
    if (scnode) {
277
0
        ly_set_add(&log_location.scnodes, (void *)scnode, 1, NULL);
278
0
    } else if (reset) {
279
0
        ly_set_erase(&log_location.scnodes, NULL);
280
0
    }
281
282
0
    if (dnode) {
283
0
        ly_set_add(&log_location.dnodes, (void *)dnode, 1, NULL);
284
0
    } else if (reset) {
285
0
        ly_set_erase(&log_location.dnodes, NULL);
286
0
    }
287
288
0
    if (path) {
289
0
        char *s = strdup(path);
290
0
        LY_CHECK_ERR_RET(!s, LOGMEM(NULL), );
291
0
        ly_set_add(&log_location.paths, s, 1, NULL);
292
0
    } else if (reset) {
293
0
        ly_set_erase(&log_location.paths, free);
294
0
    }
295
296
0
    if (in) {
297
0
        ly_set_add(&log_location.inputs, (void *)in, 1, NULL);
298
0
    } else if (reset) {
299
0
        ly_set_erase(&log_location.inputs, NULL);
300
0
    }
301
302
0
    if (line) {
303
0
        log_location.line = line;
304
0
    }
305
0
}
306
307
void
308
ly_log_location_revert(uint32_t scnode_steps, uint32_t dnode_steps,
309
        uint32_t path_steps, uint32_t in_steps)
310
0
{
311
0
    for (uint32_t i = scnode_steps; i && log_location.scnodes.count; i--) {
312
0
        log_location.scnodes.count--;
313
0
    }
314
315
0
    for (uint32_t i = dnode_steps; i && log_location.dnodes.count; i--) {
316
0
        log_location.dnodes.count--;
317
0
    }
318
319
0
    for (uint32_t i = path_steps; i && log_location.paths.count; i--) {
320
0
        ly_set_rm_index(&log_location.paths, log_location.paths.count - 1, free);
321
0
    }
322
323
0
    for (uint32_t i = in_steps; i && log_location.inputs.count; i--) {
324
0
        log_location.inputs.count--;
325
0
    }
326
327
    /* deallocate the empty sets */
328
0
    if (scnode_steps && !log_location.scnodes.count) {
329
0
        ly_set_erase(&log_location.scnodes, NULL);
330
0
    }
331
0
    if (dnode_steps && !log_location.dnodes.count) {
332
0
        ly_set_erase(&log_location.dnodes, NULL);
333
0
    }
334
0
    if (path_steps && !log_location.paths.count) {
335
0
        ly_set_erase(&log_location.paths, free);
336
0
    }
337
0
    if (in_steps && !log_location.inputs.count) {
338
0
        ly_set_erase(&log_location.inputs, NULL);
339
0
    }
340
0
}
341
342
static LY_ERR
343
log_store(const struct ly_ctx *ctx, LY_LOG_LEVEL level, LY_ERR no, LY_VECODE vecode, char *msg, char *path, char *apptag)
344
0
{
345
0
    struct ly_err_item *eitem, *last;
346
347
0
    assert(ctx && (level < LY_LLVRB));
348
349
0
    eitem = pthread_getspecific(ctx->errlist_key);
350
0
    if (!eitem) {
351
        /* if we are only to fill in path, there must have been an error stored */
352
0
        assert(msg);
353
0
        eitem = malloc(sizeof *eitem);
354
0
        LY_CHECK_GOTO(!eitem, mem_fail);
355
0
        eitem->prev = eitem;
356
0
        eitem->next = NULL;
357
358
0
        pthread_setspecific(ctx->errlist_key, eitem);
359
0
    } else if (!msg) {
360
        /* only filling the path */
361
0
        assert(path);
362
363
        /* find last error */
364
0
        eitem = eitem->prev;
365
0
        do {
366
0
            if (eitem->level == LY_LLERR) {
367
                /* fill the path */
368
0
                free(eitem->path);
369
0
                eitem->path = path;
370
0
                return LY_SUCCESS;
371
0
            }
372
0
            eitem = eitem->prev;
373
0
        } while (eitem->prev->next);
374
        /* last error was not found */
375
0
        assert(0);
376
0
    } else if ((ly_log_opts & LY_LOSTORE_LAST) == LY_LOSTORE_LAST) {
377
        /* overwrite last message */
378
0
        free(eitem->msg);
379
0
        free(eitem->path);
380
0
        free(eitem->apptag);
381
0
    } else {
382
        /* store new message */
383
0
        last = eitem->prev;
384
0
        eitem->prev = malloc(sizeof *eitem);
385
0
        LY_CHECK_GOTO(!eitem->prev, mem_fail);
386
0
        eitem = eitem->prev;
387
0
        eitem->prev = last;
388
0
        eitem->next = NULL;
389
0
        last->next = eitem;
390
0
    }
391
392
    /* fill in the information */
393
0
    eitem->level = level;
394
0
    eitem->no = no;
395
0
    eitem->vecode = vecode;
396
0
    eitem->msg = msg;
397
0
    eitem->path = path;
398
0
    eitem->apptag = apptag;
399
0
    return LY_SUCCESS;
400
401
0
mem_fail:
402
0
    LOGMEM(NULL);
403
0
    free(msg);
404
0
    free(path);
405
0
    free(apptag);
406
0
    return LY_EMEM;
407
0
}
408
409
static void
410
log_vprintf(const struct ly_ctx *ctx, LY_LOG_LEVEL level, LY_ERR no, LY_VECODE vecode, char *path,
411
        const char *format, va_list args)
412
0
{
413
0
    char *msg = NULL;
414
0
    ly_bool free_strs;
415
416
0
    if (level > ly_ll) {
417
        /* do not print or store the message */
418
0
        free(path);
419
0
        return;
420
0
    }
421
422
0
    if (no == LY_EMEM) {
423
        /* just print it, anything else would most likely fail anyway */
424
0
        if (ly_log_opts & LY_LOLOG) {
425
0
            if (log_clb) {
426
0
                log_clb(level, LY_EMEM_MSG, path);
427
0
            } else {
428
0
                fprintf(stderr, "libyang[%d]: ", level);
429
0
                vfprintf(stderr, format, args);
430
0
                if (path) {
431
0
                    fprintf(stderr, " (path: %s)\n", path);
432
0
                } else {
433
0
                    fprintf(stderr, "\n");
434
0
                }
435
0
            }
436
0
        }
437
0
        free(path);
438
0
        return;
439
0
    }
440
441
    /* store the error/warning (if we need to store errors internally, it does not matter what are the user log options) */
442
0
    if ((level < LY_LLVRB) && ctx && (ly_log_opts & LY_LOSTORE)) {
443
0
        assert(format);
444
0
        if (vasprintf(&msg, format, args) == -1) {
445
0
            LOGMEM(ctx);
446
0
            free(path);
447
0
            return;
448
0
        }
449
0
        if (((no & ~LY_EPLUGIN) == LY_EVALID) && (vecode == LYVE_SUCCESS)) {
450
            /* assume we are inheriting the error, so inherit vecode as well */
451
0
            vecode = ly_vecode(ctx);
452
0
        }
453
0
        if (log_store(ctx, level, no, vecode, msg, path, NULL)) {
454
0
            return;
455
0
        }
456
0
        free_strs = 0;
457
0
    } else {
458
0
        if (vasprintf(&msg, format, args) == -1) {
459
0
            LOGMEM(ctx);
460
0
            free(path);
461
0
            return;
462
0
        }
463
0
        free_strs = 1;
464
0
    }
465
466
    /* if we are only storing errors internally, never print the message (yet) */
467
0
    if (ly_log_opts & LY_LOLOG) {
468
0
        if (log_clb) {
469
0
            log_clb(level, msg, path);
470
0
        } else {
471
0
            fprintf(stderr, "libyang[%d]: %s%s", level, msg, path ? " " : "\n");
472
0
            if (path) {
473
0
                fprintf(stderr, "(path: %s)\n", path);
474
0
            }
475
0
        }
476
0
    }
477
478
0
    if (free_strs) {
479
0
        free(path);
480
0
        free(msg);
481
0
    }
482
0
}
483
484
#ifndef NDEBUG
485
486
void
487
ly_log_dbg(uint32_t group, const char *format, ...)
488
{
489
    char *dbg_format;
490
    const char *str_group;
491
    va_list ap;
492
493
    if (!(ly_ldbg_groups & group)) {
494
        return;
495
    }
496
497
    switch (group) {
498
    case LY_LDGDICT:
499
        str_group = "DICT";
500
        break;
501
    case LY_LDGXPATH:
502
        str_group = "XPATH";
503
        break;
504
    default:
505
        LOGINT(NULL);
506
        return;
507
    }
508
509
    if (asprintf(&dbg_format, "%s: %s", str_group, format) == -1) {
510
        LOGMEM(NULL);
511
        return;
512
    }
513
514
    va_start(ap, format);
515
    log_vprintf(NULL, LY_LLDBG, 0, 0, NULL, dbg_format, ap);
516
    va_end(ap);
517
}
518
519
#endif
520
521
void
522
ly_log(const struct ly_ctx *ctx, LY_LOG_LEVEL level, LY_ERR no, const char *format, ...)
523
0
{
524
0
    va_list ap;
525
526
0
    va_start(ap, format);
527
0
    log_vprintf(ctx, level, no, 0, NULL, format, ap);
528
0
    va_end(ap);
529
0
}
530
531
static LY_ERR
532
ly_vlog_build_path(const struct ly_ctx *ctx, char **path)
533
0
{
534
0
    int rc;
535
0
    char *str = NULL, *prev = NULL;
536
537
0
    *path = NULL;
538
539
0
    if (log_location.paths.count && ((const char *)(log_location.paths.objs[log_location.paths.count - 1]))[0]) {
540
        /* simply get what is in the provided path string */
541
0
        *path = strdup((const char *)log_location.paths.objs[log_location.paths.count - 1]);
542
0
        LY_CHECK_ERR_RET(!(*path), LOGMEM(ctx), LY_EMEM);
543
0
    } else {
544
        /* generate location string */
545
0
        if (log_location.scnodes.count) {
546
0
            str = lysc_path(log_location.scnodes.objs[log_location.scnodes.count - 1], LYSC_PATH_LOG, NULL, 0);
547
0
            LY_CHECK_ERR_RET(!str, LOGMEM(ctx), LY_EMEM);
548
549
0
            rc = asprintf(path, "Schema location %s", str);
550
0
            free(str);
551
0
            LY_CHECK_ERR_RET(rc == -1, LOGMEM(ctx), LY_EMEM);
552
0
        }
553
0
        if (log_location.dnodes.count) {
554
0
            prev = *path;
555
0
            str = lyd_path(log_location.dnodes.objs[log_location.dnodes.count - 1], LYD_PATH_STD, NULL, 0);
556
0
            LY_CHECK_ERR_RET(!str, LOGMEM(ctx), LY_EMEM);
557
558
0
            rc = asprintf(path, "%s%sata location %s", prev ? prev : "", prev ? ", d" : "D", str);
559
0
            free(str);
560
0
            free(prev);
561
0
            LY_CHECK_ERR_RET(rc == -1, LOGMEM(ctx), LY_EMEM);
562
0
        }
563
0
        if (log_location.line) {
564
0
            prev = *path;
565
0
            rc = asprintf(path, "%s%sine number %" PRIu64, prev ? prev : "", prev ? ", l" : "L", log_location.line);
566
0
            free(prev);
567
0
            LY_CHECK_ERR_RET(rc == -1, LOGMEM(ctx), LY_EMEM);
568
569
0
            log_location.line = 0;
570
0
        } else if (log_location.inputs.count) {
571
0
            prev = *path;
572
0
            rc = asprintf(path, "%s%sine number %" PRIu64, prev ? prev : "", prev ? ", l" : "L",
573
0
                    ((struct ly_in *)log_location.inputs.objs[log_location.inputs.count - 1])->line);
574
0
            free(prev);
575
0
            LY_CHECK_ERR_RET(rc == -1, LOGMEM(ctx), LY_EMEM);
576
0
        }
577
578
0
        if (*path) {
579
0
            prev = *path;
580
0
            rc = asprintf(path, "%s.", prev);
581
0
            free(prev);
582
0
            LY_CHECK_ERR_RET(rc == -1, LOGMEM(ctx), LY_EMEM);
583
0
        }
584
0
    }
585
586
0
    return LY_SUCCESS;
587
0
}
588
589
void
590
ly_vlog(const struct ly_ctx *ctx, LY_VECODE code, const char *format, ...)
591
0
{
592
0
    va_list ap;
593
0
    char *path = NULL;
594
595
0
    if (path_flag && ctx) {
596
0
        ly_vlog_build_path(ctx, &path);
597
0
    }
598
599
0
    va_start(ap, format);
600
0
    log_vprintf(ctx, LY_LLERR, LY_EVALID, code, path, format, ap);
601
    /* path is spent and should not be freed! */
602
0
    va_end(ap);
603
0
}
604
605
API void
606
lyplg_ext_log(const struct lysc_ext_instance *ext, LY_LOG_LEVEL level, LY_ERR err_no, const char *path, const char *format, ...)
607
0
{
608
0
    va_list ap;
609
0
    char *plugin_msg;
610
0
    int ret;
611
612
0
    if (ly_ll < level) {
613
0
        return;
614
0
    }
615
0
    ret = asprintf(&plugin_msg, "Extension plugin \"%s\": %s", ext->def->plugin->id, format);
616
0
    if (ret == -1) {
617
0
        LOGMEM(ext->module->ctx);
618
0
        return;
619
0
    }
620
621
0
    va_start(ap, format);
622
0
    log_vprintf(ext->module->ctx, level, (level == LY_LLERR ? LY_EPLUGIN : 0) | err_no, LYVE_OTHER, path ? strdup(path) : NULL, plugin_msg, ap);
623
0
    va_end(ap);
624
625
0
    free(plugin_msg);
626
0
}
627
628
/**
629
 * @brief Exact same functionality as ::ly_err_print() but has variable arguments so log_vprintf() can be called.
630
 */
631
static void
632
_ly_err_print(const struct ly_ctx *ctx, struct ly_err_item *eitem, const char *format, ...)
633
0
{
634
0
    va_list ap;
635
0
    char *path_dup = NULL;
636
637
0
    LY_CHECK_ARG_RET(ctx, eitem, );
638
639
0
    if (eitem->path) {
640
        /* duplicate path because it will be freed */
641
0
        path_dup = strdup(eitem->path);
642
0
        LY_CHECK_ERR_RET(!path_dup, LOGMEM(ctx), );
643
0
    }
644
645
0
    va_start(ap, format);
646
0
    log_vprintf(ctx, eitem->level, eitem->no, eitem->vecode, path_dup, format, ap);
647
0
    va_end(ap);
648
0
}
649
650
API void
651
ly_err_print(const struct ly_ctx *ctx, struct ly_err_item *eitem)
652
0
{
653
    /* String ::ly_err_item.msg cannot be used directly because it may contain the % character */
654
0
    _ly_err_print(ctx, eitem, "%s", eitem->msg);
655
0
}