Newer
Older
Kevin P. Fleming
committed
/* Get the maximum penalty from the variable ${QUEUE_MAX_PENALTY} */
if ((max_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MAX_PENALTY"))) {
if (sscanf(max_penalty_str, "%d", &max_penalty) == 1) {
ast_debug(1, "%s: Got max penalty %d from ${QUEUE_MAX_PENALTY}.\n", chan->name, max_penalty);
Kevin P. Fleming
committed
} else {
ast_log(LOG_WARNING, "${QUEUE_MAX_PENALTY}: Invalid value (%s), channel %s.\n",
max_penalty_str, chan->name);
max_penalty = 0;
}
} else {
max_penalty = 0;
}
if (args.options && (strchr(args.options, 'r')))
Mark Spencer
committed
ringing = 1;
Jason Parker
committed
if (args.options && (strchr(args.options, 'c')))
qcontinue = 1;
ast_debug(1, "queue: %s, options: %s, url: %s, announce: %s, expires: %ld, priority: %d\n",
args.queuename, args.options, args.url, args.announceoverride, (long)qe.expire, prio);
Kevin P. Fleming
committed
qe.prio = prio;
qe.max_penalty = max_penalty;
qe.last_pos_said = 0;
qe.last_pos = 0;
qe.last_periodic_announce_time = time(NULL);
qe.last_periodic_announce_sound = 0;
if (!join_queue(args.queuename, &qe, &reason)) {
ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ENTERQUEUE", "%s|%s", S_OR(args.url, ""),
if (ringing) {
ast_indicate(chan, AST_CONTROL_RINGING);
Kevin P. Fleming
committed
ast_moh_start(chan, qe.moh, NULL);
/* This is the wait loop for callers 2 through maxlen */
res = wait_our_turn(&qe, ringing, &reason);
Mark Michelson
committed
if (res) {
Mark Michelson
committed
}
Steve Murphy
committed
makeannouncement = 0;
for (;;) {
/* 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 timeout. */
enum queue_member_status stat;
Mark Spencer
committed
/* Leave if we have exceeded our queuetimeout */
if (qe.expire && (time(NULL) > qe.expire)) {
record_abandoned(&qe);
reason = QUEUE_TIMEOUT;
res = 0;
ast_queue_log(args.queuename, chan->uniqueid,"NONE", "EXITWITHTIMEOUT", "%d|%d|%ld",
qe.pos, qe.opos, (long) time(NULL) - qe.start);
break;
}
Kevin P. Fleming
committed
if (makeannouncement) {
/* Make a position announcement, if enabled */
Steve Murphy
committed
if (qe.parent->announcefrequency)
if ((res = say_position(&qe,ringing)))
/* Make a periodic announcement, if enabled */
Steve Murphy
committed
if (qe.parent->periodicannouncefrequency)
if ((res = say_periodic_announcement(&qe,ringing)))
/* Try calling all queue members for 'timeout' seconds */
res = try_calling(&qe, args.options, args.announceoverride, args.url, &tries, &noption, args.agi, args.macro, args.gosub, ringing);
Mark Michelson
committed
if (res) {
Mark Michelson
committed
}
Mark Spencer
committed
stat = get_member_status(qe.parent, qe.max_penalty);
/* exit after 'timeout' cycle if 'n' option enabled */
if (noption && tries >= qe.parent->membercount) {
ast_verb(3, "Exiting on time-out cycle\n");
ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
record_abandoned(&qe);
reason = QUEUE_TIMEOUT;
res = 0;
break;
}
Mark Spencer
committed
/* leave the queue if no agents, if enabled */
if (qe.parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) {
record_abandoned(&qe);
reason = QUEUE_LEAVEEMPTY;
ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
res = 0;
break;
}
Mark Spencer
committed
/* leave the queue if no reachable agents, if enabled */
if ((qe.parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS || stat == QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS)) {
record_abandoned(&qe);
reason = QUEUE_LEAVEUNAVAIL;
ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
res = 0;
break;
}
if ((qe.parent->leavewhenempty == QUEUE_EMPTY_LOOSE) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) {
record_abandoned(&qe);
reason = QUEUE_LEAVEUNAVAIL;
res = 0;
break;
}
/* Leave if we have exceeded our queuetimeout */
if (qe.expire && (time(NULL) > qe.expire)) {
record_abandoned(&qe);
reason = QUEUE_TIMEOUT;
res = 0;
ast_queue_log(qe.parent->name, qe.chan->uniqueid,"NONE", "EXITWITHTIMEOUT", "%d|%d|%ld", qe.pos, qe.opos, (long) time(NULL) - qe.start);
break;
}
/* If using dynamic realtime members, we should regenerate the member list for this queue */
update_realtime_members(qe.parent);
/* OK, we didn't get anybody; wait for 'retry' seconds; may get a digit to exit with */
res = wait_a_bit(&qe);
if (res)
goto stop;
/* 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_debug(1, "Darn priorities, going back in queue (%s)!\n", qe.chan->name);
goto check_turns;
stop:
if (res) {
if (res < 0) {
if (!qe.handled) {
record_abandoned(&qe);
ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ABANDON",
"%d|%d|%ld", qe.pos, qe.opos,
(long) time(NULL) - qe.start);
}
res = -1;
} else if (qe.valid_digits) {
ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHKEY",
"%s|%d", qe.digits, qe.pos);
}
}
if (res >= 0 && res != AST_PBX_KEEPALIVE) {
if (ringing) {
ast_indicate(chan, -1);
} else {
ast_moh_stop(chan);
}
set_queue_variables(&qe);
Mark Spencer
committed
if (reason != QUEUE_UNKNOWN)
set_queue_result(chan, reason);
ast_log(LOG_WARNING, "Unable to join queue '%s'\n", args.queuename);
Mark Spencer
committed
set_queue_result(chan, reason);
res = 0;
Kevin P. Fleming
committed
static int queue_function_var(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
{
int res = -1;
struct call_queue *q, tmpq = {
.name = data,
};
char interfacevar[256]="";
float sl = 0;
if (ast_strlen_zero(data)) {
ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
return -1;
}
Mark Michelson
committed
if ((q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
ao2_lock(q);
if (q->setqueuevar) {
sl = 0;
res = 0;
if (q->callscompleted > 0)
sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
snprintf(interfacevar,sizeof(interfacevar),
"QUEUEMAX=%d|QUEUESTRATEGY=%s|QUEUECALLS=%d|QUEUEHOLDTIME=%d|QUEUECOMPLETED=%d|QUEUEABANDONED=%d|QUEUESRVLEVEL=%d|QUEUESRVLEVELPERF=%2.1f",
q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->callscompleted, q->callsabandoned, q->servicelevel, sl);
pbx_builtin_setvar(chan, interfacevar);
}
Mark Michelson
committed
ao2_unlock(q);
queue_unref(q);
} else
ast_log(LOG_WARNING, "queue %s was not found\n", data);
snprintf(buf, len, "%d", res);
return 0;
}
Kevin P. Fleming
committed
static int queue_function_qac(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
Kevin P. Fleming
committed
{
int count = 0;
Mark Michelson
committed
struct member *m;
struct ao2_iterator mem_iter;
char *option;
Mark Michelson
committed
if (ast_strlen_zero(data)) {
ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
return -1;
}
if ((option = strchr(data, ',')))
*option++ = '\0';
else
option = "logged";
if ((q = load_realtime_queue(data))) {
Mark Michelson
committed
4252
4253
4254
4255
4256
4257
4258
4259
4260
4261
4262
4263
4264
4265
4266
4267
4268
4269
4270
4271
4272
4273
4274
4275
4276
4277
4278
4279
4280
4281
4282
4283
4284
4285
4286
ao2_lock(q);
if(!strcasecmp(option, "logged")) {
mem_iter = ao2_iterator_init(q->members, 0);
while ((m = ao2_iterator_next(&mem_iter))) {
/* Count the agents who are logged in and presently answering calls */
if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
count++;
}
ao2_ref(m, -1);
}
} else if(!strcasecmp(option, "free")) {
mem_iter = ao2_iterator_init(q->members, 0);
while ((m = ao2_iterator_next(&mem_iter))) {
/* Count the agents who are logged in and presently answering calls */
if ((m->status == AST_DEVICE_NOT_INUSE) && (!m->paused)) {
count++;
}
ao2_ref(m, -1);
}
} else /* must be "count" */
count = q->membercount;
ao2_unlock(q);
queue_unref(q);
} else
ast_log(LOG_WARNING, "queue %s was not found\n", data);
snprintf(buf, len, "%d", count);
return 0;
}
static int queue_function_qac_dep(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
{
int count = 0;
struct member *m;
Mark Michelson
committed
struct ao2_iterator mem_iter;
static int depflag = 1;
if (depflag) {
Mark Michelson
committed
depflag = 0;
ast_log(LOG_NOTICE, "The function QUEUE_MEMBER_COUNT has been deprecated in favor of the QUEUE_MEMBER function and will not be in further releases.\n");
}
Kevin P. Fleming
committed
if (ast_strlen_zero(data)) {
ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
Kevin P. Fleming
committed
}
Mark Michelson
committed
if((q = load_realtime_queue(data))) {
Mark Michelson
committed
ao2_lock(q);
mem_iter = ao2_iterator_init(q->members, 0);
while ((m = ao2_iterator_next(&mem_iter))) {
/* Count the agents who are logged in and presently answering calls */
if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
count++;
}
Mark Michelson
committed
ao2_unlock(q);
queue_unref(q);
} else
ast_log(LOG_WARNING, "queue %s was not found\n", data);
snprintf(buf, len, "%d", count);
Mark Michelson
committed
Kevin P. Fleming
committed
static int queue_function_queuewaitingcount(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
struct call_queue *q, tmpq = {
.name = data,
};
buf[0] = '\0';
if (ast_strlen_zero(data)) {
ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
return -1;
}
Mark Michelson
committed
if ((q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
ao2_lock(q);
Mark Michelson
committed
ao2_unlock(q);
queue_unref(q);
} else
ast_log(LOG_WARNING, "queue %s was not found\n", data);
Kevin P. Fleming
committed
snprintf(buf, len, "%d", count);
Kevin P. Fleming
committed
}
Kevin P. Fleming
committed
static int queue_function_queuememberlist(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
struct call_queue *q, tmpq = {
.name = data,
};
struct member *m;
/* Ensure an otherwise empty list doesn't return garbage */
buf[0] = '\0';
if (ast_strlen_zero(data)) {
ast_log(LOG_ERROR, "QUEUE_MEMBER_LIST requires an argument: queuename\n");
Mark Michelson
committed
if ((q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
int buflen = 0, count = 0;
struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
Mark Michelson
committed
ao2_lock(q);
while ((m = ao2_iterator_next(&mem_iter))) {
/* strcat() is always faster than printf() */
if (count++) {
strncat(buf + buflen, ",", len - buflen - 1);
buflen++;
}
strncat(buf + buflen, m->membername, len - buflen - 1);
buflen += strlen(m->membername);
/* Safeguard against overflow (negative length) */
if (buflen >= len - 2) {
ast_log(LOG_WARNING, "Truncating list\n");
break;
}
Mark Michelson
committed
ao2_unlock(q);
queue_unref(q);
} else
ast_log(LOG_WARNING, "queue %s was not found\n", data);
/* We should already be terminated, but let's make sure. */
buf[len - 1] = '\0';
/*! \brief Dialplan function QUEUE_MEMBER_PENALTY()
* Gets the members penalty. */
Joshua Colp
committed
static int queue_function_memberpenalty_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
{
4400
4401
4402
4403
4404
4405
4406
4407
4408
4409
4410
4411
4412
4413
4414
4415
4416
4417
4418
4419
4420
4421
4422
4423
int penalty;
AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(queuename);
AST_APP_ARG(interface);
);
/* Make sure the returned value on error is NULL. */
buf[0] = '\0';
if (ast_strlen_zero(data)) {
ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
return -1;
}
AST_STANDARD_APP_ARGS(args, data);
if (args.argc < 2) {
ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
return -1;
}
penalty = get_member_penalty (args.queuename, args.interface);
if (penalty >= 0) /* remember that buf is already '\0' */
snprintf (buf, len, "%d", penalty);
Joshua Colp
committed
return 0;
}
/*! Dialplan function QUEUE_MEMBER_PENALTY()
* Sets the members penalty. */
Joshua Colp
committed
static int queue_function_memberpenalty_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
{
4432
4433
4434
4435
4436
4437
4438
4439
4440
4441
4442
4443
4444
4445
4446
4447
4448
4449
4450
4451
4452
4453
4454
4455
4456
4457
4458
4459
4460
4461
4462
4463
4464
4465
4466
int penalty;
AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(queuename);
AST_APP_ARG(interface);
);
if (ast_strlen_zero(data)) {
ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
return -1;
}
AST_STANDARD_APP_ARGS(args, data);
if (args.argc < 2) {
ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
return -1;
}
penalty = atoi(value);
if (penalty < 0) {
ast_log(LOG_ERROR, "Invalid penalty\n");
return -1;
}
if (ast_strlen_zero(args.interface)) {
ast_log (LOG_ERROR, "<interface> parameter can't be null\n");
return -1;
}
/* if queuename = NULL then penalty will be set for interface in all the queues. */
set_member_penalty(args.queuename, args.interface, penalty);
return 0;
}
static struct ast_custom_function queuevar_function = {
.name = "QUEUE_VARIABLES",
.synopsis = "Return Queue information in variables",
.syntax = "QUEUE_VARIABLES(<queuename>)",
.desc =
"Makes the following queue variables available.\n"
"QUEUEMAX maxmimum number of calls allowed\n"
"QUEUESTRATEGY the strategy of the queue\n"
"QUEUECALLS number of calls currently in the queue\n"
"QUEUEHOLDTIME current average hold time\n"
"QUEUECOMPLETED number of completed calls for the queue\n"
"QUEUEABANDONED number of abandoned calls\n"
"QUEUESRVLEVEL queue service level\n"
"QUEUESRVLEVELPERF current service level performance\n"
"Returns 0 if queue is found and setqueuevar is defined, -1 otherwise",
.read = queue_function_var,
};
static struct ast_custom_function queuemembercount_function = {
Mark Michelson
committed
.name = "QUEUE_MEMBER",
.synopsis = "Count number of members answering a queue",
.syntax = "QUEUE_MEMBER(<queuename>, <option>)",
.desc =
"Returns the number of members currently associated with the specified queue.\n"
"One of three options may be passed to determine the count returned:\n"
"\"logged\" - Returns the number of logged-in members for the specified queue\n"
"\"free\" - Returns the number of logged-in members for the specified queue available to take a call\n"
"\"count\" - Returns the total number of members for the specified queue\n",
.read = queue_function_qac,
};
static struct ast_custom_function queuemembercount_dep = {
.name = "QUEUE_MEMBER_COUNT",
.synopsis = "Count number of members answering a queue",
.syntax = "QUEUE_MEMBER_COUNT(<queuename>)",
.desc =
Mark Michelson
committed
"Returns the number of members currently associated with the specified queue.\n\n"
"This function has been deprecated in favor of the QUEUE_MEMBER function\n",
.read = queue_function_qac_dep,
static struct ast_custom_function queuewaitingcount_function = {
.name = "QUEUE_WAITING_COUNT",
.synopsis = "Count number of calls currently waiting in a queue",
.syntax = "QUEUE_WAITING_COUNT(<queuename>)",
"Returns the number of callers currently waiting in the specified queue.\n",
.read = queue_function_queuewaitingcount,
};
static struct ast_custom_function queuememberlist_function = {
.name = "QUEUE_MEMBER_LIST",
.synopsis = "Returns a list of interfaces on a queue",
.syntax = "QUEUE_MEMBER_LIST(<queuename>)",
.desc =
"Returns a comma-separated list of members associated with the specified queue.\n",
.read = queue_function_queuememberlist,
};
static struct ast_custom_function queuememberpenalty_function = {
.name = "QUEUE_MEMBER_PENALTY",
.synopsis = "Gets or sets queue members penalty.",
.syntax = "QUEUE_MEMBER_PENALTY(<queuename>,<interface>)",
.desc =
"Gets or sets queue members penalty\n",
.read = queue_function_memberpenalty_read,
.write = queue_function_memberpenalty_write,
};
static int reload_queues(int reload)
struct call_queue *q;
struct ast_config *cfg;
char *cat, *tmp;
struct ast_variable *var;
struct ao2_iterator mem_iter;
Tilghman Lesher
committed
const char *general_val = NULL;
char parse[80];
char *interface;
int penalty;
struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
Mark Michelson
committed
struct ao2_iterator queue_iter;
AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(interface);
AST_APP_ARG(penalty);
AST_APP_ARG(membername);
);
if (!(cfg = ast_config_load("queues.conf", config_flags))) {
ast_log(LOG_NOTICE, "No call queueing config file (queues.conf), so no call queues\n");
return 0;
} else if (cfg == CONFIG_STATUS_FILEUNCHANGED)
return 0;
Mark Michelson
committed
ao2_lock(queues);
Mark Michelson
committed
queue_iter = ao2_iterator_init(queues, F_AO2I_DONTLOCK);
while ((q = ao2_iterator_next(&queue_iter))) {
Mark Michelson
committed
queue_unref(q);
cat = NULL;
while ((cat = ast_category_browse(cfg, cat)) ) {
if (!strcasecmp(cat, "general")) {
/* Initialize global settings */
queue_keep_stats = 0;
if ((general_val = ast_variable_retrieve(cfg, "general", "keepstats")))
queue_keep_stats = ast_true(general_val);
queue_persistent_members = 0;
if ((general_val = ast_variable_retrieve(cfg, "general", "persistentmembers")))
queue_persistent_members = ast_true(general_val);
autofill_default = 0;
if ((general_val = ast_variable_retrieve(cfg, "general", "autofill")))
autofill_default = ast_true(general_val);
montype_default = 0;
if ((general_val = ast_variable_retrieve(cfg, "general", "monitor-type"))) {
if (!strcasecmp(general_val, "mixmonitor"))
montype_default = 1;
}
update_cdr = 0;
if ((general_val = ast_variable_retrieve(cfg, "general", "updatecdr")))
update_cdr = ast_true(general_val);
Mark Michelson
committed
shared_lastcall = 0;
if ((general_val = ast_variable_retrieve(cfg, "general", "shared_lastcall")))
shared_lastcall = ast_true(general_val);
} else { /* Define queue */
struct call_queue tmpq = {
.name = cat,
};
if (!(q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
BJ Weschke
committed
if (!(q = alloc_queue(cat))) {
/* TODO: Handle memory allocation failure */
}
new = 1;
new = 0;
Mark Michelson
committed
const char *tmpvar;
if (!new)
Mark Michelson
committed
ao2_lock(q);
/* Check if a queue with this name already exists */
if (q->found) {
ast_log(LOG_WARNING, "Queue '%s' already defined! Skipping!\n", cat);
if(!new)
Mark Michelson
committed
ao2_unlock(q);
Mark Michelson
committed
/* Due to the fact that the "linear" strategy will have a different allocation
* scheme for queue members, we must devise the queue's strategy before other initializations
*/
if((tmpvar = ast_variable_retrieve(cfg, cat, "strategy"))) {
q->strategy = strat2int(tmpvar);
if (q->strategy < 0) {
ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
tmpvar, q->name);
q->strategy = QUEUE_STRATEGY_RINGALL;
}
} else
q->strategy = QUEUE_STRATEGY_RINGALL;
/* Re-initialize the queue, and clear statistics */
init_queue(q);
if (!queue_keep_stats)
clear_queue(q);
mem_iter = ao2_iterator_init(q->members, 0);
while ((cur = ao2_iterator_next(&mem_iter))) {
if (!cur->dynamic) {
cur->delme = 1;
}
for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
ast_copy_string(parse, var->value, sizeof(parse));
AST_NONSTANDARD_APP_ARGS(args, parse, ',');
interface = args.interface;
if(!ast_strlen_zero(args.penalty)) {
tmp = args.penalty;
while (*tmp && *tmp < 33) tmp++;
penalty = atoi(tmp);
if (penalty < 0) {
penalty = 0;
}
} else
penalty = 0;
if (!ast_strlen_zero(args.membername)) {
membername = args.membername;
while (*membername && *membername < 33) membername++;
/* Find the old position in the list */
ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
cur = ao2_find(q->members, &tmpmem, OBJ_POINTER | OBJ_UNLINK);
newm = create_queue_member(interface, membername, penalty, cur ? cur->paused : 0);
ao2_ref(newm, -1);
newm = NULL;
if (cur)
ao2_ref(cur, -1);
else {
BJ Weschke
committed
/* Add them to the master int list if necessary */
add_to_interfaces(interface);
queue_set_param(q, var->name, var->value, var->lineno, 1);
/* Free remaining members marked as delme */
mem_iter = ao2_iterator_init(q->members, 0);
while ((cur = ao2_iterator_next(&mem_iter))) {
if (! cur->delme) {
ao2_ref(cur, -1);
remove_from_interfaces(cur->interface);
ao2_unlink(q->members, cur);
ao2_ref(cur, -1);
Mark Michelson
committed
ao2_link(queues, q);
Mark Michelson
committed
ao2_unlock(q);
queue_unref(q);
Mark Michelson
committed
queue_iter = ao2_iterator_init(queues, 0);
while ((q = ao2_iterator_next(&queue_iter))) {
Mark Spencer
committed
if (q->dead) {
Mark Michelson
committed
ao2_unlink(queues, q);
Mark Michelson
committed
ao2_lock(q);
mem_iter = ao2_iterator_init(q->members, 0);
while ((cur = ao2_iterator_next(&mem_iter))) {
cur->status = ast_device_state(cur->interface);
Mark Michelson
committed
ao2_unlock(q);
Mark Michelson
committed
queue_unref(q);
Mark Michelson
committed
ao2_unlock(queues);
return 1;
/*! \brief direct ouput to manager or cli with proper terminator */
static void do_print(struct mansession *s, int fd, const char *str)
{
if (s)
astman_append(s, "%s\r\n", str);
else
ast_cli(fd, "%s\n", str);
}
static char *__queues_show(struct mansession *s, int fd, int argc, char **argv)
struct ast_str *out = ast_str_alloca(240);
int found = 0;
time_t now = time(NULL);
Mark Michelson
committed
struct ao2_iterator queue_iter;
struct ao2_iterator mem_iter;
Tilghman Lesher
committed
/* We only want to load realtime queues when a specific queue is asked for. */
if (argc == 3) /* specific queue */
Tilghman Lesher
committed
load_realtime_queue(argv[2]);
Mark Michelson
committed
queue_iter = ao2_iterator_init(queues, 0);
while ((q = ao2_iterator_next(&queue_iter))) {
Mark Michelson
committed
ao2_lock(q);
Joshua Colp
committed
if (argc == 3 && strcasecmp(q->name, argv[2])) {
Mark Michelson
committed
ao2_unlock(q);
found = 1;
ast_str_set(&out, 0, "%-12.12s has %d calls (max ", q->name, q->count);
ast_str_append(&out, 0, "%d", q->maxlen);
ast_str_append(&out, 0, "unlimited");
if (q->callscompleted > 0)
sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
ast_str_append(&out, 0, ") in '%s' strategy (%ds holdtime), W:%d, C:%d, A:%d, SL:%2.1f%% within %ds",
int2strat(q->strategy), q->holdtime, q->weight,
q->callscompleted, q->callsabandoned,sl,q->servicelevel);
do_print(s, fd, out->str);
if (!ao2_container_count(q->members))
do_print(s, fd, " No Members");
else {
struct member *mem;
do_print(s, fd, " Members: ");
mem_iter = ao2_iterator_init(q->members, 0);
while ((mem = ao2_iterator_next(&mem_iter))) {
ast_str_set(&out, 0, " %s", mem->membername);
if (mem->penalty)
ast_str_append(&out, 0, " with penalty %d", mem->penalty);
ast_str_append(&out, 0, "%s%s%s (%s)",
mem->dynamic ? " (dynamic)" : "",
mem->realtime ? " (realtime)" : "",
mem->paused ? " (paused)" : "",
devstate2str(mem->status));
if (mem->calls)
ast_str_append(&out, 0, " has taken %d calls (last was %ld secs ago)",
else
ast_str_append(&out, 0, " has taken no calls yet");
do_print(s, fd, out->str);
}
}
if (!q->head)
do_print(s, fd, " No Callers");
else {
struct queue_ent *qe;
int pos = 1;
do_print(s, fd, " Callers: ");
for (qe = q->head; qe; qe = qe->next) {
ast_str_set(&out, 0, " %d. %s (wait: %ld:%2.2ld, prio: %d)",
pos++, qe->chan->name, (long) (now - qe->start) / 60,
(long) (now - qe->start) % 60, qe->prio);
do_print(s, fd, out->str);
}
}
do_print(s, fd, ""); /* blank line between entries */
Mark Michelson
committed
ao2_unlock(q);
if (argc == 3) { /* print a specific entry */
queue_unref(q);
Mark Michelson
committed
}
queue_unref(q);
if (!found) {
if (argc == 3)
ast_str_set(&out, 0, "No such queue: %s.", argv[2]);
else
ast_str_set(&out, 0, "No queues.");
do_print(s, fd, out->str);
}
Russell Bryant
committed
static char *complete_queue(const char *line, const char *word, int pos, int state)
char *ret = NULL;
int which = 0;
int wordlen = strlen(word);
Mark Michelson
committed
struct ao2_iterator queue_iter;
Mark Michelson
committed
queue_iter = ao2_iterator_init(queues, 0);
while((q = ao2_iterator_next(&queue_iter))) {
if (!strncasecmp(word, q->name, wordlen) && ++which > state) {
Mark Michelson
committed
queue_unref(q);
break;
Mark Michelson
committed
queue_unref(q);
static char *complete_queue_show(const char *line, const char *word, int pos, int state)
{
if (pos == 2)
return complete_queue(line, word, pos, state);
return NULL;
}
static char *queue_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
switch ( cmd ) {
case CLI_INIT:
e->command = "queue show";
e->usage =
"Usage: queue show\n"
" Provides summary information on a specified queue.\n";
return NULL;
case CLI_GENERATE:
return complete_queue_show(a->line, a->word, a->pos, a->n);
}
return __queues_show(NULL, a->fd, a->argc, a->argv);
}
/*!\brief callback to display queues status in manager
\addtogroup Group_AMI
static int manager_queues_show(struct mansession *s, const struct message *m)
char *a[] = { "queue", "show" };
astman_append(s, "\r\n\r\n"); /* Properly terminate Manager output */
Kevin P. Fleming
committed
return RESULT_SUCCESS;
static int manager_queues_summary(struct mansession *s, const struct message *m)
{
time_t now;
int qmemcount = 0;
int qmemavail = 0;
int qchancount = 0;
const char *id = astman_get_header(m, "ActionID");
const char *queuefilter = astman_get_header(m, "Queue");
char idText[256] = "";
struct call_queue *q;
struct queue_ent *qe;
struct member *mem;
Mark Michelson
committed
struct ao2_iterator queue_iter;
struct ao2_iterator mem_iter;
astman_send_ack(s, m, "Queue summary will follow");
time(&now);
if (!ast_strlen_zero(id))
snprintf(idText, 256, "ActionID: %s\r\n", id);
Mark Michelson
committed
queue_iter = ao2_iterator_init(queues, 0);
while((q = ao2_iterator_next(&queue_iter))) {
ao2_lock(q);
/* List queue properties */
if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) {
/* Reset the necessary local variables if no queuefilter is set*/
qmemcount = 0;
qmemavail = 0;
qchancount = 0;
qlongestholdtime = 0;
mem_iter = ao2_iterator_init(q->members, 0);
while ((mem = ao2_iterator_next(&mem_iter))) {
if ((mem->status != AST_DEVICE_UNAVAILABLE) && (mem->status != AST_DEVICE_INVALID)) {
++qmemcount;
if (((mem->status == AST_DEVICE_NOT_INUSE) || (mem->status == AST_DEVICE_UNKNOWN)) && !(mem->paused)) {
++qmemavail;
}
}
}
for (qe = q->head; qe; qe = qe->next) {
if ((now - qe->start) > qlongestholdtime) {
qlongestholdtime = now - qe->start;
}
++qchancount;
}
astman_append(s, "Event: QueueSummary\r\n"
"Queue: %s\r\n"
"LoggedIn: %d\r\n"
"Available: %d\r\n"
"Callers: %d\r\n"
"HoldTime: %d\r\n"
q->name, qmemcount, qmemavail, qchancount, q->holdtime, qlongestholdtime, idText);
Mark Michelson
committed
ao2_unlock(q);
queue_unref(q);
}
astman_append(s,
"Event: QueueSummaryComplete\r\n"
"%s"
"\r\n", idText);
return RESULT_SUCCESS;
}
static int manager_queues_status(struct mansession *s, const struct message *m)
const char *id = astman_get_header(m,"ActionID");
const char *queuefilter = astman_get_header(m,"Queue");
const char *memberfilter = astman_get_header(m,"Member");
float sl = 0;
struct member *mem;
Mark Michelson
committed
struct ao2_iterator queue_iter;
struct ao2_iterator mem_iter;
astman_send_ack(s, m, "Queue status will follow");
if (!ast_strlen_zero(id))
snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
Mark Michelson
committed
queue_iter = ao2_iterator_init(queues, 0);
while ((q = ao2_iterator_next(&queue_iter))) {
ao2_lock(q);
if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) {
sl = ((q->callscompleted > 0) ? 100 * ((float)q->callscompletedinsl / (float)q->callscompleted) : 0);
astman_append(s, "Event: QueueParams\r\n"