Commit "distrotech" app_queue changes to Trunk


			
			
				10-digiumphones
			
			
		
Gregory Nietsky 14 years ago
parent 37d6d89d97
commit f99a06d030

@ -439,6 +439,15 @@ Queue changes
supports sending the event arguments to 5 individual fields, although it supports sending the event arguments to 5 individual fields, although it
will fallback to the previous data definition, if the new table layout is will fallback to the previous data definition, if the new table layout is
not found. not found.
* Added general option negative_penalty_invalid default off. when set
members are seen as invalid/logged out when there penalty is negative.
for realtime members when set remove from queue will set penalty to -1.
* Added queue option autopausedelay when autopause is enabled it will be
delayed for this number of seconds since last successful call if there
was no prior call the agent will be autopaused immediately.
* Added member option ignorebusy this when set and ringinuse is not
will allow per member control of multiple calls as ringinuse does for
the Queue.
mISDN channel driver (chan_misdn) changes mISDN channel driver (chan_misdn) changes
---------------------------------------- ----------------------------------------

@ -50,5 +50,9 @@ pbx_lua:
- the autoservice now defaults to being on by default - the autoservice now defaults to being on by default
- autoservice_start() and autoservice_start() no longer return a value. - autoservice_start() and autoservice_start() no longer return a value.
Queue:
- Mark QUEUE_MEMBER_PENALTY Deprecated it never worked for realtime members
- QUEUE_MEMBER is now R/W supporting setting paused, ignorebusy and penalty.
=========================================================== ===========================================================
=========================================================== ===========================================================

@ -521,11 +521,25 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<enum name="count"> <enum name="count">
<para>Returns the total number of members for the specified queue.</para> <para>Returns the total number of members for the specified queue.</para>
</enum> </enum>
<enum name="penalty">
<para>Gets or sets queue member penalty.</para>
</enum>
<enum name="paused">
<para>Gets or sets queue member paused status.</para>
</enum>
<enum name="ignorebusy">
<para>Gets or sets queue member ignorebusy.</para>
</enum>
</enumlist> </enumlist>
</parameter> </parameter>
<parameter name="interface" required="false" />
</syntax> </syntax>
<description> <description>
<para>Returns the number of members currently associated with the specified <replaceable>queuename</replaceable>.</para> <para>Allows access to queue counts [R] and member information [R/W].</para>
<para>
<replaceable>queuename</replaceable> is required for all operations
<replaceable>interface</replaceable> is required for all member operations.
</para>
</description> </description>
<see-also> <see-also>
<ref type="application">Queue</ref> <ref type="application">Queue</ref>
@ -658,6 +672,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
</syntax> </syntax>
<description> <description>
<para>Gets or sets queue members penalty.</para> <para>Gets or sets queue members penalty.</para>
<warning><para>This function has been deprecated in favor of the <literal>QUEUE_MEMBER()</literal> function</para></warning>
</description> </description>
<see-also> <see-also>
<ref type="application">Queue</ref> <ref type="application">Queue</ref>
@ -934,6 +949,9 @@ static struct ast_event_sub *device_state_sub;
/*! \brief queues.conf [general] option */ /*! \brief queues.conf [general] option */
static int update_cdr = 0; static int update_cdr = 0;
/*! \brief queues.conf [general] option */
static int negative_penalty_invalid = 0;
enum queue_result { enum queue_result {
QUEUE_UNKNOWN = 0, QUEUE_UNKNOWN = 0,
QUEUE_TIMEOUT = 1, QUEUE_TIMEOUT = 1,
@ -1043,6 +1061,7 @@ struct member {
unsigned int dead:1; /*!< Used to detect members deleted in realtime */ unsigned int dead:1; /*!< Used to detect members deleted in realtime */
unsigned int delme:1; /*!< Flag to delete entry on reload */ unsigned int delme:1; /*!< Flag to delete entry on reload */
char rt_uniqueid[80]; /*!< Unique id of realtime member entry */ char rt_uniqueid[80]; /*!< Unique id of realtime member entry */
unsigned int ignorebusy:1; /*!< Flag to ignore member if the status is not available */
}; };
enum empty_conditions { enum empty_conditions {
@ -1160,6 +1179,7 @@ struct call_queue {
int timeout; /*!< How long to wait for an answer */ int timeout; /*!< How long to wait for an answer */
int weight; /*!< Respective weight */ int weight; /*!< Respective weight */
int autopause; /*!< Auto pause queue members if they fail to answer */ int autopause; /*!< Auto pause queue members if they fail to answer */
int autopausedelay; /*!< Delay auto pause for autopausedelay seconds since last call */
int timeoutpriority; /*!< Do we allow a fraction of the timeout to occur for a ring? */ int timeoutpriority; /*!< Do we allow a fraction of the timeout to occur for a ring? */
/* Queue strategy things */ /* Queue strategy things */
@ -1190,6 +1210,7 @@ static AST_LIST_HEAD_STATIC(rule_lists, rule_list);
static struct ao2_container *queues; static struct ao2_container *queues;
static void update_realtime_members(struct call_queue *q); static void update_realtime_members(struct call_queue *q);
static struct member *interface_exists(struct call_queue *q, const char *interface);
static int set_member_paused(const char *queuename, const char *interface, const char *reason, int paused); static int set_member_paused(const char *queuename, const char *interface, const char *reason, int paused);
static void queue_transfer_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan); static void queue_transfer_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan);
@ -1698,6 +1719,7 @@ static void init_queue(struct call_queue *q)
q->numperiodicannounce = 0; q->numperiodicannounce = 0;
q->autopause = QUEUE_AUTOPAUSE_OFF; q->autopause = QUEUE_AUTOPAUSE_OFF;
q->timeoutpriority = TIMEOUT_PRIORITY_APP; q->timeoutpriority = TIMEOUT_PRIORITY_APP;
q->autopausedelay = 0;
if (!q->members) { if (!q->members) {
if (q->strategy == QUEUE_STRATEGY_LINEAR || q->strategy == QUEUE_STRATEGY_RRORDERED) if (q->strategy == QUEUE_STRATEGY_LINEAR || q->strategy == QUEUE_STRATEGY_RRORDERED)
/* linear strategy depends on order, so we have to place all members in a single bucket */ /* linear strategy depends on order, so we have to place all members in a single bucket */
@ -2003,6 +2025,8 @@ static void queue_set_param(struct call_queue *q, const char *param, const char
q->montype = 1; q->montype = 1;
} else if (!strcasecmp(param, "autopause")) { } else if (!strcasecmp(param, "autopause")) {
q->autopause = autopause2int(val); q->autopause = autopause2int(val);
} else if (!strcasecmp(param, "autopausedelay")) {
q->autopausedelay = atoi(val);
} else if (!strcasecmp(param, "maxlen")) { } else if (!strcasecmp(param, "maxlen")) {
q->maxlen = atoi(val); q->maxlen = atoi(val);
if (q->maxlen < 0) if (q->maxlen < 0)
@ -2081,7 +2105,9 @@ static void rt_handle_member_record(struct call_queue *q, char *interface, struc
int penalty = 0; int penalty = 0;
int paused = 0; int paused = 0;
int found = 0; int found = 0;
int ignorebusy = 0;
const char *config_val;
const char *rt_uniqueid = ast_variable_retrieve(member_config, interface, "uniqueid"); const char *rt_uniqueid = ast_variable_retrieve(member_config, interface, "uniqueid");
const char *membername = S_OR(ast_variable_retrieve(member_config, interface, "membername"), interface); const char *membername = S_OR(ast_variable_retrieve(member_config, interface, "membername"), interface);
const char *state_interface = S_OR(ast_variable_retrieve(member_config, interface, "state_interface"), interface); const char *state_interface = S_OR(ast_variable_retrieve(member_config, interface, "state_interface"), interface);
@ -2095,9 +2121,12 @@ static void rt_handle_member_record(struct call_queue *q, char *interface, struc
if (penalty_str) { if (penalty_str) {
penalty = atoi(penalty_str); penalty = atoi(penalty_str);
if (penalty < 0) if ((penalty < 0) && negative_penalty_invalid) {
return;
} else if (penalty < 0) {
penalty = 0; penalty = 0;
} }
}
if (paused_str) { if (paused_str) {
paused = atoi(paused_str); paused = atoi(paused_str);
@ -2105,6 +2134,12 @@ static void rt_handle_member_record(struct call_queue *q, char *interface, struc
paused = 0; paused = 0;
} }
if ((config_val = ast_variable_retrieve(member_config, interface, "ignorebusy"))) {
ignorebusy = ast_true(config_val);
} else {
ignorebusy = 1;
}
/* Find member by realtime uniqueid and update */ /* Find member by realtime uniqueid and update */
mem_iter = ao2_iterator_init(q->members, 0); mem_iter = ao2_iterator_init(q->members, 0);
while ((m = ao2_iterator_next(&mem_iter))) { while ((m = ao2_iterator_next(&mem_iter))) {
@ -2117,6 +2152,7 @@ static void rt_handle_member_record(struct call_queue *q, char *interface, struc
ast_copy_string(m->state_interface, state_interface, sizeof(m->state_interface)); ast_copy_string(m->state_interface, state_interface, sizeof(m->state_interface));
} }
m->penalty = penalty; m->penalty = penalty;
m->ignorebusy = ignorebusy;
found = 1; found = 1;
ao2_ref(m, -1); ao2_ref(m, -1);
break; break;
@ -2130,6 +2166,7 @@ static void rt_handle_member_record(struct call_queue *q, char *interface, struc
if ((m = create_queue_member(interface, membername, penalty, paused, state_interface))) { if ((m = create_queue_member(interface, membername, penalty, paused, state_interface))) {
m->dead = 0; m->dead = 0;
m->realtime = 1; m->realtime = 1;
m->ignorebusy = ignorebusy;
ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid)); ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid));
ast_queue_log(q->name, "REALTIME", m->interface, "ADDMEMBER", "%s", ""); ast_queue_log(q->name, "REALTIME", m->interface, "ADDMEMBER", "%s", "");
ao2_link(q->members, m); ao2_link(q->members, m);
@ -2873,9 +2910,17 @@ static int num_available_members(struct call_queue *q)
mem_iter = ao2_iterator_init(q->members, 0); mem_iter = ao2_iterator_init(q->members, 0);
while ((mem = ao2_iterator_next(&mem_iter))) { while ((mem = ao2_iterator_next(&mem_iter))) {
switch (mem->status) { switch (mem->status) {
case AST_DEVICE_INVALID:
case AST_DEVICE_UNAVAILABLE:
break;
case AST_DEVICE_INUSE: case AST_DEVICE_INUSE:
if (!q->ringinuse) case AST_DEVICE_BUSY:
case AST_DEVICE_RINGING:
case AST_DEVICE_RINGINUSE:
case AST_DEVICE_ONHOLD:
if ((!q->ringinuse) || (!mem->ignorebusy)) {
break; break;
}
/* else fall through */ /* else fall through */
case AST_DEVICE_NOT_INUSE: case AST_DEVICE_NOT_INUSE:
case AST_DEVICE_UNKNOWN: case AST_DEVICE_UNKNOWN:
@ -3010,38 +3055,54 @@ static int ring_entry(struct queue_ent *qe, struct callattempt *tmp, int *busies
char tech[256]; char tech[256];
char *location; char *location;
const char *macrocontext, *macroexten; const char *macrocontext, *macroexten;
enum ast_device_state newstate;
/* on entry here, we know that tmp->chan == NULL */ /* on entry here, we know that tmp->chan == NULL */
if (tmp->member->paused) {
ast_debug(1, "%s paused, can't receive call\n", tmp->interface);
if (qe->chan->cdr) {
ast_cdr_busy(qe->chan->cdr);
}
tmp->stillgoing = 0;
return 0;
}
if ((tmp->lastqueue && tmp->lastqueue->wrapuptime && (time(NULL) - tmp->lastcall < tmp->lastqueue->wrapuptime)) || if ((tmp->lastqueue && tmp->lastqueue->wrapuptime && (time(NULL) - tmp->lastcall < tmp->lastqueue->wrapuptime)) ||
(!tmp->lastqueue && qe->parent->wrapuptime && (time(NULL) - tmp->lastcall < qe->parent->wrapuptime))) { (!tmp->lastqueue && qe->parent->wrapuptime && (time(NULL) - tmp->lastcall < qe->parent->wrapuptime))) {
ast_debug(1, "Wrapuptime not yet expired on queue %s for %s\n", ast_debug(1, "Wrapuptime not yet expired on queue %s for %s\n",
(tmp->lastqueue ? tmp->lastqueue->name : qe->parent->name), tmp->interface); (tmp->lastqueue ? tmp->lastqueue->name : qe->parent->name), tmp->interface);
if (qe->chan->cdr) if (qe->chan->cdr) {
ast_cdr_busy(qe->chan->cdr); ast_cdr_busy(qe->chan->cdr);
}
tmp->stillgoing = 0; tmp->stillgoing = 0;
(*busies)++; (*busies)++;
return 0; return 0;
} }
if (!qe->parent->ringinuse && (tmp->member->status != AST_DEVICE_NOT_INUSE) && (tmp->member->status != AST_DEVICE_UNKNOWN)) { if (!qe->parent->ringinuse || !tmp->member->ignorebusy) {
if ((tmp->member->status == AST_DEVICE_UNKNOWN) || (tmp->member->status == AST_DEVICE_NOT_INUSE)) {
newstate = ast_parse_device_state(tmp->member->interface);
if (newstate != tmp->member->status) {
ast_log(LOG_ERROR, "Found a channel matching iterface %s while status was %i changed to %i\n",
tmp->member->interface, tmp->member->status, newstate);
update_status(qe->parent, tmp->member, newstate);
}
}
if ((tmp->member->status != AST_DEVICE_NOT_INUSE) && (tmp->member->status != AST_DEVICE_UNKNOWN)) {
ast_debug(1, "%s in use, can't receive call\n", tmp->interface); ast_debug(1, "%s in use, can't receive call\n", tmp->interface);
if (qe->chan->cdr) if (qe->chan->cdr) {
ast_cdr_busy(qe->chan->cdr); ast_cdr_busy(qe->chan->cdr);
tmp->stillgoing = 0;
return 0;
} }
if (tmp->member->paused) {
ast_debug(1, "%s paused, can't receive call\n", tmp->interface);
if (qe->chan->cdr)
ast_cdr_busy(qe->chan->cdr);
tmp->stillgoing = 0; tmp->stillgoing = 0;
return 0; return 0;
} }
}
if (use_weight && compare_weight(qe->parent,tmp->member)) { if (use_weight && compare_weight(qe->parent,tmp->member)) {
ast_debug(1, "Priority queue delaying call to %s:%s\n", qe->parent->name, tmp->interface); ast_debug(1, "Priority queue delaying call to %s:%s\n", qe->parent->name, tmp->interface);
if (qe->chan->cdr) if (qe->chan->cdr) {
ast_cdr_busy(qe->chan->cdr); ast_cdr_busy(qe->chan->cdr);
}
tmp->stillgoing = 0; tmp->stillgoing = 0;
(*busies)++; (*busies)++;
return 0; return 0;
@ -3056,8 +3117,9 @@ static int ring_entry(struct queue_ent *qe, struct callattempt *tmp, int *busies
/* Request the peer */ /* Request the peer */
tmp->chan = ast_request(tech, qe->chan->nativeformats, qe->chan, location, &status); tmp->chan = ast_request(tech, qe->chan->nativeformats, qe->chan, location, &status);
if (!tmp->chan) { /* If we can't, just go on to the next call */ if (!tmp->chan) { /* If we can't, just go on to the next call */
if (qe->chan->cdr) if (qe->chan->cdr) {
ast_cdr_busy(qe->chan->cdr); ast_cdr_busy(qe->chan->cdr);
}
tmp->stillgoing = 0; tmp->stillgoing = 0;
ao2_lock(qe->parent); ao2_lock(qe->parent);
@ -3396,6 +3458,18 @@ static void rna(int rnatime, struct queue_ent *qe, char *interface, char *member
} }
ast_queue_log(qe->parent->name, qe->chan->uniqueid, membername, "RINGNOANSWER", "%d", rnatime); ast_queue_log(qe->parent->name, qe->chan->uniqueid, membername, "RINGNOANSWER", "%d", rnatime);
if (qe->parent->autopause != QUEUE_AUTOPAUSE_OFF && pause) { if (qe->parent->autopause != QUEUE_AUTOPAUSE_OFF && pause) {
if (qe->parent->autopausedelay > 0) {
struct member *mem;
ao2_lock(qe->parent);
if ((mem = interface_exists(qe->parent, interface))) {
time_t idletime = time(&idletime)-mem->lastcall;
if ((mem->lastcall != 0) && (qe->parent->autopausedelay > idletime)) {
ao2_unlock(qe->parent);
return;
}
}
ao2_unlock(qe->parent);
}
if (qe->parent->autopause == QUEUE_AUTOPAUSE_ON) { if (qe->parent->autopause == QUEUE_AUTOPAUSE_ON) {
if (!set_member_paused(qe->parent->name, interface, "Auto-Pause", 1)) { if (!set_member_paused(qe->parent->name, interface, "Auto-Pause", 1)) {
ast_verb(3, "Auto-Pausing Queue Member %s in queue %s since they failed to answer.\n", ast_verb(3, "Auto-Pausing Queue Member %s in queue %s since they failed to answer.\n",
@ -4707,8 +4781,9 @@ static int try_calling(struct queue_ent *qe, const char *options, char *announce
else else
ast_moh_stop(qe->chan); ast_moh_stop(qe->chan);
/* If appropriate, log that we have a destination channel */ /* If appropriate, log that we have a destination channel */
if (qe->chan->cdr) if (qe->chan->cdr) {
ast_cdr_setdestchan(qe->chan->cdr, peer->name); ast_cdr_setdestchan(qe->chan->cdr, peer->name);
}
/* Make sure channels are compatible */ /* Make sure channels are compatible */
res = ast_channel_make_compatible(qe->chan, peer); res = ast_channel_make_compatible(qe->chan, peer);
if (res < 0) { if (res < 0) {
@ -4788,10 +4863,11 @@ static int try_calling(struct queue_ent *qe, const char *options, char *announce
if (mixmonapp) { if (mixmonapp) {
ast_debug(1, "Starting MixMonitor as requested.\n"); ast_debug(1, "Starting MixMonitor as requested.\n");
if (!monitorfilename) { if (!monitorfilename) {
if (qe->chan->cdr) if (qe->chan->cdr) {
ast_copy_string(tmpid, qe->chan->cdr->uniqueid, sizeof(tmpid)); ast_copy_string(tmpid, qe->chan->cdr->uniqueid, sizeof(tmpid));
else } else {
snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random()); snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random());
}
} else { } else {
const char *m = monitorfilename; const char *m = monitorfilename;
for (p = tmpid2; p < tmpid2 + sizeof(tmpid2) - 1; p++, m++) { for (p = tmpid2; p < tmpid2 + sizeof(tmpid2) - 1; p++, m++) {
@ -4858,12 +4934,13 @@ static int try_calling(struct queue_ent *qe, const char *options, char *announce
ast_debug(1, "Arguments being passed to MixMonitor: %s\n", mixmonargs); ast_debug(1, "Arguments being passed to MixMonitor: %s\n", mixmonargs);
/* We purposely lock the CDR so that pbx_exec does not update the application data */ /* We purposely lock the CDR so that pbx_exec does not update the application data */
if (qe->chan->cdr) if (qe->chan->cdr) {
ast_set_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED); ast_set_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED);
}
pbx_exec(qe->chan, mixmonapp, mixmonargs); pbx_exec(qe->chan, mixmonapp, mixmonargs);
if (qe->chan->cdr) if (qe->chan->cdr) {
ast_clear_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED); ast_clear_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED);
}
} else { } else {
ast_log(LOG_WARNING, "Asked to run MixMonitor on this call, but cannot find the MixMonitor app!\n"); ast_log(LOG_WARNING, "Asked to run MixMonitor on this call, but cannot find the MixMonitor app!\n");
} }
@ -5180,7 +5257,10 @@ static int remove_from_queue(const char *queuename, const char *interface)
ao2_lock(q); ao2_lock(q);
if ((mem = ao2_find(q->members, &tmpmem, OBJ_POINTER))) { if ((mem = ao2_find(q->members, &tmpmem, OBJ_POINTER))) {
/* XXX future changes should beware of this assumption!! */ /* XXX future changes should beware of this assumption!! */
if (!mem->dynamic) { /*Change Penalty on realtime users*/
if (mem->realtime && !ast_strlen_zero(mem->rt_uniqueid) && negative_penalty_invalid) {
update_realtime_member_field(mem, q->name, "penalty", "-1");
} else if (!mem->dynamic) {
ao2_ref(mem, -1); ao2_ref(mem, -1);
ao2_unlock(q); ao2_unlock(q);
queue_t_unref(q, "Interface wasn't dynamic, expiring temporary reference"); queue_t_unref(q, "Interface wasn't dynamic, expiring temporary reference");
@ -5355,22 +5435,24 @@ static int set_member_penalty(const char *queuename, const char *interface, int
int foundinterface = 0, foundqueue = 0; int foundinterface = 0, foundqueue = 0;
struct call_queue *q; struct call_queue *q;
struct member *mem; struct member *mem;
struct ao2_iterator queue_iter; char rtpenalty[80];
if (penalty < 0) { if (penalty < 0 && !negative_penalty_invalid) {
ast_log(LOG_ERROR, "Invalid penalty (%d)\n", penalty); ast_log(LOG_ERROR, "Invalid penalty (%d)\n", penalty);
return RESULT_FAILURE; return RESULT_FAILURE;
} }
queue_iter = ao2_iterator_init(queues, 0); if ((q = load_realtime_queue(queuename))) {
while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
ao2_lock(q);
if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
foundqueue++; foundqueue++;
ao2_lock(q);
if ((mem = interface_exists(q, interface))) { if ((mem = interface_exists(q, interface))) {
foundinterface++; foundinterface++;
if (!mem->realtime) {
mem->penalty = penalty; mem->penalty = penalty;
} else {
sprintf(rtpenalty,"%i", penalty);
update_realtime_member_field(mem, q->name, "penalty", rtpenalty);
}
ast_queue_log(q->name, "NONE", interface, "PENALTY", "%d", penalty); ast_queue_log(q->name, "NONE", interface, "PENALTY", "%d", penalty);
manager_event(EVENT_FLAG_AGENT, "QueueMemberPenalty", manager_event(EVENT_FLAG_AGENT, "QueueMemberPenalty",
"Queue: %s\r\n" "Queue: %s\r\n"
@ -5379,11 +5461,8 @@ static int set_member_penalty(const char *queuename, const char *interface, int
q->name, mem->interface, penalty); q->name, mem->interface, penalty);
ao2_ref(mem, -1); ao2_ref(mem, -1);
} }
}
ao2_unlock(q); ao2_unlock(q);
queue_t_unref(q, "Done with iterator");
} }
ao2_iterator_destroy(&queue_iter);
if (foundinterface) { if (foundinterface) {
return RESULT_SUCCESS; return RESULT_SUCCESS;
@ -6159,29 +6238,35 @@ static int queue_function_exists(struct ast_channel *chan, const char *cmd, char
/*! /*!
* \brief Get number either busy / free / ready or total members of a specific queue * \brief Get number either busy / free / ready or total members of a specific queue
* \retval number of members (busy / free / ready / total) * \brief Get or set member properties penalty / paused / ignorebusy
* \retval number of members (busy / free / ready / total) or member info (penalty / paused / ignorebusy)
* \retval -1 on error * \retval -1 on error
*/ */
static int queue_function_qac(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len) static int queue_function_mem_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
{ {
int count = 0; int count = 0;
struct member *m; struct member *m;
struct ao2_iterator mem_iter; struct ao2_iterator mem_iter;
struct call_queue *q; struct call_queue *q;
char *option;
AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(queuename);
AST_APP_ARG(option);
AST_APP_ARG(interface);
);
/* Make sure the returned value on error is zero length string. */
buf[0] = '\0';
if (ast_strlen_zero(data)) { if (ast_strlen_zero(data)) {
ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd); ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
return -1; return -1;
} }
if ((option = strchr(data, ','))) AST_STANDARD_APP_ARGS(args, data);
*option++ = '\0';
else if ((q = load_realtime_queue(args.queuename))) {
option = "logged";
if ((q = load_realtime_queue(data))) {
ao2_lock(q); ao2_lock(q);
if (!strcasecmp(option, "logged")) { if (!strcasecmp(args.option, "logged")) {
mem_iter = ao2_iterator_init(q->members, 0); mem_iter = ao2_iterator_init(q->members, 0);
while ((m = ao2_iterator_next(&mem_iter))) { while ((m = ao2_iterator_next(&mem_iter))) {
/* Count the agents who are logged in and presently answering calls */ /* Count the agents who are logged in and presently answering calls */
@ -6191,7 +6276,7 @@ static int queue_function_qac(struct ast_channel *chan, const char *cmd, char *d
ao2_ref(m, -1); ao2_ref(m, -1);
} }
ao2_iterator_destroy(&mem_iter); ao2_iterator_destroy(&mem_iter);
} else if (!strcasecmp(option, "free")) { } else if (!strcasecmp(args.option, "free")) {
mem_iter = ao2_iterator_init(q->members, 0); mem_iter = ao2_iterator_init(q->members, 0);
while ((m = ao2_iterator_next(&mem_iter))) { while ((m = ao2_iterator_next(&mem_iter))) {
/* Count the agents who are logged in and presently answering calls */ /* Count the agents who are logged in and presently answering calls */
@ -6201,7 +6286,7 @@ static int queue_function_qac(struct ast_channel *chan, const char *cmd, char *d
ao2_ref(m, -1); ao2_ref(m, -1);
} }
ao2_iterator_destroy(&mem_iter); ao2_iterator_destroy(&mem_iter);
} else if (!strcasecmp(option, "ready")) { } else if (!strcasecmp(args.option, "ready")) {
time_t now; time_t now;
time(&now); time(&now);
mem_iter = ao2_iterator_init(q->members, 0); mem_iter = ao2_iterator_init(q->members, 0);
@ -6214,18 +6299,100 @@ static int queue_function_qac(struct ast_channel *chan, const char *cmd, char *d
ao2_ref(m, -1); ao2_ref(m, -1);
} }
ao2_iterator_destroy(&mem_iter); ao2_iterator_destroy(&mem_iter);
} else /* must be "count" */ } else if (!strcasecmp(args.option, "count") || ast_strlen_zero(args.option)) {
count = q->membercount; count = q->membercount;
} else if (!strcasecmp(args.option, "penalty") && !ast_strlen_zero(args.interface) &&
((m = interface_exists(q, args.interface)))) {
count = m->penalty;
} else if (!strcasecmp(args.option, "paused") && !ast_strlen_zero(args.interface) &&
((m = interface_exists(q, args.interface)))) {
count = m->paused;
} else if (!strcasecmp(args.option, "ignorebusy") && !ast_strlen_zero(args.interface) &&
((m = interface_exists(q, args.interface)))) {
count = m->ignorebusy;
}
ao2_unlock(q); ao2_unlock(q);
queue_t_unref(q, "Done with temporary reference in QUEUE_MEMBER()"); queue_t_unref(q, "Done with temporary reference in QUEUE_MEMBER()");
} else } else {
ast_log(LOG_WARNING, "queue %s was not found\n", data); ast_log(LOG_WARNING, "queue %s was not found\n", args.queuename);
}
snprintf(buf, len, "%d", count); snprintf(buf, len, "%d", count);
return 0; return 0;
} }
/*! \brief Dialplan function QUEUE_MEMBER() Sets the members penalty / paused / ignorebusy. */
static int queue_function_mem_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
{
int memvalue;
struct call_queue *q;
struct member *m;
char rtvalue[80];
AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(queuename);
AST_APP_ARG(option);
AST_APP_ARG(interface);
);
if (ast_strlen_zero(data)) {
ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER(<queuename>,<option>,<interface>)\n");
return -1;
}
AST_STANDARD_APP_ARGS(args, data);
if (args.argc < 3) {
ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
return -1;
}
if (ast_strlen_zero(args.interface) && ast_strlen_zero(args.option)) {
ast_log (LOG_ERROR, "<interface> and <option> parameter's can't be null\n");
return -1;
}
memvalue = atoi(value);
if (!strcasecmp(args.option, "penalty")) {
/* if queuename = NULL then penalty will be set for interface in all the queues.*/
if (set_member_penalty(args.queuename, args.interface, memvalue)) {
ast_log(LOG_ERROR, "Invalid interface, queue or penalty\n");
return -1;
}
} else if ((q = load_realtime_queue(args.queuename))) {
ao2_lock(q);
if ((m = interface_exists(q, args.interface))) {
sprintf(rtvalue, "%s",(memvalue <= 0) ? "0" : "1");
if (!strcasecmp(args.option, "paused")) {
if (m->realtime) {
update_realtime_member_field(m, q->name, args.option, rtvalue);
} else {
m->paused = (memvalue <= 0) ? 0 : 1;
}
} else if (!strcasecmp(args.option, "ignorebusy")) {
if (m->realtime) {
update_realtime_member_field(m, q->name, args.option, rtvalue);
} else {
m->ignorebusy = (memvalue <= 0) ? 0 : 1;
}
} else {
ast_log(LOG_ERROR, "Invalid option, only penalty , paused or ignorebusy are valid\n");
return -1;
}
} else {
ast_log(LOG_ERROR, "Invalid interface or queue\n");
return -1;
}
ao2_unlock(q);
} else {
ast_log(LOG_ERROR, "Invalid queue\n");
return -1;
}
return 0;
}
/*! /*!
* \brief Get the total number of members in a specific queue (Deprecated) * \brief Get the total number of members in a specific queue (Deprecated)
* \retval number of members * \retval number of members
@ -6435,7 +6602,8 @@ static struct ast_custom_function queuevar_function = {
static struct ast_custom_function queuemembercount_function = { static struct ast_custom_function queuemembercount_function = {
.name = "QUEUE_MEMBER", .name = "QUEUE_MEMBER",
.read = queue_function_qac, .read = queue_function_mem_read,
.write = queue_function_mem_write,
}; };
static struct ast_custom_function queuemembercount_dep = { static struct ast_custom_function queuemembercount_dep = {
@ -6517,8 +6685,9 @@ static void queue_set_global_params(struct ast_config *cfg)
{ {
const char *general_val = NULL; const char *general_val = NULL;
queue_persistent_members = 0; queue_persistent_members = 0;
if ((general_val = ast_variable_retrieve(cfg, "general", "persistentmembers"))) if ((general_val = ast_variable_retrieve(cfg, "general", "persistentmembers"))) {
queue_persistent_members = ast_true(general_val); queue_persistent_members = ast_true(general_val);
}
autofill_default = 0; autofill_default = 0;
if ((general_val = ast_variable_retrieve(cfg, "general", "autofill"))) if ((general_val = ast_variable_retrieve(cfg, "general", "autofill")))
autofill_default = ast_true(general_val); autofill_default = ast_true(general_val);
@ -6533,6 +6702,9 @@ static void queue_set_global_params(struct ast_config *cfg)
shared_lastcall = 0; shared_lastcall = 0;
if ((general_val = ast_variable_retrieve(cfg, "general", "shared_lastcall"))) if ((general_val = ast_variable_retrieve(cfg, "general", "shared_lastcall")))
shared_lastcall = ast_true(general_val); shared_lastcall = ast_true(general_val);
negative_penalty_invalid = 0;
if ((general_val = ast_variable_retrieve(cfg, "general", "negative_penalty_invalid")))
negative_penalty_invalid = ast_true(general_val);
} }
/*! \brief reload information pertaining to a single member /*! \brief reload information pertaining to a single member

@ -61,6 +61,10 @@ monitor-type = MixMonitor
; ;
;shared_lastcall=no ;shared_lastcall=no
; ;
; Negative_penalty_invalid will treat members with a negative penalty as logged off
;
;negative_penalty_invalid = no
;
;[markq] ;[markq]
; ;
; A sample call queue ; A sample call queue
@ -196,6 +200,10 @@ monitor-type = MixMonitor
; all: Memeber will be paused in all queues he/she is a member ; all: Memeber will be paused in all queues he/she is a member
;autopause=yes ;autopause=yes
; ;
; Autopausedelay delay autopause for autopausedelay seconds from the
; last call if a member has not taken a call the delay has no effect.
;autopausedelay=60
;
; Maximum number of people waiting in the queue (0 for unlimited) ; Maximum number of people waiting in the queue (0 for unlimited)
; ;
;maxlen = 0 ;maxlen = 0
@ -459,6 +467,9 @@ monitor-type = MixMonitor
; uncomment this option. (Note: only the SIP channel driver currently is able ; uncomment this option. (Note: only the SIP channel driver currently is able
; to report 'in use'.) ; to report 'in use'.)
; ;
; A member can have the ignorebusy flag set or unset when ringinuse is set to
; allow a per member control.
;
; ringinuse = no ; ringinuse = no
; ;
; If you wish to have a delay before the member is connected to the caller (or ; If you wish to have a delay before the member is connected to the caller (or

Loading…
Cancel
Save