Coverage Report

Created: 2026-02-26 06:35

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
 * @author Michal Vasko <mvasko@cesnet.cz>
5
 * @brief Logger routines implementations
6
 *
7
 * Copyright (c) 2015 - 2026 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 "compat.h"
30
#include "in_internal.h"
31
#include "ly_common.h"
32
#include "plugins_exts.h"
33
#include "plugins_internal.h"
34
#include "set.h"
35
#include "tree_data.h"
36
#include "tree_data_internal.h"
37
#include "tree_schema.h"
38
#include "tree_schema_internal.h"
39
40
ATOMIC_T ly_ll = (uint_fast32_t)LY_LLWRN;
41
ATOMIC_T ly_log_opts = (uint_fast32_t)(LY_LOLOG | LY_LOSTORE_LAST);
42
THREAD_LOCAL uint32_t *temp_ly_log_opts;
43
static ly_log_clb log_clb;
44
THREAD_LOCAL ly_log_clb temp_log_clb;
45
THREAD_LOCAL char last_msg[LY_LAST_MSG_SIZE];
46
#ifndef NDEBUG
47
ATOMIC_T ly_ldbg_groups = 0;
48
#endif
49
50
THREAD_LOCAL struct ly_log_location_s log_location = {0};
51
52
LIBYANG_API_DEF const char *
53
ly_strerr(LY_ERR err)
54
0
{
55
    /* ignore plugin flag */
56
0
    err &= ~LY_EPLUGIN;
57
58
0
    switch (err) {
59
0
    case LY_SUCCESS:
60
0
        return "Success";
61
0
    case LY_EMEM:
62
0
        return "Out of memory";
63
0
    case LY_ESYS:
64
0
        return "System call failed";
65
0
    case LY_EINVAL:
66
0
        return "Invalid value";
67
0
    case LY_EEXIST:
68
0
        return "Already exists";
69
0
    case LY_ENOTFOUND:
70
0
        return "Not found";
71
0
    case LY_EINT:
72
0
        return "Internal error";
73
0
    case LY_EVALID:
74
0
        return "Validation failed";
75
0
    case LY_EDENIED:
76
0
        return "Operation denied";
77
0
    case LY_EINCOMPLETE:
78
0
        return "Operation incomplete";
79
0
    case LY_ERECOMPILE:
80
0
        return "Recompilation required";
81
0
    case LY_ENOT:
82
0
        return "Negative result";
83
0
    case LY_EOTHER:
84
0
        return "Another failure reason";
85
0
    case LY_EPLUGIN:
86
0
        break;
87
0
    }
88
89
    /* unreachable */
90
0
    return "Unknown";
91
0
}
92
93
LIBYANG_API_DEF const char *
94
ly_strvecode(LY_VECODE vecode)
95
0
{
96
0
    switch (vecode) {
97
0
    case LYVE_SUCCESS:
98
0
        return "Success";
99
0
    case LYVE_SYNTAX:
100
0
        return "General syntax error";
101
0
    case LYVE_SYNTAX_YANG:
102
0
        return "YANG syntax error";
103
0
    case LYVE_SYNTAX_YIN:
104
0
        return "YIN syntax error";
105
0
    case LYVE_REFERENCE:
106
0
        return "Reference error";
107
0
    case LYVE_XPATH:
108
0
        return "XPath error";
109
0
    case LYVE_SEMANTICS:
110
0
        return "Semantic error";
111
0
    case LYVE_SYNTAX_XML:
112
0
        return "XML syntax error";
113
0
    case LYVE_SYNTAX_JSON:
114
0
        return "JSON syntax error";
115
0
    case LYVE_DATA:
116
0
        return "YANG data error";
117
0
    case LYVE_OTHER:
118
0
        return "Another error";
119
0
    }
120
121
    /* unreachable */
122
0
    return "Unknown";
123
0
}
124
125
LIBYANG_API_DEF const char *
126
ly_last_logmsg(void)
127
0
{
128
0
    return last_msg;
129
0
}
130
131
LIBYANG_API_DEF LY_ERR
132
ly_err_new(struct ly_err_item **err, LY_ERR ecode, LY_VECODE vecode, char *data_path, char *apptag,
133
        const char *err_format, ...)
134
297k
{
135
297k
    char *msg = NULL;
136
297k
    struct ly_err_item *e;
137
138
297k
    if (!err || (ecode == LY_SUCCESS)) {
139
        /* nothing to do */
140
0
        return ecode;
141
0
    }
142
143
297k
    e = calloc(1, sizeof *e);
144
297k
    LY_CHECK_ERR_RET(!e, LOGMEM(NULL), LY_EMEM);
145
146
297k
    e->prev = (*err) ? (*err)->prev : e;
147
297k
    if (*err) {
148
0
        (*err)->prev->next = e;
149
0
    }
150
151
    /* fill in the information */
152
297k
    e->level = LY_LLERR;
153
297k
    e->err = ecode;
154
297k
    e->vecode = vecode;
155
297k
    e->data_path = data_path;
156
297k
    e->apptag = apptag;
157
158
297k
    if (err_format) {
159
297k
        va_list print_args;
160
161
297k
        va_start(print_args, err_format);
162
163
297k
        if (vasprintf(&msg, err_format, print_args) == -1) {
164
            /* we don't have anything more to do, just set msg to NULL to avoid undefined content,
165
             * still keep the information about the original error instead of LY_EMEM or other printf's error */
166
0
            msg = NULL;
167
0
        }
168
169
297k
        va_end(print_args);
170
297k
    }
171
297k
    e->msg = msg;
172
173
297k
    if (!(*err)) {
174
297k
        *err = e;
175
297k
    }
176
177
297k
    return e->err;
178
297k
}
179
180
LIBYANG_API_DEF const struct ly_err_item *
181
ly_err_first(const struct ly_ctx *ctx)
182
0
{
183
0
    struct ly_ctx_private_data *ctx_data;
184
185
0
    LY_CHECK_ARG_RET(NULL, ctx, NULL);
186
187
0
    ctx_data = ly_ctx_private_data_get_or_create(ctx);
188
0
    LY_CHECK_RET(!ctx_data, NULL);
189
0
    return ctx_data->errs;
190
0
}
191
192
LIBYANG_API_DEF const struct ly_err_item *
193
ly_err_last(const struct ly_ctx *ctx)
194
264k
{
195
264k
    struct ly_ctx_private_data *ctx_data;
196
197
264k
    LY_CHECK_ARG_RET(NULL, ctx, NULL);
198
199
264k
    ctx_data = ly_ctx_private_data_get_or_create(ctx);
200
264k
    LY_CHECK_RET(!ctx_data, NULL);
201
264k
    return ctx_data->errs ? ctx_data->errs->prev : NULL;
202
264k
}
203
204
LIBYANG_API_DEF void
205
ly_err_free(void *ptr)
206
436k
{
207
436k
    struct ly_err_item *e, *next;
208
209
    /* clean the error list */
210
436k
    LY_LIST_FOR_SAFE(ptr, next, e) {
211
297k
        free(e->msg);
212
297k
        free(e->data_path);
213
297k
        free(e->schema_path);
214
297k
        free(e->apptag);
215
297k
        free(e);
216
297k
    }
217
436k
}
218
219
LIBYANG_API_DEF void
220
ly_err_clean(const struct ly_ctx *ctx, struct ly_err_item *eitem)
221
24
{
222
24
    struct ly_ctx_private_data *ctx_data;
223
24
    struct ly_err_item *e;
224
225
24
    if (!ctx) {
226
0
        return;
227
0
    }
228
229
24
    ctx_data = ly_ctx_private_data_get_or_create(ctx);
230
24
    if (ctx_data->errs == eitem) {
231
0
        eitem = NULL;
232
0
    }
233
234
24
    if (!eitem) {
235
        /* free all err */
236
24
        ly_err_free(ctx_data->errs);
237
24
        ctx_data->errs = NULL;
238
24
    } else {
239
        /* disconnect the error */
240
0
        for (e = ctx_data->errs; e && (e->next != eitem); e = e->next) {}
241
0
        assert(e);
242
0
        e->next = NULL;
243
0
        ctx_data->errs->prev = e;
244
245
        /* free this err and newer */
246
0
        ly_err_free(eitem);
247
0
    }
248
24
}
249
250
LIBYANG_API_DEF LY_LOG_LEVEL
251
ly_log_level(LY_LOG_LEVEL level)
252
0
{
253
0
    LY_LOG_LEVEL prev = ATOMIC_LOAD_RELAXED(ly_ll);
254
255
0
    ATOMIC_STORE_RELAXED(ly_ll, level);
256
0
    return prev;
257
0
}
258
259
LIBYANG_API_DEF uint32_t
260
ly_log_options(uint32_t opts)
261
1
{
262
1
    uint32_t prev = ATOMIC_LOAD_RELAXED(ly_log_opts);
263
264
1
    ATOMIC_STORE_RELAXED(ly_log_opts, opts);
265
1
    return prev;
266
1
}
267
268
LIBYANG_API_DEF uint32_t *
269
ly_temp_log_options(uint32_t *opts)
270
508k
{
271
508k
    uint32_t *prev_lo = temp_ly_log_opts;
272
273
508k
    temp_ly_log_opts = opts;
274
275
508k
    return prev_lo;
276
508k
}
277
278
LIBYANG_API_DEF uint32_t
279
ly_log_dbg_groups(uint32_t dbg_groups)
280
0
{
281
0
#ifndef NDEBUG
282
0
    uint32_t prev = ATOMIC_LOAD_RELAXED(ly_ldbg_groups);
283
284
0
    ATOMIC_STORE_RELAXED(ly_ldbg_groups, dbg_groups);
285
0
    return prev;
286
#else
287
    (void)dbg_groups;
288
    return 0;
289
#endif
290
0
}
291
292
LIBYANG_API_DEF void
293
ly_set_log_clb(ly_log_clb clb)
294
0
{
295
0
    log_clb = clb;
296
0
}
297
298
LIBYANG_API_DEF ly_log_clb
299
ly_get_log_clb(void)
300
0
{
301
0
    return log_clb;
302
0
}
303
304
LIBYANG_API_DEF ly_log_clb
305
ly_temp_log_clb(ly_log_clb clb)
306
0
{
307
0
    ly_log_clb prev_cb = temp_log_clb;
308
309
0
    temp_log_clb = clb;
310
311
0
    return prev_cb;
312
0
}
313
314
void
315
ly_log_location(const struct lysc_node *scnode, const char *path, const struct ly_in *in)
316
1.16M
{
317
1.16M
    if (scnode) {
318
225k
        ly_set_add(&log_location.scnodes, (void *)scnode, 1, NULL);
319
225k
    }
320
321
1.16M
    if (path) {
322
919k
        char *s = strdup(path);
323
324
919k
        LY_CHECK_ERR_RET(!s, LOGMEM(NULL), );
325
919k
        ly_set_add(&log_location.paths, s, 1, NULL);
326
919k
    }
327
328
1.16M
    if (in) {
329
20.7k
        ly_set_add(&log_location.inputs, in, 1, NULL);
330
20.7k
    }
331
1.16M
}
332
333
void
334
ly_log_location_revert(uint32_t scnode_steps, uint32_t path_steps, uint32_t in_steps)
335
1.26M
{
336
1.26M
    uint32_t i;
337
338
1.48M
    for (i = scnode_steps; i && log_location.scnodes.count; i--) {
339
225k
        log_location.scnodes.count--;
340
225k
    }
341
342
2.18M
    for (i = path_steps; i && log_location.paths.count; i--) {
343
919k
        ly_set_rm_index(&log_location.paths, log_location.paths.count - 1, free);
344
919k
    }
345
346
1.28M
    for (i = in_steps; i && log_location.inputs.count; i--) {
347
20.7k
        log_location.inputs.count--;
348
20.7k
    }
349
350
    /* deallocate the empty sets */
351
1.26M
    if (scnode_steps && !log_location.scnodes.count) {
352
117k
        ly_set_erase(&log_location.scnodes, NULL);
353
117k
    }
354
1.26M
    if (path_steps && !log_location.paths.count) {
355
969k
        ly_set_erase(&log_location.paths, free);
356
969k
    }
357
1.26M
    if (in_steps && !log_location.inputs.count) {
358
20.7k
        ly_set_erase(&log_location.inputs, NULL);
359
20.7k
    }
360
1.26M
}
361
362
/**
363
 * @brief Store generated error in a context.
364
 *
365
 * @param[in] ctx Context to use.
366
 * @param[in] level Message log level.
367
 * @param[in] err Error number.
368
 * @param[in] vecode Error validation error code.
369
 * @param[in] msg Error message, always spent.
370
 * @param[in] data_path Error data path, always spent.
371
 * @param[in] schema_path Error schema path, always spent.
372
 * @param[in] line Error input line, if any.
373
 * @param[in] apptag Error app tag, always spent.
374
 * @return LY_ERR value.
375
 */
376
static LY_ERR
377
log_store(const struct ly_ctx *ctx, LY_LOG_LEVEL level, LY_ERR err, LY_VECODE vecode, char *msg, char *data_path,
378
        char *schema_path, uint64_t line, char *apptag)
379
24
{
380
24
    struct ly_ctx_private_data *ctx_data;
381
24
    struct ly_err_item *e, *last;
382
383
24
    assert(ctx && (level < LY_LLVRB));
384
385
    /* get context private data */
386
24
    ctx_data = ly_ctx_private_data_get_or_create(ctx);
387
24
    LY_CHECK_GOTO(!ctx_data, mem_fail);
388
389
24
    e = ctx_data->errs;
390
24
    if (!e) {
391
        /* if we are only to fill in path, there must have been an error stored */
392
24
        assert(msg);
393
24
        e = calloc(1, sizeof *e);
394
24
        LY_CHECK_GOTO(!e, mem_fail);
395
24
        e->prev = e;
396
24
        e->next = NULL;
397
398
24
        ctx_data->errs = e;
399
24
    } else if (!msg) {
400
        /* only filling the path */
401
0
        assert(data_path || schema_path);
402
403
        /* find last error */
404
0
        e = e->prev;
405
0
        do {
406
0
            if (e->level == LY_LLERR) {
407
                /* fill the path */
408
0
                if (data_path) {
409
0
                    free(e->data_path);
410
0
                    e->data_path = data_path;
411
0
                } else {
412
0
                    free(e->schema_path);
413
0
                    e->schema_path = schema_path;
414
0
                }
415
0
                return LY_SUCCESS;
416
0
            }
417
0
            e = e->prev;
418
0
        } while (e->prev->next);
419
        /* last error was not found */
420
0
        assert(0);
421
0
    } else if ((temp_ly_log_opts && ((*temp_ly_log_opts & LY_LOSTORE_LAST) == LY_LOSTORE_LAST)) ||
422
0
            (!temp_ly_log_opts && ((ATOMIC_LOAD_RELAXED(ly_log_opts) & LY_LOSTORE_LAST) == LY_LOSTORE_LAST))) {
423
        /* overwrite last message */
424
0
        free(e->msg);
425
0
        free(e->data_path);
426
0
        free(e->schema_path);
427
0
        free(e->apptag);
428
0
    } else {
429
        /* store new message */
430
0
        last = e->prev;
431
0
        e->prev = calloc(1, sizeof *e);
432
0
        LY_CHECK_GOTO(!e->prev, mem_fail);
433
0
        e = e->prev;
434
0
        e->prev = last;
435
0
        e->next = NULL;
436
0
        last->next = e;
437
0
    }
438
439
    /* fill in the information */
440
24
    e->level = level;
441
24
    e->err = err;
442
24
    e->vecode = vecode;
443
24
    e->msg = msg;
444
24
    e->data_path = data_path;
445
24
    e->schema_path = schema_path;
446
24
    e->line = line;
447
24
    e->apptag = apptag;
448
24
    return LY_SUCCESS;
449
450
0
mem_fail:
451
0
    LOGMEM(NULL);
452
0
    free(msg);
453
0
    free(data_path);
454
0
    free(schema_path);
455
0
    free(apptag);
456
0
    return LY_EMEM;
457
24
}
458
459
/**
460
 * @brief Log data path/schema path/line to stderr after the message has been printed.
461
 *
462
 * @param[in] data_path Error data path.
463
 * @param[in] schema_path Error schema path.
464
 * @param[in] line Error input line.
465
 */
466
static void
467
log_stderr_path_line(const char *data_path, const char *schema_path, uint64_t line)
468
0
{
469
0
    ly_bool par = 0;
470
471
0
    if (data_path) {
472
0
        fprintf(stderr, "%sdata path: %s", " (", data_path);
473
0
        par = 1;
474
0
    }
475
476
0
    if (schema_path) {
477
0
        fprintf(stderr, "%sschemadata path: %s", par ? ", " : " (", schema_path);
478
0
        par = 1;
479
0
    }
480
481
0
    if (line) {
482
0
        fprintf(stderr, "%sline: %" PRIu64, par ? ", " : " (", line);
483
0
        par = 1;
484
0
    }
485
486
0
    fprintf(stderr, par ? ")\n" : "\n");
487
0
}
488
489
/**
490
 * @brief Learn whether a log is a no-operation or must be produced, based on current ly_log_opts.
491
 *
492
 * @param[out] will_log Optionally learn whether the log will be printed.
493
 * @param[out] will_store Optionally learn whether the log will be stored.
494
 * @return 1 if the log is a no-operation, 0 otherwise.
495
 */
496
static ly_bool
497
log_is_noop(ly_bool *will_log, ly_bool *will_store)
498
94.7k
{
499
94.7k
    ly_bool lolog, lostore;
500
501
    /* learn effective logger options */
502
94.7k
    if (temp_ly_log_opts) {
503
63.7k
        lolog = *temp_ly_log_opts & LY_LOLOG;
504
63.7k
        lostore = *temp_ly_log_opts & LY_LOSTORE;
505
63.7k
    } else {
506
30.9k
        lolog = ATOMIC_LOAD_RELAXED(ly_log_opts) & LY_LOLOG;
507
30.9k
        lostore = ATOMIC_LOAD_RELAXED(ly_log_opts) & LY_LOSTORE;
508
30.9k
    }
509
510
94.7k
    if (will_log) {
511
94.7k
        *will_log = lolog;
512
94.7k
    }
513
94.7k
    if (will_store) {
514
94.7k
        *will_store = lostore;
515
94.7k
    }
516
517
94.7k
    return !lolog && !lostore;
518
94.7k
}
519
520
/**
521
 * @brief Log a message.
522
 *
523
 * @param[in] ctx Context to use.
524
 * @param[in] level Message log level.
525
 * @param[in] err Error number.
526
 * @param[in] vecode Error validation error code.
527
 * @param[in] data_path Error data path, always spent.
528
 * @param[in] schema_path Error schema path, always spent.
529
 * @param[in] line Error input line, if any.
530
 * @param[in] apptag Error app tag.
531
 * @param[in] format Error message format.
532
 * @param[in] args Error message format arguments.
533
 */
534
static void
535
log_vprintf(const struct ly_ctx *ctx, LY_LOG_LEVEL level, LY_ERR err, LY_VECODE vecode, char *data_path,
536
        char *schema_path, uint64_t line, const char *apptag, const char *format, va_list args)
537
94.7k
{
538
94.7k
    char *dyn_msg = NULL;
539
94.7k
    const char *msg;
540
94.7k
    ly_bool free_strs = 1, lolog, lostore;
541
542
94.7k
    if (log_is_noop(&lolog, &lostore)) {
543
        /* do not print or store the message */
544
94.7k
        goto cleanup;
545
94.7k
    }
546
547
24
    if (err == LY_EMEM) {
548
        /* no not use more dynamic memory */
549
0
        vsnprintf(last_msg, LY_LAST_MSG_SIZE, format, args);
550
0
        msg = last_msg;
551
24
    } else {
552
        /* print into a single message */
553
24
        if (vasprintf(&dyn_msg, format, args) == -1) {
554
0
            LOGMEM(ctx);
555
0
            goto cleanup;
556
0
        }
557
24
        msg = dyn_msg;
558
559
        /* store as the last message */
560
24
        strncpy(last_msg, msg, LY_LAST_MSG_SIZE - 1);
561
24
    }
562
563
    /* store the error/warning in the context (if we need to store errors internally, it does not matter what are
564
     * the user log options), if the message is not dynamic, it would most likely fail to store (no memory) */
565
24
    if ((level <= ATOMIC_LOAD_RELAXED(ly_ll)) && (level < LY_LLVRB) && ctx && lostore && dyn_msg) {
566
24
        free_strs = 0;
567
24
        if (log_store(ctx, level, err, vecode, dyn_msg, data_path, schema_path, line, apptag ? strdup(apptag) : NULL)) {
568
0
            goto cleanup;
569
0
        }
570
24
    }
571
572
    /* if we are only storing errors internally, never print the message (yet) */
573
24
    if (lolog) {
574
0
        if (temp_log_clb) {
575
0
            temp_log_clb(level, msg, data_path, schema_path, line);
576
0
        } else if (log_clb) {
577
0
            log_clb(level, msg, data_path, schema_path, line);
578
0
        } else if (level <= ATOMIC_LOAD_RELAXED(ly_ll)) {
579
0
            fprintf(stderr, "libyang[%d]: ", level);
580
0
            fprintf(stderr, "%s", msg);
581
0
            log_stderr_path_line(data_path, schema_path, line);
582
0
        }
583
0
    }
584
585
94.7k
cleanup:
586
94.7k
    if (free_strs) {
587
94.7k
        free(data_path);
588
94.7k
        free(schema_path);
589
94.7k
        free(dyn_msg);
590
94.7k
    }
591
94.7k
}
592
593
#ifndef NDEBUG
594
595
void
596
ly_log_dbg(uint32_t group, const char *format, ...)
597
13.4M
{
598
13.4M
    char *dbg_format;
599
13.4M
    const char *str_group;
600
13.4M
    va_list ap;
601
602
13.4M
    if (!(ATOMIC_LOAD_RELAXED(ly_ldbg_groups) & group)) {
603
13.4M
        return;
604
13.4M
    }
605
606
0
    switch (group) {
607
0
    case LY_LDGDICT:
608
0
        str_group = "DICT";
609
0
        break;
610
0
    case LY_LDGXPATH:
611
0
        str_group = "XPATH";
612
0
        break;
613
0
    case LY_LDGDEPSETS:
614
0
        str_group = "DEPSETS";
615
0
        break;
616
0
    default:
617
0
        LOGINT(NULL);
618
0
        return;
619
0
    }
620
621
0
    if (asprintf(&dbg_format, "%s: %s", str_group, format) == -1) {
622
0
        LOGMEM(NULL);
623
0
        return;
624
0
    }
625
626
0
    va_start(ap, format);
627
0
    log_vprintf(NULL, LY_LLDBG, 0, 0, NULL, NULL, 0, NULL, dbg_format, ap);
628
0
    va_end(ap);
629
630
0
    free(dbg_format);
631
0
}
632
633
#endif
634
635
void
636
ly_log(const struct ly_ctx *ctx, LY_LOG_LEVEL level, LY_ERR err, const char *format, ...)
637
13.7k
{
638
13.7k
    va_list ap;
639
640
13.7k
    va_start(ap, format);
641
13.7k
    log_vprintf(ctx, level, err, 0, NULL, NULL, 0, NULL, format, ap);
642
13.7k
    va_end(ap);
643
13.7k
}
644
645
/**
646
 * @brief Build log path/input line from the stored log location information.
647
 *
648
 * @param[in] ctx Context to use.
649
 * @param[in] lnode Data node to use for logging.
650
 * @param[in] snode Explciti schema node to use for logging/append to @p lnode.
651
 * @param[out] data_path Generated data path.
652
 * @param[out] schema_path Generated data path.
653
 * @param[out] line Input line.
654
 * @return LY_ERR value.
655
 */
656
static LY_ERR
657
ly_vlog_build_path_line(const struct ly_ctx *ctx, const struct lyd_node *lnode, const struct lysc_node *snode,
658
        char **data_path, char **schema_path, uint64_t *line)
659
81.0k
{
660
81.0k
    int r;
661
81.0k
    char *path;
662
663
81.0k
    *data_path = NULL;
664
81.0k
    *schema_path = NULL;
665
81.0k
    *line = 0;
666
667
    /* data/schema node */
668
81.0k
    if (lnode) {
669
1.70k
        *data_path = lyd_path(lnode, LYD_PATH_STD, NULL, 0);
670
1.70k
        LY_CHECK_ERR_RET(!*data_path, LOGMEM(ctx), LY_EMEM);
671
672
1.70k
        if (!snode && log_location.scnodes.count) {
673
0
            snode = log_location.scnodes.objs[log_location.scnodes.count - 1];
674
0
        }
675
1.70k
        if (snode && (!lnode || (lysc_data_parent(snode) == lnode->schema))) {
676
25
            if (lyd_node_module(lnode) != snode->module) {
677
0
                r = asprintf(&path, "%s/%s:%s", *data_path, snode->module->name, snode->name);
678
25
            } else {
679
25
                r = asprintf(&path, "%s/%s", *data_path, snode->name);
680
25
            }
681
25
            free(*data_path);
682
25
            LY_CHECK_ERR_RET(r == -1, LOGMEM(ctx), LY_EMEM);
683
25
            *data_path = path;
684
25
            path = NULL;
685
25
        }
686
79.3k
    } else if (snode) {
687
281
        *schema_path = lysc_path(snode, LYSC_PATH_LOG, NULL, 0);
688
281
        LY_CHECK_ERR_RET(!*schema_path, LOGMEM(ctx), LY_EMEM);
689
79.0k
    } else if (log_location.scnodes.count) {
690
63.6k
        *schema_path = lysc_path(log_location.scnodes.objs[log_location.scnodes.count - 1], LYSC_PATH_LOG, NULL, 0);
691
63.6k
        LY_CHECK_ERR_RET(!*schema_path, LOGMEM(ctx), LY_EMEM);
692
63.6k
    }
693
694
81.0k
    if (log_location.paths.count && ((const char *)(log_location.paths.objs[log_location.paths.count - 1]))[0]) {
695
        /* append the provided path string to data/schema path, if any */
696
74
        if (*data_path) {
697
50
            r = asprintf(&path, "%s%s", *data_path, (char *)log_location.paths.objs[log_location.paths.count - 1]);
698
50
        } else if (*schema_path) {
699
0
            r = asprintf(&path, "%s%s", *schema_path, (char *)log_location.paths.objs[log_location.paths.count - 1]);
700
24
        } else {
701
24
            r = asprintf(&path, "%s", (char *)log_location.paths.objs[log_location.paths.count - 1]);
702
24
        }
703
74
        LY_CHECK_ERR_RET(r == -1, LOGMEM(ctx), LY_EMEM);
704
705
74
        if (*data_path) {
706
50
            free(*data_path);
707
50
            *data_path = path;
708
50
        } else {
709
24
            free(*schema_path);
710
24
            *schema_path = path;
711
24
        }
712
74
    }
713
714
    /* line */
715
81.0k
    if (log_location.inputs.count) {
716
80.9k
        *line = ((struct ly_in *)log_location.inputs.objs[log_location.inputs.count - 1])->line;
717
80.9k
    }
718
719
81.0k
    return LY_SUCCESS;
720
81.0k
}
721
722
void
723
ly_vlog(const struct ly_ctx *ctx, const char *apptag, const struct lyd_node *lnode, LY_VECODE code, const char *format, ...)
724
80.6k
{
725
80.6k
    va_list ap;
726
80.6k
    char *data_path = NULL, *schema_path = NULL;
727
80.6k
    uint64_t line = 0;
728
729
80.6k
    if (ctx) {
730
80.6k
        ly_vlog_build_path_line(ctx, lnode, NULL, &data_path, &schema_path, &line);
731
80.6k
    }
732
733
80.6k
    va_start(ap, format);
734
735
    /* spends paths */
736
80.6k
    log_vprintf(ctx, LY_LLERR, LY_EVALID, code, data_path, schema_path, line, apptag, format, ap);
737
738
80.6k
    va_end(ap);
739
80.6k
}
740
741
/**
742
 * @brief Print a log message from an extension plugin callback.
743
 *
744
 * @param[in] ctx libyang context to store the error record. If not provided, the error is just printed.
745
 * @param[in] plugin_name Name of the plugin generating the message.
746
 * @param[in] level Log message level (error, warning, etc.)
747
 * @param[in] err Error type code.
748
 * @param[in] data_path Error data path, always spent.
749
 * @param[in] schema_path Error schema path, always spent.
750
 * @param[in] line Error input line, if any.
751
 * @param[in] format Format string to print.
752
 * @param[in] ap Var arg list for @p format.
753
 */
754
static void
755
ly_ext_log(const struct ly_ctx *ctx, const char *plugin_name, LY_LOG_LEVEL level, LY_ERR err, char *data_path,
756
        char *schema_path, uint64_t line, const char *format, va_list ap)
757
0
{
758
0
    char *plugin_msg;
759
760
0
    if (level > ATOMIC_LOAD_RELAXED(ly_ll)) {
761
0
        return;
762
0
    }
763
764
0
    if (asprintf(&plugin_msg, "Ext plugin \"%s\": %s", plugin_name, format) == -1) {
765
0
        LOGMEM(ctx);
766
0
        return;
767
0
    }
768
769
0
    log_vprintf(ctx, level, (level == LY_LLERR ? LY_EPLUGIN : 0) | err, LYVE_OTHER, data_path, schema_path, line, NULL,
770
0
            plugin_msg, ap);
771
0
    free(plugin_msg);
772
0
}
773
774
LIBYANG_API_DEF void
775
lyplg_ext_parse_log(const struct lysp_ctx *pctx, const struct lysp_ext_instance *ext, LY_LOG_LEVEL level, LY_ERR err,
776
        const char *format, ...)
777
0
{
778
0
    va_list ap;
779
0
    char *data_path, *schema_path;
780
0
    uint64_t line;
781
782
0
    ly_vlog_build_path_line(PARSER_CTX(pctx), NULL, NULL, &data_path, &schema_path, &line);
783
784
0
    va_start(ap, format);
785
0
    ly_ext_log(PARSER_CTX(pctx), LYSC_GET_EXT_PLG(ext->plugin_ref)->id, level, err, data_path, schema_path, line, format, ap);
786
0
    va_end(ap);
787
0
}
788
789
LIBYANG_API_DEF void
790
lyplg_ext_compile_log(const struct lysc_ctx *cctx, const struct lysc_ext_instance *ext, LY_LOG_LEVEL level, LY_ERR err,
791
        const char *format, ...)
792
0
{
793
0
    va_list ap;
794
0
    char *schema_path = NULL;
795
796
0
    if (cctx && !(schema_path = strdup(cctx->path))) {
797
0
        LOGMEM(cctx->ctx);
798
0
        return;
799
0
    }
800
801
0
    va_start(ap, format);
802
0
    ly_ext_log(ext->module->ctx, LYSC_GET_EXT_PLG(ext->def->plugin_ref)->id, level, err, NULL, schema_path, 0, format, ap);
803
0
    va_end(ap);
804
0
}
805
806
LIBYANG_API_DEF void
807
lyplg_ext_compile_log_path(const char *path, const struct lysc_ext_instance *ext, LY_LOG_LEVEL level, LY_ERR err,
808
        const char *format, ...)
809
0
{
810
0
    va_list ap;
811
0
    char *schema_path = NULL;
812
813
0
    if (path && !(schema_path = strdup(path))) {
814
0
        LOGMEM(ext->module->ctx);
815
0
        return;
816
0
    }
817
818
0
    va_start(ap, format);
819
0
    ly_ext_log(ext->module->ctx, LYSC_GET_EXT_PLG(ext->def->plugin_ref)->id, level, err, NULL, schema_path, 0, format, ap);
820
0
    va_end(ap);
821
0
}
822
823
/**
824
 * @brief Serves only for creating ap.
825
 */
826
static void
827
_lyplg_ext_compile_log_err(const struct ly_err_item *eitem, const struct lysc_ext_instance *ext, ...)
828
0
{
829
0
    va_list ap;
830
0
    char *data_path = NULL, *schema_path = NULL;
831
832
0
    if (eitem->data_path) {
833
0
        data_path = strdup(eitem->data_path);
834
0
    }
835
0
    if (eitem->schema_path) {
836
0
        schema_path = strdup(eitem->schema_path);
837
0
    }
838
839
0
    va_start(ap, ext);
840
0
    ly_ext_log(ext->module->ctx, LYSC_GET_EXT_PLG(ext->def->plugin_ref)->id, eitem->level, eitem->err,
841
0
            data_path, schema_path, eitem->line, "%s", ap);
842
0
    va_end(ap);
843
0
}
844
845
LIBYANG_API_DEF void
846
lyplg_ext_compile_log_err(const struct ly_err_item *eitem, const struct lysc_ext_instance *ext)
847
0
{
848
0
    _lyplg_ext_compile_log_err(eitem, ext, eitem->msg);
849
0
}
850
851
/**
852
 * @brief Exact same functionality as ::ly_err_print() but has variable arguments so log_vprintf() can be called.
853
 */
854
static void
855
_ly_err_print(const struct ly_ctx *ctx, const struct ly_err_item *eitem, char *data_path, char *schema_path,
856
        uint64_t line, const char *format, ...)
857
383
{
858
383
    va_list ap;
859
860
383
    va_start(ap, format);
861
383
    log_vprintf(ctx, eitem->level, eitem->err, eitem->vecode, data_path, schema_path, line, eitem->apptag, format, ap);
862
383
    va_end(ap);
863
383
}
864
865
LIBYANG_API_DEF void
866
ly_err_print(const struct ly_ctx *ctx, const struct ly_err_item *eitem, const struct lyd_node *lnode,
867
        const struct lysc_node *snode)
868
383
{
869
383
    char *data_path = NULL, *schema_path = NULL;
870
383
    uint64_t line = 0;
871
872
383
    LY_CHECK_ARG_RET(ctx, eitem, );
873
874
383
    if (eitem->level > ATOMIC_LOAD_RELAXED(ly_ll)) {
875
0
        return;
876
0
    }
877
878
383
    ly_vlog_build_path_line(ctx, lnode, snode, &data_path, &schema_path, &line);
879
880
    /* string ::ly_err_item.msg cannot be used directly because it may contain the % character;
881
     * the paths are spent */
882
383
    _ly_err_print(ctx, eitem, data_path, schema_path, line, "%s", eitem->msg);
883
383
}