summaryrefslogtreecommitdiff
path: root/src/libcharon/bus
diff options
context:
space:
mode:
Diffstat (limited to 'src/libcharon/bus')
-rw-r--r--src/libcharon/bus/bus.c92
-rw-r--r--src/libcharon/bus/bus.h8
-rw-r--r--src/libcharon/bus/listeners/file_logger.c14
-rw-r--r--src/libcharon/bus/listeners/listener.h18
4 files changed, 121 insertions, 11 deletions
diff --git a/src/libcharon/bus/bus.c b/src/libcharon/bus/bus.c
index b46184809..d1c138cd1 100644
--- a/src/libcharon/bus/bus.c
+++ b/src/libcharon/bus/bus.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011-2012 Tobias Brunner
+ * Copyright (C) 2011-2014 Tobias Brunner
* Copyright (C) 2006 Martin Willi
* Hochschule fuer Technik Rapperswil
*
@@ -23,6 +23,31 @@
#include <threading/mutex.h>
#include <threading/rwlock.h>
+/**
+ * These operations allow us to speed up the log level checks on some platforms.
+ * In particular if acquiring the read lock is expensive even in the absence of
+ * any writers.
+ *
+ * Note that while holding the read/write lock the read does not have to be
+ * atomic as the write lock must be held to set the level.
+ */
+#ifdef HAVE_GCC_ATOMIC_OPERATIONS
+
+#define skip_level(ptr, level) (__atomic_load_n(ptr, __ATOMIC_RELAXED) < level)
+#define set_level(ptr, val) __atomic_store_n(ptr, val, __ATOMIC_RELAXED)
+
+#elif defined(HAVE_GCC_SYNC_OPERATIONS)
+
+#define skip_level(ptr, level) (__sync_fetch_and_add(ptr, 0) < level)
+#define set_level(ptr, val) __sync_bool_compare_and_swap(ptr, *ptr, val)
+
+#else
+
+#define skip_level(ptr, level) FALSE
+#define set_level(ptr, val) ({ *ptr = val; })
+
+#endif
+
typedef struct private_bus_t private_bus_t;
/**
@@ -173,11 +198,12 @@ static inline void register_logger(private_bus_t *this, debug_t group,
if (entry->logger->log)
{
- this->max_level[group] = max(this->max_level[group], level);
+ set_level(&this->max_level[group], max(this->max_level[group], level));
}
if (entry->logger->vlog)
{
- this->max_vlevel[group] = max(this->max_vlevel[group], level);
+ set_level(&this->max_vlevel[group],
+ max(this->max_vlevel[group], level));
}
}
@@ -205,6 +231,7 @@ static inline void unregister_logger(private_bus_t *this, logger_t *logger)
if (found)
{
+ level_t level = LEVEL_SILENT, vlevel = LEVEL_SILENT;
debug_t group;
for (group = 0; group < DBG_MAX; group++)
@@ -214,13 +241,19 @@ static inline void unregister_logger(private_bus_t *this, logger_t *logger)
loggers = this->loggers[group];
loggers->remove(loggers, found, NULL);
- this->max_level[group] = LEVEL_SILENT;
- this->max_vlevel[group] = LEVEL_SILENT;
if (loggers->get_first(loggers, (void**)&entry) == SUCCESS)
{
- this->max_level[group] = entry->levels[group];
- this->max_vlevel[group] = entry->levels[group];
+ if (entry->logger->log)
+ {
+ level = entry->levels[group];
+ }
+ if (entry->logger->vlog)
+ {
+ vlevel = entry->levels[group];
+ }
}
+ set_level(&this->max_level[group], level);
+ set_level(&this->max_vlevel[group], vlevel);
}
}
free(found);
@@ -324,6 +357,19 @@ METHOD(bus_t, vlog, void,
linked_list_t *loggers;
log_data_t data;
+ /* NOTE: This is not 100% thread-safe and done here only because it is
+ * performance critical. We therefore ignore the following two issues for
+ * this particular case: 1) We might miss some log messages if another
+ * thread concurrently increases the log level or registers a new logger.
+ * 2) We might have to acquire the read lock below even if it wouldn't be
+ * necessary anymore due to another thread concurrently unregistering a
+ * logger or reducing the level. */
+ if (skip_level(&this->max_level[group], level) &&
+ skip_level(&this->max_vlevel[group], level))
+ {
+ return;
+ }
+
this->log_lock->read_lock(this->log_lock);
loggers = this->loggers[group];
@@ -345,7 +391,9 @@ METHOD(bus_t, vlog, void,
{
len++;
data.message = malloc(len);
- len = vsnprintf(data.message, len, format, args);
+ va_copy(data.args, args);
+ len = vsnprintf(data.message, len, format, data.args);
+ va_end(data.args);
}
if (len > 0)
{
@@ -833,6 +881,33 @@ METHOD(bus_t, assign_vips, void,
this->mutex->unlock(this->mutex);
}
+METHOD(bus_t, handle_vips, void,
+ private_bus_t *this, ike_sa_t *ike_sa, bool handle)
+{
+ 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->handle_vips)
+ {
+ continue;
+ }
+ entry->calling++;
+ keep = entry->listener->handle_vips(entry->listener, ike_sa, handle);
+ entry->calling--;
+ if (!keep)
+ {
+ unregister_listener(this, entry, enumerator);
+ }
+ }
+ enumerator->destroy(enumerator);
+ this->mutex->unlock(this->mutex);
+}
+
/**
* Credential manager hook function to forward bus alerts
*/
@@ -909,6 +984,7 @@ bus_t *bus_create()
.authorize = _authorize,
.narrow = _narrow,
.assign_vips = _assign_vips,
+ .handle_vips = _handle_vips,
.destroy = _destroy,
},
.listeners = linked_list_create(),
diff --git a/src/libcharon/bus/bus.h b/src/libcharon/bus/bus.h
index 4a0ac68e3..1d708c5a5 100644
--- a/src/libcharon/bus/bus.h
+++ b/src/libcharon/bus/bus.h
@@ -412,6 +412,14 @@ struct bus_t {
void (*assign_vips)(bus_t *this, ike_sa_t *ike_sa, bool assign);
/**
+ * Virtual IP handler hook.
+ *
+ * @param ike_sa IKE_SA the VIPs/attributes got handled on
+ * @param assign TRUE after installing attributes, FALSE on release
+ */
+ void (*handle_vips)(bus_t *this, ike_sa_t *ike_sa, bool handle);
+
+ /**
* Destroy the event bus.
*/
void (*destroy) (bus_t *this);
diff --git a/src/libcharon/bus/listeners/file_logger.c b/src/libcharon/bus/listeners/file_logger.c
index 68a386d11..e3661bde6 100644
--- a/src/libcharon/bus/listeners/file_logger.c
+++ b/src/libcharon/bus/listeners/file_logger.c
@@ -50,6 +50,11 @@ struct private_file_logger_t {
FILE *out;
/**
+ * Flush after writing a line?
+ */
+ bool flush_line;
+
+ /**
* Maximum level to log, for each group
*/
level_t levels[DBG_MAX];
@@ -137,6 +142,12 @@ METHOD(logger_t, log_, void,
fprintf(this->out, "%.*s\n", (int)(next - current), current);
current = next + 1;
}
+#ifndef HAVE_SETLINEBUF
+ if (this->flush_line)
+ {
+ fflush(this->out);
+ }
+#endif /* !HAVE_SETLINEBUF */
this->mutex->unlock(this->mutex);
this->lock->unlock(this->lock);
}
@@ -214,14 +225,17 @@ METHOD(file_logger_t, open_, void,
this->filename, strerror(errno));
return;
}
+#ifdef HAVE_SETLINEBUF
if (flush_line)
{
setlinebuf(file);
}
+#endif /* HAVE_SETLINEBUF */
}
this->lock->write_lock(this->lock);
close_file(this);
this->out = file;
+ this->flush_line = flush_line;
this->lock->unlock(this->lock);
}
diff --git a/src/libcharon/bus/listeners/listener.h b/src/libcharon/bus/listeners/listener.h
index 57445df01..abcc765e5 100644
--- a/src/libcharon/bus/listeners/listener.h
+++ b/src/libcharon/bus/listeners/listener.h
@@ -192,10 +192,10 @@ struct listener_t {
narrow_hook_t type, linked_list_t *local, linked_list_t *remote);
/**
- * Virtual IP address assignment hook
+ * Virtual IP address assignment hook.
*
- * This hook gets invoked when a a Virtual IP address is assigned to an
- * IKE_SA (assign = TRUE) and again when it is released (assign = FALSE)
+ * This hook gets invoked after virtual IPs have been assigned to a peer
+ * for a specific IKE_SA, and again before they get released.
*
* @param ike_sa IKE_SA the VIPs are assigned to
* @param assign TRUE if assigned to IKE_SA, FALSE if released
@@ -203,6 +203,18 @@ struct listener_t {
*/
bool (*assign_vips)(listener_t *this, ike_sa_t *ike_sa, bool assign);
+ /**
+ * Virtual IP and configuration attribute handler hook.
+ *
+ * This hook gets invoked after virtual IP and other configuration
+ * attributes just got installed or are about to get uninstalled on a peer
+ * receiving them.
+ *
+ * @param ike_sa IKE_SA the VIPs/attributes are handled on
+ * @param handle TRUE if handled by IKE_SA, FALSE on release
+ * @return TRUE to stay registered, FALSE to unregister
+ */
+ bool (*handle_vips)(listener_t *this, ike_sa_t *ike_sa, bool handle);
};
#endif /** LISTENER_H_ @}*/