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