diff --git a/channel.c b/channel.c index f975db52f5..87d527d37d 100755 --- a/channel.c +++ b/channel.c @@ -29,6 +29,10 @@ #include <asterisk/logger.h> #include <asterisk/file.h> #include <asterisk/translate.h> +#include <asterisk/manager.h> +#include <asterisk/chanvars.h> +#include <asterisk/linkedlists.h> + static int shutting_down = 0; @@ -72,13 +76,15 @@ int ast_check_hangup(struct ast_channel *chan) time_t myt; /* if soft hangup flag, return true */ - if (chan->softhangup) return 1; + if (chan->_softhangup) return 1; + /* if no private structure, return true */ + if (!chan->pvt->pvt) return 1; /* if no hangup scheduled, just return here */ if (!chan->whentohangup) return 0; time(&myt); /* get current time */ /* return, if not yet */ if (chan->whentohangup > myt) return 0; - chan->softhangup = 1; + chan->_softhangup |= AST_SOFTHANGUP_TIMEOUT; return 1; } @@ -90,7 +96,7 @@ void ast_begin_shutdown(int hangup) PTHREAD_MUTEX_LOCK(&chlock); c = channels; while(c) { - c->softhangup = 1; + ast_softhangup(c, AST_SOFTHANGUP_SHUTDOWN); c = c->next; } PTHREAD_MUTEX_UNLOCK(&chlock); @@ -214,9 +220,13 @@ int ast_best_codec(int fmts) /* Okay, we're down to vocoders now, so pick GSM because it's small and easier to translate and sounds pretty good */ AST_FORMAT_GSM, + /* Speex is free, but computationally more expensive than GSM */ + AST_FORMAT_SPEEX, /* Ick, LPC10 sounds terrible, but at least we have code for it, if you're tacky enough to use it */ AST_FORMAT_LPC10, + /* G.729a is faster than 723 and slightly less expensive */ + AST_FORMAT_G729A, /* Down to G.723.1 which is proprietary but at least designed for voice */ AST_FORMAT_G723_1, /* Last and least, MP3 which was of course never designed for real-time voice */ @@ -237,6 +247,9 @@ struct ast_channel *ast_channel_alloc(int needqueue) struct ast_channel_pvt *pvt; int x; int flags; + struct varshead *headp; + + /* If shutting down, don't allocate any new channels */ if (shutting_down) return NULL; @@ -271,12 +284,17 @@ struct ast_channel *ast_channel_alloc(int needqueue) tmp->fds[AST_MAX_FDS-1] = pvt->alertpipe[0]; strncpy(tmp->name, "**Unknown**", sizeof(tmp->name)-1); tmp->pvt = pvt; - tmp->state = AST_STATE_DOWN; + /* Initial state */ + tmp->_state = AST_STATE_DOWN; tmp->stack = -1; tmp->streamid = -1; tmp->appl = NULL; tmp->data = NULL; + headp=&tmp->varshead; ast_pthread_mutex_init(&tmp->lock); + AST_LIST_HEAD_INIT(headp); + tmp->vars=ast_var_assign("tempvar","tempval"); + AST_LIST_INSERT_HEAD(headp,tmp->vars,entries); strncpy(tmp->context, "default", sizeof(tmp->context)-1); strncpy(tmp->language, defaultlanguage, sizeof(tmp->language)-1); strncpy(tmp->exten, "s", sizeof(tmp->exten)-1); @@ -328,8 +346,8 @@ int ast_queue_frame(struct ast_channel *chan, struct ast_frame *fin, int lock) chan->pvt->readq = f; if (chan->pvt->alertpipe[1] > -1) { if (write(chan->pvt->alertpipe[1], &blah, sizeof(blah)) != sizeof(blah)) - ast_log(LOG_WARNING, "Unable to write to alert pipe, frametype/subclass %d/%d (qlen = %d): %s!\n", - f->frametype, f->subclass, qlen, strerror(errno)); + ast_log(LOG_WARNING, "Unable to write to alert pipe on %s, frametype/subclass %d/%d (qlen = %d): %s!\n", + chan->name, f->frametype, f->subclass, qlen, strerror(errno)); } if (qlen > 128) { ast_log(LOG_WARNING, "Exceptionally long queue length queuing to %s\n", chan->name); @@ -408,7 +426,12 @@ void ast_channel_free(struct ast_channel *chan) { struct ast_channel *last=NULL, *cur; int fd; + struct ast_var_t *vardata; struct ast_frame *f, *fp; + struct varshead *headp; + + headp=&chan->varshead; + PTHREAD_MUTEX_LOCK(&chlock); cur = channels; while(cur) { @@ -446,29 +469,53 @@ void ast_channel_free(struct ast_channel *chan) if ((fd = chan->pvt->alertpipe[1]) > -1) close(fd); f = chan->pvt->readq; + chan->pvt->readq = NULL; while(f) { fp = f; f = f->next; ast_frfree(fp); } + + /* loop over the variables list, freeing all data and deleting list items */ + /* no need to lock the list, as the channel is already locked */ + + while (!AST_LIST_EMPTY(headp)) { /* List Deletion. */ + vardata = AST_LIST_FIRST(headp); + AST_LIST_REMOVE_HEAD(headp, entries); +// printf("deleting var %s=%s\n",ast_var_name(vardata),ast_var_value(vardata)); + ast_var_delete(vardata); + } + + free(chan->pvt); free(chan); PTHREAD_MUTEX_UNLOCK(&chlock); } -int ast_softhangup(struct ast_channel *chan) +int ast_softhangup_nolock(struct ast_channel *chan, int cause) { int res = 0; + struct ast_frame f = { AST_FRAME_NULL }; if (option_debug) ast_log(LOG_DEBUG, "Soft-Hanging up channel '%s'\n", chan->name); /* Inform channel driver that we need to be hung up, if it cares */ - chan->softhangup = 1; + chan->_softhangup |= cause; + ast_queue_frame(chan, &f, 0); /* Interrupt any select call or such */ if (chan->blocking) pthread_kill(chan->blocker, SIGURG); return res; } +int ast_softhangup(struct ast_channel *chan, int cause) +{ + int res; + ast_pthread_mutex_lock(&chan->lock); + res = ast_softhangup_nolock(chan, cause); + ast_pthread_mutex_unlock(&chan->lock); + return res; +} + static void free_translation(struct ast_channel *clone) { if (clone->pvt->writetrans) @@ -532,6 +579,9 @@ int ast_hangup(struct ast_channel *chan) ast_log(LOG_DEBUG, "Hanging up zombie '%s'\n", chan->name); ast_pthread_mutex_unlock(&chan->lock); + manager_event(EVENT_FLAG_CALL, "Hangup", + "Channel: %s\r\n", + chan->name); ast_channel_free(chan); return res; } @@ -568,12 +618,12 @@ int ast_answer(struct ast_channel *chan) /* Stop if we're a zombie or need a soft hangup */ if (chan->zombie || ast_check_hangup(chan)) return -1; - switch(chan->state) { + switch(chan->_state) { case AST_STATE_RINGING: case AST_STATE_RING: if (chan->pvt->answer) res = chan->pvt->answer(chan); - chan->state = AST_STATE_UP; + ast_setstate(chan, AST_STATE_UP); if (chan->cdr) ast_cdr_answer(chan->cdr); return res; @@ -589,6 +639,7 @@ void ast_deactivate_generator(struct ast_channel *chan) if (chan->generatordata) { chan->generator->release(chan, chan->generatordata); chan->generatordata = NULL; + chan->generator = NULL; chan->writeinterrupt = 0; } } @@ -816,6 +867,8 @@ struct ast_frame *ast_read(struct ast_channel *chan) /* Stop if we're a zombie or need a soft hangup */ if (chan->zombie || ast_check_hangup(chan)) { + if (chan->generator) + ast_deactivate_generator(chan); pthread_mutex_unlock(&chan->lock); return NULL; } @@ -869,7 +922,9 @@ struct ast_frame *ast_read(struct ast_channel *chan) /* Make sure we always return NULL in the future */ if (!f) { - chan->softhangup = 1; + chan->_softhangup |= AST_SOFTHANGUP_DEV; + if (chan->generator) + ast_deactivate_generator(chan); /* End the CDR if appropriate */ if (chan->cdr) ast_cdr_end(chan->cdr); @@ -881,7 +936,7 @@ struct ast_frame *ast_read(struct ast_channel *chan) f = &null_frame; } else if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_ANSWER)) { /* Answer the CDR */ - chan->state = AST_STATE_UP; + ast_setstate(chan, AST_STATE_UP); ast_cdr_answer(chan->cdr); } pthread_mutex_unlock(&chan->lock); @@ -1012,7 +1067,7 @@ int ast_write(struct ast_channel *chan, struct ast_frame *fr) chan->blocking = 0; /* Consider a write failure to force a soft hangup */ if (res < 0) - chan->softhangup = 1; + chan->_softhangup |= AST_SOFTHANGUP_DEV; return res; } @@ -1074,6 +1129,65 @@ int ast_set_read_format(struct ast_channel *chan, int fmts) return 0; } +struct ast_channel *ast_request_and_dial(char *type, int format, void *data, int timeout, int *outstate) +{ + int state = 0; + struct ast_channel *chan; + struct ast_frame *f; + int res; + + chan = ast_request(type, format, data); + if (chan) { + if (!ast_call(chan, data, 0)) { + while(timeout && (chan->_state != AST_STATE_UP)) { + res = ast_waitfor(chan, timeout); + if (res < 0) { + /* Something not cool, or timed out */ + ast_hangup(chan); + chan = NULL; + break; + } + /* If done, break out */ + if (!res) + break; + if (timeout > -1) + timeout = res; + f = ast_read(chan); + if (!f) { + state = AST_CONTROL_HANGUP; + ast_hangup(chan); + chan = NULL; + break; + } + if (f->frametype == AST_FRAME_CONTROL) { + if (f->subclass == AST_CONTROL_RINGING) + state = AST_CONTROL_RINGING; + else if ((f->subclass == AST_CONTROL_BUSY) || (f->subclass == AST_CONTROL_CONGESTION)) { + state = f->subclass; + break; + } else if (f->subclass == AST_CONTROL_ANSWER) { + state = f->subclass; + break; + } else { + ast_log(LOG_NOTICE, "Don't know what to do with control frame %d\n", f->subclass); + } + } + ast_frfree(f); + } + } else { + ast_hangup(chan); + chan = NULL; + ast_log(LOG_NOTICE, "Unable to request channel %s/%s\n", type, (char *)data); + } + } else + ast_log(LOG_NOTICE, "Unable to request channel %s/%s\n", type, (char *)data); + if (chan && (chan->_state == AST_STATE_UP)) + state = AST_CONTROL_ANSWER; + if (outstate) + *outstate = state; + return chan; +} + struct ast_channel *ast_request(char *type, int format, void *data) { struct chanlist *chan; @@ -1099,6 +1213,13 @@ struct ast_channel *ast_request(char *type, int format, void *data) PTHREAD_MUTEX_UNLOCK(&chlock); if (chan->requester) c = chan->requester(type, capabilities, data); + if (c) { + manager_event(EVENT_FLAG_CALL, "Newchannel", + "Channel: %s\r\n" + "State: %s\r\n" + "Callerid: %s\r\n", + c->name, ast_state2str(c->_state), c->callerid ? c->callerid : "<unknown>"); + } return c; } chan = chan->next; @@ -1258,6 +1379,10 @@ static int ast_do_masquerade(struct ast_channel *original) struct ast_channel *clone = original->masq; int rformat = original->readformat; int wformat = original->writeformat; + char newn[100]; + char orig[100]; + char masqn[100]; + char zombn[100]; #if 0 ast_log(LOG_DEBUG, "Actually Masquerading %s(%d) into the structure of %s(%d)\n", @@ -1279,19 +1404,30 @@ static int ast_do_masquerade(struct ast_channel *original) /* Unlink the masquerade */ original->masq = NULL; clone->masqr = NULL; + + /* Save the original name */ + strncpy(orig, original->name, sizeof(orig) - 1); + /* Save the new name */ + strncpy(newn, clone->name, sizeof(newn) - 1); + /* Create the masq name */ + snprintf(masqn, sizeof(masqn), "%s<MASQ>", newn); /* Copy the name from the clone channel */ - strncpy(original->name, clone->name, sizeof(original->name)-1); + strncpy(original->name, newn, sizeof(original->name)-1); /* Mangle the name of the clone channel */ - strncat(clone->name, "<MASQ>", sizeof(clone->name)); + strncpy(clone->name, masqn, sizeof(clone->name) - 1); + + /* Notify any managers of the change, first the masq then the other */ + manager_event(EVENT_FLAG_CALL, "Rename", "Oldname: %s\r\nNewname: %s\r\n", newn, masqn); + manager_event(EVENT_FLAG_CALL, "Rename", "Oldname: %s\r\nNewname: %s\r\n", orig, newn); /* Swap the guts */ p = original->pvt; original->pvt = clone->pvt; clone->pvt = p; - clone->softhangup = 1; + clone->_softhangup = AST_SOFTHANGUP_DEV; if (clone->pvt->fixup){ @@ -1309,8 +1445,10 @@ static int ast_do_masquerade(struct ast_channel *original) return -1; } + snprintf(zombn, sizeof(zombn), "%s<ZOMBIE>", orig); /* Mangle the name of the clone channel */ - snprintf(clone->name, sizeof(clone->name), "%s<ZOMBIE>", original->name); + strncpy(clone->name, zombn, sizeof(clone->name) - 1); + manager_event(EVENT_FLAG_CALL, "Rename", "Oldname: %s\r\nNewname: %s\r\n", masqn, zombn); /* Keep the same language. */ /* Update the type. */ @@ -1382,7 +1520,44 @@ static int ast_do_masquerade(struct ast_channel *original) if (original->blocking) pthread_kill(original->blocker, SIGURG); ast_log(LOG_DEBUG, "Done Masquerading %s(%d) into the structure of %s(%d)\n", - clone->name, clone->state, original->name, original->state); + clone->name, clone->_state, original->name, original->_state); + return 0; +} + +void ast_set_callerid(struct ast_channel *chan, char *callerid) +{ + if (chan->callerid) + free(chan->callerid); + if (callerid) + chan->callerid = strdup(callerid); + else + chan->callerid = NULL; + manager_event(EVENT_FLAG_CALL, "Newcallerid", + "Channel: %s\r\n" + "Callerid: %s\r\n", + chan->name, chan->callerid ? + chan->callerid : "<Unknown>"); +} + +int ast_setstate(struct ast_channel *chan, int state) +{ + if (chan->_state != state) { + int oldstate = chan->_state; + chan->_state = state; + if (oldstate == AST_STATE_DOWN) { + manager_event(EVENT_FLAG_CALL, "Newchannel", + "Channel: %s\r\n" + "State: %s\r\n" + "Callerid: %s\r\n", + chan->name, ast_state2str(chan->_state), chan->callerid ? chan->callerid : "<unknown>"); + } else { + manager_event(EVENT_FLAG_CALL, "Newstate", + "Channel: %s\r\n" + "State: %s\r\n" + "Callerid: %s\r\n", + chan->name, ast_state2str(chan->_state), chan->callerid ? chan->callerid : "<unknown>"); + } + } return 0; } @@ -1415,6 +1590,12 @@ int ast_channel_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags c1->bridge = c0; cs[0] = c0; cs[1] = c1; + + manager_event(EVENT_FLAG_CALL, "Link", + "Channel1: %s\r\n" + "Channel2: %s\r\n", + c0->name, c1->name); + for (/* ever */;;) { /* Stop if we're a zombie or need a soft hangup */ if (c0->zombie || ast_check_hangup(c0) || c1->zombie || ast_check_hangup(c1)) { @@ -1431,6 +1612,10 @@ int ast_channel_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags if (!(res = c0->pvt->bridge(c0, c1, flags, fo, rc))) { c0->bridge = NULL; c1->bridge = NULL; + manager_event(EVENT_FLAG_CALL, "Unlink", + "Channel1: %s\r\n" + "Channel2: %s\r\n", + c0->name, c1->name); return 0; } /* If they return non-zero then continue on normally. Let "-2" mean don't worry about @@ -1445,6 +1630,10 @@ int ast_channel_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags !(c0->generator || c1->generator)) { if (ast_channel_make_compatible(c0, c1)) { ast_log(LOG_WARNING, "Can't make %s and %s compatible\n", c0->name, c1->name); + manager_event(EVENT_FLAG_CALL, "Unlink", + "Channel1: %s\r\n" + "Channel2: %s\r\n", + c0->name, c1->name); return -1; } } @@ -1519,6 +1708,10 @@ tackygoto: } c0->bridge = NULL; c1->bridge = NULL; + manager_event(EVENT_FLAG_CALL, "Unlink", + "Channel1: %s\r\n" + "Channel2: %s\r\n", + c0->name, c1->name); return res; }