1 /*
2 * Copyright (C) 2011 Red Hat, Inc.
3 *
4 * All rights reserved.
5 *
6 * Author: Angus Salkeld <asalkeld@redhat.com>
7 *
8 * libqb is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU Lesser General Public License as published by
10 * the Free Software Foundation, either version 2.1 of the License, or
11 * (at your option) any later version.
12 *
13 * libqb is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with libqb. If not, see <http://www.gnu.org/licenses/>.
20 */
21 #include "os_base.h"
22 #include <ctype.h>
23 #ifdef HAVE_LINK_H
24 #include <link.h>
25 #endif /* HAVE_LINK_H */
26 #include <stdarg.h>
27 #include <pthread.h>
28 #include <stdarg.h>
29 #include <string.h>
30
31 #include <qb/qbdefs.h>
32 #include <qb/qblist.h>
33 #include <qb/qblog.h>
34 #include <qb/qbutil.h>
35 #include <qb/qbarray.h>
36 #include <qb/qbatomic.h>
37 #include "log_int.h"
38 #include "util_int.h"
39 #include <regex.h>
40
41 static struct qb_log_target conf[QB_LOG_TARGET_MAX];
42 static uint32_t conf_active_max = 0;
43 static int32_t in_logger = QB_FALSE;
44 static int32_t logger_inited = QB_FALSE;
45 static pthread_rwlock_t _listlock;
46 static qb_log_filter_fn _custom_filter_fn = NULL;
47
48 static QB_LIST_DECLARE(tags_head);
49 static QB_LIST_DECLARE(callsite_sections);
50
51 struct callsite_section {
52 struct qb_log_callsite *start;
53 struct qb_log_callsite *stop;
54 struct qb_list_head list;
55 };
56
57 static int32_t _log_target_enable(struct qb_log_target *t);
58 static void _log_target_disable(struct qb_log_target *t);
59 static void _log_filter_apply(struct callsite_section *sect,
60 uint32_t t, enum qb_log_filter_conf c,
61 enum qb_log_filter_type type,
62 const char *text,
63 regex_t *regex,
64 uint8_t high_priority, uint8_t low_priority);
65 static void _log_filter_apply_to_cs(struct qb_log_callsite *cs,
66 uint32_t t, enum qb_log_filter_conf c,
67 enum qb_log_filter_type type,
68 const char *text,
69 regex_t *regex,
70 uint8_t high_priority, uint8_t low_priority);
71
72 /* deprecated method of getting internal log messages */
73 static qb_util_log_fn_t old_internal_log_fn = NULL;
74 void
qb_util_set_log_function(qb_util_log_fn_t fn)75 qb_util_set_log_function(qb_util_log_fn_t fn)
76 {
77 old_internal_log_fn = fn;
78 }
79
80 static void
_log_free_filter(struct qb_log_filter * flt)81 _log_free_filter(struct qb_log_filter *flt)
82 {
83 if (flt->regex) {
84 regfree(flt->regex);
85 }
86 free(flt->regex);
87 free(flt->text);
88 free(flt);
89 }
90
91 static int32_t
_cs_matches_filter_(struct qb_log_callsite * cs,enum qb_log_filter_type type,const char * text,regex_t * regex,uint8_t high_priority,uint8_t low_priority)92 _cs_matches_filter_(struct qb_log_callsite *cs,
93 enum qb_log_filter_type type,
94 const char *text,
95 regex_t *regex,
96 uint8_t high_priority,
97 uint8_t low_priority)
98 {
99 int32_t match = QB_FALSE;
100 const char *offset = NULL;
101 const char *next = NULL;
102
103 if (cs->priority > low_priority ||
104 cs->priority < high_priority) {
105 return QB_FALSE;
106 }
107 if (strcmp(text, "*") == 0) {
108 return QB_TRUE;
109 }
110
111 switch (type) {
112 case QB_LOG_FILTER_FILE:
113 case QB_LOG_FILTER_FUNCTION:
114 next = text;
115 do {
116 char token[500];
117 offset = next;
118 next = strchrnul(offset, ',');
119 snprintf(token, 499, "%.*s", (int)(next - offset), offset);
120
121 if (type == QB_LOG_FILTER_FILE) {
122 match = (strcmp(cs->filename, token) == 0) ? 1 : 0;
123 } else {
124 match = (strcmp(cs->function, token) == 0) ? 1 : 0;
125 }
126 if (!match && next[0] != 0) {
127 next++;
128 }
129
130 } while (match == QB_FALSE && next != NULL && next[0] != 0);
131 break;
132 case QB_LOG_FILTER_FILE_REGEX:
133 next = next ? next : cs->filename;
134 /* fallthrough */
135 case QB_LOG_FILTER_FUNCTION_REGEX:
136 next = next ? next : cs->function;
137 /* fallthrough */
138 case QB_LOG_FILTER_FORMAT_REGEX:
139 next = next ? next : cs->format;
140
141 if (regex == NULL) {
142 return QB_FALSE;
143 }
144 match = regexec(regex, next, 0, NULL, 0);
145 if (match == 0) {
146 match = QB_TRUE;
147 } else {
148 match = QB_FALSE;
149 }
150 break;
151 case QB_LOG_FILTER_FORMAT:
152 if (strstr(cs->format, text)) {
153 match = QB_TRUE;
154 }
155 break;
156 }
157
158 return match;
159 }
160
161 /**
162 * @internal
163 * @brief Format a log message into a string buffer
164 *
165 * @param[out] str Destination buffer to contain formatted string
166 * @param[in] cs Callsite containing format to use
167 * @param[in] ap Variable arguments for format
168 */
169 /* suppress suggestion that we currently can do nothing better about
170 as the format specification is hidden in cs argument */
171 #ifdef HAVE_GCC_FORMAT_COMPLAINTS
172 #pragma GCC diagnostic push
173 #ifdef HAVE_GCC_MISSING_FORMAT_ATTRIBUTE
174 #pragma GCC diagnostic ignored "-Wmissing-format-attribute"
175 #endif
176 #ifdef HAVE_GCC_SUGGEST_ATTRIBUTE_FORMAT
177 #pragma GCC diagnostic ignored "-Wsuggest-attribute=format"
178 #endif
179 #endif
180 static inline void
cs_format(char * str,size_t maxlen,struct qb_log_callsite * cs,va_list ap)181 cs_format(char *str, size_t maxlen, struct qb_log_callsite *cs, va_list ap)
182 {
183 va_list ap_copy;
184 int len;
185
186 va_copy(ap_copy, ap);
187 len = vsnprintf(str, maxlen, cs->format, ap_copy);
188 va_end(ap_copy);
189
190 if (len > maxlen) {
191 len = maxlen;
192 }
193 if (str[len - 1] == '\n') {
194 str[len - 1] = '\0';
195 }
196 }
197 #ifdef HAVE_GCC_FORMAT_COMPLAINTS
198 #pragma GCC diagnostic pop
199 #endif
200
201 void
qb_log_real_va_(struct qb_log_callsite * cs,va_list ap)202 qb_log_real_va_(struct qb_log_callsite *cs, va_list ap)
203 {
204 int32_t found_threaded = QB_FALSE;
205 struct qb_log_target *t;
206 struct timespec tv;
207 enum qb_log_target_slot pos;
208 size_t max_line_length = 0;
209 int32_t formatted = QB_FALSE;
210 char buf[QB_LOG_MAX_LEN];
211 char *str = buf;
212 va_list ap_copy;
213
214 if (qb_atomic_int_compare_and_exchange(&in_logger, QB_FALSE, QB_TRUE) == QB_FALSE || cs == NULL) {
215 return;
216 }
217
218 /* 0 Work out the longest line length available */
219 for (pos = QB_LOG_TARGET_START; pos <= conf_active_max; pos++) {
220 t = &conf[pos];
221 if ((t->state == QB_LOG_STATE_ENABLED)
222 && qb_bit_is_set(cs->targets, pos)) {
223 if (t->max_line_length > max_line_length) {
224 max_line_length = t->max_line_length;
225 }
226 }
227 }
228
229 if (max_line_length > QB_LOG_MAX_LEN) {
230 str = malloc(max_line_length);
231 if (!str) {
232 return;
233 }
234 }
235
236 if (old_internal_log_fn &&
237 qb_bit_is_set(cs->tags, QB_LOG_TAG_LIBQB_MSG_BIT)) {
238 if (formatted == QB_FALSE) {
239 cs_format(str, max_line_length, cs, ap);
240 formatted = QB_TRUE;
241 }
242 qb_do_extended(str, QB_TRUE,
243 old_internal_log_fn(cs->filename, cs->lineno,
244 cs->priority, str));
245 }
246
247 qb_util_timespec_from_epoch_get(&tv);
248
249 /*
250 * 1 if we can find a threaded target that needs this log then post it
251 * 2 foreach non-threaded target call it's logger function
252 */
253 for (pos = QB_LOG_TARGET_START; pos <= conf_active_max; pos++) {
254 t = &conf[pos];
255 if ((t->state == QB_LOG_STATE_ENABLED)
256 && qb_bit_is_set(cs->targets, pos)) {
257 if (t->threaded) {
258 if (!found_threaded) {
259 found_threaded = QB_TRUE;
260 if (formatted == QB_FALSE) {
261 cs_format(str, max_line_length, cs, ap);
262 formatted = QB_TRUE;
263 }
264 }
265
266 } else if (t->vlogger) {
267 va_copy(ap_copy, ap);
268 t->vlogger(t->pos, cs, &tv, ap_copy);
269 va_end(ap_copy);
270
271 } else if (t->logger) {
272 if (formatted == QB_FALSE) {
273 cs_format(str, max_line_length, cs, ap);
274 formatted = QB_TRUE;
275 }
276 qb_do_extended(str, t->extended,
277 t->logger(t->pos, cs, &tv, str));
278 }
279 }
280 }
281
282 if (found_threaded) {
283 qb_log_thread_log_post(cs, &tv, str);
284 }
285 qb_atomic_int_set(&in_logger, QB_FALSE);
286
287 if (max_line_length > QB_LOG_MAX_LEN) {
288 free(str);
289 }
290 }
291
292 void
qb_log_real_(struct qb_log_callsite * cs,...)293 qb_log_real_(struct qb_log_callsite *cs, ...)
294 {
295 va_list ap;
296
297 va_start(ap, cs);
298 qb_log_real_va_(cs, ap);
299 va_end(ap);
300 }
301
302 void
qb_log_thread_log_write(struct qb_log_callsite * cs,struct timespec * timestamp,const char * buffer)303 qb_log_thread_log_write(struct qb_log_callsite *cs,
304 struct timespec *timestamp, const char *buffer)
305 {
306 struct qb_log_target *t;
307 enum qb_log_target_slot pos;
308
309 for (pos = QB_LOG_TARGET_START; pos <= conf_active_max; pos++) {
310 t = &conf[pos];
311 if ((t->state == QB_LOG_STATE_ENABLED) && t->threaded
312 && qb_bit_is_set(cs->targets, t->pos)) {
313 qb_do_extended(buffer, t->extended,
314 t->logger(t->pos, cs, timestamp, buffer));
315 }
316 }
317 }
318
319 struct qb_log_callsite*
qb_log_callsite_get2(const char * message_id,const char * function,const char * filename,const char * format,uint8_t priority,uint32_t lineno,uint32_t tags)320 qb_log_callsite_get2(const char *message_id,
321 const char *function,
322 const char *filename,
323 const char *format,
324 uint8_t priority,
325 uint32_t lineno,
326 uint32_t tags)
327 {
328 struct qb_log_target *t;
329 struct qb_log_filter *flt;
330 struct qb_log_callsite *cs;
331 int32_t new_dcs = QB_FALSE;
332 struct qb_list_head *f_item;
333 enum qb_log_target_slot pos;
334
335 if (!logger_inited) {
336 return NULL;
337 }
338
339 cs = qb_log_dcs_get(&new_dcs, message_id, function, filename,
340 format, priority, lineno, tags);
341
342 if (cs == NULL) {
343 return NULL;
344 }
345
346 if (new_dcs) {
347 pthread_rwlock_rdlock(&_listlock);
348 for (pos = QB_LOG_TARGET_START; pos <= conf_active_max; pos++) {
349 t = &conf[pos];
350 if (t->state != QB_LOG_STATE_ENABLED) {
351 continue;
352 }
353 qb_list_for_each(f_item, &t->filter_head) {
354 flt = qb_list_entry(f_item, struct qb_log_filter, list);
355 _log_filter_apply_to_cs(cs, t->pos, flt->conf, flt->type,
356 flt->text, flt->regex, flt->high_priority,
357 flt->low_priority);
358 }
359 }
360 if (tags == 0) {
361 qb_list_for_each(f_item, &tags_head) {
362 flt = qb_list_entry(f_item, struct qb_log_filter, list);
363 _log_filter_apply_to_cs(cs, flt->new_value, flt->conf, flt->type,
364 flt->text, flt->regex, flt->high_priority,
365 flt->low_priority);
366 }
367 } else {
368 cs->tags = tags;
369 }
370 if (_custom_filter_fn) {
371 _custom_filter_fn(cs);
372 }
373 pthread_rwlock_unlock(&_listlock);
374 } else {
375 if (tags && cs->tags != tags) {
376 cs->tags = tags;
377 }
378 if (_custom_filter_fn) {
379 _custom_filter_fn(cs);
380 }
381 }
382 return cs;
383 }
384
385 struct qb_log_callsite*
qb_log_callsite_get(const char * function,const char * filename,const char * format,uint8_t priority,uint32_t lineno,uint32_t tags)386 qb_log_callsite_get(const char *function,
387 const char *filename,
388 const char *format,
389 uint8_t priority,
390 uint32_t lineno,
391 uint32_t tags)
392 {
393 return qb_log_callsite_get2(NULL, function, filename, format,
394 priority, lineno, tags);
395 }
396
397 void
qb_log_from_external_source_va2(const char * message_id,const char * function,const char * filename,const char * format,uint8_t priority,uint32_t lineno,uint32_t tags,va_list ap)398 qb_log_from_external_source_va2(const char *message_id,
399 const char *function,
400 const char *filename,
401 const char *format,
402 uint8_t priority,
403 uint32_t lineno, uint32_t tags, va_list ap)
404 {
405 struct qb_log_callsite *cs;
406
407 if (!logger_inited) {
408 return;
409 }
410
411 cs = qb_log_callsite_get2(message_id, function, filename,
412 format, priority, lineno, tags);
413 qb_log_real_va_(cs, ap);
414 }
415
416 void
qb_log_from_external_source_va(const char * function,const char * filename,const char * format,uint8_t priority,uint32_t lineno,uint32_t tags,va_list ap)417 qb_log_from_external_source_va(const char *function,
418 const char *filename,
419 const char *format,
420 uint8_t priority,
421 uint32_t lineno, uint32_t tags, va_list ap)
422 {
423 qb_log_from_external_source_va2(NULL, function, filename,
424 format, priority, lineno, tags, ap);
425 }
426
427 void
qb_log_from_external_source(const char * function,const char * filename,const char * format,uint8_t priority,uint32_t lineno,uint32_t tags,...)428 qb_log_from_external_source(const char *function,
429 const char *filename,
430 const char *format,
431 uint8_t priority,
432 uint32_t lineno, uint32_t tags, ...)
433 {
434 struct qb_log_callsite *cs;
435 va_list ap;
436
437 if (!logger_inited) {
438 return;
439 }
440
441 cs = qb_log_callsite_get(function, filename,
442 format, priority, lineno, tags);
443 va_start(ap, tags);
444 qb_log_real_va_(cs, ap);
445 va_end(ap);
446 }
447
448 static void
qb_log_callsites_dump_sect(struct callsite_section * sect)449 qb_log_callsites_dump_sect(struct callsite_section *sect)
450 {
451 struct qb_log_callsite *cs;
452 printf(" start %p - stop %p\n", sect->start, sect->stop);
453 printf("filename lineno targets tags\n");
454 for (cs = sect->start; cs < sect->stop; cs++) {
455 if (cs->lineno > 0) {
456 #ifndef S_SPLINT_S
457 printf("%12s %6" PRIu32 " %16" PRIu32 " %16u\n",
458 cs->filename, cs->lineno, cs->targets,
459 cs->tags);
460 #endif /* S_SPLINT_S */
461 }
462 }
463 }
464
465
466 int32_t
qb_log_callsites_register(struct qb_log_callsite * _start,struct qb_log_callsite * _stop)467 qb_log_callsites_register(struct qb_log_callsite *_start,
468 struct qb_log_callsite *_stop)
469 {
470 struct callsite_section *sect;
471 struct qb_log_callsite *cs;
472 struct qb_log_target *t;
473 struct qb_log_filter *flt;
474 enum qb_log_target_slot pos;
475
476 if (_start == NULL || _stop == NULL) {
477 return -EINVAL;
478 }
479
480 pthread_rwlock_rdlock(&_listlock);
481 qb_list_for_each_entry(sect, &callsite_sections, list) {
482 if (sect->start == _start || sect->stop == _stop) {
483 pthread_rwlock_unlock(&_listlock);
484 return -EEXIST;
485 }
486 }
487 pthread_rwlock_unlock(&_listlock);
488
489 sect = calloc(1, sizeof(struct callsite_section));
490 if (sect == NULL) {
491 return -ENOMEM;
492 }
493 sect->start = _start;
494 sect->stop = _stop;
495 qb_list_init(§->list);
496
497 pthread_rwlock_wrlock(&_listlock);
498 qb_list_add(§->list, &callsite_sections);
499
500 /*
501 * Now apply the filters on these new callsites
502 */
503 for (pos = QB_LOG_TARGET_START; pos <= conf_active_max; pos++) {
504 t = &conf[pos];
505 if (t->state != QB_LOG_STATE_ENABLED) {
506 continue;
507 }
508 qb_list_for_each_entry(flt, &t->filter_head, list) {
509 _log_filter_apply(sect, t->pos, flt->conf,
510 flt->type, flt->text, flt->regex,
511 flt->high_priority, flt->low_priority);
512 }
513 }
514 qb_list_for_each_entry(flt, &tags_head, list) {
515 _log_filter_apply(sect, flt->new_value, flt->conf,
516 flt->type, flt->text, flt->regex,
517 flt->high_priority, flt->low_priority);
518 }
519 pthread_rwlock_unlock(&_listlock);
520 if (_custom_filter_fn) {
521 for (cs = sect->start; cs < sect->stop; cs++) {
522 if (cs->lineno > 0) {
523 _custom_filter_fn(cs);
524 }
525 }
526 }
527 /* qb_log_callsites_dump_sect(sect); */
528
529 return 0;
530 }
531
532 void
qb_log_callsites_dump(void)533 qb_log_callsites_dump(void)
534 {
535 struct callsite_section *sect;
536 int32_t l;
537
538 pthread_rwlock_rdlock(&_listlock);
539 l = qb_list_length(&callsite_sections);
540 printf("Callsite Database [%d]\n", l);
541 printf("---------------------\n");
542 qb_list_for_each_entry(sect, &callsite_sections, list) {
543 qb_log_callsites_dump_sect(sect);
544 }
545 pthread_rwlock_unlock(&_listlock);
546 }
547
548 static int32_t
_log_filter_exists(struct qb_list_head * list_head,enum qb_log_filter_type type,const char * text,uint8_t high_priority,uint8_t low_priority,uint32_t new_value)549 _log_filter_exists(struct qb_list_head *list_head,
550 enum qb_log_filter_type type,
551 const char *text,
552 uint8_t high_priority,
553 uint8_t low_priority,
554 uint32_t new_value)
555 {
556 struct qb_log_filter *flt;
557
558 qb_list_for_each_entry(flt, list_head, list) {
559 if (flt->type == type &&
560 flt->high_priority == high_priority &&
561 flt->low_priority == low_priority &&
562 flt->new_value == new_value &&
563 strcmp(flt->text, text) == 0) {
564 return QB_TRUE;
565 }
566 }
567 return QB_FALSE;
568 }
569
570 static int32_t
_log_filter_store(uint32_t t,enum qb_log_filter_conf c,enum qb_log_filter_type type,const char * text,uint8_t high_priority,uint8_t low_priority,struct qb_log_filter ** new)571 _log_filter_store(uint32_t t, enum qb_log_filter_conf c,
572 enum qb_log_filter_type type,
573 const char *text,
574 uint8_t high_priority,
575 uint8_t low_priority,
576 struct qb_log_filter **new)
577 {
578 struct qb_log_filter *flt;
579 struct qb_list_head *iter;
580 struct qb_list_head *next;
581 struct qb_list_head *list_head;
582
583 switch (c) {
584 case QB_LOG_FILTER_ADD:
585 case QB_LOG_FILTER_REMOVE:
586 case QB_LOG_FILTER_CLEAR_ALL:
587 list_head = &conf[t].filter_head;
588 break;
589
590 case QB_LOG_TAG_SET:
591 case QB_LOG_TAG_CLEAR:
592 case QB_LOG_TAG_CLEAR_ALL:
593 list_head = &tags_head;
594 break;
595 default:
596 return -ENOSYS;
597 }
598
599 if (c == QB_LOG_FILTER_ADD || c == QB_LOG_TAG_SET) {
600 if (text == NULL) {
601 return -EINVAL;
602 }
603 if (_log_filter_exists(list_head, type, text,
604 high_priority, low_priority, t)) {
605 return -EEXIST;
606 }
607 flt = calloc(1, sizeof(struct qb_log_filter));
608 if (flt == NULL) {
609 return -ENOMEM;
610 }
611 qb_list_init(&flt->list);
612 flt->conf = c;
613 flt->type = type;
614 flt->text = strdup(text);
615 if (flt->text == NULL) {
616 _log_free_filter(flt);
617 return -ENOMEM;
618 }
619
620 if (type == QB_LOG_FILTER_FUNCTION_REGEX ||
621 type == QB_LOG_FILTER_FILE_REGEX ||
622 type == QB_LOG_FILTER_FORMAT_REGEX) {
623 int res;
624
625 flt->regex = calloc(1, sizeof(regex_t));
626 if (flt->regex == NULL) {
627 _log_free_filter(flt);
628 return -ENOMEM;
629 }
630 res = regcomp(flt->regex, flt->text, 0);
631 if (res) {
632 _log_free_filter(flt);
633 return -EINVAL;
634 }
635 }
636 flt->high_priority = high_priority;
637 flt->low_priority = low_priority;
638 flt->new_value = t;
639 qb_list_add_tail(&flt->list, list_head);
640 if (new) {
641 *new = flt;
642 }
643 } else if (c == QB_LOG_FILTER_REMOVE || c == QB_LOG_TAG_CLEAR) {
644 qb_list_for_each_safe(iter, next, list_head) {
645 flt = qb_list_entry(iter, struct qb_log_filter, list);
646 if (flt->type == type &&
647 flt->low_priority <= low_priority &&
648 flt->high_priority >= high_priority &&
649 (strcmp(flt->text, text) == 0 ||
650 strcmp("*", text) == 0)) {
651 qb_list_del(iter);
652 _log_free_filter(flt);
653 return 0;
654 }
655 }
656
657 } else if (c == QB_LOG_FILTER_CLEAR_ALL || c == QB_LOG_TAG_CLEAR_ALL) {
658 qb_list_for_each_safe(iter, next, list_head) {
659 flt = qb_list_entry(iter, struct qb_log_filter, list);
660 qb_list_del(iter);
661 _log_free_filter(flt);
662 }
663 }
664 return 0;
665 }
666
667 static void
_log_filter_apply(struct callsite_section * sect,uint32_t t,enum qb_log_filter_conf c,enum qb_log_filter_type type,const char * text,regex_t * regex,uint8_t high_priority,uint8_t low_priority)668 _log_filter_apply(struct callsite_section *sect,
669 uint32_t t, enum qb_log_filter_conf c,
670 enum qb_log_filter_type type,
671 const char *text,
672 regex_t *regex,
673 uint8_t high_priority, uint8_t low_priority)
674 {
675 struct qb_log_callsite *cs;
676
677 for (cs = sect->start; cs < sect->stop; cs++) {
678 if (cs->lineno > 0) {
679 _log_filter_apply_to_cs(cs, t, c, type, text, regex,
680 high_priority, low_priority);
681 }
682 }
683 }
684
685 /* #define _QB_FILTER_DEBUGGING_ 1 */
686 static void
_log_filter_apply_to_cs(struct qb_log_callsite * cs,uint32_t t,enum qb_log_filter_conf c,enum qb_log_filter_type type,const char * text,regex_t * regex,uint8_t high_priority,uint8_t low_priority)687 _log_filter_apply_to_cs(struct qb_log_callsite *cs,
688 uint32_t t, enum qb_log_filter_conf c,
689 enum qb_log_filter_type type,
690 const char *text,
691 regex_t *regex,
692 uint8_t high_priority, uint8_t low_priority)
693 {
694
695 if (c == QB_LOG_FILTER_CLEAR_ALL) {
696 qb_bit_clear(cs->targets, t);
697 return;
698 } else if (c == QB_LOG_TAG_CLEAR_ALL) {
699 cs->tags = 0;
700 return;
701 }
702
703 if (_cs_matches_filter_(cs, type, text, regex, high_priority, low_priority)) {
704 #ifdef _QB_FILTER_DEBUGGING_
705 uint32_t old_targets = cs->targets;
706 uint32_t old_tags = cs->tags;
707 #endif /* _QB_FILTER_DEBUGGING_ */
708 if (c == QB_LOG_FILTER_ADD) {
709 qb_bit_set(cs->targets, t);
710 } else if (c == QB_LOG_FILTER_REMOVE) {
711 qb_bit_clear(cs->targets, t);
712 } else if (c == QB_LOG_TAG_SET) {
713 cs->tags = t;
714 } else if (c == QB_LOG_TAG_CLEAR) {
715 cs->tags = 0;
716 }
717 #ifdef _QB_FILTER_DEBUGGING_
718 if (old_targets != cs->targets) {
719 printf("targets: %s:%u value(%d) %d -> %d\n",
720 cs->filename, cs->lineno, t,
721 old_targets, cs->targets);
722 }
723 if (old_tags != cs->tags) {
724 printf("tags: %s:%u value(%d) %d -> %d\n",
725 cs->filename, cs->lineno, t, old_tags, cs->tags);
726 }
727 #endif /* _QB_FILTER_DEBUGGING_ */
728 }
729 }
730
731 int32_t
qb_log_filter_ctl2(int32_t t,enum qb_log_filter_conf c,enum qb_log_filter_type type,const char * text,uint8_t high_priority,uint8_t low_priority)732 qb_log_filter_ctl2(int32_t t, enum qb_log_filter_conf c,
733 enum qb_log_filter_type type, const char * text,
734 uint8_t high_priority, uint8_t low_priority)
735 {
736 struct qb_log_filter *new_flt = NULL;
737 regex_t *regex = NULL;
738 struct callsite_section *sect;
739 int32_t rc;
740
741 if (!logger_inited) {
742 return -EINVAL;
743 }
744
745 if (c == QB_LOG_FILTER_ADD ||
746 c == QB_LOG_FILTER_CLEAR_ALL ||
747 c == QB_LOG_FILTER_REMOVE) {
748 if (t < 0 || t >= QB_LOG_TARGET_MAX ||
749 conf[t].state == QB_LOG_STATE_UNUSED) {
750 return -EBADF;
751 }
752 }
753
754 if (text == NULL ||
755 low_priority < high_priority ||
756 type > QB_LOG_FILTER_FORMAT_REGEX ||
757 c > QB_LOG_TAG_CLEAR_ALL) {
758 return -EINVAL;
759 }
760 pthread_rwlock_rdlock(&_listlock);
761 rc = _log_filter_store(t, c, type, text, high_priority, low_priority, &new_flt);
762 if (rc < 0) {
763 pthread_rwlock_unlock(&_listlock);
764 return rc;
765 }
766
767 if (new_flt && new_flt->regex) {
768 regex = new_flt->regex;
769 }
770 qb_list_for_each_entry(sect, &callsite_sections, list) {
771 _log_filter_apply(sect, t, c, type, text, regex, high_priority, low_priority);
772 }
773 pthread_rwlock_unlock(&_listlock);
774 return 0;
775 }
776
777 int32_t
qb_log_filter_fn_set(qb_log_filter_fn fn)778 qb_log_filter_fn_set(qb_log_filter_fn fn)
779 {
780 struct callsite_section *sect;
781 struct qb_log_callsite *cs;
782
783 if (!logger_inited) {
784 return -EINVAL;
785 }
786 _custom_filter_fn = fn;
787 if (_custom_filter_fn == NULL) {
788 return 0;
789 }
790
791 qb_list_for_each_entry(sect, &callsite_sections, list) {
792 for (cs = sect->start; cs < sect->stop; cs++) {
793 if (cs->lineno > 0) {
794 _custom_filter_fn(cs);
795 }
796 }
797 }
798 return 0;
799 }
800
801 int32_t
qb_log_filter_ctl(int32_t t,enum qb_log_filter_conf c,enum qb_log_filter_type type,const char * text,uint8_t priority)802 qb_log_filter_ctl(int32_t t, enum qb_log_filter_conf c,
803 enum qb_log_filter_type type,
804 const char *text, uint8_t priority)
805 {
806 return qb_log_filter_ctl2(t, c, type, text, LOG_EMERG, priority);
807 }
808
809 static void
_log_target_state_set(struct qb_log_target * t,enum qb_log_target_state s)810 _log_target_state_set(struct qb_log_target *t, enum qb_log_target_state s)
811 {
812 enum qb_log_target_slot i;
813 int32_t a_set = QB_FALSE;
814 int32_t u_set = QB_FALSE;
815
816 t->state = s;
817
818 for (i = QB_LOG_TARGET_MAX; i > QB_LOG_TARGET_START; i--) {
819 if (!a_set && conf[i-1].state == QB_LOG_STATE_ENABLED) {
820 a_set = QB_TRUE;
821 conf_active_max = i-1;
822 }
823 if (!u_set && conf[i-1].state != QB_LOG_STATE_UNUSED) {
824 u_set = QB_TRUE;
825 }
826 }
827 }
828
829 void
qb_log_init(const char * name,int32_t facility,uint8_t priority)830 qb_log_init(const char *name, int32_t facility, uint8_t priority)
831 {
832 int32_t l;
833 enum qb_log_target_slot i;
834
835 l = pthread_rwlock_init(&_listlock, NULL);
836 assert(l == 0);
837 qb_log_format_init();
838
839 for (i = QB_LOG_TARGET_START; i < QB_LOG_TARGET_MAX; i++) {
840 conf[i].pos = i;
841 conf[i].debug = QB_FALSE;
842 conf[i].file_sync = QB_FALSE;
843 conf[i].extended = QB_TRUE;
844 conf[i].state = QB_LOG_STATE_UNUSED;
845 (void)strlcpy(conf[i].name, name, PATH_MAX);
846 conf[i].facility = facility;
847 conf[i].max_line_length = QB_LOG_MAX_LEN;
848 qb_list_init(&conf[i].filter_head);
849 }
850
851 qb_log_dcs_init();
852
853 for (i = QB_LOG_TARGET_STATIC_START; i < QB_LOG_TARGET_STATIC_MAX; i++)
854 conf[i].state = QB_LOG_STATE_DISABLED;
855
856 logger_inited = QB_TRUE;
857 (void)qb_log_syslog_open(&conf[QB_LOG_SYSLOG]);
858 _log_target_state_set(&conf[QB_LOG_SYSLOG], QB_LOG_STATE_ENABLED);
859 (void)qb_log_filter_ctl(QB_LOG_SYSLOG, QB_LOG_FILTER_ADD,
860 QB_LOG_FILTER_FILE, "*", priority);
861 }
862
863 void
qb_log_fini(void)864 qb_log_fini(void)
865 {
866 struct qb_log_target *t;
867 struct qb_log_filter *flt;
868 struct callsite_section *s;
869 struct qb_list_head *iter;
870 struct qb_list_head *iter2;
871 struct qb_list_head *next;
872 struct qb_list_head *next2;
873 enum qb_log_target_slot pos;
874
875 if (!logger_inited) {
876 return;
877 }
878 logger_inited = QB_FALSE;
879 qb_log_thread_stop();
880 pthread_rwlock_destroy(&_listlock);
881
882 for (pos = QB_LOG_TARGET_START; pos <= conf_active_max; pos++) {
883 t = &conf[pos];
884 _log_target_disable(t);
885 qb_list_for_each_safe(iter2, next2, &t->filter_head) {
886 flt = qb_list_entry(iter2, struct qb_log_filter, list);
887 qb_list_del(iter2);
888 _log_free_filter(flt);
889 }
890 }
891 qb_log_format_fini();
892 qb_log_dcs_fini();
893 qb_list_for_each_safe(iter, next, &callsite_sections) {
894 s = qb_list_entry(iter, struct callsite_section, list);
895 qb_list_del(iter);
896 free(s);
897 }
898 qb_list_for_each_safe(iter, next, &tags_head) {
899 flt = qb_list_entry(iter, struct qb_log_filter, list);
900 qb_list_del(iter);
901 _log_free_filter(flt);
902 }
903 }
904
905 struct qb_log_target *
qb_log_target_alloc(void)906 qb_log_target_alloc(void)
907 {
908 enum qb_log_target_slot i;
909 for (i = QB_LOG_TARGET_START; i < QB_LOG_TARGET_MAX; i++) {
910 if (conf[i].state == QB_LOG_STATE_UNUSED) {
911 _log_target_state_set(&conf[i], QB_LOG_STATE_DISABLED);
912 return &conf[i];
913 }
914 }
915 errno = EMFILE;
916 return NULL;
917 }
918
919 void
qb_log_target_free(struct qb_log_target * t)920 qb_log_target_free(struct qb_log_target *t)
921 {
922 (void)qb_log_filter_ctl(t->pos, QB_LOG_FILTER_CLEAR_ALL,
923 QB_LOG_FILTER_FILE, NULL, 0);
924 t->debug = QB_FALSE;
925 t->filename[0] = '\0';
926 qb_log_format_set(t->pos, NULL);
927 _log_target_state_set(t, QB_LOG_STATE_UNUSED);
928 }
929
930 struct qb_log_target *
qb_log_target_get(int32_t pos)931 qb_log_target_get(int32_t pos)
932 {
933 return &conf[pos];
934 }
935
936 void *
qb_log_target_user_data_get(int32_t t)937 qb_log_target_user_data_get(int32_t t)
938 {
939 if (t < 0 || t >= QB_LOG_TARGET_MAX ||
940 conf[t].state == QB_LOG_STATE_UNUSED) {
941 errno = EBADF;
942 return NULL;
943 }
944
945 return conf[t].instance;
946 }
947
948 int32_t
qb_log_target_user_data_set(int32_t t,void * user_data)949 qb_log_target_user_data_set(int32_t t, void *user_data)
950 {
951 if (!logger_inited) {
952 return -EINVAL;
953 }
954 if (t < 0 || t >= QB_LOG_TARGET_MAX ||
955 conf[t].state == QB_LOG_STATE_UNUSED) {
956 return -EBADF;
957 }
958
959 conf[t].instance = user_data;
960 return 0;
961 }
962
963 int32_t
qb_log_custom_open(qb_log_logger_fn log_fn,qb_log_close_fn close_fn,qb_log_reload_fn reload_fn,void * user_data)964 qb_log_custom_open(qb_log_logger_fn log_fn,
965 qb_log_close_fn close_fn,
966 qb_log_reload_fn reload_fn, void *user_data)
967 {
968 struct qb_log_target *t;
969
970 t = qb_log_target_alloc();
971 if (t == NULL) {
972 return -errno;
973 }
974
975 t->instance = user_data;
976 #ifndef S_SPLINT_S
977 snprintf(t->filename, PATH_MAX, "custom-%" PRIu32, t->pos);
978 #endif /* S_SPLINT_S */
979
980 t->logger = log_fn;
981 t->vlogger = NULL;
982 t->reload = reload_fn;
983 t->close = close_fn;
984
985 return t->pos;
986 }
987
988 void
qb_log_custom_close(int32_t t)989 qb_log_custom_close(int32_t t)
990 {
991 struct qb_log_target *target;
992
993 if (!logger_inited) {
994 return;
995 }
996 if (t < 0 || t >= QB_LOG_TARGET_MAX ||
997 conf[t].state == QB_LOG_STATE_UNUSED) {
998 return;
999 }
1000
1001 target = qb_log_target_get(t);
1002
1003 if (target->close) {
1004 qb_atomic_int_set(&in_logger, QB_TRUE);
1005 target->close(t);
1006 qb_atomic_int_set(&in_logger, QB_FALSE);
1007 }
1008 qb_log_target_free(target);
1009 }
1010
1011 static int32_t
_log_target_enable(struct qb_log_target * t)1012 _log_target_enable(struct qb_log_target *t)
1013 {
1014 int32_t rc = 0;
1015
1016 if (t->state == QB_LOG_STATE_ENABLED) {
1017 return 0;
1018 }
1019 if (t->pos == QB_LOG_STDERR ||
1020 t->pos == QB_LOG_STDOUT) {
1021 rc = qb_log_stderr_open(t);
1022 } else if (t->pos == QB_LOG_SYSLOG) {
1023 rc = qb_log_syslog_open(t);
1024 } else if (t->pos == QB_LOG_BLACKBOX) {
1025 rc = qb_log_blackbox_open(t);
1026 }
1027 if (rc == 0) {
1028 _log_target_state_set(t, QB_LOG_STATE_ENABLED);
1029 }
1030 return rc;
1031 }
1032
1033 static void
_log_target_disable(struct qb_log_target * t)1034 _log_target_disable(struct qb_log_target *t)
1035 {
1036 if (t->state != QB_LOG_STATE_ENABLED) {
1037 return;
1038 }
1039 _log_target_state_set(t, QB_LOG_STATE_DISABLED);
1040 if (t->close) {
1041 qb_atomic_int_set(&in_logger, QB_TRUE);
1042 t->close(t->pos);
1043 qb_atomic_int_set(&in_logger, QB_FALSE);
1044 }
1045 }
1046
1047 int32_t
qb_log_ctl2(int32_t t,enum qb_log_conf c,qb_log_ctl2_arg_t arg_not4directuse)1048 qb_log_ctl2(int32_t t, enum qb_log_conf c, qb_log_ctl2_arg_t arg_not4directuse)
1049 {
1050 int32_t rc = 0;
1051 int32_t need_reload = QB_FALSE;
1052
1053 /* extract the constants and do not touch the origin anymore */
1054 const int32_t arg_i32 = arg_not4directuse.i32;
1055 const char * const arg_s = arg_not4directuse.s;
1056
1057 if (!logger_inited) {
1058 return -EINVAL;
1059 }
1060 if (t < 0 || t >= QB_LOG_TARGET_MAX ||
1061 conf[t].state == QB_LOG_STATE_UNUSED) {
1062 return -EBADF;
1063 }
1064
1065 /* Starting/stopping the thread has its own locking that can interfere with this */
1066 if (c != QB_LOG_CONF_THREADED) {
1067 qb_log_thread_pause(&conf[t]);
1068 }
1069
1070 switch (c) {
1071 case QB_LOG_CONF_ENABLED:
1072 if (arg_i32) {
1073 rc = _log_target_enable(&conf[t]);
1074 } else {
1075 _log_target_disable(&conf[t]);
1076 }
1077 break;
1078 case QB_LOG_CONF_STATE_GET:
1079 rc = conf[t].state;
1080 break;
1081 case QB_LOG_CONF_FACILITY:
1082 conf[t].facility = arg_i32;
1083 if (t == QB_LOG_SYSLOG) {
1084 need_reload = QB_TRUE;
1085 }
1086 break;
1087 case QB_LOG_CONF_IDENT:
1088 (void)strlcpy(conf[t].name, arg_s, PATH_MAX);
1089 if (t == QB_LOG_SYSLOG) {
1090 need_reload = QB_TRUE;
1091 }
1092 break;
1093 case QB_LOG_CONF_FILE_SYNC:
1094 conf[t].file_sync = arg_i32;
1095 break;
1096 case QB_LOG_CONF_PRIORITY_BUMP:
1097 conf[t].priority_bump = arg_i32;
1098 break;
1099 case QB_LOG_CONF_SIZE:
1100 if (t == QB_LOG_BLACKBOX) {
1101 if (arg_i32 <= 0) {
1102 rc = -EINVAL;
1103 goto unlock_fini;
1104 }
1105 conf[t].size = arg_i32;
1106 need_reload = QB_TRUE;
1107 } else {
1108 rc = -ENOSYS;
1109 }
1110 break;
1111 case QB_LOG_CONF_THREADED:
1112 conf[t].threaded = arg_i32;
1113 break;
1114 case QB_LOG_CONF_EXTENDED:
1115 conf[t].extended = arg_i32;
1116 break;
1117 case QB_LOG_CONF_MAX_LINE_LEN:
1118 /* arbitrary limit, but you'd be insane to go further */
1119 if (arg_i32 > QB_LOG_ABSOLUTE_MAX_LEN) {
1120 rc = -EINVAL;
1121 } else {
1122 conf[t].max_line_length = arg_i32;
1123 }
1124 break;
1125 case QB_LOG_CONF_ELLIPSIS:
1126 conf[t].ellipsis = arg_i32;
1127 break;
1128 case QB_LOG_CONF_USE_JOURNAL:
1129 #ifdef USE_JOURNAL
1130 if (t == QB_LOG_SYSLOG) {
1131 conf[t].use_journal = arg_i32;
1132 need_reload = QB_TRUE;
1133 } else {
1134 rc = -EINVAL;
1135 }
1136 #else
1137 rc = -EOPNOTSUPP;
1138 #endif
1139 break;
1140
1141 default:
1142 rc = -EINVAL;
1143 }
1144 if (rc == 0 && need_reload && conf[t].reload) {
1145 qb_atomic_int_set(&in_logger, QB_TRUE);
1146 conf[t].reload(t);
1147 qb_atomic_int_set(&in_logger, QB_FALSE);
1148 }
1149
1150 unlock_fini:
1151 if (c != QB_LOG_CONF_THREADED) {
1152 qb_log_thread_resume(&conf[t]);
1153 }
1154 return rc;
1155 }
1156
1157 int32_t
qb_log_ctl(int32_t t,enum qb_log_conf c,int32_t arg)1158 qb_log_ctl(int32_t t, enum qb_log_conf c, int32_t arg)
1159 {
1160 return qb_log_ctl2(t, c, QB_LOG_CTL2_I32(arg));
1161 }
1162