res_pjsip_registrar_expire.c 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  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. /*** MODULEINFO
  19. <depend>pjproject</depend>
  20. <depend>res_pjsip</depend>
  21. <support_level>core</support_level>
  22. ***/
  23. #include "asterisk.h"
  24. #include <pjsip.h>
  25. #include "asterisk/res_pjsip.h"
  26. #include "asterisk/module.h"
  27. #include "asterisk/sched.h"
  28. #define CONTACT_AUTOEXPIRE_BUCKETS 977
  29. static struct ao2_container *contact_autoexpire;
  30. /*! \brief Scheduler used for automatically expiring contacts */
  31. static struct ast_sched_context *sched;
  32. /*! \brief Structure used for contact auto-expiration */
  33. struct contact_expiration {
  34. /*! \brief Contact that is being auto-expired */
  35. struct ast_sip_contact *contact;
  36. /*! \brief Scheduled item for performing expiration */
  37. int sched;
  38. };
  39. /*! \brief Destructor function for contact auto-expiration */
  40. static void contact_expiration_destroy(void *obj)
  41. {
  42. struct contact_expiration *expiration = obj;
  43. ao2_cleanup(expiration->contact);
  44. }
  45. /*! \brief Hashing function for contact auto-expiration */
  46. static int contact_expiration_hash(const void *obj, const int flags)
  47. {
  48. const struct contact_expiration *expiration = obj;
  49. const char *id = obj;
  50. return ast_str_hash(flags & OBJ_KEY ? id : ast_sorcery_object_get_id(expiration->contact));
  51. }
  52. /*! \brief Comparison function for contact auto-expiration */
  53. static int contact_expiration_cmp(void *obj, void *arg, int flags)
  54. {
  55. struct contact_expiration *expiration1 = obj, *expiration2 = arg;
  56. const char *id = arg;
  57. return !strcmp(ast_sorcery_object_get_id(expiration1->contact), flags & OBJ_KEY ? id :
  58. ast_sorcery_object_get_id(expiration2->contact)) ? CMP_MATCH | CMP_STOP : 0;
  59. }
  60. /*! \brief Scheduler function which deletes a contact */
  61. static int contact_expiration_expire(const void *data)
  62. {
  63. RAII_VAR(struct contact_expiration *, expiration, (void*)data, ao2_cleanup);
  64. expiration->sched = -1;
  65. /* This will end up invoking the deleted observer callback, which will perform the unlinking and such */
  66. ast_sorcery_delete(ast_sip_get_sorcery(), expiration->contact);
  67. return 0;
  68. }
  69. /*! \brief Observer callback for when a contact is created */
  70. static void contact_expiration_observer_created(const void *object)
  71. {
  72. const struct ast_sip_contact *contact = object;
  73. RAII_VAR(struct contact_expiration *, expiration, NULL, ao2_cleanup);
  74. int expires = MAX(0, ast_tvdiff_ms(contact->expiration_time, ast_tvnow()));
  75. if (ast_tvzero(contact->expiration_time)) {
  76. return;
  77. }
  78. if (!(expiration = ao2_alloc_options(sizeof(*expiration), contact_expiration_destroy, AO2_ALLOC_OPT_LOCK_NOLOCK))) {
  79. return;
  80. }
  81. expiration->contact = (struct ast_sip_contact*)contact;
  82. ao2_ref(expiration->contact, +1);
  83. ao2_ref(expiration, +1);
  84. if ((expiration->sched = ast_sched_add(sched, expires, contact_expiration_expire, expiration)) < 0) {
  85. ao2_cleanup(expiration);
  86. ast_log(LOG_ERROR, "Scheduled expiration for contact '%s' could not be performed, contact may persist past life\n",
  87. ast_sorcery_object_get_id(contact));
  88. return;
  89. }
  90. ao2_link(contact_autoexpire, expiration);
  91. }
  92. /*! \brief Observer callback for when a contact is updated */
  93. static void contact_expiration_observer_updated(const void *object)
  94. {
  95. const struct ast_sip_contact *contact = object;
  96. RAII_VAR(struct contact_expiration *, expiration, ao2_find(contact_autoexpire, ast_sorcery_object_get_id(contact), OBJ_KEY), ao2_cleanup);
  97. int expires = MAX(0, ast_tvdiff_ms(contact->expiration_time, ast_tvnow()));
  98. if (!expiration) {
  99. return;
  100. }
  101. AST_SCHED_REPLACE_UNREF(expiration->sched, sched, expires, contact_expiration_expire, expiration, ao2_cleanup(expiration), ao2_cleanup(expiration), ao2_ref(expiration, +1));
  102. }
  103. /*! \brief Observer callback for when a contact is deleted */
  104. static void contact_expiration_observer_deleted(const void *object)
  105. {
  106. RAII_VAR(struct contact_expiration *, expiration, ao2_find(contact_autoexpire, ast_sorcery_object_get_id(object), OBJ_KEY | OBJ_UNLINK), ao2_cleanup);
  107. if (!expiration) {
  108. return;
  109. }
  110. AST_SCHED_DEL_UNREF(sched, expiration->sched, ao2_cleanup(expiration));
  111. }
  112. /*! \brief Observer callbacks for autoexpiring contacts */
  113. static const struct ast_sorcery_observer contact_expiration_observer = {
  114. .created = contact_expiration_observer_created,
  115. .updated = contact_expiration_observer_updated,
  116. .deleted = contact_expiration_observer_deleted,
  117. };
  118. /*! \brief Callback function which deletes a contact if it has expired or sets up auto-expiry */
  119. static int contact_expiration_setup(void *obj, void *arg, int flags)
  120. {
  121. struct ast_sip_contact *contact = obj;
  122. int expires = MAX(0, ast_tvdiff_ms(contact->expiration_time, ast_tvnow()));
  123. if (!expires) {
  124. ast_sorcery_delete(ast_sip_get_sorcery(), contact);
  125. } else {
  126. contact_expiration_observer_created(contact);
  127. }
  128. return 0;
  129. }
  130. /*! \brief Initialize auto-expiration of any existing contacts */
  131. static void contact_expiration_initialize_existing(void)
  132. {
  133. RAII_VAR(struct ao2_container *, contacts, NULL, ao2_cleanup);
  134. if (!(contacts = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "contact", AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL))) {
  135. return;
  136. }
  137. ao2_callback(contacts, OBJ_NODATA, contact_expiration_setup, NULL);
  138. }
  139. static int load_module(void)
  140. {
  141. CHECK_PJSIP_MODULE_LOADED();
  142. if (!(contact_autoexpire = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_NOLOCK, CONTACT_AUTOEXPIRE_BUCKETS,
  143. contact_expiration_hash, contact_expiration_cmp))) {
  144. ast_log(LOG_ERROR, "Could not create container for contact auto-expiration\n");
  145. return AST_MODULE_LOAD_FAILURE;
  146. }
  147. if (!(sched = ast_sched_context_create())) {
  148. ast_log(LOG_ERROR, "Could not create scheduler for contact auto-expiration\n");
  149. goto error;
  150. }
  151. if (ast_sched_start_thread(sched)) {
  152. ast_log(LOG_ERROR, "Could not start scheduler thread for contact auto-expiration\n");
  153. goto error;
  154. }
  155. contact_expiration_initialize_existing();
  156. if (ast_sorcery_observer_add(ast_sip_get_sorcery(), "contact", &contact_expiration_observer)) {
  157. ast_log(LOG_ERROR, "Could not add observer for notifications about contacts for contact auto-expiration\n");
  158. goto error;
  159. }
  160. return AST_MODULE_LOAD_SUCCESS;
  161. error:
  162. if (sched) {
  163. ast_sched_context_destroy(sched);
  164. }
  165. ao2_cleanup(contact_autoexpire);
  166. return AST_MODULE_LOAD_FAILURE;
  167. }
  168. static int unload_module(void)
  169. {
  170. ast_sorcery_observer_remove(ast_sip_get_sorcery(), "contact", &contact_expiration_observer);
  171. ast_sched_context_destroy(sched);
  172. ao2_cleanup(contact_autoexpire);
  173. return 0;
  174. }
  175. AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP Contact Auto-Expiration",
  176. .support_level = AST_MODULE_SUPPORT_CORE,
  177. .load = load_module,
  178. .unload = unload_module,
  179. .load_pri = AST_MODPRI_APP_DEPEND,
  180. );