daemon: Flush shunts before unloading plugins
[strongswan.git] / src / libcharon / control / controller.c
index 11f4038..fd8349e 100644 (file)
@@ -1,4 +1,5 @@
 /*
 /*
+ * Copyright (C) 2011-2012 Tobias Brunner
  * Copyright (C) 2007-2011 Martin Willi
  * Copyright (C) 2011 revosec AG
  * Hochschule fuer Technik Rapperswil
  * Copyright (C) 2007-2011 Martin Willi
  * Copyright (C) 2011 revosec AG
  * Hochschule fuer Technik Rapperswil
 #include <sys/types.h>
 #include <dirent.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <dirent.h>
 #include <sys/stat.h>
-#include <dlfcn.h>
 
 #include <daemon.h>
 #include <library.h>
 
 #include <daemon.h>
 #include <library.h>
-
+#include <threading/thread.h>
+#include <threading/spinlock.h>
+#include <threading/semaphore.h>
 
 typedef struct private_controller_t private_controller_t;
 typedef struct interface_listener_t interface_listener_t;
 
 typedef struct private_controller_t private_controller_t;
 typedef struct interface_listener_t interface_listener_t;
+typedef struct interface_logger_t interface_logger_t;
 
 /**
  * Private data of an stroke_t object.
 
 /**
  * Private data of an stroke_t object.
@@ -40,19 +43,18 @@ struct private_controller_t {
 };
 
 /**
 };
 
 /**
- * helper struct to map listener callbacks to interface callbacks
+ * helper struct for the logger interface
  */
  */
-struct interface_listener_t {
-
+struct interface_logger_t {
        /**
        /**
-        * public bus listener interface
+        * public logger interface
         */
         */
-       listener_t public;
+       logger_t public;
 
        /**
 
        /**
-        * status of the operation, return to method callers
+        * reference to the listener
         */
         */
-       status_t status;
+       interface_listener_t *listener;
 
        /**
         *  interface callback (listener gets redirected to here)
 
        /**
         *  interface callback (listener gets redirected to here)
@@ -63,6 +65,27 @@ struct interface_listener_t {
         * user parameter to pass to callback
         */
        void *param;
         * user parameter to pass to callback
         */
        void *param;
+};
+
+/**
+ * helper struct to map listener callbacks to interface callbacks
+ */
+struct interface_listener_t {
+
+       /**
+        * public bus listener interface
+        */
+       listener_t public;
+
+       /**
+        * logger interface
+        */
+       interface_logger_t logger;
+
+       /**
+        * status of the operation, return to method callers
+        */
+       status_t status;
 
        /**
         * child configuration, used for initiate
 
        /**
         * child configuration, used for initiate
@@ -80,14 +103,19 @@ struct interface_listener_t {
        ike_sa_t *ike_sa;
 
        /**
        ike_sa_t *ike_sa;
 
        /**
-        * CHILD_SA to handle
+        * unique ID, used for various methods
         */
         */
-       child_sa_t *child_sa;
+       u_int32_t id;
 
        /**
 
        /**
-        * unique ID, used for various methods
+        * semaphore to implement wait_for_listener()
         */
         */
-       u_int32_t id;
+       semaphore_t *done;
+
+       /**
+        * spinlock to update the IKE_SA handle properly
+        */
+       spinlock_t *lock;
 };
 
 
 };
 
 
@@ -107,20 +135,103 @@ struct interface_job_t {
         * associated listener
         */
        interface_listener_t listener;
         * associated listener
         */
        interface_listener_t listener;
+
+       /**
+        * the job is reference counted as the thread executing a job as well as
+        * the thread waiting in wait_for_listener() require it but either of them
+        * could be done first
+        */
+       refcount_t refcount;
 };
 
 };
 
-METHOD(listener_t, listener_log, bool,
-       interface_listener_t *this, debug_t group, level_t level, int thread,
-       ike_sa_t *ike_sa, char* format, va_list args)
+/**
+ * This function wakes a thread that is waiting in wait_for_listener(),
+ * either from a listener or from a job.
+ */
+static inline bool listener_done(interface_listener_t *listener)
 {
 {
-       if (this->ike_sa == ike_sa)
+       if (listener->done)
        {
        {
-               if (!this->callback(this->param, group, level, ike_sa, format, args))
+               listener->done->post(listener->done);
+       }
+       return FALSE;
+}
+
+/**
+ * thread_cleanup_t handler to unregister a listener.
+ */
+static void listener_unregister(interface_listener_t *listener)
+{
+       charon->bus->remove_listener(charon->bus, &listener->public);
+       charon->bus->remove_logger(charon->bus, &listener->logger.public);
+}
+
+/**
+ * Registers the listener, executes the job and then waits synchronously until
+ * the listener is done or the timeout occurred.
+ *
+ * @note Use 'return listener_done(listener)' to properly unregister a listener
+ *
+ * @param listener  listener to register
+ * @param job       job to execute asynchronously when registered, or NULL
+ * @param timeout   max timeout in ms to listen for events, 0 to disable
+ * @return          TRUE if timed out
+ */
+static bool wait_for_listener(interface_job_t *job, u_int timeout)
+{
+       interface_listener_t *listener = &job->listener;
+       bool old, timed_out = FALSE;
+
+       /* avoid that the job is destroyed too early */
+       ref_get(&job->refcount);
+
+       listener->done = semaphore_create(0);
+
+       charon->bus->add_logger(charon->bus, &listener->logger.public);
+       charon->bus->add_listener(charon->bus, &listener->public);
+       lib->processor->queue_job(lib->processor, &job->public);
+
+       thread_cleanup_push((thread_cleanup_t)listener_unregister, listener);
+       old = thread_cancelability(TRUE);
+       if (timeout)
+       {
+               timed_out = listener->done->timed_wait(listener->done, timeout);
+       }
+       else
+       {
+               listener->done->wait(listener->done);
+       }
+       thread_cancelability(old);
+       thread_cleanup_pop(TRUE);
+       return timed_out;
+}
+
+METHOD(logger_t, listener_log, void,
+       interface_logger_t *this, debug_t group, level_t level, int thread,
+       ike_sa_t *ike_sa, const char *message)
+{
+       ike_sa_t *target;
+
+       this->listener->lock->lock(this->listener->lock);
+       target = this->listener->ike_sa;
+       this->listener->lock->unlock(this->listener->lock);
+
+       if (target == ike_sa)
+       {
+               if (!this->callback(this->param, group, level, ike_sa, message))
                {
                {
-                       return FALSE;
+                       this->listener->status = NEED_MORE;
+                       listener_done(this->listener);
                }
        }
                }
        }
-       return TRUE;
+}
+
+METHOD(logger_t, listener_get_level, level_t,
+       interface_logger_t *this, debug_t group)
+{
+       /* in order to allow callback listeners to decide what they want to log
+        * we request any log message, but only if we actually want logging */
+       return this->callback == controller_cb_empty ? LEVEL_SILENT : LEVEL_PRIVATE;
 }
 
 METHOD(job_t, get_priority_medium, job_priority_t,
 }
 
 METHOD(job_t, get_priority_medium, job_priority_t,
@@ -132,7 +243,13 @@ METHOD(job_t, get_priority_medium, job_priority_t,
 METHOD(listener_t, ike_state_change, bool,
        interface_listener_t *this, ike_sa_t *ike_sa, ike_sa_state_t state)
 {
 METHOD(listener_t, ike_state_change, bool,
        interface_listener_t *this, ike_sa_t *ike_sa, ike_sa_state_t state)
 {
-       if (this->ike_sa == ike_sa)
+       ike_sa_t *target;
+
+       this->lock->lock(this->lock);
+       target = this->ike_sa;
+       this->lock->unlock(this->lock);
+
+       if (target == ike_sa)
        {
                switch (state)
                {
        {
                switch (state)
                {
@@ -144,7 +261,7 @@ METHOD(listener_t, ike_state_change, bool,
                                if (peer_cfg->is_mediation(peer_cfg))
                                {
                                        this->status = SUCCESS;
                                if (peer_cfg->is_mediation(peer_cfg))
                                {
                                        this->status = SUCCESS;
-                                       return FALSE;
+                                       return listener_done(this);
                                }
                                break;
                        }
                                }
                                break;
                        }
@@ -154,7 +271,7 @@ METHOD(listener_t, ike_state_change, bool,
                                {       /* proper termination */
                                        this->status = SUCCESS;
                                }
                                {       /* proper termination */
                                        this->status = SUCCESS;
                                }
-                               return FALSE;
+                               return listener_done(this);
                        default:
                                break;
                }
                        default:
                                break;
                }
@@ -166,13 +283,19 @@ METHOD(listener_t, child_state_change, bool,
        interface_listener_t *this, ike_sa_t *ike_sa, child_sa_t *child_sa,
        child_sa_state_t state)
 {
        interface_listener_t *this, ike_sa_t *ike_sa, child_sa_t *child_sa,
        child_sa_state_t state)
 {
-       if (this->ike_sa == ike_sa)
+       ike_sa_t *target;
+
+       this->lock->lock(this->lock);
+       target = this->ike_sa;
+       this->lock->unlock(this->lock);
+
+       if (target == ike_sa)
        {
                switch (state)
                {
                        case CHILD_INSTALLED:
                                this->status = SUCCESS;
        {
                switch (state)
                {
                        case CHILD_INSTALLED:
                                this->status = SUCCESS;
-                               return FALSE;
+                               return listener_done(this);
                        case CHILD_DESTROYING:
                                switch (child_sa->get_state(child_sa))
                                {
                        case CHILD_DESTROYING:
                                switch (child_sa->get_state(child_sa))
                                {
@@ -180,10 +303,22 @@ METHOD(listener_t, child_state_change, bool,
                                                /* proper delete */
                                                this->status = SUCCESS;
                                                break;
                                                /* proper delete */
                                                this->status = SUCCESS;
                                                break;
+                                       case CHILD_RETRYING:
+                                               /* retrying with a different DH group; survive another
+                                                * initiation round */
+                                               this->status = NEED_MORE;
+                                               return TRUE;
+                                       case CHILD_CREATED:
+                                               if (this->status == NEED_MORE)
+                                               {
+                                                       this->status = FAILED;
+                                                       return TRUE;
+                                               }
+                                               break;
                                        default:
                                                break;
                                }
                                        default:
                                                break;
                                }
-                               return FALSE;
+                               return listener_done(this);
                        default:
                                break;
                }
                        default:
                                break;
                }
@@ -191,13 +326,14 @@ METHOD(listener_t, child_state_change, bool,
        return TRUE;
 }
 
        return TRUE;
 }
 
-METHOD(job_t, recheckin, void,
-       interface_job_t *job)
+METHOD(job_t, destroy_job, void,
+       interface_job_t *this)
 {
 {
-       if (job->listener.ike_sa)
+       if (ref_put(&this->refcount))
        {
        {
-               charon->ike_sa_manager->checkin(charon->ike_sa_manager,
-                                                                               job->listener.ike_sa);
+               this->listener.lock->destroy(this->listener.lock);
+               DESTROY_IF(this->listener.done);
+               free(this);
        }
 }
 
        }
 }
 
@@ -208,7 +344,7 @@ METHOD(controller_t, create_ike_sa_enumerator, enumerator_t*,
                                                                                                         wait);
 }
 
                                                                                                         wait);
 }
 
-METHOD(job_t, initiate_execute, void,
+METHOD(job_t, initiate_execute, job_requeue_t,
        interface_job_t *job)
 {
        ike_sa_t *ike_sa;
        interface_job_t *job)
 {
        ike_sa_t *ike_sa;
@@ -221,14 +357,14 @@ METHOD(job_t, initiate_execute, void,
        {
                listener->child_cfg->destroy(listener->child_cfg);
                peer_cfg->destroy(peer_cfg);
        {
                listener->child_cfg->destroy(listener->child_cfg);
                peer_cfg->destroy(peer_cfg);
-               /* trigger down event to release listener */
-               listener->ike_sa = charon->ike_sa_manager->checkout_new(
-                                                                               charon->ike_sa_manager, IKE_ANY, TRUE);
-               DESTROY_IF(listener->ike_sa);
                listener->status = FAILED;
                listener->status = FAILED;
-               return;
+               /* release listener */
+               listener_done(listener);
+               return JOB_REQUEUE_NONE;
        }
        }
+       listener->lock->lock(listener->lock);
        listener->ike_sa = ike_sa;
        listener->ike_sa = ike_sa;
+       listener->lock->unlock(listener->lock);
 
        if (ike_sa->get_peer_cfg(ike_sa) == NULL)
        {
 
        if (ike_sa->get_peer_cfg(ike_sa) == NULL)
        {
@@ -238,228 +374,269 @@ METHOD(job_t, initiate_execute, void,
 
        if (ike_sa->initiate(ike_sa, listener->child_cfg, 0, NULL, NULL) == SUCCESS)
        {
 
        if (ike_sa->initiate(ike_sa, listener->child_cfg, 0, NULL, NULL) == SUCCESS)
        {
+               if (!listener->logger.callback)
+               {
+                       listener->status = SUCCESS;
+               }
                charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
                charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
-               listener->status = SUCCESS;
        }
        else
        {
        }
        else
        {
+               listener->status = FAILED;
                charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager,
                                                                                                        ike_sa);
                charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager,
                                                                                                        ike_sa);
-               listener->status = FAILED;
        }
        }
+       return JOB_REQUEUE_NONE;
 }
 
 METHOD(controller_t, initiate, status_t,
        private_controller_t *this, peer_cfg_t *peer_cfg, child_cfg_t *child_cfg,
        controller_cb_t callback, void *param, u_int timeout)
 {
 }
 
 METHOD(controller_t, initiate, status_t,
        private_controller_t *this, peer_cfg_t *peer_cfg, child_cfg_t *child_cfg,
        controller_cb_t callback, void *param, u_int timeout)
 {
-       interface_job_t job = {
+       interface_job_t *job;
+       status_t status;
+
+       INIT(job,
                .listener = {
                        .public = {
                .listener = {
                        .public = {
-                               .log = _listener_log,
                                .ike_state_change = _ike_state_change,
                                .child_state_change = _child_state_change,
                        },
                                .ike_state_change = _ike_state_change,
                                .child_state_change = _child_state_change,
                        },
-                       .callback = callback,
-                       .param = param,
+                       .logger = {
+                               .public = {
+                                       .log = _listener_log,
+                                       .get_level = _listener_get_level,
+                               },
+                               .callback = callback,
+                               .param = param,
+                       },
                        .status = FAILED,
                        .child_cfg = child_cfg,
                        .peer_cfg = peer_cfg,
                        .status = FAILED,
                        .child_cfg = child_cfg,
                        .peer_cfg = peer_cfg,
+                       .lock = spinlock_create(),
                },
                .public = {
                        .execute = _initiate_execute,
                        .get_priority = _get_priority_medium,
                },
                .public = {
                        .execute = _initiate_execute,
                        .get_priority = _get_priority_medium,
-                       .destroy = _recheckin,
+                       .destroy = _destroy_job,
                },
                },
-       };
+               .refcount = 1,
+       );
+       job->listener.logger.listener = &job->listener;
+       thread_cleanup_push((void*)destroy_job, job);
+
        if (callback == NULL)
        {
        if (callback == NULL)
        {
-               initiate_execute(&job);
+               initiate_execute(job);
        }
        else
        {
        }
        else
        {
-               if (charon->bus->listen(charon->bus, &job.listener.public, &job.public,
-                                                               timeout))
+               if (wait_for_listener(job, timeout))
                {
                {
-                       job.listener.status = OUT_OF_RES;
+                       job->listener.status = OUT_OF_RES;
                }
        }
                }
        }
-       return job.listener.status;
+       status = job->listener.status;
+       thread_cleanup_pop(TRUE);
+       return status;
 }
 
 }
 
-METHOD(job_t, terminate_ike_execute, void,
+METHOD(job_t, terminate_ike_execute, job_requeue_t,
        interface_job_t *job)
 {
        interface_listener_t *listener = &job->listener;
        interface_job_t *job)
 {
        interface_listener_t *listener = &job->listener;
-       ike_sa_t *ike_sa = listener->ike_sa;
+       u_int32_t unique_id = listener->id;
+       ike_sa_t *ike_sa;
 
 
-       charon->bus->set_sa(charon->bus, ike_sa);
+       ike_sa = charon->ike_sa_manager->checkout_by_id(charon->ike_sa_manager,
+                                                                                                       unique_id);
+       if (!ike_sa)
+       {
+               DBG1(DBG_IKE, "unable to terminate IKE_SA: ID %d not found", unique_id);
+               listener->status = NOT_FOUND;
+               /* release listener */
+               listener_done(listener);
+               return JOB_REQUEUE_NONE;
+       }
+       listener->lock->lock(listener->lock);
+       listener->ike_sa = ike_sa;
+       listener->lock->unlock(listener->lock);
 
        if (ike_sa->delete(ike_sa) != DESTROY_ME)
 
        if (ike_sa->delete(ike_sa) != DESTROY_ME)
-       {
-               charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
-               /* delete failed */
+       {       /* delete failed */
                listener->status = FAILED;
                listener->status = FAILED;
+               charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
        }
        else
        {
        }
        else
        {
+               if (!listener->logger.callback)
+               {
+                       listener->status = SUCCESS;
+               }
                charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager,
                                                                                                        ike_sa);
                charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager,
                                                                                                        ike_sa);
-               listener->status = SUCCESS;
        }
        }
+       return JOB_REQUEUE_NONE;
 }
 
 METHOD(controller_t, terminate_ike, status_t,
        controller_t *this, u_int32_t unique_id,
        controller_cb_t callback, void *param, u_int timeout)
 {
 }
 
 METHOD(controller_t, terminate_ike, status_t,
        controller_t *this, u_int32_t unique_id,
        controller_cb_t callback, void *param, u_int timeout)
 {
-       ike_sa_t *ike_sa;
-       interface_job_t job = {
+       interface_job_t *job;
+       status_t status;
+
+       INIT(job,
                .listener = {
                        .public = {
                .listener = {
                        .public = {
-                               .log = _listener_log,
                                .ike_state_change = _ike_state_change,
                                .child_state_change = _child_state_change,
                        },
                                .ike_state_change = _ike_state_change,
                                .child_state_change = _child_state_change,
                        },
-                       .callback = callback,
-                       .param = param,
+                       .logger = {
+                               .public = {
+                                       .log = _listener_log,
+                                       .get_level = _listener_get_level,
+                               },
+                               .callback = callback,
+                               .param = param,
+                       },
                        .status = FAILED,
                        .id = unique_id,
                        .status = FAILED,
                        .id = unique_id,
+                       .lock = spinlock_create(),
                },
                .public = {
                        .execute = _terminate_ike_execute,
                        .get_priority = _get_priority_medium,
                },
                .public = {
                        .execute = _terminate_ike_execute,
                        .get_priority = _get_priority_medium,
-                       .destroy = _recheckin,
+                       .destroy = _destroy_job,
                },
                },
-       };
-
-       ike_sa = charon->ike_sa_manager->checkout_by_id(charon->ike_sa_manager,
-                                                                                                       unique_id, FALSE);
-       if (ike_sa == NULL)
-       {
-               DBG1(DBG_IKE, "unable to terminate IKE_SA: ID %d not found", unique_id);
-               return NOT_FOUND;
-       }
-       job.listener.ike_sa = ike_sa;
+               .refcount = 1,
+       );
+       job->listener.logger.listener = &job->listener;
+       thread_cleanup_push((void*)destroy_job, job);
 
        if (callback == NULL)
        {
 
        if (callback == NULL)
        {
-               terminate_ike_execute(&job);
+               terminate_ike_execute(job);
        }
        else
        {
        }
        else
        {
-               if (charon->bus->listen(charon->bus, &job.listener.public, &job.public,
-                                                               timeout))
+               if (wait_for_listener(job, timeout))
                {
                {
-                       job.listener.status = OUT_OF_RES;
+                       job->listener.status = OUT_OF_RES;
                }
                }
-               /* checkin of the ike_sa happened in the thread that executed the job */
-               charon->bus->set_sa(charon->bus, NULL);
        }
        }
-       return job.listener.status;
+       status = job->listener.status;
+       thread_cleanup_pop(TRUE);
+       return status;
 }
 
 }
 
-METHOD(job_t, terminate_child_execute, void,
+METHOD(job_t, terminate_child_execute, job_requeue_t,
        interface_job_t *job)
 {
        interface_listener_t *listener = &job->listener;
        interface_job_t *job)
 {
        interface_listener_t *listener = &job->listener;
-       ike_sa_t *ike_sa = listener->ike_sa;
-       child_sa_t *child_sa = listener->child_sa;
+       u_int32_t id = listener->id;
+       child_sa_t *child_sa;
+       ike_sa_t *ike_sa;
+
+       ike_sa = charon->child_sa_manager->checkout_by_id(charon->child_sa_manager,
+                                                                                                         id, &child_sa);
+       if (!ike_sa)
+       {
+               DBG1(DBG_IKE, "unable to terminate, CHILD_SA with ID %d not found", id);
+               listener->status = NOT_FOUND;
+               /* release listener */
+               listener_done(listener);
+               return JOB_REQUEUE_NONE;
+       }
+       listener->lock->lock(listener->lock);
+       listener->ike_sa = ike_sa;
+       listener->lock->unlock(listener->lock);
+
+       if (child_sa->get_state(child_sa) == CHILD_ROUTED)
+       {
+               DBG1(DBG_IKE, "unable to terminate, established "
+                        "CHILD_SA with ID %d not found", id);
+               charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
+               listener->status = NOT_FOUND;
+               /* release listener */
+               listener_done(listener);
+               return JOB_REQUEUE_NONE;
+       }
 
 
-       charon->bus->set_sa(charon->bus, ike_sa);
        if (ike_sa->delete_child_sa(ike_sa, child_sa->get_protocol(child_sa),
                                        child_sa->get_spi(child_sa, TRUE), FALSE) != DESTROY_ME)
        {
        if (ike_sa->delete_child_sa(ike_sa, child_sa->get_protocol(child_sa),
                                        child_sa->get_spi(child_sa, TRUE), FALSE) != DESTROY_ME)
        {
+               if (!listener->logger.callback)
+               {
+                       listener->status = SUCCESS;
+               }
                charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
                charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
-               listener->status = SUCCESS;
        }
        else
        {
        }
        else
        {
+               listener->status = FAILED;
                charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager,
                                                                                                        ike_sa);
                charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager,
                                                                                                        ike_sa);
-               listener->status = FAILED;
        }
        }
+       return JOB_REQUEUE_NONE;
 }
 
 METHOD(controller_t, terminate_child, status_t,
 }
 
 METHOD(controller_t, terminate_child, status_t,
-       controller_t *this, u_int32_t reqid,
+       controller_t *this, u_int32_t unique_id,
        controller_cb_t callback, void *param, u_int timeout)
 {
        controller_cb_t callback, void *param, u_int timeout)
 {
-       ike_sa_t *ike_sa;
-       child_sa_t *child_sa;
-       enumerator_t *enumerator;
-       interface_job_t job = {
+       interface_job_t *job;
+       status_t status;
+
+       INIT(job,
                .listener = {
                        .public = {
                .listener = {
                        .public = {
-                               .log = _listener_log,
                                .ike_state_change = _ike_state_change,
                                .child_state_change = _child_state_change,
                        },
                                .ike_state_change = _ike_state_change,
                                .child_state_change = _child_state_change,
                        },
-                       .callback = callback,
-                       .param = param,
+                       .logger = {
+                               .public = {
+                                       .log = _listener_log,
+                                       .get_level = _listener_get_level,
+                               },
+                               .callback = callback,
+                               .param = param,
+                       },
                        .status = FAILED,
                        .status = FAILED,
-                       .id = reqid,
+                       .id = unique_id,
+                       .lock = spinlock_create(),
                },
                .public = {
                        .execute = _terminate_child_execute,
                        .get_priority = _get_priority_medium,
                },
                .public = {
                        .execute = _terminate_child_execute,
                        .get_priority = _get_priority_medium,
-                       .destroy = _recheckin,
+                       .destroy = _destroy_job,
                },
                },
-       };
-
-       ike_sa = charon->ike_sa_manager->checkout_by_id(charon->ike_sa_manager,
-                                                                                                       reqid, TRUE);
-       if (ike_sa == NULL)
-       {
-               DBG1(DBG_IKE, "unable to terminate, CHILD_SA with ID %d not found",
-                        reqid);
-               return NOT_FOUND;
-       }
-       job.listener.ike_sa = ike_sa;
-
-       enumerator = ike_sa->create_child_sa_enumerator(ike_sa);
-       while (enumerator->enumerate(enumerator, (void**)&child_sa))
-       {
-               if (child_sa->get_state(child_sa) != CHILD_ROUTED &&
-                       child_sa->get_reqid(child_sa) == reqid)
-               {
-                       break;
-               }
-               child_sa = NULL;
-       }
-       enumerator->destroy(enumerator);
-
-       if (child_sa == NULL)
-       {
-               DBG1(DBG_IKE, "unable to terminate, established "
-                        "CHILD_SA with ID %d not found", reqid);
-               charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
-               return NOT_FOUND;
-       }
-       job.listener.child_sa = child_sa;
+               .refcount = 1,
+       );
+       job->listener.logger.listener = &job->listener;
+       thread_cleanup_push((void*)destroy_job, job);
 
        if (callback == NULL)
        {
 
        if (callback == NULL)
        {
-               terminate_child_execute(&job);
+               terminate_child_execute(job);
        }
        else
        {
        }
        else
        {
-               if (charon->bus->listen(charon->bus, &job.listener.public, &job.public,
-                                                               timeout))
+               if (wait_for_listener(job, timeout))
                {
                {
-                       job.listener.status = OUT_OF_RES;
+                       job->listener.status = OUT_OF_RES;
                }
                }
-               /* checkin of the ike_sa happened in the thread that executed the job */
-               charon->bus->set_sa(charon->bus, NULL);
        }
        }
-       return job.listener.status;
+       status = job->listener.status;
+       thread_cleanup_pop(TRUE);
+       return status;
 }
 
 /**
  * See header
  */
 bool controller_cb_empty(void *param, debug_t group, level_t level,
 }
 
 /**
  * See header
  */
 bool controller_cb_empty(void *param, debug_t group, level_t level,
-                                                ike_sa_t *ike_sa, char *format, va_list args)
+                                                ike_sa_t *ike_sa, const char *message)
 {
        return TRUE;
 }
 {
        return TRUE;
 }
@@ -489,4 +666,3 @@ controller_t *controller_create(void)
 
        return &this->public;
 }
 
        return &this->public;
 }
-