loader: Add dependency fields to module structures.
[asterisk/asterisk.git] / res / res_pjsip_registrar_expire.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2013, Digium, Inc.
5  *
6  * Joshua Colp <jcolp@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*** MODULEINFO
20         <depend>pjproject</depend>
21         <depend>res_pjsip</depend>
22         <support_level>core</support_level>
23  ***/
24
25 #include "asterisk.h"
26
27 #include <pjsip.h>
28 #include <sys/time.h>
29 #include <signal.h>
30
31 #include "asterisk/res_pjsip.h"
32 #include "asterisk/module.h"
33 #include "asterisk/named_locks.h"
34
35 /*! \brief Thread keeping things alive */
36 static pthread_t check_thread = AST_PTHREADT_NULL;
37
38 /*! \brief The global interval at which to check for contact expiration */
39 static unsigned int check_interval;
40
41 /*! \brief Callback function which deletes a contact */
42 static int expire_contact(void *obj, void *arg, int flags)
43 {
44         struct ast_sip_contact *contact = obj;
45         struct ast_named_lock *lock;
46
47         lock = ast_named_lock_get(AST_NAMED_LOCK_TYPE_MUTEX, "aor", contact->aor);
48         if (!lock) {
49                 return 0;
50         }
51
52         /*
53          * We need to check the expiration again with the aor lock held
54          * in case another thread is attempting to renew the contact.
55          */
56         ao2_lock(lock);
57         if (ast_tvdiff_ms(ast_tvnow(), contact->expiration_time) > 0) {
58                 ast_sip_location_delete_contact(contact);
59         }
60         ao2_unlock(lock);
61         ast_named_lock_put(lock);
62
63         return 0;
64 }
65
66 static void *check_expiration_thread(void *data)
67 {
68         struct ao2_container *contacts;
69         struct ast_variable *var;
70         char *time = alloca(64);
71
72         while (check_interval) {
73                 sleep(check_interval);
74
75                 sprintf(time, "%ld", ast_tvnow().tv_sec);
76                 var = ast_variable_new("expiration_time <=", time, "");
77
78                 ast_debug(4, "Woke up at %s  Interval: %d\n", time, check_interval);
79
80                 contacts = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "contact",
81                         AST_RETRIEVE_FLAG_MULTIPLE, var);
82
83                 ast_variables_destroy(var);
84                 if (contacts) {
85                         ast_debug(3, "Expiring %d contacts\n", ao2_container_count(contacts));
86                         ao2_callback(contacts, OBJ_NODATA, expire_contact, NULL);
87                         ao2_ref(contacts, -1);
88                 }
89         }
90
91         return NULL;
92 }
93
94 static void expiration_global_loaded(const char *object_type)
95 {
96         check_interval = ast_sip_get_contact_expiration_check_interval();
97
98         /* Observer calls are serialized so this is safe without it's own lock */
99         if (check_interval) {
100                 if (check_thread == AST_PTHREADT_NULL) {
101                         if (ast_pthread_create_background(&check_thread, NULL, check_expiration_thread, NULL)) {
102                                 ast_log(LOG_ERROR, "Could not create thread for checking contact expiration.\n");
103                                 return;
104                         }
105                         ast_debug(3, "Interval = %d, starting thread\n", check_interval);
106                 }
107         } else {
108                 if (check_thread != AST_PTHREADT_NULL) {
109                         pthread_kill(check_thread, SIGURG);
110                         pthread_join(check_thread, NULL);
111                         check_thread = AST_PTHREADT_NULL;
112                         ast_debug(3, "Interval = 0, shutting thread down\n");
113                 }
114         }
115 }
116
117 /*! \brief Observer which is used to update our interval when the global setting changes */
118 static struct ast_sorcery_observer expiration_global_observer = {
119         .loaded = expiration_global_loaded,
120 };
121
122 static int unload_module(void)
123 {
124         if (check_thread != AST_PTHREADT_NULL) {
125                 check_interval = 0;
126                 pthread_kill(check_thread, SIGURG);
127                 pthread_join(check_thread, NULL);
128
129                 check_thread = AST_PTHREADT_NULL;
130         }
131
132         ast_sorcery_observer_remove(ast_sip_get_sorcery(), "global", &expiration_global_observer);
133
134         return 0;
135 }
136
137
138 static int load_module(void)
139 {
140         CHECK_PJSIP_MODULE_LOADED();
141
142         ast_sorcery_observer_add(ast_sip_get_sorcery(), "global", &expiration_global_observer);
143         ast_sorcery_reload_object(ast_sip_get_sorcery(), "global");
144
145         return AST_MODULE_LOAD_SUCCESS;
146 }
147
148 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP Contact Auto-Expiration",
149         .support_level = AST_MODULE_SUPPORT_CORE,
150         .load = load_module,
151         .unload = unload_module,
152         .load_pri = AST_MODPRI_APP_DEPEND,
153         .requires = "res_pjsip",
154 );