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 | } |