diff options
Diffstat (limited to 'src/libcharon/bus/bus.c')
-rw-r--r-- | src/libcharon/bus/bus.c | 380 |
1 files changed, 229 insertions, 151 deletions
diff --git a/src/libcharon/bus/bus.c b/src/libcharon/bus/bus.c index bf0ab2286..1f9592e6e 100644 --- a/src/libcharon/bus/bus.c +++ b/src/libcharon/bus/bus.c @@ -1,4 +1,5 @@ /* + * Copyright (C) 2011-2012 Tobias Brunner * Copyright (C) 2006 Martin Willi * Hochschule fuer Technik Rapperswil * @@ -19,8 +20,8 @@ #include <threading/thread.h> #include <threading/thread_value.h> -#include <threading/condvar.h> #include <threading/mutex.h> +#include <threading/rwlock.h> typedef struct private_bus_t private_bus_t; @@ -34,16 +35,34 @@ struct private_bus_t { bus_t public; /** - * List of registered listeners as entry_t's + * List of registered listeners as entry_t. */ linked_list_t *listeners; /** - * mutex to synchronize active listeners, recursively + * List of registered loggers for each log group as log_entry_t. + * Loggers are ordered by descending log level. + * The extra list stores all loggers so we can properly unregister them. + */ + linked_list_t *loggers[DBG_MAX + 1]; + + /** + * Maximum log level of any registered logger for each log group. + * This allows to check quickly if a log message has to be logged at all. + */ + level_t max_level[DBG_MAX + 1]; + + /** + * Mutex for the list of listeners, recursively. */ mutex_t *mutex; /** + * Read-write lock for the list of loggers. + */ + rwlock_t *log_lock; + + /** * Thread local storage the threads IKE_SA */ thread_value_t *thread_sa; @@ -52,7 +71,7 @@ struct private_bus_t { typedef struct entry_t entry_t; /** - * a listener entry, either active or passive + * a listener entry */ struct entry_t { @@ -62,50 +81,42 @@ struct entry_t { listener_t *listener; /** - * is this a active listen() call with a blocking thread - */ - bool blocker; - - /** * are we currently calling this listener */ int calling; - /** - * condvar where active listeners wait - */ - condvar_t *condvar; }; +typedef struct log_entry_t log_entry_t; + /** - * create a listener entry + * a logger entry */ -static entry_t *entry_create(listener_t *listener, bool blocker) -{ - entry_t *this = malloc_thing(entry_t); +struct log_entry_t { - this->listener = listener; - this->blocker = blocker; - this->calling = 0; - this->condvar = condvar_create(CONDVAR_TYPE_DEFAULT); + /** + * registered logger interface + */ + logger_t *logger; - return this; -} + /** + * registered log levels per group + */ + level_t levels[DBG_MAX]; -/** - * destroy an entry_t - */ -static void entry_destroy(entry_t *entry) -{ - entry->condvar->destroy(entry->condvar); - free(entry); -} +}; METHOD(bus_t, add_listener, void, private_bus_t *this, listener_t *listener) { + entry_t *entry; + + INIT(entry, + .listener = listener, + ); + this->mutex->lock(this->mutex); - this->listeners->insert_last(this->listeners, entry_create(listener, FALSE)); + this->listeners->insert_last(this->listeners, entry); this->mutex->unlock(this->mutex); } @@ -122,7 +133,7 @@ METHOD(bus_t, remove_listener, void, if (entry->listener == listener) { this->listeners->remove_at(this->listeners, enumerator); - entry_destroy(entry); + free(entry); break; } } @@ -130,74 +141,107 @@ METHOD(bus_t, remove_listener, void, this->mutex->unlock(this->mutex); } -typedef struct cleanup_data_t cleanup_data_t; - /** - * data to remove a listener using thread_cleanup_t handler + * Register a logger on the given log group according to the requested level */ -struct cleanup_data_t { - /** bus instance */ - private_bus_t *this; - /** listener entry */ - entry_t *entry; -}; - -/** - * thread_cleanup_t handler to remove a listener - */ -static void listener_cleanup(cleanup_data_t *data) +static inline void register_logger(private_bus_t *this, debug_t group, + log_entry_t *entry) { - data->this->listeners->remove(data->this->listeners, data->entry, NULL); - entry_destroy(data->entry); + enumerator_t *enumerator; + linked_list_t *loggers; + log_entry_t *current; + level_t level; + + loggers = this->loggers[group]; + level = entry->levels[group]; + + enumerator = loggers->create_enumerator(loggers); + while (enumerator->enumerate(enumerator, (void**)¤t)) + { + if (current->levels[group] <= level) + { + break; + } + } + loggers->insert_before(loggers, enumerator, entry); + enumerator->destroy(enumerator); + + this->max_level[group] = max(this->max_level[group], level); } -METHOD(bus_t, listen_, bool, - private_bus_t *this, listener_t *listener, job_t *job, u_int timeout) +/** + * Unregister a logger from all log groups (destroys the log_entry_t) + */ +static inline void unregister_logger(private_bus_t *this, logger_t *logger) { - bool old, timed_out = FALSE; - cleanup_data_t data; - timeval_t tv, add; + enumerator_t *enumerator; + linked_list_t *loggers; + log_entry_t *entry, *found = NULL; - if (timeout) + loggers = this->loggers[DBG_MAX]; + enumerator = loggers->create_enumerator(loggers); + while (enumerator->enumerate(enumerator, &entry)) { - add.tv_sec = timeout / 1000; - add.tv_usec = (timeout - (add.tv_sec * 1000)) * 1000; - time_monotonic(&tv); - timeradd(&tv, &add, &tv); + if (entry->logger == logger) + { + loggers->remove_at(loggers, enumerator); + found = entry; + break; + } } + enumerator->destroy(enumerator); - data.this = this; - data.entry = entry_create(listener, TRUE); - - this->mutex->lock(this->mutex); - this->listeners->insert_last(this->listeners, data.entry); - lib->processor->queue_job(lib->processor, job); - thread_cleanup_push((thread_cleanup_t)this->mutex->unlock, this->mutex); - thread_cleanup_push((thread_cleanup_t)listener_cleanup, &data); - old = thread_cancelability(TRUE); - while (data.entry->blocker) + if (found) { - if (timeout) + debug_t group; + for (group = 0; group < DBG_MAX; group++) { - if (data.entry->condvar->timed_wait_abs(data.entry->condvar, - this->mutex, tv)) + if (found->levels[group] > LEVEL_SILENT) { - this->listeners->remove(this->listeners, data.entry, NULL); - timed_out = TRUE; - break; + loggers = this->loggers[group]; + loggers->remove(loggers, found, NULL); + + this->max_level[group] = LEVEL_SILENT; + if (loggers->get_first(loggers, (void**)&entry) == SUCCESS) + { + this->max_level[group] = entry->levels[group]; + } } } - else + free(found); + } +} + +METHOD(bus_t, add_logger, void, + private_bus_t *this, logger_t *logger) +{ + log_entry_t *entry; + debug_t group; + + INIT(entry, + .logger = logger, + ); + + this->log_lock->write_lock(this->log_lock); + unregister_logger(this, logger); + for (group = 0; group < DBG_MAX; group++) + { + entry->levels[group] = logger->get_level(logger, group); + if (entry->levels[group] > LEVEL_SILENT) { - data.entry->condvar->wait(data.entry->condvar, this->mutex); + register_logger(this, group, entry); } } - thread_cancelability(old); - thread_cleanup_pop(FALSE); - /* unlock mutex */ - thread_cleanup_pop(TRUE); - entry_destroy(data.entry); - return timed_out; + this->loggers[DBG_MAX]->insert_last(this->loggers[DBG_MAX], entry); + this->log_lock->unlock(this->log_lock); +} + +METHOD(bus_t, remove_logger, void, + private_bus_t *this, logger_t *logger) +{ + this->log_lock->write_lock(this->log_lock); + unregister_logger(this, logger); + this->log_lock->unlock(this->log_lock); } METHOD(bus_t, set_sa, void, @@ -224,66 +268,61 @@ typedef struct { debug_t group; /** debug level */ level_t level; - /** format string */ - char *format; - /** argument list */ - va_list args; + /** message */ + char *message; } log_data_t; /** - * listener->log() invocation as a list remove callback + * logger->log() invocation as a invoke_function callback */ -static bool log_cb(entry_t *entry, log_data_t *data) +static void log_cb(log_entry_t *entry, log_data_t *data) { - va_list args; - - if (entry->calling || !entry->listener->log) - { /* avoid recursive calls */ - return FALSE; - } - entry->calling++; - va_copy(args, data->args); - if (!entry->listener->log(entry->listener, data->group, data->level, - data->thread, data->ike_sa, data->format, args)) + if (entry->levels[data->group] < data->level) { - if (entry->blocker) - { - entry->blocker = FALSE; - entry->condvar->signal(entry->condvar); - entry->calling--; - } - else - { - entry_destroy(entry); - } - va_end(args); - return TRUE; + return; } - va_end(args); - entry->calling--; - return FALSE; + entry->logger->log(entry->logger, data->group, data->level, + data->thread, data->ike_sa, data->message); } METHOD(bus_t, vlog, void, private_bus_t *this, debug_t group, level_t level, char* format, va_list args) { - log_data_t data; - - data.ike_sa = this->thread_sa->get(this->thread_sa); - data.thread = thread_current_id(); - data.group = group; - data.level = level; - data.format = format; - va_copy(data.args, args); - - this->mutex->lock(this->mutex); - /* We use the remove() method to invoke all listeners. This is cheap and - * does not require an allocation for this performance critical function. */ - this->listeners->remove(this->listeners, &data, (void*)log_cb); - this->mutex->unlock(this->mutex); - - va_end(data.args); + this->log_lock->read_lock(this->log_lock); + if (this->max_level[group] >= level) + { + linked_list_t *loggers = this->loggers[group]; + log_data_t data; + va_list copy; + char buf[1024]; + ssize_t len; + + data.ike_sa = this->thread_sa->get(this->thread_sa); + data.thread = thread_current_id(); + data.group = group; + data.level = level; + data.message = buf; + + va_copy(copy, args); + len = vsnprintf(data.message, sizeof(buf), format, copy); + va_end(copy); + if (len >= sizeof(buf)) + { + data.message = malloc(len); + len = vsnprintf(data.message, len, format, args); + } + if (len > 0) + { + loggers->invoke_function(loggers, (linked_list_invoke_t)log_cb, + &data); + } + if (data.message != buf) + { + free(data.message); + } + } + this->log_lock->unlock(this->log_lock); } METHOD(bus_t, log_, void, @@ -299,19 +338,11 @@ METHOD(bus_t, log_, void, /** * unregister a listener */ -static void unregister_listener(private_bus_t *this, entry_t *entry, - enumerator_t *enumerator) +static inline void unregister_listener(private_bus_t *this, entry_t *entry, + enumerator_t *enumerator) { - if (entry->blocker) - { - entry->blocker = FALSE; - entry->condvar->signal(entry->condvar); - } - else - { - entry_destroy(entry); - } this->listeners->remove_at(this->listeners, enumerator); + free(entry); } METHOD(bus_t, alert, void, @@ -406,7 +437,7 @@ METHOD(bus_t, child_state_change, void, } METHOD(bus_t, message, void, - private_bus_t *this, message_t *message, bool incoming) + private_bus_t *this, message_t *message, bool incoming, bool plain) { enumerator_t *enumerator; ike_sa_t *ike_sa; @@ -425,7 +456,7 @@ METHOD(bus_t, message, void, } entry->calling++; keep = entry->listener->message(entry->listener, ike_sa, - message, incoming); + message, incoming, plain); entry->calling--; if (!keep) { @@ -438,7 +469,8 @@ METHOD(bus_t, message, void, METHOD(bus_t, ike_keys, void, private_bus_t *this, ike_sa_t *ike_sa, diffie_hellman_t *dh, - chunk_t nonce_i, chunk_t nonce_r, ike_sa_t *rekey) + chunk_t dh_other, chunk_t nonce_i, chunk_t nonce_r, + ike_sa_t *rekey, shared_key_t *shared) { enumerator_t *enumerator; entry_t *entry; @@ -453,8 +485,8 @@ METHOD(bus_t, ike_keys, void, continue; } entry->calling++; - keep = entry->listener->ike_keys(entry->listener, ike_sa, dh, - nonce_i, nonce_r, rekey); + keep = entry->listener->ike_keys(entry->listener, ike_sa, dh, dh_other, + nonce_i, nonce_r, rekey, shared); entry->calling--; if (!keep) { @@ -485,8 +517,8 @@ METHOD(bus_t, child_keys, void, continue; } entry->calling++; - keep = entry->listener->child_keys(entry->listener, ike_sa, child_sa, - initiator, dh, nonce_i, nonce_r); + keep = entry->listener->child_keys(entry->listener, ike_sa, + child_sa, initiator, dh, nonce_i, nonce_r); entry->calling--; if (!keep) { @@ -547,7 +579,8 @@ METHOD(bus_t, child_rekey, void, continue; } entry->calling++; - keep = entry->listener->child_rekey(entry->listener, ike_sa, old, new); + keep = entry->listener->child_rekey(entry->listener, ike_sa, + old, new); entry->calling--; if (!keep) { @@ -626,6 +659,33 @@ METHOD(bus_t, ike_rekey, void, this->mutex->unlock(this->mutex); } +METHOD(bus_t, ike_reestablish, void, + private_bus_t *this, ike_sa_t *old, ike_sa_t *new) +{ + enumerator_t *enumerator; + entry_t *entry; + bool keep; + + this->mutex->lock(this->mutex); + enumerator = this->listeners->create_enumerator(this->listeners); + while (enumerator->enumerate(enumerator, &entry)) + { + if (entry->calling || !entry->listener->ike_reestablish) + { + continue; + } + entry->calling++; + keep = entry->listener->ike_reestablish(entry->listener, old, new); + entry->calling--; + if (!keep) + { + unregister_listener(this, entry, enumerator); + } + } + enumerator->destroy(enumerator); + this->mutex->unlock(this->mutex); +} + METHOD(bus_t, authorize, bool, private_bus_t *this, bool final) { @@ -697,9 +757,17 @@ METHOD(bus_t, narrow, void, METHOD(bus_t, destroy, void, private_bus_t *this) { + debug_t group; + for (group = 0; group < DBG_MAX; group++) + { + this->loggers[group]->destroy(this->loggers[group]); + } + this->loggers[DBG_MAX]->destroy_function(this->loggers[DBG_MAX], + (void*)free); + this->listeners->destroy_function(this->listeners, (void*)free); this->thread_sa->destroy(this->thread_sa); + this->log_lock->destroy(this->log_lock); this->mutex->destroy(this->mutex); - this->listeners->destroy_function(this->listeners, (void*)entry_destroy); free(this); } @@ -709,12 +777,14 @@ METHOD(bus_t, destroy, void, bus_t *bus_create() { private_bus_t *this; + debug_t group; INIT(this, .public = { .add_listener = _add_listener, .remove_listener = _remove_listener, - .listen = _listen_, + .add_logger = _add_logger, + .remove_logger = _remove_logger, .set_sa = _set_sa, .get_sa = _get_sa, .log = _log_, @@ -727,6 +797,7 @@ bus_t *bus_create() .child_keys = _child_keys, .ike_updown = _ike_updown, .ike_rekey = _ike_rekey, + .ike_reestablish = _ike_reestablish, .child_updown = _child_updown, .child_rekey = _child_rekey, .authorize = _authorize, @@ -735,9 +806,16 @@ bus_t *bus_create() }, .listeners = linked_list_create(), .mutex = mutex_create(MUTEX_TYPE_RECURSIVE), + .log_lock = rwlock_create(RWLOCK_TYPE_DEFAULT), .thread_sa = thread_value_create(NULL), ); + for (group = 0; group <= DBG_MAX; group++) + { + this->loggers[group] = linked_list_create(); + this->max_level[group] = LEVEL_SILENT; + } + return &this->public; } |