Coverage Report

Created: 2025-12-14 06:43

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