Coverage Report

Created: 2023-11-19 07:06

/src/libyang/src/log.c
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * @file log.c
3
 * @author Radek Krejci <rkrejci@cesnet.cz>
4
 * @author Michal Vasko <mvasko@cesnet.cz>
5
 * @brief Logger routines implementations
6
 *
7
 * Copyright (c) 2015 - 2023 CESNET, z.s.p.o.
8
 *
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
16
#define _GNU_SOURCE /* asprintf, strdup */
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_data_internal.h"
36
#include "tree_schema.h"
37
#include "tree_schema_internal.h"
38
39
ATOMIC_T ly_ll = (uint_fast32_t)LY_LLWRN;
40
ATOMIC_T ly_log_opts = (uint_fast32_t)(LY_LOLOG | LY_LOSTORE_LAST);
41
THREAD_LOCAL uint32_t *temp_ly_log_opts;
42
static ly_log_clb log_clb;
43
static ATOMIC_T path_flag = 1;
44
THREAD_LOCAL char last_msg[LY_LAST_MSG_SIZE];
45
#ifndef NDEBUG
46
ATOMIC_T ly_ldbg_groups = 0;
47
#endif
48
49
THREAD_LOCAL struct ly_log_location_s log_location = {0};
50
51
LIBYANG_API_DEF LY_ERR
52
ly_errcode(const struct ly_ctx *ctx)
53
0
{
54
0
    struct ly_err_item *i;
55
56
0
    i = ly_err_last(ctx);
57
0
    if (i) {
58
0
        return i->no;
59
0
    }
60
61
0
    return LY_SUCCESS;
62
0
}
63
64
LIBYANG_API_DEF const char *
65
ly_strerrcode(LY_ERR err)
66
0
{
67
    /* ignore plugin flag */
68
0
    err &= ~LY_EPLUGIN;
69
70
0
    switch (err) {
71
0
    case LY_SUCCESS:
72
0
        return "Success";
73
0
    case LY_EMEM:
74
0
        return "Out of memory";
75
0
    case LY_ESYS:
76
0
        return "System call failed";
77
0
    case LY_EINVAL:
78
0
        return "Invalid value";
79
0
    case LY_EEXIST:
80
0
        return "Already exists";
81
0
    case LY_ENOTFOUND:
82
0
        return "Not found";
83
0
    case LY_EINT:
84
0
        return "Internal error";
85
0
    case LY_EVALID:
86
0
        return "Validation failed";
87
0
    case LY_EDENIED:
88
0
        return "Operation denied";
89
0
    case LY_EINCOMPLETE:
90
0
        return "Operation incomplete";
91
0
    case LY_ERECOMPILE:
92
0
        return "Recompilation required";
93
0
    case LY_ENOT:
94
0
        return "Negative result";
95
0
    case LY_EOTHER:
96
0
        return "Another failure reason";
97
0
    case LY_EPLUGIN:
98
0
        break;
99
0
    }
100
101
    /* unreachable */
102
0
    return "Unknown";
103
0
}
104
105
LIBYANG_API_DEF LY_VECODE
106
ly_vecode(const struct ly_ctx *ctx)
107
0
{
108
0
    struct ly_err_item *i;
109
110
0
    i = ly_err_last(ctx);
111
0
    if (i) {
112
0
        return i->vecode;
113
0
    }
114
115
0
    return LYVE_SUCCESS;
116
0
}
117
118
LIBYANG_API_DEF const char *
119
ly_strvecode(LY_VECODE vecode)
120
0
{
121
0
    switch (vecode) {
122
0
    case LYVE_SUCCESS:
123
0
        return "Success";
124
0
    case LYVE_SYNTAX:
125
0
        return "General syntax error";
126
0
    case LYVE_SYNTAX_YANG:
127
0
        return "YANG syntax error";
128
0
    case LYVE_SYNTAX_YIN:
129
0
        return "YIN syntax error";
130
0
    case LYVE_REFERENCE:
131
0
        return "Reference error";
132
0
    case LYVE_XPATH:
133
0
        return "XPath error";
134
0
    case LYVE_SEMANTICS:
135
0
        return "Semantic error";
136
0
    case LYVE_SYNTAX_XML:
137
0
        return "XML syntax error";
138
0
    case LYVE_SYNTAX_JSON:
139
0
        return "JSON syntax error";
140
0
    case LYVE_DATA:
141
0
        return "YANG data error";
142
0
    case LYVE_OTHER:
143
0
        return "Another error";
144
0
    }
145
146
    /* unreachable */
147
0
    return "Unknown";
148
0
}
149
150
LIBYANG_API_DEF const char *
151
ly_errmsg(const struct ly_ctx *ctx)
152
0
{
153
0
    struct ly_err_item *i;
154
155
0
    LY_CHECK_ARG_RET(NULL, ctx, NULL);
156
157
0
    i = ly_err_last(ctx);
158
0
    if (i) {
159
0
        return i->msg;
160
0
    }
161
162
0
    return NULL;
163
0
}
164
165
LIBYANG_API_DEF const char *
166
ly_last_errmsg(void)
167
0
{
168
0
    return last_msg;
169
0
}
170
171
LIBYANG_API_DEF const char *
172
ly_errpath(const struct ly_ctx *ctx)
173
0
{
174
0
    struct ly_err_item *i;
175
176
0
    LY_CHECK_ARG_RET(NULL, ctx, NULL);
177
178
0
    i = ly_err_last(ctx);
179
0
    if (i) {
180
0
        return i->path;
181
0
    }
182
183
0
    return NULL;
184
0
}
185
186
LIBYANG_API_DEF const char *
187
ly_errapptag(const struct ly_ctx *ctx)
188
0
{
189
0
    struct ly_err_item *i;
190
191
0
    LY_CHECK_ARG_RET(NULL, ctx, NULL);
192
193
0
    i = ly_err_last(ctx);
194
0
    if (i) {
195
0
        return i->apptag;
196
0
    }
197
198
0
    return NULL;
199
0
}
200
201
LIBYANG_API_DEF LY_ERR
202
ly_err_new(struct ly_err_item **err, LY_ERR ecode, LY_VECODE vecode, char *path, char *apptag, const char *err_format, ...)
203
25.0k
{
204
25.0k
    char *msg = NULL;
205
25.0k
    struct ly_err_item *e;
206
207
25.0k
    if (!err || (ecode == LY_SUCCESS)) {
208
        /* nothing to do */
209
0
        return ecode;
210
0
    }
211
212
25.0k
    e = malloc(sizeof *e);
213
25.0k
    LY_CHECK_ERR_RET(!e, LOGMEM(NULL), LY_EMEM);
214
25.0k
    e->prev = (*err) ? (*err)->prev : e;
215
25.0k
    e->next = NULL;
216
25.0k
    if (*err) {
217
0
        (*err)->prev->next = e;
218
0
    }
219
220
    /* fill in the information */
221
25.0k
    e->level = LY_LLERR;
222
25.0k
    e->no = ecode;
223
25.0k
    e->vecode = vecode;
224
25.0k
    e->path = path;
225
25.0k
    e->apptag = apptag;
226
227
25.0k
    if (err_format) {
228
25.0k
        va_list print_args;
229
230
25.0k
        va_start(print_args, err_format);
231
232
25.0k
        if (vasprintf(&msg, err_format, print_args) == -1) {
233
            /* we don't have anything more to do, just set msg to NULL to avoid undefined content,
234
             * still keep the information about the original error instead of LY_EMEM or other printf's error */
235
0
            msg = NULL;
236
0
        }
237
238
25.0k
        va_end(print_args);
239
25.0k
    }
240
25.0k
    e->msg = msg;
241
242
25.0k
    if (!(*err)) {
243
25.0k
        *err = e;
244
25.0k
    }
245
246
25.0k
    return e->no;
247
25.0k
}
248
249
/**
250
 * @brief Get error record from error hash table of a context for the current thread.
251
 *
252
 * @param[in] ctx Context to use.
253
 * @return Thread error record, if any.
254
 */
255
static struct ly_ctx_err_rec *
256
ly_err_get_rec(const struct ly_ctx *ctx)
257
27.3k
{
258
27.3k
    struct ly_ctx_err_rec rec, *match;
259
260
    /* prepare record */
261
27.3k
    rec.tid = pthread_self();
262
263
    /* get the pointer to the matching record */
264
27.3k
    if (lyht_find(ctx->err_ht, &rec, lyht_hash((void *)&rec.tid, sizeof rec.tid), (void **)&match)) {
265
27.3k
        return NULL;
266
27.3k
    }
267
268
0
    return match;
269
27.3k
}
270
271
/**
272
 * @brief Insert new error record to error hash table of a context for the current thread.
273
 *
274
 * @param[in] ctx Context to use.
275
 * @return Thread error record.
276
 */
277
static struct ly_ctx_err_rec *
278
ly_err_new_rec(const struct ly_ctx *ctx)
279
0
{
280
0
    struct ly_ctx_err_rec new, *rec;
281
0
    LY_ERR r;
282
283
    /* insert a new record */
284
0
    new.err = NULL;
285
0
    new.tid = pthread_self();
286
287
    /* reuse lock */
288
    /* LOCK */
289
0
    pthread_mutex_lock((pthread_mutex_t *)&ctx->lyb_hash_lock);
290
291
0
    r = lyht_insert(ctx->err_ht, &new, lyht_hash((void *)&new.tid, sizeof new.tid), (void **)&rec);
292
293
    /* UNLOCK */
294
0
    pthread_mutex_unlock((pthread_mutex_t *)&ctx->lyb_hash_lock);
295
296
0
    return r ? NULL : rec;
297
0
}
298
299
LIBYANG_API_DEF struct ly_err_item *
300
ly_err_first(const struct ly_ctx *ctx)
301
0
{
302
0
    struct ly_ctx_err_rec *rec;
303
304
0
    LY_CHECK_ARG_RET(NULL, ctx, NULL);
305
306
    /* get the pointer to the matching record */
307
0
    rec = ly_err_get_rec(ctx);
308
309
0
    return rec ? rec->err : NULL;
310
0
}
311
312
LIBYANG_API_DEF struct ly_err_item *
313
ly_err_last(const struct ly_ctx *ctx)
314
27.3k
{
315
27.3k
    struct ly_ctx_err_rec *rec;
316
317
27.3k
    LY_CHECK_ARG_RET(NULL, ctx, NULL);
318
319
    /* get the pointer to the matching record */
320
27.3k
    if (!(rec = ly_err_get_rec(ctx))) {
321
27.3k
        return NULL;
322
27.3k
    }
323
324
0
    return rec->err ? rec->err->prev : NULL;
325
27.3k
}
326
327
void
328
ly_err_move(struct ly_ctx *src_ctx, struct ly_ctx *trg_ctx)
329
0
{
330
0
    struct ly_ctx_err_rec *rec;
331
0
    struct ly_err_item *err = NULL;
332
333
    /* get and remove the errors from src */
334
0
    rec = ly_err_get_rec(src_ctx);
335
0
    if (rec) {
336
0
        err = rec->err;
337
0
        rec->err = NULL;
338
0
    }
339
340
    /* set them for trg */
341
0
    if (!(rec = ly_err_get_rec(trg_ctx))) {
342
0
        if (!(rec = ly_err_new_rec(trg_ctx))) {
343
0
            LOGINT(NULL);
344
0
            ly_err_free(err);
345
0
            return;
346
0
        }
347
0
    }
348
0
    ly_err_free(rec->err);
349
0
    rec->err = err;
350
0
}
351
352
LIBYANG_API_DEF void
353
ly_err_free(void *ptr)
354
31.1k
{
355
31.1k
    struct ly_err_item *e, *next;
356
357
    /* clean the error list */
358
31.1k
    LY_LIST_FOR_SAFE(ptr, next, e) {
359
25.0k
        free(e->msg);
360
25.0k
        free(e->path);
361
25.0k
        free(e->apptag);
362
25.0k
        free(e);
363
25.0k
    }
364
31.1k
}
365
366
LIBYANG_API_DEF void
367
ly_err_clean(struct ly_ctx *ctx, struct ly_err_item *eitem)
368
0
{
369
0
    struct ly_ctx_err_rec *rec;
370
0
    struct ly_err_item *e;
371
372
0
    if (!(rec = ly_err_get_rec(ctx))) {
373
0
        return;
374
0
    }
375
0
    if (rec->err == eitem) {
376
0
        eitem = NULL;
377
0
    }
378
379
0
    if (!eitem) {
380
        /* free all err */
381
0
        ly_err_free(rec->err);
382
0
        rec->err = NULL;
383
0
    } else {
384
        /* disconnect the error */
385
0
        for (e = rec->err; e && (e->next != eitem); e = e->next) {}
386
0
        assert(e);
387
0
        e->next = NULL;
388
0
        rec->err->prev = e;
389
        /* free this err and newer */
390
0
        ly_err_free(eitem);
391
0
    }
392
0
}
393
394
LIBYANG_API_DEF LY_LOG_LEVEL
395
ly_log_level(LY_LOG_LEVEL level)
396
0
{
397
0
    LY_LOG_LEVEL prev = ATOMIC_LOAD_RELAXED(ly_ll);
398
399
0
    ATOMIC_STORE_RELAXED(ly_ll, level);
400
0
    return prev;
401
0
}
402
403
LIBYANG_API_DEF uint32_t
404
ly_log_options(uint32_t opts)
405
3
{
406
3
    uint32_t prev = ATOMIC_LOAD_RELAXED(ly_log_opts);
407
408
3
    ATOMIC_STORE_RELAXED(ly_log_opts, opts);
409
3
    return prev;
410
3
}
411
412
LIBYANG_API_DEF void
413
ly_temp_log_options(uint32_t *opts)
414
15.0k
{
415
15.0k
    temp_ly_log_opts = opts;
416
15.0k
}
417
418
LIBYANG_API_DEF uint32_t
419
ly_log_dbg_groups(uint32_t dbg_groups)
420
0
{
421
0
#ifndef NDEBUG
422
0
    uint32_t prev = ATOMIC_LOAD_RELAXED(ly_ldbg_groups);
423
424
0
    ATOMIC_STORE_RELAXED(ly_ldbg_groups, dbg_groups);
425
0
    return prev;
426
#else
427
    (void)dbg_groups;
428
    return 0;
429
#endif
430
0
}
431
432
LIBYANG_API_DEF void
433
ly_set_log_clb(ly_log_clb clb, ly_bool path)
434
0
{
435
0
    log_clb = clb;
436
0
    ATOMIC_STORE_RELAXED(path_flag, path);
437
0
}
438
439
LIBYANG_API_DEF ly_log_clb
440
ly_get_log_clb(void)
441
0
{
442
0
    return log_clb;
443
0
}
444
445
void
446
ly_log_location(const struct lysc_node *scnode, const struct lyd_node *dnode, const char *path, const struct ly_in *in,
447
        uint64_t line)
448
11.0M
{
449
11.0M
    if (scnode) {
450
1.21M
        ly_set_add(&log_location.scnodes, (void *)scnode, 1, NULL);
451
1.21M
    }
452
11.0M
    if (dnode) {
453
264k
        ly_set_add(&log_location.dnodes, (void *)dnode, 1, NULL);
454
264k
    }
455
11.0M
    if (path) {
456
8.86M
        char *s = strdup(path);
457
458
8.86M
        LY_CHECK_ERR_RET(!s, LOGMEM(NULL), );
459
8.86M
        ly_set_add(&log_location.paths, s, 1, NULL);
460
8.86M
    }
461
11.0M
    if (in) {
462
222k
        ly_set_add(&log_location.inputs, (void *)in, 1, NULL);
463
222k
    }
464
11.0M
    if (line) {
465
0
        log_location.line = line;
466
0
    }
467
11.0M
}
468
469
void
470
ly_log_location_revert(uint32_t scnode_steps, uint32_t dnode_steps, uint32_t path_steps, uint32_t in_steps)
471
11.4M
{
472
12.7M
    for (uint32_t i = scnode_steps; i && log_location.scnodes.count; i--) {
473
1.21M
        log_location.scnodes.count--;
474
1.21M
    }
475
476
11.7M
    for (uint32_t i = dnode_steps; i && log_location.dnodes.count; i--) {
477
264k
        log_location.dnodes.count--;
478
264k
    }
479
480
20.3M
    for (uint32_t i = path_steps; i && log_location.paths.count; i--) {
481
8.86M
        ly_set_rm_index(&log_location.paths, log_location.paths.count - 1, free);
482
8.86M
    }
483
484
11.7M
    for (uint32_t i = in_steps; i && log_location.inputs.count; i--) {
485
222k
        log_location.inputs.count--;
486
222k
    }
487
488
    /* deallocate the empty sets */
489
11.4M
    if (scnode_steps && !log_location.scnodes.count) {
490
863k
        ly_set_erase(&log_location.scnodes, NULL);
491
863k
    }
492
11.4M
    if (dnode_steps && !log_location.dnodes.count) {
493
17.5k
        ly_set_erase(&log_location.dnodes, NULL);
494
17.5k
    }
495
11.4M
    if (path_steps && !log_location.paths.count) {
496
9.29M
        ly_set_erase(&log_location.paths, free);
497
9.29M
    }
498
11.4M
    if (in_steps && !log_location.inputs.count) {
499
222k
        ly_set_erase(&log_location.inputs, NULL);
500
222k
    }
501
11.4M
}
502
503
const struct lyd_node *
504
ly_log_location_dnode(uint32_t idx)
505
0
{
506
0
    if (idx < log_location.dnodes.count) {
507
0
        return log_location.dnodes.dnodes[idx];
508
0
    }
509
510
0
    return NULL;
511
0
}
512
513
uint32_t
514
ly_log_location_dnode_count(void)
515
0
{
516
0
    return log_location.dnodes.count;
517
0
}
518
519
/**
520
 * @brief Store generated error in a context.
521
 *
522
 * @param[in] ctx Context to use.
523
 * @param[in] level Message log level.
524
 * @param[in] no Error number.
525
 * @param[in] vecode Error validation error code.
526
 * @param[in] msg Error message, always spent.
527
 * @param[in] path Error path, always spent.
528
 * @param[in] apptag Error app tag, always spent.
529
 * @return LY_ERR value.
530
 */
531
static LY_ERR
532
log_store(const struct ly_ctx *ctx, LY_LOG_LEVEL level, LY_ERR no, LY_VECODE vecode, char *msg, char *path, char *apptag)
533
0
{
534
0
    struct ly_ctx_err_rec *rec;
535
0
    struct ly_err_item *e, *last;
536
537
0
    assert(ctx && (level < LY_LLVRB));
538
539
0
    if (!(rec = ly_err_get_rec(ctx))) {
540
0
        if (!(rec = ly_err_new_rec(ctx))) {
541
0
            goto mem_fail;
542
0
        }
543
0
    }
544
545
0
    e = rec->err;
546
0
    if (!e) {
547
        /* if we are only to fill in path, there must have been an error stored */
548
0
        assert(msg);
549
0
        e = malloc(sizeof *e);
550
0
        LY_CHECK_GOTO(!e, mem_fail);
551
0
        e->prev = e;
552
0
        e->next = NULL;
553
554
0
        rec->err = e;
555
0
    } else if (!msg) {
556
        /* only filling the path */
557
0
        assert(path);
558
559
        /* find last error */
560
0
        e = e->prev;
561
0
        do {
562
0
            if (e->level == LY_LLERR) {
563
                /* fill the path */
564
0
                free(e->path);
565
0
                e->path = path;
566
0
                return LY_SUCCESS;
567
0
            }
568
0
            e = e->prev;
569
0
        } while (e->prev->next);
570
        /* last error was not found */
571
0
        assert(0);
572
0
    } else if ((temp_ly_log_opts && ((*temp_ly_log_opts & LY_LOSTORE_LAST) == LY_LOSTORE_LAST)) ||
573
0
            (!temp_ly_log_opts && ((ATOMIC_LOAD_RELAXED(ly_log_opts) & LY_LOSTORE_LAST) == LY_LOSTORE_LAST))) {
574
        /* overwrite last message */
575
0
        free(e->msg);
576
0
        free(e->path);
577
0
        free(e->apptag);
578
0
    } else {
579
        /* store new message */
580
0
        last = e->prev;
581
0
        e->prev = malloc(sizeof *e);
582
0
        LY_CHECK_GOTO(!e->prev, mem_fail);
583
0
        e = e->prev;
584
0
        e->prev = last;
585
0
        e->next = NULL;
586
0
        last->next = e;
587
0
    }
588
589
    /* fill in the information */
590
0
    e->level = level;
591
0
    e->no = no;
592
0
    e->vecode = vecode;
593
0
    e->msg = msg;
594
0
    e->path = path;
595
0
    e->apptag = apptag;
596
0
    return LY_SUCCESS;
597
598
0
mem_fail:
599
0
    LOGMEM(NULL);
600
0
    free(msg);
601
0
    free(path);
602
0
    free(apptag);
603
0
    return LY_EMEM;
604
0
}
605
606
static void
607
log_vprintf(const struct ly_ctx *ctx, LY_LOG_LEVEL level, LY_ERR no, LY_VECODE vecode, char *path, const char *apptag,
608
        const char *format, va_list args)
609
175k
{
610
175k
    char *msg = NULL;
611
175k
    ly_bool free_strs, lolog, lostore;
612
613
    /* learn effective logger options */
614
175k
    if (temp_ly_log_opts) {
615
7.39k
        lolog = *temp_ly_log_opts & LY_LOLOG;
616
7.39k
        lostore = *temp_ly_log_opts & LY_LOSTORE;
617
167k
    } else {
618
167k
        lolog = ATOMIC_LOAD_RELAXED(ly_log_opts) & LY_LOLOG;
619
167k
        lostore = ATOMIC_LOAD_RELAXED(ly_log_opts) & LY_LOSTORE;
620
167k
    }
621
622
175k
    if (level > ATOMIC_LOAD_RELAXED(ly_ll)) {
623
        /* do not print or store the message */
624
107k
        free(path);
625
107k
        return;
626
107k
    }
627
628
68.2k
    if (no == LY_EMEM) {
629
        /* just print it, anything else would most likely fail anyway */
630
0
        if (lolog) {
631
0
            if (log_clb) {
632
0
                log_clb(level, LY_EMEM_MSG, path);
633
0
            } else {
634
0
                fprintf(stderr, "libyang[%d]: ", level);
635
0
                vfprintf(stderr, format, args);
636
0
                if (path) {
637
0
                    fprintf(stderr, " (path: %s)\n", path);
638
0
                } else {
639
0
                    fprintf(stderr, "\n");
640
0
                }
641
0
            }
642
0
        }
643
0
        free(path);
644
0
        return;
645
0
    }
646
647
    /* print into a single message */
648
68.2k
    if (vasprintf(&msg, format, args) == -1) {
649
0
        LOGMEM(ctx);
650
0
        free(path);
651
0
        return;
652
0
    }
653
654
    /* store as the last message */
655
68.2k
    strncpy(last_msg, msg, LY_LAST_MSG_SIZE - 1);
656
657
    /* store the error/warning in the context (if we need to store errors internally, it does not matter what are
658
     * the user log options) */
659
68.2k
    if ((level < LY_LLVRB) && ctx && lostore) {
660
0
        if (((no & ~LY_EPLUGIN) == LY_EVALID) && (vecode == LYVE_SUCCESS)) {
661
            /* assume we are inheriting the error, so inherit vecode as well */
662
0
            vecode = ly_vecode(ctx);
663
0
        }
664
0
        if (log_store(ctx, level, no, vecode, msg, path, apptag ? strdup(apptag) : NULL)) {
665
0
            return;
666
0
        }
667
0
        free_strs = 0;
668
68.2k
    } else {
669
68.2k
        free_strs = 1;
670
68.2k
    }
671
672
    /* if we are only storing errors internally, never print the message (yet) */
673
68.2k
    if (lolog) {
674
0
        if (log_clb) {
675
0
            log_clb(level, msg, path);
676
0
        } else {
677
0
            fprintf(stderr, "libyang[%d]: %s%s", level, msg, path ? " " : "\n");
678
0
            if (path) {
679
0
                fprintf(stderr, "(path: %s)\n", path);
680
0
            }
681
0
        }
682
0
    }
683
684
68.2k
    if (free_strs) {
685
68.2k
        free(path);
686
68.2k
        free(msg);
687
68.2k
    }
688
68.2k
}
689
690
#ifndef NDEBUG
691
692
void
693
ly_log_dbg(uint32_t group, const char *format, ...)
694
45.5M
{
695
45.5M
    char *dbg_format;
696
45.5M
    const char *str_group;
697
45.5M
    va_list ap;
698
699
45.5M
    if (!(ATOMIC_LOAD_RELAXED(ly_ldbg_groups) & group)) {
700
45.5M
        return;
701
45.5M
    }
702
703
0
    switch (group) {
704
0
    case LY_LDGDICT:
705
0
        str_group = "DICT";
706
0
        break;
707
0
    case LY_LDGXPATH:
708
0
        str_group = "XPATH";
709
0
        break;
710
0
    case LY_LDGDEPSETS:
711
0
        str_group = "DEPSETS";
712
0
        break;
713
0
    default:
714
0
        LOGINT(NULL);
715
0
        return;
716
0
    }
717
718
0
    if (asprintf(&dbg_format, "%s: %s", str_group, format) == -1) {
719
0
        LOGMEM(NULL);
720
0
        return;
721
0
    }
722
723
0
    va_start(ap, format);
724
0
    log_vprintf(NULL, LY_LLDBG, 0, 0, NULL, NULL, dbg_format, ap);
725
0
    va_end(ap);
726
727
0
    free(dbg_format);
728
0
}
729
730
#endif
731
732
void
733
ly_log(const struct ly_ctx *ctx, LY_LOG_LEVEL level, LY_ERR no, const char *format, ...)
734
144k
{
735
144k
    va_list ap;
736
737
144k
    va_start(ap, format);
738
144k
    log_vprintf(ctx, level, no, 0, NULL, NULL, format, ap);
739
144k
    va_end(ap);
740
144k
}
741
742
/**
743
 * @brief Append a schema node name to a generated data path, only if it fits.
744
 *
745
 * @param[in,out] str Generated path to update.
746
 * @param[in] snode Schema node to append.
747
 * @param[in] parent Last printed data node.
748
 * @return LY_ERR value.
749
 */
750
static LY_ERR
751
ly_vlog_build_path_append(char **str, const struct lysc_node *snode, const struct lyd_node *parent)
752
153
{
753
153
    const struct lys_module *mod, *prev_mod;
754
153
    uint32_t len, new_len;
755
153
    void *mem;
756
757
153
    if (snode->nodetype & (LYS_CHOICE | LYS_CASE)) {
758
        /* schema-only node */
759
0
        return LY_SUCCESS;
760
153
    } else if (lysc_data_parent(snode) != lyd_node_schema(parent)) {
761
        /* not a direct descendant node */
762
128
        return LY_SUCCESS;
763
128
    }
764
765
    /* get module to print, if any */
766
25
    mod = snode->module;
767
25
    prev_mod = lyd_node_module(parent);
768
25
    if (prev_mod == mod) {
769
13
        mod = NULL;
770
13
    }
771
772
    /* realloc string */
773
25
    len = *str ? strlen(*str) : 0;
774
25
    new_len = len + 1 + (mod ? strlen(mod->name) + 1 : 0) + strlen(snode->name);
775
25
    mem = realloc(*str, new_len + 1);
776
25
    LY_CHECK_ERR_RET(!mem, LOGMEM(LYD_CTX(parent)), LY_EMEM);
777
25
    *str = mem;
778
779
    /* print the last schema node */
780
25
    sprintf(*str + len, "/%s%s%s", mod ? mod->name : "", mod ? ":" : "", snode->name);
781
25
    return LY_SUCCESS;
782
25
}
783
784
LY_ERR
785
ly_vlog_build_data_path(const struct ly_ctx *ctx, char **path)
786
2.54k
{
787
2.54k
    LY_ERR rc = LY_SUCCESS;
788
2.54k
    const struct lyd_node *dnode = NULL;
789
790
2.54k
    *path = NULL;
791
792
2.54k
    if (log_location.dnodes.count) {
793
2.53k
        dnode = log_location.dnodes.objs[log_location.dnodes.count - 1];
794
2.53k
        if (dnode->parent || !lysc_data_parent(dnode->schema)) {
795
            /* data node with all of its parents */
796
2.26k
            *path = lyd_path(log_location.dnodes.objs[log_location.dnodes.count - 1], LYD_PATH_STD, NULL, 0);
797
2.26k
            LY_CHECK_ERR_GOTO(!*path, LOGMEM(ctx); rc = LY_EMEM, cleanup);
798
2.26k
        } else {
799
            /* data parsers put all the parent nodes in the set, but they are not connected */
800
271
            *path = lyd_path_set(&log_location.dnodes, LYD_PATH_STD);
801
271
            LY_CHECK_ERR_GOTO(!*path, LOGMEM(ctx); rc = LY_EMEM, cleanup);
802
271
        }
803
2.53k
    }
804
805
    /* sometimes the last node is not created yet and we only have the schema node */
806
2.54k
    if (log_location.scnodes.count) {
807
153
        rc = ly_vlog_build_path_append(path, log_location.scnodes.objs[log_location.scnodes.count - 1], dnode);
808
153
        LY_CHECK_GOTO(rc, cleanup);
809
153
    }
810
811
2.54k
cleanup:
812
2.54k
    if (rc) {
813
0
        free(*path);
814
0
        *path = NULL;
815
0
    }
816
2.54k
    return rc;
817
2.54k
}
818
819
/**
820
 * @brief Build log path from the stored log location information.
821
 *
822
 * @param[in] ctx Context to use.
823
 * @param[out] path Generated log path.
824
 * @return LY_ERR value.
825
 */
826
static LY_ERR
827
ly_vlog_build_path(const struct ly_ctx *ctx, char **path)
828
30.5k
{
829
30.5k
    int r;
830
30.5k
    char *str = NULL, *prev = NULL;
831
832
30.5k
    *path = NULL;
833
834
30.5k
    if (log_location.paths.count && ((const char *)(log_location.paths.objs[log_location.paths.count - 1]))[0]) {
835
        /* simply get what is in the provided path string */
836
12
        r = asprintf(path, "Path \"%s\"", (const char *)log_location.paths.objs[log_location.paths.count - 1]);
837
12
        LY_CHECK_ERR_RET(r == -1, LOGMEM(ctx), LY_EMEM);
838
30.5k
    } else {
839
        /* data/schema node */
840
30.5k
        if (log_location.dnodes.count) {
841
2.53k
            LY_CHECK_RET(ly_vlog_build_data_path(ctx, &str));
842
843
2.53k
            r = asprintf(path, "Data location \"%s\"", str);
844
2.53k
            free(str);
845
2.53k
            LY_CHECK_ERR_RET(r == -1, LOGMEM(ctx), LY_EMEM);
846
28.0k
        } else if (log_location.scnodes.count) {
847
9.42k
            str = lysc_path(log_location.scnodes.objs[log_location.scnodes.count - 1], LYSC_PATH_LOG, NULL, 0);
848
9.42k
            LY_CHECK_ERR_RET(!str, LOGMEM(ctx), LY_EMEM);
849
850
9.42k
            r = asprintf(path, "Schema location \"%s\"", str);
851
9.42k
            free(str);
852
9.42k
            LY_CHECK_ERR_RET(r == -1, LOGMEM(ctx), LY_EMEM);
853
9.42k
        }
854
30.5k
    }
855
856
    /* line */
857
30.5k
    prev = *path;
858
30.5k
    if (log_location.line) {
859
0
        r = asprintf(path, "%s%sine number %" PRIu64, prev ? prev : "", prev ? ", l" : "L", log_location.line);
860
0
        free(prev);
861
0
        LY_CHECK_ERR_RET(r == -1, LOGMEM(ctx), LY_EMEM);
862
863
0
        log_location.line = 0;
864
30.5k
    } else if (log_location.inputs.count) {
865
30.2k
        r = asprintf(path, "%s%sine number %" PRIu64, prev ? prev : "", prev ? ", l" : "L",
866
30.2k
                ((struct ly_in *)log_location.inputs.objs[log_location.inputs.count - 1])->line);
867
30.2k
        free(prev);
868
30.2k
        LY_CHECK_ERR_RET(r == -1, LOGMEM(ctx), LY_EMEM);
869
30.2k
    }
870
871
30.5k
    if (*path) {
872
30.5k
        prev = *path;
873
30.5k
        r = asprintf(path, "%s.", prev);
874
30.5k
        free(prev);
875
30.5k
        LY_CHECK_ERR_RET(r == -1, LOGMEM(ctx), LY_EMEM);
876
30.5k
    }
877
878
30.5k
    return LY_SUCCESS;
879
30.5k
}
880
881
void
882
ly_vlog(const struct ly_ctx *ctx, const char *apptag, LY_VECODE code, const char *format, ...)
883
30.5k
{
884
30.5k
    va_list ap;
885
30.5k
    char *path = NULL;
886
887
30.5k
    if (ATOMIC_LOAD_RELAXED(path_flag) && ctx) {
888
30.5k
        ly_vlog_build_path(ctx, &path);
889
30.5k
    }
890
891
30.5k
    va_start(ap, format);
892
30.5k
    log_vprintf(ctx, LY_LLERR, LY_EVALID, code, path, apptag, format, ap);
893
    /* path is spent and should not be freed! */
894
30.5k
    va_end(ap);
895
30.5k
}
896
897
/**
898
 * @brief Print a log message from an extension plugin callback.
899
 *
900
 * @param[in] ctx libyang context to store the error record. If not provided, the error is just printed.
901
 * @param[in] plugin_name Name of the plugin generating the message.
902
 * @param[in] level Log message level (error, warning, etc.)
903
 * @param[in] err_no Error type code.
904
 * @param[in] path Optional path of the error, used if set.
905
 * @param[in] format Format string to print.
906
 * @param[in] ap Var arg list for @p format.
907
 */
908
static void
909
ly_ext_log(const struct ly_ctx *ctx, const char *plugin_name, LY_LOG_LEVEL level, LY_ERR err_no, char *path,
910
        const char *format, va_list ap)
911
0
{
912
0
    char *plugin_msg;
913
914
0
    if (ATOMIC_LOAD_RELAXED(ly_ll) < level) {
915
0
        return;
916
0
    }
917
0
    if (asprintf(&plugin_msg, "Ext plugin \"%s\": %s", plugin_name, format) == -1) {
918
0
        LOGMEM(ctx);
919
0
        return;
920
0
    }
921
922
0
    log_vprintf(ctx, level, (level == LY_LLERR ? LY_EPLUGIN : 0) | err_no, LYVE_OTHER, path, NULL, plugin_msg, ap);
923
0
    free(plugin_msg);
924
0
}
925
926
LIBYANG_API_DEF void
927
lyplg_ext_parse_log(const struct lysp_ctx *pctx, const struct lysp_ext_instance *ext, LY_LOG_LEVEL level, LY_ERR err_no,
928
        const char *format, ...)
929
0
{
930
0
    va_list ap;
931
0
    char *path = NULL;
932
933
0
    if (ATOMIC_LOAD_RELAXED(path_flag)) {
934
0
        ly_vlog_build_path(PARSER_CTX(pctx), &path);
935
0
    }
936
937
0
    va_start(ap, format);
938
0
    ly_ext_log(PARSER_CTX(pctx), ext->record->plugin.id, level, err_no, path, format, ap);
939
0
    va_end(ap);
940
0
}
941
942
LIBYANG_API_DEF void
943
lyplg_ext_compile_log(const struct lysc_ctx *cctx, const struct lysc_ext_instance *ext, LY_LOG_LEVEL level, LY_ERR err_no,
944
        const char *format, ...)
945
0
{
946
0
    va_list ap;
947
0
    char *path = NULL;
948
949
0
    if (cctx && (asprintf(&path, "Path \"%s\".", cctx->path) == -1)) {
950
0
        LOGMEM(cctx->ctx);
951
0
        return;
952
0
    }
953
954
0
    va_start(ap, format);
955
0
    ly_ext_log(ext->module->ctx, ext->def->plugin->id, level, err_no, path, format, ap);
956
0
    va_end(ap);
957
0
}
958
959
LIBYANG_API_DEF void
960
lyplg_ext_compile_log_path(const char *path, const struct lysc_ext_instance *ext, LY_LOG_LEVEL level, LY_ERR err_no,
961
        const char *format, ...)
962
0
{
963
0
    va_list ap;
964
0
    char *log_path = NULL;
965
966
0
    if (path && (asprintf(&log_path, "Path \"%s\".", path) == -1)) {
967
0
        LOGMEM(ext->module->ctx);
968
0
        return;
969
0
    }
970
971
0
    va_start(ap, format);
972
0
    ly_ext_log(ext->module->ctx, ext->def->plugin->id, level, err_no, log_path, format, ap);
973
0
    va_end(ap);
974
0
}
975
976
/**
977
 * @brief Serves only for creating ap.
978
 */
979
static void
980
_lyplg_ext_compile_log_err(const struct ly_err_item *err, const struct lysc_ext_instance *ext, ...)
981
0
{
982
0
    va_list ap;
983
984
0
    va_start(ap, ext);
985
0
    ly_ext_log(ext->module->ctx, ext->def->plugin->id, err->level, err->no, err->path ? strdup(err->path) : NULL, "%s", ap);
986
0
    va_end(ap);
987
0
}
988
989
LIBYANG_API_DEF void
990
lyplg_ext_compile_log_err(const struct ly_err_item *err, const struct lysc_ext_instance *ext)
991
0
{
992
0
    _lyplg_ext_compile_log_err(err, ext, err->msg);
993
0
}
994
995
/**
996
 * @brief Exact same functionality as ::ly_err_print() but has variable arguments so log_vprintf() can be called.
997
 */
998
static void
999
_ly_err_print(const struct ly_ctx *ctx, struct ly_err_item *eitem, const char *format, ...)
1000
0
{
1001
0
    va_list ap;
1002
0
    char *path_dup = NULL;
1003
1004
0
    LY_CHECK_ARG_RET(ctx, eitem, );
1005
1006
0
    if (eitem->path) {
1007
        /* duplicate path because it will be freed */
1008
0
        path_dup = strdup(eitem->path);
1009
0
        LY_CHECK_ERR_RET(!path_dup, LOGMEM(ctx), );
1010
0
    }
1011
1012
0
    va_start(ap, format);
1013
0
    log_vprintf(ctx, eitem->level, eitem->no, eitem->vecode, path_dup, eitem->apptag, format, ap);
1014
0
    va_end(ap);
1015
0
}
1016
1017
LIBYANG_API_DEF void
1018
ly_err_print(const struct ly_ctx *ctx, struct ly_err_item *eitem)
1019
0
{
1020
    /* String ::ly_err_item.msg cannot be used directly because it may contain the % character */
1021
0
    _ly_err_print(ctx, eitem, "%s", eitem->msg);
1022
0
}