diff options
author | zuul <zuul@gerrit.asterisk.org> | 2016-09-23 16:59:59 -0500 |
---|---|---|
committer | Gerrit Code Review <gerrit2@gerrit.digium.api> | 2016-09-23 16:59:59 -0500 |
commit | eeeff9487fd5e6be5641f3dad01fd58ea7717aab (patch) | |
tree | 6579b6e38fe2e735eb66ac1895f74d2140650193 /channels | |
parent | 91513c5e8d3557a8dde420e799d3aea2d957b1e8 (diff) | |
parent | d425971009f84d38206392be2b20fc8da4bef820 (diff) |
Merge "chan_sip: Address runaway when realtime peers subscribe to mailboxes"
Diffstat (limited to 'channels')
-rw-r--r-- | channels/chan_sip.c | 59 | ||||
-rw-r--r-- | channels/sip/include/sip.h | 9 |
2 files changed, 48 insertions, 20 deletions
diff --git a/channels/chan_sip.c b/channels/chan_sip.c index 08a16805f..74514b1ef 100644 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -1278,6 +1278,7 @@ static void mwi_event_cb(void *, struct stasis_subscription *, struct stasis_mes static void network_change_stasis_cb(void *data, struct stasis_subscription *sub, struct stasis_message *message); static void acl_change_stasis_cb(void *data, struct stasis_subscription *sub, struct stasis_message *message); static void sip_keepalive_all_peers(void); +#define peer_in_destruction(peer) (ao2_ref(peer, 0) == 0) /*--- Applications, functions, CLI and manager command helpers */ static const char *sip_nat_mode(const struct sip_pvt *p); @@ -5202,13 +5203,24 @@ static void destroy_mailbox(struct sip_mailbox *mailbox) ast_free(mailbox); } +#define REMOVE_MAILBOX_WITH_LOCKED_PEER(__peer) \ +({\ + struct sip_mailbox *__mailbox;\ + ao2_lock(__peer);\ + __mailbox = AST_LIST_REMOVE_HEAD(&(__peer->mailboxes), entry);\ + ao2_unlock(__peer);\ + __mailbox;\ +}) + /*! Destroy all peer-related mailbox subscriptions */ static void clear_peer_mailboxes(struct sip_peer *peer) { struct sip_mailbox *mailbox; - while ((mailbox = AST_LIST_REMOVE_HEAD(&peer->mailboxes, entry))) + /* Lock the peer while accessing/updating the linked list but NOT while destroying the mailbox */ + while ((mailbox = REMOVE_MAILBOX_WITH_LOCKED_PEER(peer))) { destroy_mailbox(mailbox); + } } static void sip_destroy_peer_fn(void *peer) @@ -17260,19 +17272,21 @@ static void sip_peer_hold(struct sip_pvt *p, int hold) /*! \brief Receive MWI events that we have subscribed to */ static void mwi_event_cb(void *userdata, struct stasis_subscription *sub, struct stasis_message *msg) { - char *peer_name = userdata; - struct sip_peer *peer = sip_find_peer(peer_name, NULL, TRUE, FINDALLDEVICES, FALSE, 0); + struct sip_peer *peer = userdata; - if (stasis_subscription_final_message(sub, msg)) { - /* peer can be non-NULL during reload. */ - ao2_cleanup(peer); - ast_free(peer_name); + /* + * peer can't be NULL here but the peer can be in the process of being + * destroyed. If it is, we don't want to send any messages. In most cases, + * the peer is actually gone and there's no sense sending NOTIFYs that will + * never be answered. + */ + if (stasis_subscription_final_message(sub, msg) || peer_in_destruction(peer)) { return; } - if (peer && ast_mwi_state_type() == stasis_message_type(msg)) { + + if (ast_mwi_state_type() == stasis_message_type(msg)) { sip_send_mwi_to_peer(peer, 0); } - ao2_cleanup(peer); } static void network_change_stasis_subscribe(void) @@ -27991,15 +28005,14 @@ static void add_peer_mwi_subs(struct sip_peer *peer) AST_LIST_TRAVERSE(&peer->mailboxes, mailbox, entry) { struct stasis_topic *mailbox_specific_topic; - mailbox->event_sub = stasis_unsubscribe(mailbox->event_sub); + + if (mailbox->status != SIP_MAILBOX_STATUS_NEW) { + continue; + } mailbox_specific_topic = ast_mwi_topic(mailbox->id); if (mailbox_specific_topic) { - char *peer_name = ast_strdup(peer->name); - if (!peer_name) { - return; - } - mailbox->event_sub = stasis_subscribe_pool(mailbox_specific_topic, mwi_event_cb, peer_name); + mailbox->event_sub = stasis_subscribe_pool(mailbox_specific_topic, mwi_event_cb, peer); } } } @@ -29224,7 +29237,9 @@ static int get_cached_mwi(struct sip_peer *peer, int *new, int *old) } /*! \brief Send message waiting indication to alert peer that they've got voicemail - * \note Both peer and associated sip_pvt must be unlocked prior to calling this function + * \note Both peer and associated sip_pvt must be unlocked prior to calling this function. + * It's possible that this function will get called during peer destruction as final messages + * are processed. The peer will still be valid however. * \returns -1 on failure, 0 on success */ static int sip_send_mwi_to_peer(struct sip_peer *peer, int cache_only) @@ -31074,6 +31089,7 @@ static void add_peer_mailboxes(struct sip_peer *peer, const char *value) AST_LIST_TRAVERSE(&peer->mailboxes, mailbox, entry) { if (!strcmp(mailbox->id, mbox)) { duplicate = 1; + mailbox->status = SIP_MAILBOX_STATUS_EXISTING; break; } } @@ -31086,14 +31102,18 @@ static void add_peer_mailboxes(struct sip_peer *peer, const char *value) continue; } strcpy(mailbox->id, mbox); /* SAFE */ + mailbox->status = SIP_MAILBOX_STATUS_NEW; + mailbox->peer = peer; AST_LIST_INSERT_TAIL(&peer->mailboxes, mailbox, entry); } } /*! \brief Build peer from configuration (file or realtime static/dynamic) */ -static struct sip_peer *build_peer(const char *name, struct ast_variable *v, struct ast_variable *alt, int realtime, int devstate_only) +static struct sip_peer *build_peer(const char *name, struct ast_variable *v_head, struct ast_variable *alt, int realtime, int devstate_only) { + /* We preserve the original value of v_head to make analyzing backtraces easier */ + struct ast_variable *v = v_head; struct sip_peer *peer = NULL; struct ast_acl_list *oldacl = NULL; struct ast_acl_list *olddirectmediaacl = NULL; @@ -31157,6 +31177,7 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, str return NULL; } + if (realtime && !ast_test_flag(&global_flags[1], SIP_PAGE2_RTCACHEFRIENDS)) { ast_atomic_fetchadd_int(&rpeerobjs, 1); ast_debug(3, "-REALTIME- peer built. Name: %s. Peer objects: %d\n", name, rpeerobjs); @@ -31206,7 +31227,7 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, str if (!devstate_only) { struct sip_mailbox *mailbox; AST_LIST_TRAVERSE(&peer->mailboxes, mailbox, entry) { - mailbox->delme = 1; + mailbox->status = SIP_MAILBOX_STATUS_UNKNOWN; } } @@ -31666,7 +31687,7 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, str if (!devstate_only) { struct sip_mailbox *mailbox; AST_LIST_TRAVERSE_SAFE_BEGIN(&peer->mailboxes, mailbox, entry) { - if (mailbox->delme) { + if (mailbox->status == SIP_MAILBOX_STATUS_UNKNOWN) { AST_LIST_REMOVE_CURRENT(entry); destroy_mailbox(mailbox); } diff --git a/channels/sip/include/sip.h b/channels/sip/include/sip.h index 92dcd5627..e511d139b 100644 --- a/channels/sip/include/sip.h +++ b/channels/sip/include/sip.h @@ -1237,6 +1237,12 @@ struct sip_pkt { struct ast_str *data; }; +enum sip_mailbox_status { + SIP_MAILBOX_STATUS_UNKNOWN = 0, + SIP_MAILBOX_STATUS_EXISTING, + SIP_MAILBOX_STATUS_NEW, +}; + /*! * \brief A peer's mailbox * @@ -1247,7 +1253,8 @@ struct sip_mailbox { /*! Associated MWI subscription */ struct stasis_subscription *event_sub; AST_LIST_ENTRY(sip_mailbox) entry; - unsigned int delme:1; + struct sip_peer *peer; + enum sip_mailbox_status status; char id[1]; }; |