summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES8
-rw-r--r--apps/app_queue.c30
-rw-r--r--channels/chan_sip.c183
3 files changed, 208 insertions, 13 deletions
diff --git a/CHANGES b/CHANGES
index 334632a8f..e9e651b2b 100644
--- a/CHANGES
+++ b/CHANGES
@@ -723,8 +723,12 @@ Queue changes
is typically placed.
* The configuration method for the "joinempty" and "leavewhenempty" options has
changed to a comma-separated list of methods of determining member availability
- instead of vague terms such as "yes," "loose," "no," and "strict." These old four
- values are still accepted for backwards-compatibility, though.
+ instead of vague terms such as "yes," "loose," "no," and "strict." These old four
+ values are still accepted for backwards-compatibility, though.
+ * The average talktime is now calculated on queues. This information is reported via the
+ CLI commands "queue show" and "queues show"; through the AMI events AgentComplete, QueueSummary,
+ and QueueParams; and through the channelvariable QUEUETALKTIME if setinterfacevar=yes is set for
+ the queue.
MeetMe Changes
--------------
diff --git a/apps/app_queue.c b/apps/app_queue.c
index 75915cdb3..0ce20174a 100644
--- a/apps/app_queue.c
+++ b/apps/app_queue.c
@@ -764,6 +764,7 @@ struct call_queue {
int randomperiodicannounce; /*!< Are periodic announcments randomly chosen */
int roundingseconds; /*!< How many seconds do we round to? */
int holdtime; /*!< Current avg holdtime, based on an exponential average */
+ int talktime; /*!< Current avg talktime, based on the same exponential average */
int callscompleted; /*!< Number of queue calls completed */
int callsabandoned; /*!< Number of queue calls abandoned */
int servicelevel; /*!< seconds setting for servicelevel*/
@@ -885,8 +886,8 @@ static void set_queue_variables(struct queue_ent *qe)
sl = 100 * ((float) qe->parent->callscompletedinsl / (float) qe->parent->callscompleted);
snprintf(interfacevar, sizeof(interfacevar),
- "QUEUENAME=%s,QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f",
- qe->parent->name, qe->parent->maxlen, int2strat(qe->parent->strategy), qe->parent->count, qe->parent->holdtime, qe->parent->callscompleted,
+ "QUEUENAME=%s,QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUETALKTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f",
+ qe->parent->name, qe->parent->maxlen, int2strat(qe->parent->strategy), qe->parent->count, qe->parent->holdtime, qe->parent->talktime, qe->parent->callscompleted,
qe->parent->callsabandoned, qe->parent->servicelevel, sl);
pbx_builtin_setvar_multiple(qe->chan, interfacevar);
@@ -3139,8 +3140,10 @@ static int wait_our_turn(struct queue_ent *qe, int ringing, enum queue_result *r
* \brief update the queue status
* \retval Always 0
*/
-static int update_queue(struct call_queue *q, struct member *member, int callcompletedinsl)
+static int update_queue(struct call_queue *q, struct member *member, int callcompletedinsl, int newtalktime)
{
+ int oldtalktime;
+
struct member *mem;
struct call_queue *qtmp;
struct ao2_iterator queue_iter;
@@ -3169,6 +3172,9 @@ static int update_queue(struct call_queue *q, struct member *member, int callcom
q->callscompleted++;
if (callcompletedinsl)
q->callscompletedinsl++;
+ /* Calculate talktime using the same exponential average as holdtime code*/
+ oldtalktime = q->talktime;
+ q->talktime = (((oldtalktime << 2) - oldtalktime) + newtalktime) >> 2;
ao2_unlock(q);
return 0;
}
@@ -3324,7 +3330,7 @@ static void queue_transfer_fixup(void *data, struct ast_channel *old_chan, struc
new_chan->exten, new_chan->context, (long) (callstart - qe->start),
(long) (time(NULL) - callstart), qe->opos);
- update_queue(qe->parent, member, callcompletedinsl);
+ update_queue(qe->parent, member, callcompletedinsl, (time(NULL) - callstart));
if ((datastore = ast_channel_datastore_find(new_chan, &queue_transfer_info, NULL))) {
ast_channel_datastore_remove(new_chan, datastore);
@@ -4093,7 +4099,7 @@ static int try_calling(struct queue_ent *qe, const char *options, char *announce
ast_channel_datastore_remove(qe->chan, tds);
}
ast_channel_unlock(qe->chan);
- update_queue(qe->parent, member, callcompletedinsl);
+ update_queue(qe->parent, member, callcompletedinsl, (time(NULL) - callstart));
}
if (transfer_ds) {
@@ -5114,8 +5120,8 @@ static int queue_function_var(struct ast_channel *chan, const char *cmd, char *d
}
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);
+ "QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUETALKTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f",
+ q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->talktime, q->callscompleted, q->callsabandoned, q->servicelevel, sl);
pbx_builtin_setvar_multiple(chan, interfacevar);
}
@@ -5753,8 +5759,8 @@ static char *__queues_show(struct mansession *s, int fd, int argc, char **argv)
sl = 0;
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,
+ ast_str_append(&out, 0, ") in '%s' strategy (%ds holdtime, %ds talktime), W:%d, C:%d, A:%d, SL:%2.1f%% within %ds",
+ int2strat(q->strategy), q->holdtime, q->talktime, q->weight,
q->callscompleted, q->callsabandoned,sl,q->servicelevel);
do_print(s, fd, ast_str_buffer(out));
if (!ao2_container_count(q->members))
@@ -5951,10 +5957,11 @@ static int manager_queues_summary(struct mansession *s, const struct message *m)
"Available: %d\r\n"
"Callers: %d\r\n"
"HoldTime: %d\r\n"
+ "TalkTime: %d\r\n"
"LongestHoldTime: %d\r\n"
"%s"
"\r\n",
- q->name, qmemcount, qmemavail, qchancount, q->holdtime, qlongestholdtime, idText);
+ q->name, qmemcount, qmemavail, qchancount, q->holdtime, q->talktime, qlongestholdtime, idText);
}
ao2_unlock(q);
queue_unref(q);
@@ -6001,6 +6008,7 @@ static int manager_queues_status(struct mansession *s, const struct message *m)
"Strategy: %s\r\n"
"Calls: %d\r\n"
"Holdtime: %d\r\n"
+ "TalkTime: %d\r\n"
"Completed: %d\r\n"
"Abandoned: %d\r\n"
"ServiceLevel: %d\r\n"
@@ -6008,7 +6016,7 @@ static int manager_queues_status(struct mansession *s, const struct message *m)
"Weight: %d\r\n"
"%s"
"\r\n",
- q->name, q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->callscompleted,
+ q->name, q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->talktime, q->callscompleted,
q->callsabandoned, q->servicelevel, sl, q->weight, idText);
/* List Queue Members */
mem_iter = ao2_iterator_init(q->members, 0);
diff --git a/channels/chan_sip.c b/channels/chan_sip.c
index f96322c3d..4418b9ad7 100644
--- a/channels/chan_sip.c
+++ b/channels/chan_sip.c
@@ -13221,6 +13221,76 @@ static char *sip_show_tcp(struct ast_cli_entry *e, int cmd, struct ast_cli_args
#undef FORMAT2
}
+/*! \brief CLI Command 'SIP Show Users' */
+static char *sip_show_users(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ regex_t regexbuf;
+ int havepattern = FALSE;
+ struct ao2_iterator user_iter;
+ struct sip_peer *user;
+
+#define FORMAT "%-25.25s %-15.15s %-15.15s %-15.15s %-5.5s%-10.10s\n"
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "sip show users";
+ e->usage =
+ "Usage: sip show users [like <pattern>]\n"
+ " Lists all known SIP users.\n"
+ " Optional regular expression pattern is used to filter the user list.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ switch (a->argc) {
+ case 5:
+ if (!strcasecmp(a->argv[3], "like")) {
+ if (regcomp(&regexbuf, a->argv[4], REG_EXTENDED | REG_NOSUB))
+ return CLI_SHOWUSAGE;
+ havepattern = TRUE;
+ } else
+ return CLI_SHOWUSAGE;
+ case 3:
+ break;
+ default:
+ return CLI_SHOWUSAGE;
+ }
+
+ ast_cli(a->fd, FORMAT, "Username", "Secret", "Accountcode", "Def.Context", "ACL", "NAT");
+
+ user_iter = ao2_iterator_init(peers, 0);
+ while ((user = ao2_iterator_next(&user_iter))) {
+ ao2_lock(user);
+ if (user->onlymatchonip == FALSE) {
+ ao2_unlock(user);
+ unref_peer(user, "sip show users");
+ continue;
+ }
+
+ if (havepattern && regexec(&regexbuf, user->name, 0, NULL, 0)) {
+ ao2_unlock(user);
+ unref_peer(user, "sip show users");
+ continue;
+ }
+
+ ast_cli(a->fd, FORMAT, user->name,
+ user->secret,
+ user->accountcode,
+ user->context,
+ cli_yesno(user->ha != NULL),
+ nat2str(ast_test_flag(&user->flags[0], SIP_NAT)));
+ ao2_unlock(user);
+ unref_peer(user, "sip show users");
+ }
+
+ if (havepattern)
+ regfree(&regexbuf);
+
+ return CLI_SUCCESS;
+#undef FORMAT
+}
+
/*! \brief Manager Action SIPShowRegistry description */
static char mandescr_show_registry[] =
"Description: Lists all registration requests and status\n"
@@ -14244,6 +14314,117 @@ static char *_sip_show_peer(int type, int fd, struct mansession *s, const struct
return CLI_SUCCESS;
}
+/*! \brief Do completion on user name */
+static char *complete_sip_user(const char *word, int state)
+{
+ char *result = NULL;
+ int wordlen = strlen(word);
+ int which = 0;
+ struct ao2_iterator user_iter;
+ struct sip_peer *user;
+
+ user_iter = ao2_iterator_init(peers, 0);
+ while ((user = ao2_iterator_next(&user_iter))) {
+ ao2_lock(user);
+ if (user->onlymatchonip == FALSE) {
+ ao2_unlock(user);
+ unref_peer(user, "complete sip user");
+ continue;
+ }
+ /* locking of the object is not required because only the name and flags are being compared */
+ if (!strncasecmp(word, user->name, wordlen) && ++which > state) {
+ result = ast_strdup(user->name);
+ }
+ ao2_unlock(user);
+ unref_peer(user, "complete sip user");
+ }
+ return result;
+}
+/*! \brief Support routine for 'sip show user' CLI */
+static char *complete_sip_show_user(const char *line, const char *word, int pos, int state)
+{
+ if (pos == 3)
+ return complete_sip_user(word, state);
+
+ return NULL;
+}
+
+/*! \brief Show one user in detail */
+static char *sip_show_user(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ char cbuf[256];
+ struct sip_peer *user;
+ struct ast_variable *v;
+ int load_realtime;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "sip show user";
+ e->usage =
+ "Usage: sip show user <name> [load]\n"
+ " Shows all details on one SIP user and the current status.\n"
+ " Option \"load\" forces lookup of peer in realtime storage.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return complete_sip_show_user(a->line, a->word, a->pos, a->n);
+ }
+
+ if (a->argc < 4)
+ return CLI_SHOWUSAGE;
+
+ /* Load from realtime storage? */
+ load_realtime = (a->argc == 5 && !strcmp(a->argv[4], "load")) ? TRUE : FALSE;
+
+ if ((user = find_peer(a->argv[3], NULL, load_realtime, TRUE, FALSE))) {
+ ao2_lock(user);
+ ast_cli(a->fd, "\n\n");
+ ast_cli(a->fd, " * Name : %s\n", user->name);
+ ast_cli(a->fd, " Secret : %s\n", ast_strlen_zero(user->secret)?"<Not set>":"<Set>");
+ ast_cli(a->fd, " MD5Secret : %s\n", ast_strlen_zero(user->md5secret)?"<Not set>":"<Set>");
+ ast_cli(a->fd, " Context : %s\n", user->context);
+ ast_cli(a->fd, " Language : %s\n", user->language);
+ if (!ast_strlen_zero(user->accountcode))
+ ast_cli(a->fd, " Accountcode : %s\n", user->accountcode);
+ ast_cli(a->fd, " AMA flags : %s\n", ast_cdr_flags2str(user->amaflags));
+ ast_cli(a->fd, " Transfer mode: %s\n", transfermode2str(user->allowtransfer));
+ ast_cli(a->fd, " MaxCallBR : %d kbps\n", user->maxcallbitrate);
+ ast_cli(a->fd, " CallingPres : %s\n", ast_describe_caller_presentation(user->callingpres));
+ ast_cli(a->fd, " Call limit : %d\n", user->call_limit);
+ ast_cli(a->fd, " Callgroup : ");
+ print_group(a->fd, user->callgroup, 0);
+ ast_cli(a->fd, " Pickupgroup : ");
+ print_group(a->fd, user->pickupgroup, 0);
+ ast_cli(a->fd, " Callerid : %s\n", ast_callerid_merge(cbuf, sizeof(cbuf), user->cid_name, user->cid_num, "<unspecified>"));
+ ast_cli(a->fd, " ACL : %s\n", cli_yesno(user->ha != NULL));
+ ast_cli(a->fd, " Sess-Timers : %s\n", stmode2str(user->stimer.st_mode_oper));
+ ast_cli(a->fd, " Sess-Refresh : %s\n", strefresher2str(user->stimer.st_ref));
+ ast_cli(a->fd, " Sess-Expires : %d secs\n", user->stimer.st_max_se);
+ ast_cli(a->fd, " Sess-Min-SE : %d secs\n", user->stimer.st_min_se);
+
+ ast_cli(a->fd, " Codec Order : (");
+ print_codec_to_cli(a->fd, &user->prefs);
+ ast_cli(a->fd, ")\n");
+
+ ast_cli(a->fd, " Auto-Framing: %s \n", cli_yesno(user->autoframing));
+ if (user->chanvars) {
+ ast_cli(a->fd, " Variables :\n");
+ for (v = user->chanvars ; v ; v = v->next)
+ ast_cli(a->fd, " %s = %s\n", v->name, v->value);
+ }
+
+ ast_cli(a->fd, "\n");
+
+ ao2_unlock(user);
+ unref_peer(user, "sip show user");
+ } else {
+ ast_cli(a->fd, "User %s not found.\n", a->argv[3]);
+ ast_cli(a->fd, "\n");
+ }
+
+ return CLI_SUCCESS;
+}
+
+
static char *sip_show_sched(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
char cbuf[2256];
@@ -24002,6 +24183,8 @@ static struct ast_cli_entry cli_sip[] = {
AST_CLI_DEFINE(sip_show_channel, "Show detailed SIP channel info"),
AST_CLI_DEFINE(sip_show_history, "Show SIP dialog history"),
AST_CLI_DEFINE(sip_show_peer, "Show details on specific SIP peer"),
+ AST_CLI_DEFINE(sip_show_users, "List defined SIP users"),
+ AST_CLI_DEFINE(sip_show_user, "Show details on specific SIP user"),
AST_CLI_DEFINE(sip_qualify_peer, "Send an OPTIONS packet to a peer"),
AST_CLI_DEFINE(sip_show_sched, "Present a report on the status of the sched queue"),
AST_CLI_DEFINE(sip_prune_realtime, "Prune cached Realtime users/peers"),