0db5a8a9c9bd99b1dd01da2ed012a1117c561cb9
[strongswan.git] / src / libcharon / bus / bus.c
1 /*
2 * Copyright (C) 2011-2012 Tobias Brunner
3 * Copyright (C) 2006 Martin Willi
4 * Hochschule fuer Technik Rapperswil
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * for more details.
15 */
16
17 #include "bus.h"
18
19 #include <stdint.h>
20
21 #include <threading/thread.h>
22 #include <threading/thread_value.h>
23 #include <threading/mutex.h>
24 #include <threading/rwlock.h>
25
26 typedef struct private_bus_t private_bus_t;
27
28 /**
29 * Private data of a bus_t object.
30 */
31 struct private_bus_t {
32 /**
33 * Public part of a bus_t object.
34 */
35 bus_t public;
36
37 /**
38 * List of registered listeners as entry_t.
39 */
40 linked_list_t *listeners;
41
42 /**
43 * List of registered loggers for each log group as log_entry_t.
44 * Loggers are ordered by descending log level.
45 * The extra list stores all loggers so we can properly unregister them.
46 */
47 linked_list_t *loggers[DBG_MAX + 1];
48
49 /**
50 * Maximum log level of any registered logger for each log group.
51 * This allows to check quickly if a log message has to be logged at all.
52 */
53 level_t max_level[DBG_MAX + 1];
54
55 /**
56 * Mutex for the list of listeners, recursively.
57 */
58 mutex_t *mutex;
59
60 /**
61 * Read-write lock for the list of loggers.
62 */
63 rwlock_t *log_lock;
64
65 /**
66 * Thread local storage the threads IKE_SA
67 */
68 thread_value_t *thread_sa;
69 };
70
71 typedef struct entry_t entry_t;
72
73 /**
74 * a listener entry
75 */
76 struct entry_t {
77
78 /**
79 * registered listener interface
80 */
81 listener_t *listener;
82
83 /**
84 * are we currently calling this listener
85 */
86 int calling;
87
88 };
89
90 typedef struct log_entry_t log_entry_t;
91
92 /**
93 * a logger entry
94 */
95 struct log_entry_t {
96
97 /**
98 * registered logger interface
99 */
100 logger_t *logger;
101
102 /**
103 * registered log levels per group
104 */
105 level_t levels[DBG_MAX];
106
107 };
108
109 METHOD(bus_t, add_listener, void,
110 private_bus_t *this, listener_t *listener)
111 {
112 entry_t *entry;
113
114 INIT(entry,
115 .listener = listener,
116 );
117
118 this->mutex->lock(this->mutex);
119 this->listeners->insert_last(this->listeners, entry);
120 this->mutex->unlock(this->mutex);
121 }
122
123 METHOD(bus_t, remove_listener, void,
124 private_bus_t *this, listener_t *listener)
125 {
126 enumerator_t *enumerator;
127 entry_t *entry;
128
129 this->mutex->lock(this->mutex);
130 enumerator = this->listeners->create_enumerator(this->listeners);
131 while (enumerator->enumerate(enumerator, &entry))
132 {
133 if (entry->listener == listener)
134 {
135 this->listeners->remove_at(this->listeners, enumerator);
136 free(entry);
137 break;
138 }
139 }
140 enumerator->destroy(enumerator);
141 this->mutex->unlock(this->mutex);
142 }
143
144 /**
145 * Register a logger on the given log group according to the requested level
146 */
147 static inline void register_logger(private_bus_t *this, debug_t group,
148 log_entry_t *entry)
149 {
150 enumerator_t *enumerator;
151 linked_list_t *loggers;
152 log_entry_t *current;
153 level_t level;
154
155 loggers = this->loggers[group];
156 level = entry->levels[group];
157
158 enumerator = loggers->create_enumerator(loggers);
159 while (enumerator->enumerate(enumerator, (void**)&current))
160 {
161 if (current->levels[group] <= level)
162 {
163 break;
164 }
165 }
166 loggers->insert_before(loggers, enumerator, entry);
167 enumerator->destroy(enumerator);
168
169 this->max_level[group] = max(this->max_level[group], level);
170 }
171
172 /**
173 * Unregister a logger from all log groups (destroys the log_entry_t)
174 */
175 static inline void unregister_logger(private_bus_t *this, logger_t *logger)
176 {
177 enumerator_t *enumerator;
178 linked_list_t *loggers;
179 log_entry_t *entry, *found = NULL;
180
181 loggers = this->loggers[DBG_MAX];
182 enumerator = loggers->create_enumerator(loggers);
183 while (enumerator->enumerate(enumerator, &entry))
184 {
185 if (entry->logger == logger)
186 {
187 loggers->remove_at(loggers, enumerator);
188 found = entry;
189 break;
190 }
191 }
192 enumerator->destroy(enumerator);
193
194 if (found)
195 {
196 debug_t group;
197 for (group = 0; group < DBG_MAX; group++)
198 {
199 if (found->levels[group] > LEVEL_SILENT)
200 {
201 loggers = this->loggers[group];
202 loggers->remove(loggers, found, NULL);
203
204 this->max_level[group] = LEVEL_SILENT;
205 if (loggers->get_first(loggers, (void**)&entry) == SUCCESS)
206 {
207 this->max_level[group] = entry->levels[group];
208 }
209 }
210 }
211 free(found);
212 }
213 }
214
215 METHOD(bus_t, add_logger, void,
216 private_bus_t *this, logger_t *logger)
217 {
218 log_entry_t *entry;
219 debug_t group;
220
221 INIT(entry,
222 .logger = logger,
223 );
224
225 this->log_lock->write_lock(this->log_lock);
226 unregister_logger(this, logger);
227 for (group = 0; group < DBG_MAX; group++)
228 {
229 entry->levels[group] = logger->get_level(logger, group);
230 if (entry->levels[group] > LEVEL_SILENT)
231 {
232 register_logger(this, group, entry);
233 }
234 }
235 this->loggers[DBG_MAX]->insert_last(this->loggers[DBG_MAX], entry);
236 this->log_lock->unlock(this->log_lock);
237 }
238
239 METHOD(bus_t, remove_logger, void,
240 private_bus_t *this, logger_t *logger)
241 {
242 this->log_lock->write_lock(this->log_lock);
243 unregister_logger(this, logger);
244 this->log_lock->unlock(this->log_lock);
245 }
246
247 METHOD(bus_t, set_sa, void,
248 private_bus_t *this, ike_sa_t *ike_sa)
249 {
250 this->thread_sa->set(this->thread_sa, ike_sa);
251 }
252
253 METHOD(bus_t, get_sa, ike_sa_t*,
254 private_bus_t *this)
255 {
256 return this->thread_sa->get(this->thread_sa);
257 }
258
259 /**
260 * data associated to a signal, passed to callback
261 */
262 typedef struct {
263 /** associated IKE_SA */
264 ike_sa_t *ike_sa;
265 /** invoking thread */
266 long thread;
267 /** debug group */
268 debug_t group;
269 /** debug level */
270 level_t level;
271 /** message */
272 char *message;
273 } log_data_t;
274
275 /**
276 * logger->log() invocation as a invoke_function callback
277 */
278 static void log_cb(log_entry_t *entry, log_data_t *data)
279 {
280 if (entry->levels[data->group] < data->level)
281 {
282 return;
283 }
284 entry->logger->log(entry->logger, data->group, data->level,
285 data->thread, data->ike_sa, data->message);
286 }
287
288 METHOD(bus_t, vlog, void,
289 private_bus_t *this, debug_t group, level_t level,
290 char* format, va_list args)
291 {
292 this->log_lock->read_lock(this->log_lock);
293 if (this->max_level[group] >= level)
294 {
295 linked_list_t *loggers = this->loggers[group];
296 log_data_t data;
297 va_list copy;
298 char buf[1024];
299 ssize_t len;
300
301 data.ike_sa = this->thread_sa->get(this->thread_sa);
302 data.thread = thread_current_id();
303 data.group = group;
304 data.level = level;
305 data.message = buf;
306
307 va_copy(copy, args);
308 len = vsnprintf(data.message, sizeof(buf), format, copy);
309 va_end(copy);
310 if (len >= sizeof(buf))
311 {
312 len++;
313 data.message = malloc(len);
314 len = vsnprintf(data.message, len, format, args);
315 }
316 if (len > 0)
317 {
318 loggers->invoke_function(loggers, (linked_list_invoke_t)log_cb,
319 &data);
320 }
321 if (data.message != buf)
322 {
323 free(data.message);
324 }
325 }
326 this->log_lock->unlock(this->log_lock);
327 }
328
329 METHOD(bus_t, log_, void,
330 private_bus_t *this, debug_t group, level_t level, char* format, ...)
331 {
332 va_list args;
333
334 va_start(args, format);
335 vlog(this, group, level, format, args);
336 va_end(args);
337 }
338
339 /**
340 * unregister a listener
341 */
342 static inline void unregister_listener(private_bus_t *this, entry_t *entry,
343 enumerator_t *enumerator)
344 {
345 this->listeners->remove_at(this->listeners, enumerator);
346 free(entry);
347 }
348
349 METHOD(bus_t, alert, void,
350 private_bus_t *this, alert_t alert, ...)
351 {
352 enumerator_t *enumerator;
353 ike_sa_t *ike_sa;
354 entry_t *entry;
355 va_list args;
356 bool keep;
357
358 ike_sa = this->thread_sa->get(this->thread_sa);
359
360 this->mutex->lock(this->mutex);
361 enumerator = this->listeners->create_enumerator(this->listeners);
362 while (enumerator->enumerate(enumerator, &entry))
363 {
364 if (entry->calling || !entry->listener->alert)
365 {
366 continue;
367 }
368 entry->calling++;
369 va_start(args, alert);
370 keep = entry->listener->alert(entry->listener, ike_sa, alert, args);
371 va_end(args);
372 entry->calling--;
373 if (!keep)
374 {
375 unregister_listener(this, entry, enumerator);
376 }
377 }
378 enumerator->destroy(enumerator);
379 this->mutex->unlock(this->mutex);
380 }
381
382 METHOD(bus_t, ike_state_change, void,
383 private_bus_t *this, ike_sa_t *ike_sa, ike_sa_state_t state)
384 {
385 enumerator_t *enumerator;
386 entry_t *entry;
387 bool keep;
388
389 this->mutex->lock(this->mutex);
390 enumerator = this->listeners->create_enumerator(this->listeners);
391 while (enumerator->enumerate(enumerator, &entry))
392 {
393 if (entry->calling || !entry->listener->ike_state_change)
394 {
395 continue;
396 }
397 entry->calling++;
398 keep = entry->listener->ike_state_change(entry->listener, ike_sa, state);
399 entry->calling--;
400 if (!keep)
401 {
402 unregister_listener(this, entry, enumerator);
403 }
404 }
405 enumerator->destroy(enumerator);
406 this->mutex->unlock(this->mutex);
407 }
408
409 METHOD(bus_t, child_state_change, void,
410 private_bus_t *this, child_sa_t *child_sa, child_sa_state_t state)
411 {
412 enumerator_t *enumerator;
413 ike_sa_t *ike_sa;
414 entry_t *entry;
415 bool keep;
416
417 ike_sa = this->thread_sa->get(this->thread_sa);
418
419 this->mutex->lock(this->mutex);
420 enumerator = this->listeners->create_enumerator(this->listeners);
421 while (enumerator->enumerate(enumerator, &entry))
422 {
423 if (entry->calling || !entry->listener->child_state_change)
424 {
425 continue;
426 }
427 entry->calling++;
428 keep = entry->listener->child_state_change(entry->listener, ike_sa,
429 child_sa, state);
430 entry->calling--;
431 if (!keep)
432 {
433 unregister_listener(this, entry, enumerator);
434 }
435 }
436 enumerator->destroy(enumerator);
437 this->mutex->unlock(this->mutex);
438 }
439
440 METHOD(bus_t, message, void,
441 private_bus_t *this, message_t *message, bool incoming, bool plain)
442 {
443 enumerator_t *enumerator;
444 ike_sa_t *ike_sa;
445 entry_t *entry;
446 bool keep;
447
448 ike_sa = this->thread_sa->get(this->thread_sa);
449
450 this->mutex->lock(this->mutex);
451 enumerator = this->listeners->create_enumerator(this->listeners);
452 while (enumerator->enumerate(enumerator, &entry))
453 {
454 if (entry->calling || !entry->listener->message)
455 {
456 continue;
457 }
458 entry->calling++;
459 keep = entry->listener->message(entry->listener, ike_sa,
460 message, incoming, plain);
461 entry->calling--;
462 if (!keep)
463 {
464 unregister_listener(this, entry, enumerator);
465 }
466 }
467 enumerator->destroy(enumerator);
468 this->mutex->unlock(this->mutex);
469 }
470
471 METHOD(bus_t, ike_keys, void,
472 private_bus_t *this, ike_sa_t *ike_sa, diffie_hellman_t *dh,
473 chunk_t dh_other, chunk_t nonce_i, chunk_t nonce_r,
474 ike_sa_t *rekey, shared_key_t *shared)
475 {
476 enumerator_t *enumerator;
477 entry_t *entry;
478 bool keep;
479
480 this->mutex->lock(this->mutex);
481 enumerator = this->listeners->create_enumerator(this->listeners);
482 while (enumerator->enumerate(enumerator, &entry))
483 {
484 if (entry->calling || !entry->listener->ike_keys)
485 {
486 continue;
487 }
488 entry->calling++;
489 keep = entry->listener->ike_keys(entry->listener, ike_sa, dh, dh_other,
490 nonce_i, nonce_r, rekey, shared);
491 entry->calling--;
492 if (!keep)
493 {
494 unregister_listener(this, entry, enumerator);
495 }
496 }
497 enumerator->destroy(enumerator);
498 this->mutex->unlock(this->mutex);
499 }
500
501 METHOD(bus_t, child_keys, void,
502 private_bus_t *this, child_sa_t *child_sa, bool initiator,
503 diffie_hellman_t *dh, chunk_t nonce_i, chunk_t nonce_r)
504 {
505 enumerator_t *enumerator;
506 ike_sa_t *ike_sa;
507 entry_t *entry;
508 bool keep;
509
510 ike_sa = this->thread_sa->get(this->thread_sa);
511
512 this->mutex->lock(this->mutex);
513 enumerator = this->listeners->create_enumerator(this->listeners);
514 while (enumerator->enumerate(enumerator, &entry))
515 {
516 if (entry->calling || !entry->listener->child_keys)
517 {
518 continue;
519 }
520 entry->calling++;
521 keep = entry->listener->child_keys(entry->listener, ike_sa,
522 child_sa, initiator, dh, nonce_i, nonce_r);
523 entry->calling--;
524 if (!keep)
525 {
526 unregister_listener(this, entry, enumerator);
527 }
528 }
529 enumerator->destroy(enumerator);
530 this->mutex->unlock(this->mutex);
531 }
532
533 METHOD(bus_t, child_updown, void,
534 private_bus_t *this, child_sa_t *child_sa, bool up)
535 {
536 enumerator_t *enumerator;
537 ike_sa_t *ike_sa;
538 entry_t *entry;
539 bool keep;
540
541 ike_sa = this->thread_sa->get(this->thread_sa);
542
543 this->mutex->lock(this->mutex);
544 enumerator = this->listeners->create_enumerator(this->listeners);
545 while (enumerator->enumerate(enumerator, &entry))
546 {
547 if (entry->calling || !entry->listener->child_updown)
548 {
549 continue;
550 }
551 entry->calling++;
552 keep = entry->listener->child_updown(entry->listener,
553 ike_sa, child_sa, up);
554 entry->calling--;
555 if (!keep)
556 {
557 unregister_listener(this, entry, enumerator);
558 }
559 }
560 enumerator->destroy(enumerator);
561 this->mutex->unlock(this->mutex);
562 }
563
564 METHOD(bus_t, child_rekey, void,
565 private_bus_t *this, child_sa_t *old, child_sa_t *new)
566 {
567 enumerator_t *enumerator;
568 ike_sa_t *ike_sa;
569 entry_t *entry;
570 bool keep;
571
572 ike_sa = this->thread_sa->get(this->thread_sa);
573
574 this->mutex->lock(this->mutex);
575 enumerator = this->listeners->create_enumerator(this->listeners);
576 while (enumerator->enumerate(enumerator, &entry))
577 {
578 if (entry->calling || !entry->listener->child_rekey)
579 {
580 continue;
581 }
582 entry->calling++;
583 keep = entry->listener->child_rekey(entry->listener, ike_sa,
584 old, new);
585 entry->calling--;
586 if (!keep)
587 {
588 unregister_listener(this, entry, enumerator);
589 }
590 }
591 enumerator->destroy(enumerator);
592 this->mutex->unlock(this->mutex);
593 }
594
595 METHOD(bus_t, ike_updown, void,
596 private_bus_t *this, ike_sa_t *ike_sa, bool up)
597 {
598 enumerator_t *enumerator;
599 entry_t *entry;
600 bool keep;
601
602 this->mutex->lock(this->mutex);
603 enumerator = this->listeners->create_enumerator(this->listeners);
604 while (enumerator->enumerate(enumerator, &entry))
605 {
606 if (entry->calling || !entry->listener->ike_updown)
607 {
608 continue;
609 }
610 entry->calling++;
611 keep = entry->listener->ike_updown(entry->listener, ike_sa, up);
612 entry->calling--;
613 if (!keep)
614 {
615 unregister_listener(this, entry, enumerator);
616 }
617 }
618 enumerator->destroy(enumerator);
619 this->mutex->unlock(this->mutex);
620
621 /* a down event for IKE_SA implicitly downs all CHILD_SAs */
622 if (!up)
623 {
624 enumerator_t *enumerator;
625 child_sa_t *child_sa;
626
627 enumerator = ike_sa->create_child_sa_enumerator(ike_sa);
628 while (enumerator->enumerate(enumerator, (void**)&child_sa))
629 {
630 child_updown(this, child_sa, FALSE);
631 }
632 enumerator->destroy(enumerator);
633 }
634 }
635
636 METHOD(bus_t, ike_rekey, void,
637 private_bus_t *this, ike_sa_t *old, ike_sa_t *new)
638 {
639 enumerator_t *enumerator;
640 entry_t *entry;
641 bool keep;
642
643 this->mutex->lock(this->mutex);
644 enumerator = this->listeners->create_enumerator(this->listeners);
645 while (enumerator->enumerate(enumerator, &entry))
646 {
647 if (entry->calling || !entry->listener->ike_rekey)
648 {
649 continue;
650 }
651 entry->calling++;
652 keep = entry->listener->ike_rekey(entry->listener, old, new);
653 entry->calling--;
654 if (!keep)
655 {
656 unregister_listener(this, entry, enumerator);
657 }
658 }
659 enumerator->destroy(enumerator);
660 this->mutex->unlock(this->mutex);
661 }
662
663 METHOD(bus_t, ike_reestablish, void,
664 private_bus_t *this, ike_sa_t *old, ike_sa_t *new)
665 {
666 enumerator_t *enumerator;
667 entry_t *entry;
668 bool keep;
669
670 this->mutex->lock(this->mutex);
671 enumerator = this->listeners->create_enumerator(this->listeners);
672 while (enumerator->enumerate(enumerator, &entry))
673 {
674 if (entry->calling || !entry->listener->ike_reestablish)
675 {
676 continue;
677 }
678 entry->calling++;
679 keep = entry->listener->ike_reestablish(entry->listener, old, new);
680 entry->calling--;
681 if (!keep)
682 {
683 unregister_listener(this, entry, enumerator);
684 }
685 }
686 enumerator->destroy(enumerator);
687 this->mutex->unlock(this->mutex);
688 }
689
690 METHOD(bus_t, authorize, bool,
691 private_bus_t *this, bool final)
692 {
693 enumerator_t *enumerator;
694 ike_sa_t *ike_sa;
695 entry_t *entry;
696 bool keep, success = TRUE;
697
698 ike_sa = this->thread_sa->get(this->thread_sa);
699
700 this->mutex->lock(this->mutex);
701 enumerator = this->listeners->create_enumerator(this->listeners);
702 while (enumerator->enumerate(enumerator, &entry))
703 {
704 if (entry->calling || !entry->listener->authorize)
705 {
706 continue;
707 }
708 entry->calling++;
709 keep = entry->listener->authorize(entry->listener, ike_sa,
710 final, &success);
711 entry->calling--;
712 if (!keep)
713 {
714 unregister_listener(this, entry, enumerator);
715 }
716 if (!success)
717 {
718 break;
719 }
720 }
721 enumerator->destroy(enumerator);
722 this->mutex->unlock(this->mutex);
723 if (!success)
724 {
725 alert(this, ALERT_AUTHORIZATION_FAILED);
726 }
727 return success;
728 }
729
730 METHOD(bus_t, narrow, void,
731 private_bus_t *this, child_sa_t *child_sa, narrow_hook_t type,
732 linked_list_t *local, linked_list_t *remote)
733 {
734 enumerator_t *enumerator;
735 ike_sa_t *ike_sa;
736 entry_t *entry;
737 bool keep;
738
739 ike_sa = this->thread_sa->get(this->thread_sa);
740
741 this->mutex->lock(this->mutex);
742 enumerator = this->listeners->create_enumerator(this->listeners);
743 while (enumerator->enumerate(enumerator, &entry))
744 {
745 if (entry->calling || !entry->listener->narrow)
746 {
747 continue;
748 }
749 entry->calling++;
750 keep = entry->listener->narrow(entry->listener, ike_sa, child_sa,
751 type, local, remote);
752 entry->calling--;
753 if (!keep)
754 {
755 unregister_listener(this, entry, enumerator);
756 }
757 }
758 enumerator->destroy(enumerator);
759 this->mutex->unlock(this->mutex);
760 }
761
762 METHOD(bus_t, assign_vips, void,
763 private_bus_t *this, ike_sa_t *ike_sa, bool assign)
764 {
765 enumerator_t *enumerator;
766 entry_t *entry;
767 bool keep;
768
769 this->mutex->lock(this->mutex);
770 enumerator = this->listeners->create_enumerator(this->listeners);
771 while (enumerator->enumerate(enumerator, &entry))
772 {
773 if (entry->calling || !entry->listener->assign_vips)
774 {
775 continue;
776 }
777 entry->calling++;
778 keep = entry->listener->assign_vips(entry->listener, ike_sa, assign);
779 entry->calling--;
780 if (!keep)
781 {
782 unregister_listener(this, entry, enumerator);
783 }
784 }
785 enumerator->destroy(enumerator);
786 this->mutex->unlock(this->mutex);
787 }
788
789 METHOD(bus_t, destroy, void,
790 private_bus_t *this)
791 {
792 debug_t group;
793 for (group = 0; group < DBG_MAX; group++)
794 {
795 this->loggers[group]->destroy(this->loggers[group]);
796 }
797 this->loggers[DBG_MAX]->destroy_function(this->loggers[DBG_MAX],
798 (void*)free);
799 this->listeners->destroy_function(this->listeners, (void*)free);
800 this->thread_sa->destroy(this->thread_sa);
801 this->log_lock->destroy(this->log_lock);
802 this->mutex->destroy(this->mutex);
803 free(this);
804 }
805
806 /*
807 * Described in header.
808 */
809 bus_t *bus_create()
810 {
811 private_bus_t *this;
812 debug_t group;
813
814 INIT(this,
815 .public = {
816 .add_listener = _add_listener,
817 .remove_listener = _remove_listener,
818 .add_logger = _add_logger,
819 .remove_logger = _remove_logger,
820 .set_sa = _set_sa,
821 .get_sa = _get_sa,
822 .log = _log_,
823 .vlog = _vlog,
824 .alert = _alert,
825 .ike_state_change = _ike_state_change,
826 .child_state_change = _child_state_change,
827 .message = _message,
828 .ike_keys = _ike_keys,
829 .child_keys = _child_keys,
830 .ike_updown = _ike_updown,
831 .ike_rekey = _ike_rekey,
832 .ike_reestablish = _ike_reestablish,
833 .child_updown = _child_updown,
834 .child_rekey = _child_rekey,
835 .authorize = _authorize,
836 .narrow = _narrow,
837 .assign_vips = _assign_vips,
838 .destroy = _destroy,
839 },
840 .listeners = linked_list_create(),
841 .mutex = mutex_create(MUTEX_TYPE_RECURSIVE),
842 .log_lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
843 .thread_sa = thread_value_create(NULL),
844 );
845
846 for (group = 0; group <= DBG_MAX; group++)
847 {
848 this->loggers[group] = linked_list_create();
849 this->max_level[group] = LEVEL_SILENT;
850 }
851
852 return &this->public;
853 }
854