Newer
Older
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
#define BUILD_STATS do { \
o = outgoing; \
found = -1; \
pos = 1; \
numlines = 0; \
watchers[0] = in; \
while(o) { \
/* Keep track of important channels */ \
if (o->stillgoing) { \
stillgoing = 1; \
if (o->chan) { \
watchers[pos++] = o->chan; \
found = 1; \
} \
} \
o = o->next; \
numlines++; \
} \
} while(0)
static struct localuser *wait_for_answer(struct queue_ent *qe, struct localuser *outgoing, int *to, struct ast_flags *flags, char *digit, int prebusies)
{
char *queue = qe->parent->name;
struct localuser *o;
int found;
int numlines;
Mark Spencer
committed
int status;
int numbusies = prebusies;
int numnochan = 0;
int stillgoing = 0;
struct localuser *peer = NULL;
Mark Spencer
committed
struct ast_channel *watchers[AST_MAX_WATCHERS];
struct ast_channel *in = qe->chan;
BUILD_STATS;
if ((found < 0) && stillgoing && !qe->parent->strategy) {
/* On "ringall" strategy we only move to the next penalty level
when *all* ringing phones are done in the current penalty level */
ring_one(qe, outgoing, &numbusies);
BUILD_STATS;
if (numlines == (numbusies + numnochan)) {
ast_log(LOG_DEBUG, "Everyone is busy at this time\n");
} else {
ast_log(LOG_NOTICE, "No one is answering queue '%s' (%d/%d/%d)\n", queue, numlines, numbusies, numnochan);
}
*to = 0;
return NULL;
}
winner = ast_waitfor_n(watchers, pos, to);
o = outgoing;
while(o) {
if (o->stillgoing && (o->chan) && (o->chan->_state == AST_STATE_UP)) {
if (!peer) {
if (option_verbose > 2)
ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
peer = o;
ast_copy_flags(flags, o, QUEUE_FLAG_REDIR_IN | QUEUE_FLAG_REDIR_OUT | QUEUE_FLAG_DISCON_IN | QUEUE_FLAG_DISCON_OUT);
if (!ast_strlen_zero(o->chan->call_forward)) {
char tmpchan[256]="";
char *stuff;
char *tech;
strncpy(tmpchan, o->chan->call_forward, sizeof(tmpchan) - 1);
if ((stuff = strchr(tmpchan, '/'))) {
*stuff = '\0';
stuff++;
tech = tmpchan;
} else {
snprintf(tmpchan, sizeof(tmpchan), "%s@%s", o->chan->call_forward, o->chan->context);
stuff = tmpchan;
tech = "Local";
}
/* Before processing channel, go ahead and check for forwarding */
if (option_verbose > 2)
ast_verbose(VERBOSE_PREFIX_3 "Now forwarding %s to '%s/%s' (thanks to %s)\n", in->name, tech, stuff, o->chan->name);
/* Setup parameters */
Mark Spencer
committed
o->chan = ast_request(tech, in->nativeformats, stuff, &status);
if (status != o->oldstatus)
update_dial_status(qe->parent, o->member, status);
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
if (!o->chan) {
ast_log(LOG_NOTICE, "Unable to create local channel for call forward to '%s/%s'\n", tech, stuff);
o->stillgoing = 0;
numnochan++;
} else {
if (o->chan->cid.cid_num)
free(o->chan->cid.cid_num);
o->chan->cid.cid_num = NULL;
if (o->chan->cid.cid_name)
free(o->chan->cid.cid_name);
o->chan->cid.cid_name = NULL;
if (in->cid.cid_num) {
o->chan->cid.cid_num = strdup(in->cid.cid_num);
if (!o->chan->cid.cid_num)
ast_log(LOG_WARNING, "Out of memory\n");
}
if (in->cid.cid_name) {
o->chan->cid.cid_name = strdup(in->cid.cid_name);
if (!o->chan->cid.cid_name)
ast_log(LOG_WARNING, "Out of memory\n");
}
strncpy(o->chan->accountcode, in->accountcode, sizeof(o->chan->accountcode) - 1);
o->chan->cdrflags = in->cdrflags;
if (in->cid.cid_ani) {
if (o->chan->cid.cid_ani)
free(o->chan->cid.cid_ani);
o->chan->cid.cid_ani = malloc(strlen(in->cid.cid_ani) + 1);
if (o->chan->cid.cid_ani)
strncpy(o->chan->cid.cid_ani, in->cid.cid_ani, strlen(in->cid.cid_ani) + 1);
else
ast_log(LOG_WARNING, "Out of memory\n");
}
if (o->chan->cid.cid_rdnis)
free(o->chan->cid.cid_rdnis);
if (!ast_strlen_zero(in->macroexten))
o->chan->cid.cid_rdnis = strdup(in->macroexten);
else
o->chan->cid.cid_rdnis = strdup(in->exten);
if (ast_call(o->chan, tmpchan, 0)) {
ast_log(LOG_NOTICE, "Failed to dial on local channel for call forward to '%s'\n", tmpchan);
o->stillgoing = 0;
ast_hangup(o->chan);
o->chan = NULL;
numnochan++;
}
}
/* Hangup the original channel now, in case we needed it */
ast_hangup(winner);
continue;
}
f = ast_read(winner);
if (f) {
if (f->frametype == AST_FRAME_CONTROL) {
switch(f->subclass) {
/* This is our guy if someone answered. */
if (!peer) {
ast_copy_flags(flags, o, QUEUE_FLAG_REDIR_IN | QUEUE_FLAG_REDIR_OUT | QUEUE_FLAG_DISCON_IN | QUEUE_FLAG_DISCON_OUT);
if (option_verbose > 2)
ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
peer = o;
ast_copy_flags(flags, o, QUEUE_FLAG_REDIR_IN & QUEUE_FLAG_REDIR_OUT & QUEUE_FLAG_DISCON_IN & QUEUE_FLAG_DISCON_OUT);
}
break;
case AST_CONTROL_BUSY:
if (option_verbose > 2)
ast_verbose( VERBOSE_PREFIX_3 "%s is busy\n", o->chan->name);
o->stillgoing = 0;
if (in->cdr)
ast_cdr_busy(in->cdr);
ast_hangup(o->chan);
o->chan = NULL;
if (qe->parent->strategy) {
if (ast_test_flag(qe->parent, QUEUE_FLAG_TIMEOUTRESTART))
*to = orig;
ring_one(qe, outgoing, &numbusies);
numbusies++;
break;
case AST_CONTROL_CONGESTION:
if (option_verbose > 2)
ast_verbose( VERBOSE_PREFIX_3 "%s is circuit-busy\n", o->chan->name);
o->stillgoing = 0;
if (in->cdr)
ast_cdr_busy(in->cdr);
ast_hangup(o->chan);
o->chan = NULL;
if (qe->parent->strategy) {
if (ast_test_flag(qe->parent, QUEUE_FLAG_TIMEOUTRESTART))
*to = orig;
ring_one(qe, outgoing, &numbusies);
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
numbusies++;
break;
case AST_CONTROL_RINGING:
if (option_verbose > 2)
ast_verbose( VERBOSE_PREFIX_3 "%s is ringing\n", o->chan->name);
if (!sentringing) {
#if 0
ast_indicate(in, AST_CONTROL_RINGING);
#endif
sentringing++;
}
break;
case AST_CONTROL_OFFHOOK:
/* Ignore going off hook */
break;
default:
ast_log(LOG_DEBUG, "Dunno what to do with control type %d\n", f->subclass);
}
}
ast_frfree(f);
} else {
o->stillgoing = 0;
ast_hangup(o->chan);
o->chan = NULL;
if (qe->parent->strategy) {
if (ast_test_flag(qe->parent, QUEUE_FLAG_TIMEOUTRESTART))
*to = orig;
ring_one(qe, outgoing, &numbusies);
}
}
o = o->next;
}
if (winner == in) {
f = ast_read(in);
#if 0
if (f && (f->frametype != AST_FRAME_VOICE))
printf("Frame type: %d, %d\n", f->frametype, f->subclass);
else if (!f || (f->frametype != AST_FRAME_VOICE))
printf("Hangup received on %s\n", in->name);
#endif
if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) {
/* Got hung up */
*to=-1;
return NULL;
}
if (f && (f->frametype == AST_FRAME_DTMF) && ast_test_flag(flags, QUEUE_FLAG_DISCON_OUT) && (f->subclass == '*')) {
ast_verbose(VERBOSE_PREFIX_3 "User hit %c to disconnect call.\n", f->subclass);
if (f && (f->frametype == AST_FRAME_DTMF) && (f->subclass != '*') && valid_exit(qe, f->subclass)) {
if (option_verbose > 3)
ast_verbose(VERBOSE_PREFIX_3 "User pressed digit: %c", f->subclass);
*to=0;
*digit=f->subclass;
return NULL;
}
}
if (!*to && (option_verbose > 2))
ast_verbose( VERBOSE_PREFIX_3 "Nobody picked up in %d ms\n", orig);
}
return peer;
}
static int is_our_turn(struct queue_ent *qe)
{
struct queue_ent *ch;
int res;
/* Atomically read the parent head -- does not need a lock */
ch = qe->parent->head;
/* If we are now at the top of the head, break out */
if (ch == qe) {
if (option_debug)
ast_log(LOG_DEBUG, "It's our turn (%s).\n", qe->chan->name);
res = 1;
} else {
if (option_debug)
ast_log(LOG_DEBUG, "It's not our turn (%s).\n", qe->chan->name);
res = 0;
}
return res;
}
static int wait_our_turn(struct queue_ent *qe, int ringing)
/* This is the holding pen for callers 2 through maxlen */
if (is_our_turn(qe))
break;
/* If we have timed out, break out */
if (qe->expire && (time(NULL) > qe->expire))
/* leave the queue if no agents, if enabled */
if (ast_test_flag(qe->parent, QUEUE_FLAG_LEAVEWHENEMPTY) && has_no_members(qe->parent)) {
leave_queue(qe);
break;
}
/* Make a position announcement, if enabled */
if (qe->parent->announcefrequency && !ringing)
/* Wait a second before checking again */
res = ast_waitfordigit(qe->chan, RECHECK * 1000);
if (res)
break;
}
return res;
}
static int update_queue(struct ast_call_queue *q, struct member *member)
{
struct member *cur;
/* Since a reload could have taken place, we have to traverse the list to
be sure it's still valid */
ast_mutex_lock(&q->lock);
cur = q->members;
while(cur) {
time(&cur->lastcall);
cur->calls++;
break;
}
cur = cur->next;
}
ast_mutex_unlock(&q->lock);
return 0;
}
static int calc_metric(struct ast_call_queue *q, struct member *mem, int pos, struct queue_ent *qe, struct localuser *tmp)
{
switch (q->strategy) {
case QUEUE_STRATEGY_RINGALL:
/* Everyone equal, except for penalty */
tmp->metric = mem->penalty * 1000000;
case QUEUE_STRATEGY_ROUNDROBIN:
if (!pos) {
if (!ast_test_flag(q, QUEUE_FLAG_WRAPPED)) {
/* No more channels, start over */
q->rrpos = 0;
} else {
/* Prioritize next entry */
q->rrpos++;
ast_clear_flag(q, QUEUE_FLAG_WRAPPED);
}
/* Fall through */
case QUEUE_STRATEGY_RRMEMORY:
if (pos < q->rrpos) {
tmp->metric = 1000 + pos;
} else {
if (pos > q->rrpos) {
/* Indicate there is another priority */
ast_set_flag(q, QUEUE_FLAG_WRAPPED);
tmp->metric = pos;
}
tmp->metric += mem->penalty * 1000000;
break;
case QUEUE_STRATEGY_RANDOM:
tmp->metric = rand() % 1000;
tmp->metric += mem->penalty * 1000000;
break;
case QUEUE_STRATEGY_FEWESTCALLS:
tmp->metric = mem->calls;
tmp->metric += mem->penalty * 1000000;
break;
case QUEUE_STRATEGY_LEASTRECENT:
if (!mem->lastcall)
tmp->metric = 0;
else
tmp->metric = 1000000 - (time(NULL) - mem->lastcall);
tmp->metric += mem->penalty * 1000000;
break;
default:
ast_log(LOG_WARNING, "Can't calculate metric for unknown strategy %d\n", q->strategy);
break;
static int try_calling(struct queue_ent *qe, char *ooptions, char *announceoverride, char *url, int *go_on)
{
struct member *cur;
struct localuser *outgoing=NULL, *tmp = NULL;
int to;
char oldexten[AST_MAX_EXTENSION]="";
char oldcontext[AST_MAX_EXTENSION]="";
char queuename[256]="";
struct ast_channel *which;
struct localuser *lpeer;
struct member *member;
int numbusies = 0;
int x=0;
char digit = 0;
struct ast_bridge_config config;
/* Hold the lock while we setup the outgoing calls */
ast_mutex_lock(&qe->parent->lock);
if (option_debug)
ast_log(LOG_DEBUG, "%s is trying to call a queue member.\n",
qe->chan->name);
strncpy(queuename, qe->parent->name, sizeof(queuename) - 1);
if (announceoverride && !ast_strlen_zero(announceoverride))
while(cur) {
/* Get a technology/[device:]number pair */
tmp = malloc(sizeof(struct localuser));
if (!tmp) {
ast_mutex_unlock(&qe->parent->lock);
ast_log(LOG_WARNING, "Out of memory\n");
goto out;
}
memset(tmp, 0, sizeof(struct localuser));
for (; options && *options; options++)
switch (*options) {
case 't':
ast_set_flag(tmp, QUEUE_FLAG_REDIR_IN);
ast_set_flag(tmp, QUEUE_FLAG_REDIR_OUT);
ast_set_flag(tmp, QUEUE_FLAG_RINGBACKONLY);
ast_set_flag(tmp, QUEUE_FLAG_MUSICONHOLD);
ast_set_flag(tmp, QUEUE_FLAG_DATAQUALITY);
ast_set_flag(tmp, QUEUE_FLAG_DISCON_IN);
ast_set_flag(tmp, QUEUE_FLAG_DISCON_OUT);
break;
case 'n':
if ((now - qe->start >= qe->parent->timeout))
*go_on = 1;
break;
}
if (option_debug) {
if (url)
ast_log(LOG_DEBUG, "Queue with URL=%s_\n", url);
else
ast_log(LOG_DEBUG, "Simple queue (no URL)\n");
}
tmp->member = cur; /* Never directly dereference! Could change on reload */
Mark Spencer
committed
tmp->oldstatus = cur->status;
tmp->lastcall = cur->lastcall;
strncpy(tmp->interface, cur->interface, sizeof(tmp->interface)-1);
/* If we're dialing by extension, look at the extension to know what to dial */
if ((newnum = strstr(tmp->interface, "/BYEXTENSION"))) {
newnum++;
strncpy(restofit, newnum + strlen("BYEXTENSION"), sizeof(restofit) - 1);
snprintf(newnum, sizeof(tmp->interface) - (newnum - tmp->interface), "%s%s", qe->chan->exten, restofit);
ast_log(LOG_DEBUG, "Dialing by extension %s\n", tmp->interface);
/* Special case: If we ring everyone, go ahead and ring them, otherwise
just calculate their metric for the appropriate strategy */
calc_metric(qe->parent, cur, x++, qe, tmp);
/* Put them in the list of outgoing thingies... We're ready now.
XXX If we're forcibly removed, these outgoing calls won't get
hung up XXX */
tmp->next = outgoing;
outgoing = tmp;
/* If this line is up, don't try anybody else */
if (outgoing->chan && (outgoing->chan->_state == AST_STATE_UP))
break;
cur = cur->next;
}
if (qe->parent->timeout)
to = qe->parent->timeout * 1000;
else
to = -1;
ring_one(qe, outgoing, &numbusies);
ast_mutex_unlock(&qe->parent->lock);
lpeer = wait_for_answer(qe, outgoing, &to, &flags, &digit, numbusies);
ast_mutex_lock(&qe->parent->lock);
if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY) {
store_next(qe, outgoing);
}
ast_mutex_unlock(&qe->parent->lock);
if (lpeer)
peer = lpeer->chan;
else
peer = NULL;
if (to) {
} else {
if (digit && valid_exit(qe, digit))
res=digit;
else
/* Nobody answered, next please? */
res=0;
}
if (option_debug)
ast_log(LOG_DEBUG, "%s: Nobody answered.\n", qe->chan->name);
goto out;
}
if (peer) {
/* Ah ha! Someone answered within the desired timeframe. Of course after this
we will always return with -1 so that it is hung up properly after the
conversation. */
zapx = !ast_test_flag(tmp, QUEUE_FLAG_DATAQUALITY);
ast_channel_setoption(qe->chan,AST_OPTION_TONE_VERIFY,&zapx,sizeof(char),0);
}
if (!strcmp(peer->type,"Zap")) {
zapx = !ast_test_flag(tmp, QUEUE_FLAG_DATAQUALITY);
ast_channel_setoption(peer,AST_OPTION_TONE_VERIFY,&zapx,sizeof(char),0);
}
/* Update parameters for the queue */
member = lpeer->member;
hanguptree(outgoing, peer);
outgoing = NULL;
if (announce || ast_test_flag(qe->parent, QUEUE_FLAG_REPORTHOLDTIME) || qe->parent->memberdelay) {
res2 = ast_autoservice_start(qe->chan);
if (!res2) {
if (qe->parent->memberdelay) {
ast_log(LOG_NOTICE, "Delaying member connect for %d seconds\n", qe->parent->memberdelay);
res2 |= ast_safe_sleep(peer, qe->parent->memberdelay * 1000);
}
if (!res2 && announce) {
if (play_file(peer, announce))
ast_log(LOG_WARNING, "Announcement file '%s' is unavailable, continuing anyway...\n", announce);
}
if (!res2 && ast_test_flag(qe->parent, QUEUE_FLAG_REPORTHOLDTIME)) {
if (!play_file(peer, qe->parent->sound_reporthold)) {
int holdtime;
time_t now;
time(&now);
holdtime = abs((now - qe->start) / 60);
if (holdtime < 2) {
play_file(peer, qe->parent->sound_lessthan);
ast_say_number(peer, 2, AST_DIGIT_ANY, peer->language, NULL);
} else
ast_say_number(peer, holdtime, AST_DIGIT_ANY, peer->language, NULL);
play_file(peer, qe->parent->sound_minutes);
}
}
}
res2 |= ast_autoservice_stop(qe->chan);
if (peer->_softhangup) {
/* Agent must have hung up */
ast_log(LOG_WARNING, "Agent on %s hungup on the customer. They're going to be pissed.\n", peer->name);
ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "AGENTDUMP", "%s", "");
if (ast_test_flag(qe->parent, QUEUE_FLAG_EVENTWHENCALLED)) {
manager_event(EVENT_FLAG_AGENT, "AgentDump",
"Queue: %s\r\n"
"Uniqueid: %s\r\n"
"Channel: %s\r\n"
"Member: %s\r\n",
queuename, qe->chan->uniqueid, peer->name, member->interface);
}
goto out;
} else if (res2) {
/* Caller must have hung up just before being connected*/
ast_log(LOG_NOTICE, "Caller was about to talk to agent on %s but the caller hungup.\n", peer->name);
ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "ABANDON", "%d|%d|%ld", qe->pos, qe->opos, (long)time(NULL) - qe->start);
record_abandoned(qe);
ast_hangup(peer);
/* Stop music on hold */
ast_moh_stop(qe->chan);
/* If appropriate, log that we have a destination channel */
if (qe->chan->cdr)
ast_cdr_setdestchan(qe->chan->cdr, peer->name);
/* Make sure channels are compatible */
res = ast_channel_make_compatible(qe->chan, peer);
if (res < 0) {
ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "SYSCOMPAT", "%s", "");
ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", qe->chan->name, peer->name);
ast_hangup(peer);
return -1;
}
/* Begin Monitoring */
if (qe->parent->monfmt && *qe->parent->monfmt) {
monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME");
if(pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC") || pbx_builtin_getvar_helper( qe->chan, "MONITOR_EXEC_ARGS"))
which = qe->chan;
else
which = peer;
ast_monitor_start(which, qe->parent->monfmt, monitorfilename, 1 );
ast_monitor_start(which, qe->parent->monfmt, qe->chan->cdr->uniqueid, 1 );
if(ast_test_flag(qe->parent, QUEUE_FLAG_MONJOIN)) {
ast_monitor_setjoinfiles(which, 1);
/* Drop out of the queue at this point, to prepare for next caller */
leave_queue(qe);
if( url && !ast_strlen_zero(url) && ast_channel_supports_html(peer) ) {
if (option_debug)
ast_log(LOG_DEBUG, "app_queue: sendurl=%s.\n", url);
ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "CONNECT", "%ld", (long)time(NULL) - qe->start);
if (ast_test_flag(qe->parent, QUEUE_FLAG_EVENTWHENCALLED)) {
manager_event(EVENT_FLAG_AGENT, "AgentConnect",
"Queue: %s\r\n"
"Uniqueid: %s\r\n"
"Channel: %s\r\n"
"Member: %s\r\n"
"Holdtime: %ld\r\n",
queuename, qe->chan->uniqueid, peer->name, member->interface, (long)time(NULL) - qe->start);
}
strncpy(oldcontext, qe->chan->context, sizeof(oldcontext) - 1);
strncpy(oldexten, qe->chan->exten, sizeof(oldexten) - 1);
time(&callstart);
memset(&config,0,sizeof(struct ast_bridge_config));
if (ast_test_flag(&flags, QUEUE_FLAG_REDIR_IN))
ast_set_flag(&(config.features_callee), AST_FEATURE_REDIRECT);
if (ast_test_flag(&flags, QUEUE_FLAG_REDIR_OUT))
ast_set_flag(&(config.features_caller), AST_FEATURE_REDIRECT);
if (ast_test_flag(&flags, QUEUE_FLAG_DISCON_IN))
ast_set_flag(&(config.features_callee), AST_FEATURE_DISCONNECT);
if (ast_test_flag(&flags, QUEUE_FLAG_DISCON_OUT))
ast_set_flag(&(config.features_caller), AST_FEATURE_DISCONNECT);
bridge = ast_bridge_call(qe->chan,peer,&config);
if (strcasecmp(oldcontext, qe->chan->context) || strcasecmp(oldexten, qe->chan->exten)) {
ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "TRANSFER", "%s|%s", qe->chan->exten, qe->chan->context);
} else if (qe->chan->_softhangup) {
ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "COMPLETECALLER", "%ld|%ld", (long)(callstart - qe->start), (long)(time(NULL) - callstart));
if (ast_test_flag(qe->parent, QUEUE_FLAG_EVENTWHENCALLED)) {
manager_event(EVENT_FLAG_AGENT, "AgentComplete",
"Queue: %s\r\n"
"Uniqueid: %s\r\n"
"Channel: %s\r\n"
"Member: %s\r\n"
"HoldTime: %ld\r\n"
"TalkTime: %ld\r\n"
"Reason: caller\r\n",
queuename, qe->chan->uniqueid, peer->name, member->interface, (long)(callstart - qe->start), (long)(time(NULL) - callstart));
}
} else {
ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "COMPLETEAGENT", "%ld|%ld", (long)(callstart - qe->start), (long)(time(NULL) - callstart));
if (ast_test_flag(qe->parent, QUEUE_FLAG_EVENTWHENCALLED)) {
manager_event(EVENT_FLAG_AGENT, "AgentComplete",
"Queue: %s\r\n"
"Uniqueid: %s\r\n"
"Channel: %s\r\n"
"HoldTime: %ld\r\n"
"TalkTime: %ld\r\n"
"Reason: agent\r\n",
queuename, qe->chan->uniqueid, peer->name, (long)(callstart - qe->start), (long)(time(NULL) - callstart));
}
if(bridge != AST_PBX_NO_HANGUP_PEER)
ast_hangup(peer);
update_queue(qe->parent, member);
if (bridge == 0)
res = 1; /* JDG: bridge successfull, leave app_queue */
else
res = bridge; /* bridge error, stay in the queue */
}
out:
hanguptree(outgoing, NULL);
return res;
}
static int wait_a_bit(struct queue_ent *qe)
{
/* Don't need to hold the lock while we setup the outgoing calls */
int retrywait = qe->parent->retry * 1000;
return ast_waitfordigit(qe->chan, retrywait);
}
static struct member * interface_exists(struct ast_call_queue *q, char *interface)
if (q)
for (mem = q->members; mem; mem = mem->next)
if (!strcmp(interface, mem->interface))
return mem;
static struct member *create_queue_node(char *interface, int penalty, int paused)
struct member *cur;
/* Add a new member */
cur = malloc(sizeof(struct member));
if (cur) {
memset(cur, 0, sizeof(struct member));
strncpy(cur->interface, interface, sizeof(cur->interface) - 1);
if (!strchr(cur->interface, '/'))
ast_log(LOG_WARNING, "No location at interface '%s'\n", interface);
cur->status = ast_device_state(interface);
/* Dump all members in a specific queue to the databse
*
* <pm_family>/<queuename> = <interface>;<penalty>;<paused>[|...]
*
*/
static void dump_queue_members(struct ast_call_queue *pm_queue)
{
char value[PM_MAX_LEN];
int value_len = 0;
int res;
memset(value, 0, sizeof(value));
for (cur_member = pm_queue->members; cur_member; cur_member = cur_member->next) {
if (!cur_member->dynamic)
continue;
res = snprintf(value + value_len, sizeof(value) - value_len, "%s;%d;%d%s",
cur_member->interface, cur_member->penalty, cur_member->paused,
cur_member->next ? "|" : "");
if (res != strlen(value + value_len)) {
ast_log(LOG_WARNING, "Could not create persistent member string, out of space\n");
break;
}
value_len += res;
if (value_len && !cur_member) {
if (ast_db_put(pm_family, pm_queue->name, value))
ast_log(LOG_WARNING, "failed to create persistent dynamic entry!\n");
} else
/* Delete the entry if the queue is empty or there is an error */
ast_db_del(pm_family, pm_queue->name);
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
static int remove_from_queue(char *queuename, char *interface)
{
struct ast_call_queue *q;
struct member *last_member, *look;
int res = RES_NOSUCHQUEUE;
ast_mutex_lock(&qlock);
for (q = queues ; q ; q = q->next) {
ast_mutex_lock(&q->lock);
if (!strcmp(q->name, queuename)) {
if ((last_member = interface_exists(q, interface))) {
if ((look = q->members) == last_member) {
q->members = last_member->next;
} else {
while (look != NULL) {
if (look->next == last_member) {
look->next = last_member->next;
break;
} else {
look = look->next;
}
}
}
manager_event(EVENT_FLAG_AGENT, "QueueMemberRemoved",
"Queue: %s\r\n"
"Location: %s\r\n",
q->name, last_member->interface);
free(last_member);
if (queue_persistent_members)
dump_queue_members(q);
res = RES_OKAY;
} else {
res = RES_EXISTS;
}
ast_mutex_unlock(&q->lock);
break;
}
ast_mutex_unlock(&q->lock);
}
ast_mutex_unlock(&qlock);
return res;
}
static int add_to_queue(char *queuename, char *interface, int penalty, int paused, int dump)
{
struct ast_call_queue *q;
struct member *new_member;
int res = RES_NOSUCHQUEUE;
ast_mutex_lock(&qlock);
for (q = queues ; q ; q = q->next) {
ast_mutex_lock(&q->lock);
if (!strcmp(q->name, queuename)) {
if (interface_exists(q, interface) == NULL) {
new_member = create_queue_node(interface, penalty, paused);
if (new_member != NULL) {
new_member->dynamic = 1;
new_member->next = q->members;
q->members = new_member;
manager_event(EVENT_FLAG_AGENT, "QueueMemberAdded",
"Queue: %s\r\n"
"Membership: %s\r\n"
"Penalty: %d\r\n"
"CallsTaken: %d\r\n"
"LastCall: %ld\r\n"
"Status: %d\r\n"
"Paused: %d\r\n",
q->name, new_member->interface, new_member->dynamic ? "dynamic" : "static",
new_member->penalty, new_member->calls, new_member->lastcall, new_member->status, new_member->paused);
res = RES_OKAY;
} else {
res = RES_OUTOFMEMORY;
}
} else {
res = RES_EXISTS;
}
ast_mutex_unlock(&q->lock);
break;
}
ast_mutex_unlock(&q->lock);
}
ast_mutex_unlock(&qlock);
return res;
}
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
static int set_member_paused(char *queuename, char *interface, int paused)
{
int found = 0;
struct ast_call_queue *q;
struct member *mem;
/* Special event for when all queues are paused - individual events still generated */
if (ast_strlen_zero(queuename))
ast_queue_log("NONE", "NONE", interface, (paused ? "PAUSEALL" : "UNPAUSEALL"), "%s", "");
ast_mutex_lock(&qlock);
for (q = queues ; q ; q = q->next) {
ast_mutex_lock(&q->lock);
if (ast_strlen_zero(queuename) || !strcmp(q->name, queuename)) {
if ((mem = interface_exists(q, interface))) {
found++;
if (mem->paused == paused)
ast_log(LOG_DEBUG, "%spausing already-%spaused queue member %s:%s\n", (paused ? "" : "un"), (paused ? "" : "un"), q->name, interface);
mem->paused = paused;
if (queue_persistent_members)
dump_queue_members(q);
ast_queue_log(q->name, "NONE", interface, (paused ? "PAUSE" : "UNPAUSE"), "%s", "");
manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
"Queue: %s\r\n"
"Location: %s\r\n"
"Paused: %d\r\n",
q->name, mem->interface, paused);
}
}
ast_mutex_unlock(&q->lock);
}
ast_mutex_unlock(&qlock);
if (found)
return RESULT_SUCCESS;
else
return RESULT_FAILURE;
}
/* Reload dynamic queue members persisted into the astdb */
static void reload_queue_members(void)
{
char *cur_ptr;
char *queue_name;
char *member;
char *interface;
char *penalty_tok;
int penalty = 0;
char *paused_tok;
int paused = 0;
struct ast_db_entry *db_tree;
struct ast_db_entry *entry;
struct ast_call_queue *cur_queue;
char queue_data[PM_MAX_LEN];
ast_mutex_lock(&qlock);
/* Each key in 'pm_family' is the name of a queue */
db_tree = ast_db_gettree(pm_family, NULL);
for (entry = db_tree; entry; entry = entry->next) {
queue_name = entry->key + strlen(pm_family) + 2;
cur_queue = queues;
while (cur_queue) {
ast_mutex_lock(&cur_queue->lock);
if (!strcmp(queue_name, cur_queue->name))
ast_mutex_unlock(&cur_queue->lock);
cur_queue = cur_queue->next;
}
if (!cur_queue) {
/* If the queue no longer exists, remove it from the
* database */
ast_db_del(pm_family, queue_name);
continue;
} else
ast_mutex_unlock(&cur_queue->lock);
if (ast_db_get(pm_family, queue_name, queue_data, PM_MAX_LEN))
continue;
cur_ptr = queue_data;
while ((member = strsep(&cur_ptr, "|"))) {
if (ast_strlen_zero(member))
continue;
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
interface = strsep(&member, ";");
penalty_tok = strsep(&member, ";");
paused_tok = strsep(&member, ";");
if (!penalty_tok) {
ast_log(LOG_WARNING, "Error parsing persisent member string for '%s' (penalty)\n", queue_name);
break;
}
penalty = strtol(penalty_tok, NULL, 10);
if (errno == ERANGE) {
ast_log(LOG_WARNING, "Error converting penalty: %s: Out of range.\n", penalty_tok);
break;
}
if (!paused_tok) {
ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (paused)\n", queue_name);
break;
}
paused = strtol(paused_tok, NULL, 10);
if ((errno == ERANGE) || paused < 0 || paused > 1) {
ast_log(LOG_WARNING, "Error converting paused: %s: Expected 0 or 1.\n", paused_tok);
break;
}
if (option_debug)
ast_log(LOG_DEBUG, "Reload Members: Queue: %s Member: %s Penalty: %d Paused: %d\n", queue_name, interface, penalty, paused);
if (add_to_queue(queue_name, interface, penalty, paused, 0) == RES_OUTOFMEMORY) {
ast_log(LOG_ERROR, "Out of Memory when reloading persistent queue member\n");
break;
}
}
}
ast_mutex_unlock(&qlock);
if (db_tree) {
ast_log(LOG_NOTICE, "Queue members sucessfully reloaded from database.\n");
ast_db_freetree(db_tree);