mirror of
https://github.com/asterisk/asterisk.git
synced 2025-09-05 04:11:08 +00:00
Lock the channel before calling the setoption callback
The channel needs to be locked before calling these callback functions. Also, sip_setoption needs to lock the pvt and a check p->rtp is non-null before using it. Review: https://reviewboard.asterisk.org/r/1220/ git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/1.8@324048 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
@@ -218,6 +218,7 @@ static void awesome_locking(struct local_pvt *p, struct ast_channel **outchan, s
|
||||
*outchan = p->chan;
|
||||
}
|
||||
|
||||
/* Called with ast locked */
|
||||
static int local_setoption(struct ast_channel *ast, int option, void * data, int datalen)
|
||||
{
|
||||
int res = 0;
|
||||
@@ -226,27 +227,22 @@ static int local_setoption(struct ast_channel *ast, int option, void * data, int
|
||||
ast_chan_write_info_t *write_info;
|
||||
|
||||
if (option != AST_OPTION_CHANNEL_WRITE) {
|
||||
res = -1;
|
||||
goto setoption_cleanup;
|
||||
return -1;
|
||||
}
|
||||
|
||||
write_info = data;
|
||||
|
||||
if (write_info->version != AST_CHAN_WRITE_INFO_T_VERSION) {
|
||||
ast_log(LOG_ERROR, "The chan_write_info_t type has changed, and this channel hasn't been updated!\n");
|
||||
res = -1;
|
||||
goto setoption_cleanup;
|
||||
return -1
|
||||
}
|
||||
|
||||
/* get the tech pvt */
|
||||
ast_channel_lock(ast);
|
||||
if (!(p = ast->tech_pvt)) {
|
||||
ast_channel_unlock(ast);
|
||||
res = -1;
|
||||
goto setoption_cleanup;
|
||||
return -1;
|
||||
}
|
||||
ao2_ref(p, 1);
|
||||
ast_channel_unlock(ast);
|
||||
ast_channel_unlock(ast); /* Held when called, unlock before locking another channel */
|
||||
|
||||
/* get the channel we are supposed to write to */
|
||||
ao2_lock(p);
|
||||
@@ -273,6 +269,7 @@ setoption_cleanup:
|
||||
if (otherchan) {
|
||||
ast_channel_unref(otherchan);
|
||||
}
|
||||
ast_channel_lock(ast); /* Lock back before we leave */
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -349,6 +346,7 @@ static struct ast_channel *local_bridgedchannel(struct ast_channel *chan, struct
|
||||
return bridged;
|
||||
}
|
||||
|
||||
/* Called with ast locked */
|
||||
static int local_queryoption(struct ast_channel *ast, int option, void *data, int *datalen)
|
||||
{
|
||||
struct local_pvt *p;
|
||||
@@ -362,21 +360,18 @@ static int local_queryoption(struct ast_channel *ast, int option, void *data, in
|
||||
}
|
||||
|
||||
/* for some reason the channel is not locked in channel.c when this function is called */
|
||||
ast_channel_lock(ast);
|
||||
if (!(p = ast->tech_pvt)) {
|
||||
ast_channel_unlock(ast);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ao2_lock(p);
|
||||
if (!(tmp = IS_OUTBOUND(ast, p) ? p->owner : p->chan)) {
|
||||
ao2_unlock(p);
|
||||
ast_channel_unlock(ast);
|
||||
return -1;
|
||||
}
|
||||
ast_channel_ref(tmp);
|
||||
ao2_unlock(p);
|
||||
ast_channel_unlock(ast);
|
||||
ast_channel_unlock(ast); /* Held when called, unlock before locking another channel */
|
||||
|
||||
ast_channel_lock(tmp);
|
||||
if (!(bridged = ast_bridged_channel(tmp))) {
|
||||
@@ -395,6 +390,7 @@ query_cleanup:
|
||||
if (tmp) {
|
||||
tmp = ast_channel_unref(tmp);
|
||||
}
|
||||
ast_channel_lock(ast); /* Lock back before we leave */
|
||||
|
||||
return res;
|
||||
}
|
||||
|
@@ -4226,15 +4226,23 @@ static int sip_setoption(struct ast_channel *chan, int option, void *data, int d
|
||||
int res = -1;
|
||||
struct sip_pvt *p = chan->tech_pvt;
|
||||
|
||||
sip_pvt_lock(p);
|
||||
|
||||
switch (option) {
|
||||
case AST_OPTION_FORMAT_READ:
|
||||
res = ast_rtp_instance_set_read_format(p->rtp, *(int *) data);
|
||||
if (p->rtp) {
|
||||
res = ast_rtp_instance_set_read_format(p->rtp, *(int *) data);
|
||||
}
|
||||
break;
|
||||
case AST_OPTION_FORMAT_WRITE:
|
||||
res = ast_rtp_instance_set_write_format(p->rtp, *(int *) data);
|
||||
if (p->rtp) {
|
||||
res = ast_rtp_instance_set_write_format(p->rtp, *(int *) data);
|
||||
}
|
||||
break;
|
||||
case AST_OPTION_MAKE_COMPATIBLE:
|
||||
res = ast_rtp_instance_make_compatible(chan, p->rtp, (struct ast_channel *) data);
|
||||
if (p->rtp) {
|
||||
res = ast_rtp_instance_make_compatible(chan, p->rtp, (struct ast_channel *) data);
|
||||
}
|
||||
break;
|
||||
case AST_OPTION_DIGIT_DETECT:
|
||||
if ((ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_INBAND) ||
|
||||
@@ -4262,6 +4270,8 @@ static int sip_setoption(struct ast_channel *chan, int option, void *data, int d
|
||||
break;
|
||||
}
|
||||
|
||||
sip_pvt_unlock(p);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -4273,16 +4283,16 @@ static int sip_queryoption(struct ast_channel *chan, int option, void *data, int
|
||||
struct sip_pvt *p = (struct sip_pvt *) chan->tech_pvt;
|
||||
char *cp;
|
||||
|
||||
sip_pvt_lock(p);
|
||||
|
||||
switch (option) {
|
||||
case AST_OPTION_T38_STATE:
|
||||
/* Make sure we got an ast_t38_state enum passed in */
|
||||
if (*datalen != sizeof(enum ast_t38_state)) {
|
||||
ast_log(LOG_ERROR, "Invalid datalen for AST_OPTION_T38_STATE option. Expected %d, got %d\n", (int)sizeof(enum ast_t38_state), *datalen);
|
||||
return -1;
|
||||
break;
|
||||
}
|
||||
|
||||
sip_pvt_lock(p);
|
||||
|
||||
/* Now if T38 support is enabled we need to look and see what the current state is to get what we want to report back */
|
||||
if (ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT)) {
|
||||
switch (p->t38.state) {
|
||||
@@ -4298,8 +4308,6 @@ static int sip_queryoption(struct ast_channel *chan, int option, void *data, int
|
||||
}
|
||||
}
|
||||
|
||||
sip_pvt_unlock(p);
|
||||
|
||||
*((enum ast_t38_state *) data) = state;
|
||||
res = 0;
|
||||
|
||||
@@ -4331,6 +4339,8 @@ static int sip_queryoption(struct ast_channel *chan, int option, void *data, int
|
||||
break;
|
||||
}
|
||||
|
||||
sip_pvt_unlock(p);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
@@ -572,10 +572,10 @@ struct ast_channel_tech {
|
||||
/*! \brief Fix up a channel: If a channel is consumed, this is called. Basically update any ->owner links */
|
||||
int (* const fixup)(struct ast_channel *oldchan, struct ast_channel *newchan);
|
||||
|
||||
/*! \brief Set a given option */
|
||||
/*! \brief Set a given option. Called with chan locked */
|
||||
int (* const setoption)(struct ast_channel *chan, int option, void *data, int datalen);
|
||||
|
||||
/*! \brief Query a given option */
|
||||
/*! \brief Query a given option. Called with chan locked */
|
||||
int (* const queryoption)(struct ast_channel *chan, int option, void *data, int *datalen);
|
||||
|
||||
/*! \brief Blind transfer other side (see app_transfer.c and ast_transfer() */
|
||||
|
@@ -7387,28 +7387,42 @@ enum ast_bridge_result ast_channel_bridge(struct ast_channel *c0, struct ast_cha
|
||||
/*! \brief Sets an option on a channel */
|
||||
int ast_channel_setoption(struct ast_channel *chan, int option, void *data, int datalen, int block)
|
||||
{
|
||||
int res;
|
||||
|
||||
ast_channel_lock(chan);
|
||||
if (!chan->tech->setoption) {
|
||||
errno = ENOSYS;
|
||||
ast_channel_unlock(chan);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (block)
|
||||
ast_log(LOG_ERROR, "XXX Blocking not implemented yet XXX\n");
|
||||
|
||||
return chan->tech->setoption(chan, option, data, datalen);
|
||||
res = chan->tech->setoption(chan, option, data, datalen);
|
||||
ast_channel_unlock(chan);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int ast_channel_queryoption(struct ast_channel *chan, int option, void *data, int *datalen, int block)
|
||||
{
|
||||
int res;
|
||||
|
||||
ast_channel_lock(chan);
|
||||
if (!chan->tech->queryoption) {
|
||||
errno = ENOSYS;
|
||||
ast_channel_unlock(chan);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (block)
|
||||
ast_log(LOG_ERROR, "XXX Blocking not implemented yet XXX\n");
|
||||
|
||||
return chan->tech->queryoption(chan, option, data, datalen);
|
||||
res = chan->tech->queryoption(chan, option, data, datalen);
|
||||
ast_channel_unlock(chan);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
struct tonepair_def {
|
||||
|
Reference in New Issue
Block a user