diff options
-rw-r--r-- | channels/chan_local.c | 7 | ||||
-rw-r--r-- | main/cel.c | 99 |
2 files changed, 58 insertions, 48 deletions
diff --git a/channels/chan_local.c b/channels/chan_local.c index 1a8ea17ec..f7522fbba 100644 --- a/channels/chan_local.c +++ b/channels/chan_local.c @@ -1128,8 +1128,11 @@ static struct ast_channel *local_new(struct local_pvt *p, int state, const char ama = ast_channel_amaflags(p->owner); else ama = 0; - if (!(tmp = ast_channel_alloc(1, state, 0, 0, t, p->exten, p->context, linkedid, ama, "Local/%s@%s-%04x;1", p->exten, p->context, randnum)) - || !(tmp2 = ast_channel_alloc(1, AST_STATE_RING, 0, 0, t, p->exten, p->context, linkedid, ama, "Local/%s@%s-%04x;2", p->exten, p->context, randnum))) { + + /* Make sure that the ;2 channel gets the same linkedid as ;1. You can't pass linkedid to both + * allocations since if linkedid isn't set, then each channel will generate its own linkedid. */ + if (!(tmp = ast_channel_alloc(1, state, 0, 0, t, p->exten, p->context, linkedid, ama, "Local/%s@%s-%04x;1", p->exten, p->context, randnum)) + || !(tmp2 = ast_channel_alloc(1, AST_STATE_RING, 0, 0, t, p->exten, p->context, ast_channel_linkedid(tmp), ama, "Local/%s@%s-%04x;2", p->exten, p->context, randnum))) { if (tmp) { tmp = ast_channel_release(tmp); } diff --git a/main/cel.c b/main/cel.c index 8aaa94b2e..676fe101e 100644 --- a/main/cel.c +++ b/main/cel.c @@ -80,6 +80,7 @@ static int64_t eventset; * for when they start and end on a channel. */ static struct ao2_container *appset; +static struct ao2_container *linkedids; /*! * \brief Configured date format for event timestamps @@ -360,42 +361,29 @@ const char *ast_cel_get_ama_flag_name(enum ast_cel_ama_flag flag) /* called whenever a channel is destroyed or a linkedid is changed to * potentially emit a CEL_LINKEDID_END event */ - -struct channel_find_data { - const struct ast_channel *chan; - const char *linkedid; -}; - -static int linkedid_match(void *obj, void *arg, void *data, int flags) -{ - struct ast_channel *c = obj; - struct channel_find_data *find_dat = data; - int res; - - ast_channel_lock(c); - res = (c != find_dat->chan && ast_channel_linkedid(c) && !strcmp(find_dat->linkedid, ast_channel_linkedid(c))); - ast_channel_unlock(c); - - return res ? CMP_MATCH | CMP_STOP : 0; -} - void ast_cel_check_retire_linkedid(struct ast_channel *chan) { const char *linkedid = ast_channel_linkedid(chan); - struct channel_find_data find_dat; + char *lid; /* make sure we need to do all this work */ - if (!ast_strlen_zero(linkedid) && ast_cel_track_event(AST_CEL_LINKEDID_END)) { - struct ast_channel *tmp = NULL; - find_dat.chan = chan; - find_dat.linkedid = linkedid; - if ((tmp = ast_channel_callback(linkedid_match, NULL, &find_dat, 0))) { - tmp = ast_channel_unref(tmp); - } else { - ast_cel_report_event(chan, AST_CEL_LINKEDID_END, NULL, NULL, NULL); - } + if (ast_strlen_zero(linkedid) || !ast_cel_track_event(AST_CEL_LINKEDID_END)) { + return; + } + + if (!(lid = ao2_find(linkedids, (void *) linkedid, OBJ_POINTER))) { + ast_log(LOG_ERROR, "Something weird happened, couldn't find linkedid %s\n", linkedid); + return; + } + + /* We have a ref for each channel with this linkedid, the link and the above find, so if + * before unreffing the channel we have a refcount of 3, we're done. Unlink and report. */ + if (ao2_ref(lid, -1) == 3) { + ao2_unlink(linkedids, lid); + ast_cel_report_event(chan, AST_CEL_LINKEDID_END, NULL, NULL, NULL); } + ao2_ref(lid, -1); } struct ast_channel *ast_cel_fabricate_channel_from_event(const struct ast_event *event) @@ -489,24 +477,36 @@ int ast_cel_report_event(struct ast_channel *chan, enum ast_cel_event_type event struct ast_event *ev; const char *peername = ""; struct ast_channel *peer; - - ast_channel_lock(chan); - peer = ast_bridged_channel(chan); - if (peer) { - ast_channel_ref(peer); - } - ast_channel_unlock(chan); + char *linkedid = ast_strdupa(ast_channel_linkedid(chan)); /* Make sure a reload is not occurring while we're checking to see if this * is an event that we care about. We could lose an important event in this * process otherwise. */ ast_mutex_lock(&reload_lock); + /* Record the linkedid of new channels if we are tracking LINKEDID_END even if we aren't + * reporting on CHANNEL_START so we can track when to send LINKEDID_END */ + if (cel_enabled && ast_cel_track_event(AST_CEL_LINKEDID_END) && event_type == AST_CEL_CHANNEL_START && linkedid) { + char *lid; + if (!(lid = ao2_find(linkedids, (void *) linkedid, OBJ_POINTER))) { + if (!(lid = ao2_alloc(strlen(linkedid) + 1, NULL))) { + ast_mutex_unlock(&reload_lock); + return -1; + } + strcpy(lid, linkedid); + if (!ao2_link(linkedids, lid)) { + ao2_ref(lid, -1); + ast_mutex_unlock(&reload_lock); + return -1; + } + /* Leave both the link and the alloc refs to show a count of 1 + the link */ + } + /* If we've found, go ahead and keep the ref to increment count of how many channels + * have this linkedid. We'll clean it up in check_retire */ + } + if (!cel_enabled || !ast_cel_track_event(event_type)) { ast_mutex_unlock(&reload_lock); - if (peer) { - ast_channel_unref(peer); - } return 0; } @@ -514,9 +514,6 @@ int ast_cel_report_event(struct ast_channel *chan, enum ast_cel_event_type event char *app; if (!(app = ao2_find(appset, (char *) ast_channel_appl(chan), OBJ_POINTER))) { ast_mutex_unlock(&reload_lock); - if (peer) { - ast_channel_unref(peer); - } return 0; } ao2_ref(app, -1); @@ -524,6 +521,13 @@ int ast_cel_report_event(struct ast_channel *chan, enum ast_cel_event_type event ast_mutex_unlock(&reload_lock); + ast_channel_lock(chan); + peer = ast_bridged_channel(chan); + if (peer) { + ast_channel_ref(peer); + } + ast_channel_unlock(chan); + if (peer) { ast_channel_lock(peer); peername = ast_strdupa(ast_channel_name(peer)); @@ -645,6 +649,9 @@ static int app_cmp(void *obj, void *arg, int flags) return !strcasecmp(app1, app2) ? CMP_MATCH | CMP_STOP : 0; } +#define lid_hash app_hash +#define lid_cmp app_cmp + static void ast_cel_engine_term(void) { if (appset) { @@ -658,16 +665,16 @@ int ast_cel_engine_init(void) if (!(appset = ao2_container_alloc(NUM_APP_BUCKETS, app_hash, app_cmp))) { return -1; } - - if (do_reload()) { + if (!(linkedids = ao2_container_alloc(NUM_APP_BUCKETS, lid_hash, lid_cmp))) { ao2_ref(appset, -1); - appset = NULL; return -1; } - if (ast_cli_register(&cli_status)) { + if (do_reload() || ast_cli_register(&cli_status)) { ao2_ref(appset, -1); appset = NULL; + ao2_ref(linkedids, -1); + linkedids = NULL; return -1; } |