}
}
+/*! \brief Print a single queue to AMI or the CLI */
+static void print_queue(struct mansession *s, int fd, struct call_queue *q)
+{
+ float sl;
+ float sl2;
+ struct ao2_iterator mem_iter;
+ struct ast_str *out = ast_str_alloca(512);
+ time_t now = time(NULL);
+
+ ast_str_set(&out, 0, "%s has %d calls (max ", q->name, q->count);
+ if (q->maxlen) {
+ ast_str_append(&out, 0, "%d", q->maxlen);
+ } else {
+ ast_str_append(&out, 0, "unlimited");
+ }
+ sl = 0;
+ sl2 = 0;
+ if (q->callscompleted > 0) {
+ sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
+ }
+ if (q->callscompleted + q->callsabandoned > 0) {
+ sl2 =100 * (((float)q->callsabandonedinsl + (float)q->callscompletedinsl) / ((float)q->callsabandoned + (float)q->callscompleted));
+ }
+
+ ast_str_append(&out, 0, ") in '%s' strategy (%ds holdtime, %ds talktime), W:%d, C:%d, A:%d, SL:%2.1f%%, SL2:%2.1f%% within %ds",
+ int2strat(q->strategy), q->holdtime, q->talktime, q->weight, q->callscompleted, q->callsabandoned, sl, sl2, q->servicelevel);
+ do_print(s, fd, ast_str_buffer(out));
+ 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 (strcasecmp(mem->membername, mem->interface)) {
+ ast_str_append(&out, 0, " (%s", mem->interface);
+ if (!ast_strlen_zero(mem->state_interface)
+ && strcmp(mem->state_interface, mem->interface)) {
+ ast_str_append(&out, 0, " from %s", mem->state_interface);
+ }
+ ast_str_append(&out, 0, ")");
+ }
+ if (mem->penalty) {
+ ast_str_append(&out, 0, " with penalty %d", mem->penalty);
+ }
+
+ ast_str_append(&out, 0, " (ringinuse %s)", mem->ringinuse ? "enabled" : "disabled");
+
+ ast_str_append(&out, 0, "%s%s%s%s%s%s%s%s%s",
+ mem->dynamic ? ast_term_color(COLOR_CYAN, COLOR_BLACK) : "", mem->dynamic ? " (dynamic)" : "", ast_term_reset(),
+ mem->realtime ? ast_term_color(COLOR_MAGENTA, COLOR_BLACK) : "", mem->realtime ? " (realtime)" : "", ast_term_reset(),
+ mem->starttime ? ast_term_color(COLOR_BROWN, COLOR_BLACK) : "", mem->starttime ? " (in call)" : "", ast_term_reset());
+
+ if (mem->paused) {
+ ast_str_append(&out, 0, " %s(paused%s%s was %ld secs ago)%s",
+ ast_term_color(COLOR_BROWN, COLOR_BLACK),
+ ast_strlen_zero(mem->reason_paused) ? "" : ":",
+ ast_strlen_zero(mem->reason_paused) ? "" : mem->reason_paused,
+ (long) (now - mem->lastpause),
+ ast_term_reset());
+ }
+
+ ast_str_append(&out, 0, " (%s%s%s)",
+ ast_term_color(
+ mem->status == AST_DEVICE_UNAVAILABLE || mem->status == AST_DEVICE_UNKNOWN ?
+ COLOR_RED : COLOR_GREEN, COLOR_BLACK),
+ ast_devstate2str(mem->status), ast_term_reset());
+ if (mem->calls) {
+ ast_str_append(&out, 0, " has taken %d calls (last was %ld secs ago)",
+ mem->calls, (long) (now - mem->lastcall));
+ } else {
+ ast_str_append(&out, 0, " has taken no calls yet");
+ }
+ do_print(s, fd, ast_str_buffer(out));
+ ao2_ref(mem, -1);
+ }
+ ao2_iterator_destroy(&mem_iter);
+ }
+ 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++, ast_channel_name(qe->chan), (long) (now - qe->start) / 60,
+ (long) (now - qe->start) % 60, qe->prio);
+ do_print(s, fd, ast_str_buffer(out));
+ }
+ }
+ do_print(s, fd, ""); /* blank line between entries */
+}
+
+AO2_STRING_FIELD_SORT_FN(call_queue, name);
+
/*!
* \brief Show queue(s) status and statistics
*
{
struct call_queue *q;
struct ast_str *out = ast_str_alloca(512);
- int found = 0;
- time_t now = time(NULL);
+ struct ao2_container *sorted_queues;
+
struct ao2_iterator queue_iter;
- struct ao2_iterator mem_iter;
+ int found = 0;
if (argc != 2 && argc != 3) {
return CLI_SHOWUSAGE;
if (argc == 3) { /* specific queue */
if ((q = find_load_queue_rt_friendly(argv[2]))) {
- queue_t_unref(q, "Done with temporary pointer");
+ ao2_lock(q);
+ print_queue(s, fd, q);
+ ao2_unlock(q);
+ queue_unref(q);
+ } else {
+ ast_str_set(&out, 0, "No such queue: %s.", argv[2]);
+ do_print(s, fd, ast_str_buffer(out));
}
- } else if (ast_check_realtime("queues")) {
+ return CLI_SUCCESS;
+ }
+
+ if (ast_check_realtime("queues")) {
/* This block is to find any queues which are defined in realtime but
* which have not yet been added to the in-core container
*/
}
}
- ao2_lock(queues);
- queue_iter = ao2_iterator_init(queues, AO2_ITERATOR_DONTLOCK);
- while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
- float sl;
- float sl2;
+ /*
+ * Snapping a copy of the container prevents having to lock both the queues container
+ * and the queue itself at the same time. It also allows us to sort the entries.
+ */
+ sorted_queues = ao2_container_alloc_rbtree(AO2_ALLOC_OPT_LOCK_NOLOCK, 0, call_queue_sort_fn, NULL);
+ if (!sorted_queues) {
+ return CLI_SUCCESS;
+ }
+ if (ao2_container_dup(sorted_queues, queues, 0)) {
+ ao2_ref(sorted_queues, -1);
+ return CLI_SUCCESS;
+ }
+ /*
+ * No need to lock the container since it's temporary and static.
+ * We also unlink the entries as we use them so the container is
+ * empty when the iterator finishes. We can then just unref the container.
+ */
+ queue_iter = ao2_iterator_init(sorted_queues, AO2_ITERATOR_DONTLOCK | AO2_ITERATOR_UNLINK);
+ while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
struct call_queue *realtime_queue = NULL;
-
ao2_lock(q);
/* This check is to make sure we don't print information for realtime
* queues which have been deleted from realtime but which have not yet
* been deleted from the in-core container. Only do this if we're not
* looking for a specific queue.
*/
- if (argc < 3 && q->realtime) {
+ if (q->realtime) {
realtime_queue = find_load_queue_rt_friendly(q->name);
if (!realtime_queue) {
ao2_unlock(q);
queue_t_unref(realtime_queue, "Queue is already in memory");
}
- if (argc == 3 && strcasecmp(q->name, argv[2])) {
- ao2_unlock(q);
- queue_t_unref(q, "Done with iterator");
- continue;
- }
found = 1;
+ print_queue(s, fd, q);
- ast_str_set(&out, 0, "%s has %d calls (max ", q->name, q->count);
- if (q->maxlen) {
- ast_str_append(&out, 0, "%d", q->maxlen);
- } else {
- ast_str_append(&out, 0, "unlimited");
- }
- sl = 0;
- sl2 = 0;
- if (q->callscompleted > 0) {
- sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
- }
- if (q->callscompleted + q->callsabandoned > 0) {
- sl2 =100 * (((float)q->callsabandonedinsl + (float)q->callscompletedinsl) / ((float)q->callsabandoned + (float)q->callscompleted));
- }
-
- ast_str_append(&out, 0, ") in '%s' strategy (%ds holdtime, %ds talktime), W:%d, C:%d, A:%d, SL:%2.1f%%, SL2:%2.1f%% within %ds",
- int2strat(q->strategy), q->holdtime, q->talktime, q->weight, q->callscompleted, q->callsabandoned, sl, sl2, q->servicelevel);
- do_print(s, fd, ast_str_buffer(out));
- 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 (strcasecmp(mem->membername, mem->interface)) {
- ast_str_append(&out, 0, " (%s", mem->interface);
- if (!ast_strlen_zero(mem->state_interface)
- && strcmp(mem->state_interface, mem->interface)) {
- ast_str_append(&out, 0, " from %s", mem->state_interface);
- }
- ast_str_append(&out, 0, ")");
- }
- if (mem->penalty) {
- ast_str_append(&out, 0, " with penalty %d", mem->penalty);
- }
-
- ast_str_append(&out, 0, " (ringinuse %s)", mem->ringinuse ? "enabled" : "disabled");
-
- ast_str_append(&out, 0, "%s%s%s%s%s%s%s%s%s",
- mem->dynamic ? ast_term_color(COLOR_CYAN, COLOR_BLACK) : "", mem->dynamic ? " (dynamic)" : "", ast_term_reset(),
- mem->realtime ? ast_term_color(COLOR_MAGENTA, COLOR_BLACK) : "", mem->realtime ? " (realtime)" : "", ast_term_reset(),
- mem->starttime ? ast_term_color(COLOR_BROWN, COLOR_BLACK) : "", mem->starttime ? " (in call)" : "", ast_term_reset());
-
- if (mem->paused) {
- ast_str_append(&out, 0, " %s(paused%s%s was %ld secs ago)%s",
- ast_term_color(COLOR_BROWN, COLOR_BLACK),
- ast_strlen_zero(mem->reason_paused) ? "" : ":",
- ast_strlen_zero(mem->reason_paused) ? "" : mem->reason_paused,
- (long) (now - mem->lastpause),
- ast_term_reset());
- }
-
- ast_str_append(&out, 0, " (%s%s%s)",
- ast_term_color(
- mem->status == AST_DEVICE_UNAVAILABLE || mem->status == AST_DEVICE_UNKNOWN ?
- COLOR_RED : COLOR_GREEN, COLOR_BLACK),
- ast_devstate2str(mem->status), ast_term_reset());
- if (mem->calls) {
- ast_str_append(&out, 0, " has taken %d calls (last was %ld secs ago)",
- mem->calls, (long) (now - mem->lastcall));
- } else {
- ast_str_append(&out, 0, " has taken no calls yet");
- }
- do_print(s, fd, ast_str_buffer(out));
- ao2_ref(mem, -1);
- }
- ao2_iterator_destroy(&mem_iter);
- }
- 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++, ast_channel_name(qe->chan), (long) (now - qe->start) / 60,
- (long) (now - qe->start) % 60, qe->prio);
- do_print(s, fd, ast_str_buffer(out));
- }
- }
- do_print(s, fd, ""); /* blank line between entries */
ao2_unlock(q);
queue_t_unref(q, "Done with iterator"); /* Unref the iterator's reference */
}
ao2_iterator_destroy(&queue_iter);
- ao2_unlock(queues);
+ ao2_ref(sorted_queues, -1);
if (!found) {
- if (argc == 3) {
- ast_str_set(&out, 0, "No such queue: %s.", argv[2]);
- } else {
- ast_str_set(&out, 0, "No queues.");
- }
+ ast_str_set(&out, 0, "No queues.");
do_print(s, fd, ast_str_buffer(out));
}
return CLI_SUCCESS;