Skip to content

Commit 1a14ab0

Browse files
committed
app_chanspy+cel: Release channel iterator before chanspying
Refactor channel spying so it never holds on to a channel iterator. Instead, we recreate the iterator when needed and skip channels that we've seen already, creating the illusion of using an iterator. This change was needed because the iterator caused the yet-unseen channels in the iterator to be referenced by the iterator. This reference ensured that the channel does not get destroyed. (Which is good, because the iterator needs valid channels to work on.) But, hanging on to a channel reference for longer than a short while conflicts with CEL logging. The CEL hangup logging is activated by the destruction of the channel. During chanspy activity, a bunch of channels would stay in limbo. First when the chanspying was done would those channels get their CEL hangup event logged. The fix here is to hang on to channel iterators for only a short while. An alternative fix that makes CEL hangup logging independent of channel destruction was deemed more invasive. This patch makes chanspy channel selection slightly more resource intensive. But that's a small price to pay for correct CEL hangup logging. Fixes: asterisk#68
1 parent 9ef7767 commit 1a14ab0

File tree

1 file changed

+76
-16
lines changed

1 file changed

+76
-16
lines changed

apps/app_chanspy.c

+76-16
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
#include "asterisk/stasis_channels.h"
5757
#include "asterisk/json.h"
5858
#include "asterisk/format_cache.h"
59+
#include "asterisk/vector.h"
5960

6061
#define AST_NAME_STRLEN 256
6162
#define NUM_SPYGROUPS 128
@@ -856,7 +857,7 @@ static int channel_spy(struct ast_channel *chan, struct ast_autochan *spyee_auto
856857
}
857858

858859
static struct ast_autochan *next_channel(struct ast_channel_iterator *iter,
859-
struct ast_channel *chan)
860+
struct ast_channel *chan, struct ast_vector_const_string *skip)
860861
{
861862
struct ast_channel *next;
862863
struct ast_autochan *autochan_store;
@@ -867,8 +868,21 @@ static struct ast_autochan *next_channel(struct ast_channel_iterator *iter,
867868
}
868869

869870
for (; (next = ast_channel_iterator_next(iter)); ast_channel_unref(next)) {
870-
if (!strncmp(ast_channel_name(next), "DAHDI/pseudo", pseudo_len)
871-
|| next == chan) {
871+
const char *name = ast_channel_name(next);
872+
int do_next = 0;
873+
874+
if (!strncmp(name, "DAHDI/pseudo", pseudo_len) || next == chan) {
875+
do_next = 1;
876+
} else {
877+
for (unsigned i = 0; i < AST_VECTOR_SIZE(skip); ++i) {
878+
if (!strcmp(name, AST_VECTOR_GET(skip, i))) {
879+
do_next = 1;
880+
break;
881+
}
882+
}
883+
}
884+
885+
if (do_next) {
872886
continue;
873887
}
874888

@@ -899,7 +913,8 @@ struct channel_spy_context {
899913
const char *name_context;
900914
};
901915

902-
static int channel_spy_consume_iterator(struct ast_channel_iterator *iter,
916+
static int channel_spy_select_one(struct ast_channel_iterator *iter,
917+
struct ast_vector_const_string *channels_spied_upon,
903918
struct ast_channel *chan, int *num_spied_upon,
904919
int *volfactor, const int fd, const struct spy_dtmf_options *user_options,
905920
struct ast_flags *flags, const char *exitcontext, const struct channel_spy_context *ctx)
@@ -908,11 +923,12 @@ static int channel_spy_consume_iterator(struct ast_channel_iterator *iter,
908923
struct ast_channel *prev = NULL;
909924
int res = 0;
910925

911-
for (autochan = next_channel(iter, chan);
926+
for (autochan = next_channel(iter, chan, channels_spied_upon);
912927
autochan;
913928
prev = autochan->chan,
914929
ast_autochan_destroy(autochan),
915-
autochan = next_autochan ?: next_channel(iter, chan),
930+
autochan = next_autochan ?: (
931+
iter ? next_channel(iter, chan, channels_spied_upon) : NULL),
916932
next_autochan = NULL) {
917933
int igrp = !ctx->mygroup;
918934
int ienf = !ctx->myenforced;
@@ -1010,6 +1026,19 @@ static int channel_spy_consume_iterator(struct ast_channel_iterator *iter,
10101026
continue;
10111027
}
10121028

1029+
/* Important! We destroy the channel iterator here, freeing unrelated channels
1030+
* that are held by refcount. We're now holding on to exactly one victim channel. */
1031+
iter = ast_channel_iterator_destroy(iter);
1032+
1033+
/* BUG: In theory, we're leaking (hoarding) memory until either:
1034+
* - the spying channels hangs up, or
1035+
* - we do a full iteration without getting finding a single channel to spy on.
1036+
* When either event occurs, the memory is freed. This is more
1037+
* likely to happen than not. */
1038+
AST_VECTOR_APPEND(channels_spied_upon, ast_strdup(ast_channel_name(autochan->chan)));
1039+
ast_debug(1, "After adding %s we have %zu items in the channels_spied_upon list\n",
1040+
ast_channel_name(autochan->chan), AST_VECTOR_SIZE(channels_spied_upon));
1041+
10131042
if (!ast_test_flag(flags, OPTION_QUIET)) {
10141043
char peer_name[AST_NAME_STRLEN + 5];
10151044
char *ptr, *s;
@@ -1088,19 +1117,24 @@ static int channel_spy_consume_iterator(struct ast_channel_iterator *iter,
10881117
}
10891118
} else if (res == 0 && ast_test_flag(flags, OPTION_EXITONHANGUP)) {
10901119
ast_autochan_destroy(autochan);
1091-
res = -2;
1120+
res = -2; /* propagates stop, but with 0 exit status */
10921121
break;
10931122
}
10941123
}
10951124

1096-
if (ast_test_flag(flags, OPTION_STOP) && !next_autochan) {
1097-
/* a single iteration was completed */
1098-
res = -2;
1125+
if (iter) {
1126+
iter = ast_channel_iterator_destroy(iter);
10991127
}
11001128

11011129
return res;
11021130
}
11031131

1132+
/* Same as ast_free_ptr(), but this allows us to free const char*'s without the
1133+
* compiler complaining about touching const. */
1134+
static void free_const_ptr(const char *ptr) {
1135+
ast_free((char *)ptr);
1136+
}
1137+
11041138
static int common_exec(struct ast_channel *chan, struct ast_flags *flags,
11051139
int volfactor, const int fd, struct spy_dtmf_options *user_options,
11061140
const struct channel_spy_context *ctx)
@@ -1111,6 +1145,7 @@ static int common_exec(struct ast_channel *chan, struct ast_flags *flags,
11111145
int res;
11121146
int num_spied_upon = 1;
11131147
struct ast_channel_iterator *iter = NULL;
1148+
struct ast_vector_const_string channels_spied_upon;
11141149

11151150
if (ast_test_flag(flags, OPTION_EXIT)) {
11161151
const char *c;
@@ -1128,6 +1163,7 @@ static int common_exec(struct ast_channel *chan, struct ast_flags *flags,
11281163

11291164
ast_channel_set_flag(chan, AST_FLAG_SPYING);
11301165

1166+
AST_VECTOR_INIT(&channels_spied_upon, 0);
11311167

11321168
for (;;) {
11331169
if (!ast_test_flag(flags, OPTION_QUIET) && num_spied_upon) {
@@ -1177,7 +1213,6 @@ static int common_exec(struct ast_channel *chan, struct ast_flags *flags,
11771213

11781214
res = ast_waitfordigit(chan, waitms);
11791215
if (res < 0) {
1180-
iter = ast_channel_iterator_destroy(iter);
11811216
ast_channel_clear_flag(chan, AST_FLAG_SPYING);
11821217
break;
11831218
}
@@ -1186,25 +1221,50 @@ static int common_exec(struct ast_channel *chan, struct ast_flags *flags,
11861221
tmp[0] = res;
11871222
tmp[1] = '\0';
11881223
if (!ast_goto_if_exists(chan, exitcontext, tmp, 1)) {
1189-
iter = ast_channel_iterator_destroy(iter);
11901224
break;
11911225
}
11921226
ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
11931227
}
11941228

1229+
/* The channel_spy_select_one spies on at most one channel. It
1230+
* destroys the iterator so the unused channels can be freed asap. */
11951231
num_spied_upon = 0;
1196-
res = channel_spy_consume_iterator(iter, chan, &num_spied_upon, &volfactor, fd, user_options, flags, exitcontext, ctx);
1197-
1198-
iter = ast_channel_iterator_destroy(iter);
1232+
res = channel_spy_select_one(iter, &channels_spied_upon, chan, &num_spied_upon, &volfactor, fd, user_options, flags, exitcontext, ctx);
1233+
iter = NULL;
11991234

12001235
if (res == -2) {
1236+
/* propagated stop */
12011237
res = 0;
12021238
break;
1203-
} else if (res == -1 || ast_check_hangup(chan)) {
1239+
} else if (res == -1) {
1240+
/* stop because of error */
1241+
break;
1242+
} else if (ast_check_hangup(chan)) {
1243+
/* stop because spy hung up */
12041244
break;
12051245
}
1246+
1247+
if (num_spied_upon == 0) {
1248+
if (ast_test_flag(flags, OPTION_STOP)) {
1249+
/* stop after one fully depleted iterator */
1250+
break;
1251+
}
1252+
1253+
/* If we did spy on a channel, we'll keep the channels_spied_upon
1254+
* list. If we spied on nothing, we'll wipe it so we
1255+
* can restart the iteration. */
1256+
AST_VECTOR_RESET(&channels_spied_upon, free_const_ptr);
1257+
}
1258+
}
1259+
1260+
if (iter) {
1261+
iter = ast_channel_iterator_destroy(iter);
12061262
}
12071263

1264+
/* Make sure we free our temp storage. */
1265+
AST_VECTOR_RESET(&channels_spied_upon, free_const_ptr);
1266+
AST_VECTOR_FREE(&channels_spied_upon);
1267+
12081268
ast_channel_clear_flag(chan, AST_FLAG_SPYING);
12091269

12101270
ast_channel_setoption(chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);

0 commit comments

Comments
 (0)