summaryrefslogtreecommitdiff
path: root/src/libcharon/sa/shunt_manager.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libcharon/sa/shunt_manager.c')
-rw-r--r--src/libcharon/sa/shunt_manager.c71
1 files changed, 65 insertions, 6 deletions
diff --git a/src/libcharon/sa/shunt_manager.c b/src/libcharon/sa/shunt_manager.c
index 73e1abbf3..1a984435c 100644
--- a/src/libcharon/sa/shunt_manager.c
+++ b/src/libcharon/sa/shunt_manager.c
@@ -1,4 +1,5 @@
/*
+ * Copyright (C) 2015 Tobias Brunner
* Copyright (C) 2011 Andreas Steffen
* HSR Hochschule fuer Technik Rapperswil
*
@@ -18,8 +19,10 @@
#include <hydra.h>
#include <daemon.h>
#include <threading/rwlock.h>
+#include <threading/rwlock_condvar.h>
#include <collections/linked_list.h>
+#define INSTALL_DISABLED ((u_int)~0)
typedef struct private_shunt_manager_t private_shunt_manager_t;
@@ -37,6 +40,21 @@ struct private_shunt_manager_t {
* Installed shunts, as child_cfg_t
*/
linked_list_t *shunts;
+
+ /**
+ * Lock to safely access the list of shunts
+ */
+ rwlock_t *lock;
+
+ /**
+ * Number of threads currently installing shunts, or INSTALL_DISABLED
+ */
+ u_int installing;
+
+ /**
+ * Condvar to signal shunt installation
+ */
+ rwlock_condvar_t *condvar;
};
/**
@@ -117,9 +135,15 @@ METHOD(shunt_manager_t, install, bool,
{
enumerator_t *enumerator;
child_cfg_t *child_cfg;
- bool found = FALSE;
+ bool found = FALSE, success;
/* check if not already installed */
+ this->lock->write_lock(this->lock);
+ if (this->installing == INSTALL_DISABLED)
+ { /* flush() has been called */
+ this->lock->unlock(this->lock);
+ return FALSE;
+ }
enumerator = this->shunts->create_enumerator(this->shunts);
while (enumerator->enumerate(enumerator, &child_cfg))
{
@@ -130,16 +154,29 @@ METHOD(shunt_manager_t, install, bool,
}
}
enumerator->destroy(enumerator);
-
if (found)
{
DBG1(DBG_CFG, "shunt %N policy '%s' already installed",
ipsec_mode_names, child->get_mode(child), child->get_name(child));
+ this->lock->unlock(this->lock);
return TRUE;
}
this->shunts->insert_last(this->shunts, child->get_ref(child));
+ this->installing++;
+ this->lock->unlock(this->lock);
- return install_shunt_policy(child);
+ success = install_shunt_policy(child);
+
+ this->lock->write_lock(this->lock);
+ if (!success)
+ {
+ this->shunts->remove(this->shunts, child, NULL);
+ child->destroy(child);
+ }
+ this->installing--;
+ this->condvar->signal(this->condvar);
+ this->lock->unlock(this->lock);
+ return success;
}
/**
@@ -215,6 +252,7 @@ METHOD(shunt_manager_t, uninstall, bool,
enumerator_t *enumerator;
child_cfg_t *child, *found = NULL;
+ this->lock->write_lock(this->lock);
enumerator = this->shunts->create_enumerator(this->shunts);
while (enumerator->enumerate(enumerator, &child))
{
@@ -226,6 +264,7 @@ METHOD(shunt_manager_t, uninstall, bool,
}
}
enumerator->destroy(enumerator);
+ this->lock->unlock(this->lock);
if (!found)
{
@@ -239,20 +278,37 @@ METHOD(shunt_manager_t, uninstall, bool,
METHOD(shunt_manager_t, create_enumerator, enumerator_t*,
private_shunt_manager_t *this)
{
- return this->shunts->create_enumerator(this->shunts);
+ this->lock->read_lock(this->lock);
+ return enumerator_create_cleaner(
+ this->shunts->create_enumerator(this->shunts),
+ (void*)this->lock->unlock, this->lock);
}
-METHOD(shunt_manager_t, destroy, void,
+METHOD(shunt_manager_t, flush, void,
private_shunt_manager_t *this)
{
child_cfg_t *child;
+ this->lock->write_lock(this->lock);
+ while (this->installing)
+ {
+ this->condvar->wait(this->condvar, this->lock);
+ }
while (this->shunts->remove_last(this->shunts, (void**)&child) == SUCCESS)
{
uninstall_shunt_policy(child);
child->destroy(child);
}
- this->shunts->destroy(this->shunts);
+ this->installing = INSTALL_DISABLED;
+ this->lock->unlock(this->lock);
+}
+
+METHOD(shunt_manager_t, destroy, void,
+ private_shunt_manager_t *this)
+{
+ this->shunts->destroy_offset(this->shunts, offsetof(child_cfg_t, destroy));
+ this->lock->destroy(this->lock);
+ this->condvar->destroy(this->condvar);
free(this);
}
@@ -268,9 +324,12 @@ shunt_manager_t *shunt_manager_create()
.install = _install,
.uninstall = _uninstall,
.create_enumerator = _create_enumerator,
+ .flush = _flush,
.destroy = _destroy,
},
.shunts = linked_list_create(),
+ .lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
+ .condvar = rwlock_condvar_create(),
);
return &this->public;