diff options
Diffstat (limited to 'res/res_pjsip_registrar_expire.c')
-rw-r--r-- | res/res_pjsip_registrar_expire.c | 277 |
1 files changed, 57 insertions, 220 deletions
diff --git a/res/res_pjsip_registrar_expire.c b/res/res_pjsip_registrar_expire.c index 399e7bd0e..87edf5390 100644 --- a/res/res_pjsip_registrar_expire.c +++ b/res/res_pjsip_registrar_expire.c @@ -25,265 +25,102 @@ #include "asterisk.h" #include <pjsip.h> +#include <sys/time.h> +#include <signal.h> #include "asterisk/res_pjsip.h" #include "asterisk/module.h" -#include "asterisk/sched.h" -#define CONTACT_AUTOEXPIRE_BUCKETS 977 +/*! \brief Thread keeping things alive */ +static pthread_t check_thread = AST_PTHREADT_NULL; -static struct ao2_container *contact_autoexpire; +/*! \brief The global interval at which to check for contact expiration */ +static unsigned int check_interval; -/*! \brief Scheduler used for automatically expiring contacts */ -static struct ast_sched_context *sched; - -/*! \brief Structure used for contact auto-expiration */ -struct contact_expiration { - /*! \brief Contact that is being auto-expired */ - struct ast_sip_contact *contact; - - /*! \brief Scheduled item for performing expiration */ - int sched; -}; - -/*! \brief Destructor function for contact auto-expiration */ -static void contact_expiration_destroy(void *obj) -{ - struct contact_expiration *expiration = obj; - - ao2_cleanup(expiration->contact); -} - -/*! \brief Hashing function for contact auto-expiration */ -static int contact_expiration_hash(const void *obj, const int flags) +/*! \brief Callback function which deletes a contact */ +static int expire_contact(void *obj, void *arg, int flags) { - const struct contact_expiration *object; - const char *key; - - switch (flags & OBJ_SEARCH_MASK) { - case OBJ_SEARCH_KEY: - key = obj; - break; - case OBJ_SEARCH_OBJECT: - object = obj; - key = ast_sorcery_object_get_id(object->contact); - break; - default: - /* Hash can only work on something with a full key. */ - ast_assert(0); - return 0; - } - return ast_str_hash(key); -} - -/*! \brief Comparison function for contact auto-expiration */ -static int contact_expiration_cmp(void *obj, void *arg, int flags) -{ - const struct contact_expiration *object_left = obj; - const struct contact_expiration *object_right = arg; - const char *right_key = arg; - int cmp; - - switch (flags & OBJ_SEARCH_MASK) { - case OBJ_SEARCH_OBJECT: - right_key = ast_sorcery_object_get_id(object_right->contact); - /* Fall through */ - case OBJ_SEARCH_KEY: - cmp = strcmp(ast_sorcery_object_get_id(object_left->contact), right_key); - break; - case OBJ_SEARCH_PARTIAL_KEY: - /* - * We could also use a partial key struct containing a length - * so strlen() does not get called for every comparison instead. - */ - cmp = strncmp(ast_sorcery_object_get_id(object_left->contact), right_key, - strlen(right_key)); - break; - default: - /* - * What arg points to is specific to this traversal callback - * and has no special meaning to astobj2. - */ - cmp = 0; - break; - } - if (cmp) { - return 0; - } - /* - * At this point the traversal callback is identical to a sorted - * container. - */ - return CMP_MATCH; -} - -/*! \brief Scheduler function which deletes a contact */ -static int contact_expiration_expire(const void *data) -{ - struct contact_expiration *expiration = (void *) data; + struct ast_sip_contact *contact = obj; - expiration->sched = -1; + ast_sorcery_delete(ast_sip_get_sorcery(), contact); - /* This will end up invoking the deleted observer callback, which will perform the unlinking and such */ - ast_sorcery_delete(ast_sip_get_sorcery(), expiration->contact); - ao2_ref(expiration, -1); return 0; } -/*! \brief Observer callback for when a contact is created */ -static void contact_expiration_observer_created(const void *object) +static void *check_expiration_thread(void *data) { - const struct ast_sip_contact *contact = object; - struct contact_expiration *expiration; - int expires = MAX(0, ast_tvdiff_ms(contact->expiration_time, ast_tvnow())); + struct ao2_container *contacts; + struct ast_variable *var; + char *time = alloca(64); - if (ast_tvzero(contact->expiration_time)) { - return; - } + while (check_interval) { + sleep(check_interval); - expiration = ao2_alloc_options(sizeof(*expiration), contact_expiration_destroy, - AO2_ALLOC_OPT_LOCK_NOLOCK); - if (!expiration) { - return; - } + sprintf(time, "%ld", ast_tvnow().tv_sec); + var = ast_variable_new("expiration_time <=", time, ""); - expiration->contact = (struct ast_sip_contact*)contact; - ao2_ref(expiration->contact, +1); + ast_debug(4, "Woke up at %s Interval: %d\n", time, check_interval); - ao2_ref(expiration, +1); - if ((expiration->sched = ast_sched_add(sched, expires, contact_expiration_expire, expiration)) < 0) { - ao2_ref(expiration, -1); - ast_log(LOG_ERROR, "Scheduled expiration for contact '%s' could not be performed, contact may persist past life\n", - ast_sorcery_object_get_id(contact)); - } else { - ao2_link(contact_autoexpire, expiration); - } - ao2_ref(expiration, -1); -} - -/*! \brief Observer callback for when a contact is updated */ -static void contact_expiration_observer_updated(const void *object) -{ - const struct ast_sip_contact *contact = object; - struct contact_expiration *expiration; - int expires = MAX(0, ast_tvdiff_ms(contact->expiration_time, ast_tvnow())); + contacts = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "contact", + AST_RETRIEVE_FLAG_MULTIPLE, var); - expiration = ao2_find(contact_autoexpire, ast_sorcery_object_get_id(contact), - OBJ_SEARCH_KEY); - if (!expiration) { - return; + ast_variables_destroy(var); + if (contacts) { + ast_debug(3, "Expiring %d contacts\n\n", ao2_container_count(contacts)); + ao2_callback(contacts, OBJ_NODATA, expire_contact, NULL); + ao2_ref(contacts, -1); + } } - AST_SCHED_REPLACE_UNREF(expiration->sched, sched, expires, contact_expiration_expire, - expiration, ao2_cleanup(expiration), ao2_cleanup(expiration), ao2_ref(expiration, +1)); - ao2_ref(expiration, -1); + return NULL; } -/*! \brief Observer callback for when a contact is deleted */ -static void contact_expiration_observer_deleted(const void *object) +static void expiration_global_loaded(const char *object_type) { - struct contact_expiration *expiration; - - expiration = ao2_find(contact_autoexpire, ast_sorcery_object_get_id(object), - OBJ_SEARCH_KEY | OBJ_UNLINK); - if (!expiration) { - return; - } - - AST_SCHED_DEL_UNREF(sched, expiration->sched, ao2_cleanup(expiration)); - ao2_ref(expiration, -1); -} - -/*! \brief Observer callbacks for autoexpiring contacts */ -static const struct ast_sorcery_observer contact_expiration_observer = { - .created = contact_expiration_observer_created, - .updated = contact_expiration_observer_updated, - .deleted = contact_expiration_observer_deleted, -}; - -/*! \brief Callback function which deletes a contact if it has expired or sets up auto-expiry */ -static int contact_expiration_setup(void *obj, void *arg, int flags) -{ - struct ast_sip_contact *contact = obj; - int expires = MAX(0, ast_tvdiff_ms(contact->expiration_time, ast_tvnow())); - - if (!expires) { - ast_sorcery_delete(ast_sip_get_sorcery(), contact); + check_interval = ast_sip_get_contact_expiration_check_interval(); + + /* Observer calls are serialized so this is safe without it's own lock */ + if (check_interval) { + if (check_thread == AST_PTHREADT_NULL) { + if (ast_pthread_create_background(&check_thread, NULL, check_expiration_thread, NULL)) { + ast_log(LOG_ERROR, "Could not create thread for checking contact expiration.\n"); + return; + } + ast_debug(3, "Interval = %d, starting thread\n", check_interval); + } } else { - contact_expiration_observer_created(contact); - } - - return 0; -} - -/*! \brief Initialize auto-expiration of any existing contacts */ -static void contact_expiration_initialize_existing(void) -{ - struct ao2_container *contacts; - - contacts = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "contact", - AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL); - if (!contacts) { - return; + if (check_thread != AST_PTHREADT_NULL) { + pthread_kill(check_thread, SIGURG); + check_thread = AST_PTHREADT_NULL; + ast_debug(3, "Interval = 0, shutting thread down\n"); + } } - - ao2_callback(contacts, OBJ_NODATA, contact_expiration_setup, NULL); - ao2_ref(contacts, -1); } -static int unload_observer_delete(void *obj, void *arg, int flags) -{ - struct contact_expiration *expiration = obj; - - AST_SCHED_DEL_UNREF(sched, expiration->sched, ao2_cleanup(expiration)); - return CMP_MATCH; -} +/*! \brief Observer which is used to update our interval when the global setting changes */ +static struct ast_sorcery_observer expiration_global_observer = { + .loaded = expiration_global_loaded, +}; static int unload_module(void) { - ast_sorcery_observer_remove(ast_sip_get_sorcery(), "contact", &contact_expiration_observer); - if (sched) { - ao2_callback(contact_autoexpire, OBJ_MULTIPLE | OBJ_NODATA | OBJ_UNLINK, - unload_observer_delete, NULL); - ast_sched_context_destroy(sched); - sched = NULL; + if (check_thread != AST_PTHREADT_NULL) { + pthread_kill(check_thread, SIGURG); + check_thread = AST_PTHREADT_NULL; } - ao2_cleanup(contact_autoexpire); - contact_autoexpire = NULL; + + ast_sorcery_observer_remove(ast_sip_get_sorcery(), "global", &expiration_global_observer); return 0; } + static int load_module(void) { CHECK_PJSIP_MODULE_LOADED(); - contact_autoexpire = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_NOLOCK, - CONTACT_AUTOEXPIRE_BUCKETS, contact_expiration_hash, contact_expiration_cmp); - if (!contact_autoexpire) { - ast_log(LOG_ERROR, "Could not create container for contact auto-expiration\n"); - return AST_MODULE_LOAD_FAILURE; - } - - if (!(sched = ast_sched_context_create())) { - ast_log(LOG_ERROR, "Could not create scheduler for contact auto-expiration\n"); - unload_module(); - return AST_MODULE_LOAD_FAILURE; - } - - if (ast_sched_start_thread(sched)) { - ast_log(LOG_ERROR, "Could not start scheduler thread for contact auto-expiration\n"); - unload_module(); - return AST_MODULE_LOAD_FAILURE; - } - - contact_expiration_initialize_existing(); - - if (ast_sorcery_observer_add(ast_sip_get_sorcery(), "contact", &contact_expiration_observer)) { - ast_log(LOG_ERROR, "Could not add observer for notifications about contacts for contact auto-expiration\n"); - unload_module(); - return AST_MODULE_LOAD_FAILURE; - } + ast_sorcery_observer_add(ast_sip_get_sorcery(), "global", &expiration_global_observer); + ast_sorcery_reload_object(ast_sip_get_sorcery(), "global"); return AST_MODULE_LOAD_SUCCESS; } |