Newer
Older
struct localuser *lpeer;
struct member *member;
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));
tmp->allowredirect_in = 1;
if (strchr(options, 'T'))
tmp->allowredirect_out = 1;
if (strchr(options, 'r'))
tmp->ringbackonly = 1;
if (strchr(options, 'm'))
tmp->musiconhold = 1;
if (strchr(options, 'd'))
tmp->dataquality = 1;
if (strchr(options, 'H'))
tmp->allowdisconnect = 1;
if ((strchr(options, 'n')) && (now - qe->start >= qe->parent->timeout))
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 */
strncpy(tmp->tech, cur->tech, sizeof(tmp->tech)-1);
strncpy(tmp->numsubst, cur->loc, sizeof(tmp->numsubst)-1);
tmp->lastcall = cur->lastcall;
/* If we're dialing by extension, look at the extension to know what to dial */
if ((newnum = strstr(tmp->numsubst, "BYEXTENSION"))) {
strncpy(restofit, newnum + strlen("BYEXTENSION"), sizeof(restofit)-1);
snprintf(newnum, sizeof(tmp->numsubst) - (newnum - tmp->numsubst), "%s%s", qe->chan->exten,restofit);
ast_log(LOG_DEBUG, "Dialing by extension %s\n", tmp->numsubst);
/* 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);
ast_mutex_unlock(&qe->parent->lock);
lpeer = wait_for_answer(qe, outgoing, &to, &allowredir_in, &allowredir_out, &allowdisconnect, &digit);
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. */
if (!strcmp(qe->chan->type,"Zap")) {
if (tmp->dataquality) zapx = 0;
ast_channel_setoption(qe->chan,AST_OPTION_TONE_VERIFY,&zapx,sizeof(char),0);
}
if (!strcmp(peer->type,"Zap")) {
if (tmp->dataquality) zapx = 0;
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;
res2 = ast_autoservice_start(qe->chan);
if (!res2) {
res2 = ast_streamfile(peer, announce, peer->language);
if (!res2)
res2 = ast_waitstream(peer, "");
else {
ast_log(LOG_WARNING, "Announcement file '%s' is unavailable, continuing anyway...\n", announce);
res2 = 0;
}
}
res2 |= ast_autoservice_stop(qe->chan);
if (res2) {
/* 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", "");
/* 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(monitorfilename) {
ast_monitor_start( peer, qe->parent->monfmt, monitorfilename, 1 );
} else {
ast_monitor_start( peer, qe->parent->monfmt, qe->chan->cdr->uniqueid, 1 );
}
if(qe->parent->monjoin) {
ast_monitor_setjoinfiles( peer, 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);
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));
config.allowredirect_in = allowredir_in;
config.allowredirect_out = allowredir_out;
config.allowdisconnect = allowdisconnect;
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));
} else {
ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "COMPLETEAGENT", "%ld|%ld", (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);
}
// [PHM 06/26/03]
static struct member * interface_exists( struct ast_call_queue * q, char * interface )
{
struct member * ret = NULL ;
struct member *mem;
char buf[500] ;
if( q != NULL )
{
mem = q->members ;
while( mem != NULL ) {
snprintf( buf, sizeof(buf), "%s/%s", mem->tech, mem->loc);
if( strcmp( buf, interface ) == 0 ) {
ret = mem ;
break ;
}
else
mem = mem->next ;
}
}
return( ret ) ;
}
static struct member * create_queue_node( char * interface, int penalty )
{
struct member * cur ;
char * tmp ;
/* Add a new member */
cur = malloc(sizeof(struct member));
if (cur) {
memset(cur, 0, sizeof(struct member));
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
strncpy(cur->tech, interface, sizeof(cur->tech) - 1);
if ((tmp = strchr(cur->tech, '/')))
*tmp = '\0';
if ((tmp = strchr(interface, '/'))) {
tmp++;
strncpy(cur->loc, tmp, sizeof(cur->loc) - 1);
} else
ast_log(LOG_WARNING, "No location at interface '%s'\n", interface);
}
return( cur ) ;
}
static int rqm_exec(struct ast_channel *chan, void *data)
{
int res=-1;
struct localuser *u;
char *queuename;
struct member * node ;
struct member * look ;
char info[512];
char *interface=NULL;
struct ast_call_queue *q;
int found=0 ;
if (!data) {
ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename|optional interface)\n");
return -1;
}
LOCAL_USER_ADD(u); // not sure if we need this, but better be safe than sorry ;-)
/* Parse our arguments XXX Check for failure XXX */
strncpy(info, (char *)data, strlen((char *)data) + AST_MAX_EXTENSION-1);
queuename = info;
if (queuename) {
interface = strchr(queuename, '|');
if (interface) {
*interface = '\0';
interface++;
}
else {
strncpy(tmpchan, chan->name, sizeof(tmpchan) - 1);
interface = strrchr(tmpchan, '-');
if (interface)
*interface = '\0';
interface = tmpchan;
}
}
if( ( q = queues) != NULL )
{
while( q && ( res != 0 ) && (!found) )
{
ast_mutex_lock(&q->lock);
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
if( strcmp( q->name, queuename) == 0 )
{
// found queue, try to remove interface
found=1 ;
if( ( node = interface_exists( q, interface ) ) != NULL )
{
if( ( look = q->members ) == node )
{
// 1st
q->members = node->next;
}
else
{
while( look != NULL )
if( look->next == node )
{
look->next = node->next ;
break ;
}
else
look = look->next ;
}
free( node ) ;
ast_log(LOG_NOTICE, "Removed interface '%s' to queue '%s'\n",
interface, queuename);
res = 0 ;
}
else
James Golovich
committed
{
ast_log(LOG_WARNING, "Unable to remove interface '%s' from queue '%s': "
"Not there\n", interface, queuename);
James Golovich
committed
if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->callerid))
{
chan->priority += 100;
res = 0 ;
}
}
ast_mutex_unlock(&q->lock);
q = q->next;
}
}
if( ! found )
ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", queuename);
LOCAL_USER_REMOVE(u);
return res;
}
static int aqm_exec(struct ast_channel *chan, void *data)
{
int res=-1;
struct localuser *u;
char *queuename;
char info[512];
char *penaltys=NULL;
int penalty = 0;
struct ast_call_queue *q;
struct member *save;
int found=0 ;
if (!data) {
ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename|optional interface|optional penalty)\n");
return -1;
}
LOCAL_USER_ADD(u); // not sure if we need this, but better be safe than sorry ;-)
/* Parse our arguments XXX Check for failure XXX */
strncpy(info, (char *)data, strlen((char *)data) + AST_MAX_EXTENSION-1);
queuename = info;
if (queuename) {
interface = strchr(queuename, '|');
if (interface) {
*interface = '\0';
interface++;
}
if (interface) {
penaltys = strchr(interface, '|');
if (penaltys) {
*penaltys = 0;
penaltys++;
}
}
if (!interface || !strlen(interface)) {
strncpy(tmpchan, chan->name, sizeof(tmpchan) - 1);
interface = strrchr(tmpchan, '-');
if (interface)
*interface = '\0';
interface = tmpchan;
}
if (penaltys && strlen(penaltys)) {
if ((sscanf(penaltys, "%d", &penalty) != 1) || penalty < 0) {
ast_log(LOG_WARNING, "Penalty '%s' is invalid, must be an integer >= 0\n", penaltys);
penalty = 0;
}
}
}
if( ( q = queues) != NULL )
{
while( q && ( res != 0 ) && (!found) )
{
ast_mutex_lock(&q->lock);
if( strcmp( q->name, queuename) == 0 )
{
// found queue, try to enable interface
found=1 ;
if( interface_exists( q, interface ) == NULL )
{
save = q->members ;
q->members = create_queue_node( interface, penalty ) ;
q->members = save ;
ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", interface, queuename);
res = 0 ;
}
else
James Golovich
committed
{
ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': "
"Already there\n", interface, queuename);
James Golovich
committed
if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->callerid))
{
chan->priority += 100;
res = 0 ;
}
}
ast_mutex_unlock(&q->lock);
q = q->next;
}
}
if( ! found )
ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", queuename);
LOCAL_USER_REMOVE(u);
return res;
}
static int queue_exec(struct ast_channel *chan, void *data)
{
int res=-1;
struct localuser *u;
char *queuename;
char info[512];
char *options = NULL;
char *url = NULL;
char *announceoverride = NULL;
char *user_priority;
int prio;
/* whether to exit Queue application after the timeout hits */
int go_on = 0;
/* Our queue entry */
struct queue_ent qe;
if (!data) {
ast_log(LOG_WARNING, "Queue requires an argument (queuename|optional timeout|optional URL)\n");
/* Setup our queue entry */
memset(&qe, 0, sizeof(qe));
/* Parse our arguments XXX Check for failure XXX */
strncpy(info, (char *)data, strlen((char *)data) + AST_MAX_EXTENSION-1);
queuename = info;
if (queuename) {
options = strchr(queuename, '|');
if (options) {
*options = '\0';
options++;
url = strchr(options, '|');
if (url) {
*url = '\0';
url++;
announceoverride = strchr(url, '|');
if (announceoverride) {
*announceoverride = '\0';
announceoverride++;
queuetimeoutstr = strchr(announceoverride, '|');
if (queuetimeoutstr) {
*queuetimeoutstr = '\0';
queuetimeoutstr++;
qe.queuetimeout = atoi(queuetimeoutstr);
} else {
qe.queuetimeout = 0;
/* Get the priority from the variable ${QUEUE_PRIO} */
user_priority = pbx_builtin_getvar_helper(chan, "QUEUE_PRIO");
if (user_priority) {
if (sscanf(user_priority, "%d", &prio) == 1) {
if (option_debug)
ast_log(LOG_DEBUG, "%s: Got priority %d from ${QUEUE_PRIO}.\n",
chan->name, prio);
} else {
ast_log(LOG_WARNING, "${QUEUE_PRIO}: Invalid value (%s), channel %s.\n",
user_priority, chan->name);
prio = 0;
}
} else {
if (option_debug)
ast_log(LOG_DEBUG, "NO QUEUE_PRIO variable found. Using default.\n");
prio = 0;
if (options) {
if (strchr(options, 'r')) {
ringing = 1;
// if (option_debug)
ast_log(LOG_DEBUG, "queue: %s, options: %s, url: %s, announce: %s, timeout: %d, priority: %d\n",
queuename, options, url, announceoverride, qe.queuetimeout, (int)prio);
qe.prio = (int)prio;
qe.last_pos_said = 0;
qe.last_pos = 0;
ast_queue_log(queuename, chan->uniqueid, "NONE", "ENTERQUEUE", "%s|%s", url ? url : "", chan->callerid ? chan->callerid : "");
if (ringing) {
ast_indicate(chan, AST_CONTROL_RINGING);
} else {
ast_moh_start(chan, qe.moh);
}
/* This is the wait loop for callers 2 through maxlen */
res = wait_our_turn(&qe, ringing);
/* If they hungup, return immediately */
if (res < 0) {
/* Record this abandoned call */
record_abandoned(&qe);
ast_queue_log(queuename, chan->uniqueid, "NONE", "ABANDON", "%d|%d|%ld", qe.pos, qe.opos, (long)time(NULL) - qe.start);
if (option_verbose > 2) {
ast_verbose(VERBOSE_PREFIX_3 "User disconnected while waiting their turn\n");
res = -1;
}
break;
}
if (valid_exit(&qe, res)) {
ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%c|%d", res, qe.pos);
/* This is the wait loop for the head caller*/
/* To exit, they may get their call answered; */
/* they may dial a digit from the queue context; */
/* or, they may may timeout. */
/* Leave if we have exceeded our queuetimeout */
if (qe.queuetimeout && ( (time(NULL) - qe.start) >= qe.queuetimeout) ) {
res = 0;
break;
}
/* Make a position announcement, if enabled */
if (qe.parent->announcefrequency && !ringing)
say_position(&qe);
/* Try calling all queue members for 'timeout' seconds */
res = try_calling(&qe, options, announceoverride, url, &go_on);
if (res) {
if (res < 0) {
if (!qe.handled)
ast_queue_log(queuename, chan->uniqueid, "NONE", "ABANDON", "%d|%d|%ld", qe.pos, qe.opos, (long)time(NULL) - qe.start);
} else if (res > 0)
ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%c|%d", res, qe.pos);
/* Leave if we have exceeded our queuetimeout */
if (qe.queuetimeout && ( (time(NULL) - qe.start) >= qe.queuetimeout) ) {
res = 0;
break;
}
/* OK, we didn't get anybody; wait for 'retry' seconds; may get a digit to exit with */
ast_queue_log(queuename, chan->uniqueid, "NONE", "ABANDON", "%d|%d|%ld", qe.pos, qe.opos, (long)time(NULL) - qe.start);
if (option_verbose > 2) {
ast_verbose(VERBOSE_PREFIX_3 "User disconnected when they almost made it\n");
res = -1;
}
break;
}
if (res && valid_exit(&qe, res)) {
ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%c|%d", res, qe.pos);
/* exit after 'timeout' cycle if 'n' option enabled */
if (option_verbose > 2) {
ast_verbose(VERBOSE_PREFIX_3 "Exiting on time-out cycle\n");
res = -1;
}
ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
/* Since this is a priority queue and
* it is not sure that we are still at the head
* of the queue, go and check for our turn again.
*/
if (!is_our_turn(&qe)) {
ast_log(LOG_DEBUG, "Darn priorities, going back in queue (%s)!\n",
qe.chan->name);
goto check_turns;
}
if (ringing) {
ast_indicate(chan, -1);
} else {
ast_moh_stop(chan);
}
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
leave_queue(&qe);
} else {
ast_log(LOG_WARNING, "Unable to join queue '%s'\n", queuename);
res = 0;
}
LOCAL_USER_REMOVE(u);
return res;
}
static void reload_queues(void)
{
struct ast_call_queue *q, *ql, *qn;
struct ast_config *cfg;
char *cat, *tmp;
struct ast_variable *var;
struct member *prev, *cur;
int new;
cfg = ast_load("queues.conf");
if (!cfg) {
ast_log(LOG_NOTICE, "No call queueing config file, so no call queues\n");
return;
}
ast_mutex_lock(&qlock);
/* Mark all queues as dead for the moment */
q = queues;
while(q) {
q = q->next;
}
/* Chug through config file */
cat = ast_category_browse(cfg, NULL);
while(cat) {
if (strcasecmp(cat, "general")) {
/* Look for an existing one */
q = queues;
while(q) {
if (!strcmp(q->name, cat))
break;
q = q->next;
}
if (!q) {
/* Make one then */
q = malloc(sizeof(struct ast_call_queue));
if (q) {
/* Initialize it */
memset(q, 0, sizeof(struct ast_call_queue));
ast_mutex_init(&q->lock);
strncpy(q->name, cat, sizeof(q->name) - 1);
new = 1;
} else new = 0;
} else
new = 0;
if (q) {
if (!new)
ast_mutex_lock(&q->lock);
/* Re-initialize the queue */
q->dead = 0;
q->retry = 0;
q->timeout = -1;
q->maxlen = 0;
q->announcefrequency = 0;
q->announceholdtime = 0;
q->roundingseconds = 0; /* Default - don't announce seconds */
q->holdtime = 0;
q->callscompleted = 0;
q->callsabandoned = 0;
q->callscompletedinsl = 0;
q->servicelevel = 0;
q->moh[0] = '\0';
q->announce[0] = '\0';
q->context[0] = '\0';
q->monfmt[0] = '\0';
strncpy(q->sound_next, "queue-youarenext", sizeof(q->sound_next) - 1);
strncpy(q->sound_thereare, "queue-thereare", sizeof(q->sound_thereare) - 1);
strncpy(q->sound_calls, "queue-callswaiting", sizeof(q->sound_calls) - 1);
strncpy(q->sound_holdtime, "queue-holdtime", sizeof(q->sound_holdtime) - 1);
strncpy(q->sound_minutes, "queue-minutes", sizeof(q->sound_minutes) - 1);
strncpy(q->sound_seconds, "queue-seconds", sizeof(q->sound_seconds) - 1);
strncpy(q->sound_thanks, "queue-thankyou", sizeof(q->sound_thanks) - 1);
prev = q->members;
if (prev) {
/* find the end of any dynamic members */
while(prev->next)
prev = prev->next;
}
var = ast_variable_browse(cfg, cat);
while(var) {
if (!strcasecmp(var->name, "member")) {
/* Add a new member */
cur = malloc(sizeof(struct member));
if (cur) {
memset(cur, 0, sizeof(struct member));
strncpy(cur->tech, var->value, sizeof(cur->tech) - 1);
if ((tmp = strchr(cur->tech, ','))) {
*tmp = '\0';
tmp++;
cur->penalty = atoi(tmp);
if (cur->penalty < 0)
cur->penalty = 0;
}
if ((tmp = strchr(cur->tech, '/')))
*tmp = '\0';
if ((tmp = strchr(var->value, '/'))) {
tmp++;
strncpy(cur->loc, tmp, sizeof(cur->loc) - 1);
if ((tmp = strchr(cur->loc, ',')))
*tmp = '\0';
} else
ast_log(LOG_WARNING, "No location at line %d of queue.conf\n", var->lineno);
if (prev)
prev->next = cur;
else
q->members = cur;
prev = cur;
}
} else if (!strcasecmp(var->name, "music")) {
strncpy(q->moh, var->value, sizeof(q->moh) - 1);
} else if (!strcasecmp(var->name, "announce")) {
strncpy(q->announce, var->value, sizeof(q->announce) - 1);
} else if (!strcasecmp(var->name, "context")) {
strncpy(q->context, var->value, sizeof(q->context) - 1);
} else if (!strcasecmp(var->name, "timeout")) {
q->timeout = atoi(var->value);
} else if (!strcasecmp(var->name, "monitor-join")) {
q->monjoin = ast_true(var->value);
} else if (!strcasecmp(var->name, "monitor-format")) {
strncpy(q->monfmt, var->value, sizeof(q->monfmt) - 1);
} else if (!strcasecmp(var->name, "queue-youarenext")) {
strncpy(q->sound_next, var->value, sizeof(q->sound_next) - 1);
} else if (!strcasecmp(var->name, "queue-thereare")) {
strncpy(q->sound_thereare, var->value, sizeof(q->sound_thereare) - 1);
} else if (!strcasecmp(var->name, "queue-callswaiting")) {
strncpy(q->sound_calls, var->value, sizeof(q->sound_calls) - 1);
} else if (!strcasecmp(var->name, "queue-holdtime")) {
strncpy(q->sound_holdtime, var->value, sizeof(q->sound_holdtime) - 1);
} else if (!strcasecmp(var->name, "queue-minutes")) {
strncpy(q->sound_minutes, var->value, sizeof(q->sound_minutes) - 1);
} else if (!strcasecmp(var->name, "queue-seconds")) {
strncpy(q->sound_seconds, var->value, sizeof(q->sound_seconds) - 1);
} else if (!strcasecmp(var->name, "queue-thankyou")) {
strncpy(q->sound_thanks, var->value, sizeof(q->sound_thanks) - 1);
} else if (!strcasecmp(var->name, "announce-frequency")) {
q->announcefrequency = atoi(var->value);
} else if (!strcasecmp(var->name, "announce-round-seconds")) {
q->roundingseconds = atoi(var->value);
if(q->roundingseconds>60 || q->roundingseconds<0) {
ast_log(LOG_WARNING, "'%s' isn't a valid value for queue-rounding-seconds using 0 instead at line %d of queue.conf\n", var->value, var->lineno);
q->roundingseconds=0;
}
} else if (!strcasecmp(var->name, "announce-holdtime")) {
q->announceholdtime = (!strcasecmp(var->value,"once")) ? 1 : ast_true(var->value);
} else if (!strcasecmp(var->name, "retry")) {
q->retry = atoi(var->value);
} else if (!strcasecmp(var->name, "wrapuptime")) {
q->wrapuptime = atoi(var->value);
} else if (!strcasecmp(var->name, "maxlen")) {
q->maxlen = atoi(var->value);
} else if (!strcasecmp(var->name, "servicelevel")) {
q->servicelevel= atoi(var->value);
} else if (!strcasecmp(var->name, "strategy")) {
q->strategy = strat2int(var->value);
if (q->strategy < 0) {
ast_log(LOG_WARNING, "'%s' isn't a valid strategy, using ringall instead\n", var->value);
q->strategy = 0;
}
} else if (!strcasecmp(var->name, "joinempty")) {
q->joinempty = ast_true(var->value);
} else if (!strcasecmp(var->name, "eventwhencalled")) {
q->eventwhencalled = ast_true(var->value);
} else {
ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s at line %d of queue.conf\n", cat, var->name, var->lineno);
}
var = var->next;
}
if (q->retry < 1)
q->retry = DEFAULT_RETRY;
if (q->timeout < 0)
q->timeout = DEFAULT_TIMEOUT;
if (q->maxlen < 0)
q->maxlen = 0;
if (!new)
ast_mutex_unlock(&q->lock);
if (new) {
q->next = queues;
queues = q;
}
}
}
cat = ast_category_browse(cfg, cat);
}
q = queues;
ql = NULL;
while(q) {
qn = q->next;
if (q->dead) {
if (ql)
ql->next = q->next;
else
queues = q->next;
if (!q->count) {
free(q);
} else
ast_log(LOG_WARNING, "XXX Leaking a little memory :( XXX\n");
ast_mutex_unlock(&qlock);
static int __queues_show(int fd, int argc, char **argv, int queue_show)
struct queue_ent *qe;
struct member *mem;
int pos;
time_t now;
char max[80] = "";
char calls[80] = "";
if ((!queue_show && argc != 2) || (queue_show && argc != 3))
if (queue_show)
ast_cli(fd, "No such queue: %s.\n",argv[2]);
else
ast_cli(fd, "No queues.\n");
ast_mutex_lock(&q->lock);
if (queue_show) {
if (strcasecmp(q->name, argv[2]) != 0) {
ast_mutex_unlock(&q->lock);
if (!q) {
ast_cli(fd, "No such queue: %s.\n",argv[2]);
break;
}
continue;
}
}
if (q->maxlen)
snprintf(max, sizeof(max), "%d", q->maxlen);
else
strncpy(max, "unlimited", sizeof(max) - 1);
sl = 0;
if(q->callscompleted > 0)
sl = 100*((float)q->callscompletedinsl/(float)q->callscompleted);
ast_cli(fd, "%-12.12s has %d calls (max %s) in '%s' strategy (%ds holdtime), C:%d, A:%d, SL:%2.1f%% within %ds\n",
q->name, q->count, max, int2strat(q->strategy), q->holdtime, q->callscompleted, q->callsabandoned,sl,q->servicelevel);
if (q->members) {
ast_cli(fd, " Members: \n");
for (mem = q->members; mem; mem = mem->next) {
if (mem->penalty)
snprintf(max, sizeof(max) - 20, " with penalty %d", mem->penalty);
else
max[0] = '\0';
strncat(max, " (dynamic)", sizeof(max) - strlen(max) - 1);
if (mem->calls) {
snprintf(calls, sizeof(calls), " has taken %d calls (last was %ld secs ago)",
mem->calls, (long)(time(NULL) - mem->lastcall));
} else
strncpy(calls, " has taken no calls yet", sizeof(calls) - 1);
ast_cli(fd, " %s/%s%s%s\n", mem->tech, mem->loc, max, calls);
}
} else
ast_cli(fd, " No Members\n");
if (q->head) {
pos = 1;
ast_cli(fd, " Callers: \n");
for (qe = q->head; qe; qe = qe->next)
ast_cli(fd, " %d. %s (wait: %ld:%2.2ld, prio: %d)\n", pos++, qe->chan->name,
(long)(now - qe->start) / 60, (long)(now - qe->start) % 60, qe->prio);
} else
ast_cli(fd, " No Callers\n");
ast_cli(fd, "\n");
ast_mutex_unlock(&q->lock);
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
static int queues_show(int fd, int argc, char **argv)
{
return __queues_show(fd, argc, argv, 0);
}
static int queue_show(int fd, int argc, char **argv)
{
return __queues_show(fd, argc, argv, 1);
}
static char *complete_queue(char *line, char *word, int pos, int state)
{
struct ast_call_queue *q;
int which=0;
ast_mutex_lock(&qlock);
q = queues;
while(q) {
if (!strncasecmp(word, q->name, strlen(word))) {
if (++which > state)
break;
}
q = q->next;
}
ast_mutex_unlock(&qlock);