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
75   	qb_util_set_log_function(qb_util_log_fn_t fn)
76   	{
77   		old_internal_log_fn = fn;
78   	}
79   	
80   	static void
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
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;
(1) Event assignment: Assigning: "next" = "NULL".
Also see events: [null][dead_error_condition][dead_error_line]
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:
(2) Event null: At condition "next", the value of "next" must be "NULL".
(3) Event dead_error_condition: The condition "next" cannot be true.
(4) Event dead_error_line: Execution cannot reach the expression "next" inside this statement: "next = (next ? next : cs->f...".
Also see events: [assignment]
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
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
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
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
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*
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*
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
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
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
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
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
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(&sect->list);
496  	
497  		pthread_rwlock_wrlock(&_listlock);
498  		qb_list_add(&sect->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
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
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
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
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
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
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
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
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
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
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
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 *
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
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 *
931  	qb_log_target_get(int32_t pos)
932  	{
933  		return &conf[pos];
934  	}
935  	
936  	void *
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
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
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
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
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
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
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
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