[svn-commits] irroot: trunk r325483 - in /trunk: ./ apps/ configs/

SVN commits to the Digium repositories svn-commits at lists.digium.com
Wed Jun 29 01:39:30 CDT 2011


Author: irroot
Date: Wed Jun 29 01:39:26 2011
New Revision: 325483

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=325483
Log:

Commit "distrotech" app_queue changes to Trunk

 * 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.
  
 - Mark QUEUE_MEMBER_PENALTY Deprecated it never worked for realtime members
 - QUEUE_MEMBER is now R/W supporting setting paused, ignorebusy and penalty.

(closes issue ASTERISK-17421)
(closes issue ASTERISK-17391)
Reported by: irroot
Tested by: irroot, jrose
Review: https://reviewboard.asterisk.org/r/1119/

Modified:
    trunk/CHANGES
    trunk/UPGRADE.txt
    trunk/apps/app_queue.c
    trunk/configs/queues.conf.sample

Modified: trunk/CHANGES
URL: http://svnview.digium.com/svn/asterisk/trunk/CHANGES?view=diff&rev=325483&r1=325482&r2=325483
==============================================================================
--- trunk/CHANGES (original)
+++ trunk/CHANGES Wed Jun 29 01:39:26 2011
@@ -439,6 +439,15 @@
    supports sending the event arguments to 5 individual fields, although it
    will fallback to the previous data definition, if the new table layout is
    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
 ----------------------------------------

Modified: trunk/UPGRADE.txt
URL: http://svnview.digium.com/svn/asterisk/trunk/UPGRADE.txt?view=diff&rev=325483&r1=325482&r2=325483
==============================================================================
--- trunk/UPGRADE.txt (original)
+++ trunk/UPGRADE.txt Wed Jun 29 01:39:26 2011
@@ -50,5 +50,9 @@
  - the autoservice now defaults to being on by default
  - 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.
+
 ===========================================================
 ===========================================================

Modified: trunk/apps/app_queue.c
URL: http://svnview.digium.com/svn/asterisk/trunk/apps/app_queue.c?view=diff&rev=325483&r1=325482&r2=325483
==============================================================================
--- trunk/apps/app_queue.c (original)
+++ trunk/apps/app_queue.c Wed Jun 29 01:39:26 2011
@@ -521,11 +521,25 @@
 					<enum name="count">
 						<para>Returns the total number of members for the specified queue.</para>
 					</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>
 			</parameter>
+			<parameter name="interface" required="false" />
 		</syntax>
 		<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>
 		<see-also>
 			<ref type="application">Queue</ref>
@@ -658,6 +672,7 @@
 		</syntax>
 		<description>
 			<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>
 		<see-also>
 			<ref type="application">Queue</ref>
@@ -934,6 +949,9 @@
 /*! \brief queues.conf [general] option */
 static int update_cdr = 0;
 
+/*! \brief queues.conf [general] option */
+static int negative_penalty_invalid = 0;
+
 enum queue_result {
 	QUEUE_UNKNOWN = 0,
 	QUEUE_TIMEOUT = 1,
@@ -1043,6 +1061,7 @@
 	unsigned int dead:1;                 /*!< Used to detect members deleted in realtime */
 	unsigned int delme:1;                /*!< Flag to delete entry on reload */
 	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 {
@@ -1160,6 +1179,7 @@
 	int timeout;                        /*!< How long to wait for an answer */
 	int weight;                         /*!< Respective weight */
 	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? */
 
 	/* Queue strategy things */
@@ -1190,9 +1210,10 @@
 static struct ao2_container *queues;
 
 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 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);
 /*! \brief sets the QUEUESTATUS channel variable */
 static void set_queue_result(struct ast_channel *chan, enum queue_result res)
 {
@@ -1698,6 +1719,7 @@
 	q->numperiodicannounce = 0;
 	q->autopause = QUEUE_AUTOPAUSE_OFF;
 	q->timeoutpriority = TIMEOUT_PRIORITY_APP;
+	q->autopausedelay = 0;
 	if (!q->members) {
 		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 */
@@ -2003,6 +2025,8 @@
 			q->montype = 1;
 	} else if (!strcasecmp(param, "autopause")) {
 		q->autopause = autopause2int(val);
+	} else if (!strcasecmp(param, "autopausedelay")) {
+		q->autopausedelay = atoi(val);
 	} else if (!strcasecmp(param, "maxlen")) {
 		q->maxlen = atoi(val);
 		if (q->maxlen < 0)
@@ -2081,7 +2105,9 @@
 	int penalty = 0;
 	int paused  = 0;
 	int found = 0;
-
+	int ignorebusy = 0;
+
+	const char *config_val;
 	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 *state_interface = S_OR(ast_variable_retrieve(member_config, interface, "state_interface"), interface);
@@ -2095,8 +2121,11 @@
 
 	if (penalty_str) {
 		penalty = atoi(penalty_str);
-		if (penalty < 0)
+		if ((penalty < 0) && negative_penalty_invalid) {
+			return;
+		} else if (penalty < 0) {
 			penalty = 0;
+		}
 	}
 
 	if (paused_str) {
@@ -2105,31 +2134,39 @@
 			paused = 0;
 	}
 
- 	/* Find member by realtime uniqueid and update */
- 	mem_iter = ao2_iterator_init(q->members, 0);
- 	while ((m = ao2_iterator_next(&mem_iter))) {
- 		if (!strcasecmp(m->rt_uniqueid, rt_uniqueid)) {
- 			m->dead = 0;	/* Do not delete this one. */
- 			ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid));
- 			if (paused_str)
- 				m->paused = paused;
- 			if (strcasecmp(state_interface, m->state_interface)) {
- 				ast_copy_string(m->state_interface, state_interface, sizeof(m->state_interface));
- 			}	   
- 			m->penalty = penalty;
- 			found = 1;
- 			ao2_ref(m, -1);
- 			break;
- 		}
- 		ao2_ref(m, -1);
- 	}
+	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 */
+	mem_iter = ao2_iterator_init(q->members, 0);
+	while ((m = ao2_iterator_next(&mem_iter))) {
+		if (!strcasecmp(m->rt_uniqueid, rt_uniqueid)) {
+			m->dead = 0;	/* Do not delete this one. */
+			ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid));
+			if (paused_str)
+				m->paused = paused;
+			if (strcasecmp(state_interface, m->state_interface)) {
+				ast_copy_string(m->state_interface, state_interface, sizeof(m->state_interface));
+			}
+			m->penalty = penalty;
+			m->ignorebusy = ignorebusy;
+			found = 1;
+			ao2_ref(m, -1);
+			break;
+		}
+		ao2_ref(m, -1);
+	}
 	ao2_iterator_destroy(&mem_iter);
 
- 	/* Create a new member */
- 	if (!found) {
+	/* Create a new member */
+	if (!found) {
 		if ((m = create_queue_member(interface, membername, penalty, paused, state_interface))) {
 			m->dead = 0;
 			m->realtime = 1;
+			m->ignorebusy = ignorebusy;
 			ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid));
 			ast_queue_log(q->name, "REALTIME", m->interface, "ADDMEMBER", "%s", "");
 			ao2_link(q->members, m);
@@ -2873,16 +2910,24 @@
 	mem_iter = ao2_iterator_init(q->members, 0);
 	while ((mem = ao2_iterator_next(&mem_iter))) {
 		switch (mem->status) {
-		case AST_DEVICE_INUSE:
-			if (!q->ringinuse)
+			case AST_DEVICE_INVALID:
+			case AST_DEVICE_UNAVAILABLE:
 				break;
-			/* else fall through */
-		case AST_DEVICE_NOT_INUSE:
-		case AST_DEVICE_UNKNOWN:
-			if (!mem->paused) {
-				avl++;
-			}
-			break;
+			case AST_DEVICE_INUSE:
+			case AST_DEVICE_BUSY:
+			case AST_DEVICE_RINGING:
+			case AST_DEVICE_RINGINUSE:
+			case AST_DEVICE_ONHOLD:
+				if ((!q->ringinuse) || (!mem->ignorebusy)) {
+					break;
+				}
+				/* else fall through */
+			case AST_DEVICE_NOT_INUSE:
+			case AST_DEVICE_UNKNOWN:
+				if (!mem->paused) {
+					avl++;
+				}
+				break;
 		}
 		ao2_ref(mem, -1);
 
@@ -3010,38 +3055,54 @@
 	char tech[256];
 	char *location;
 	const char *macrocontext, *macroexten;
+	enum ast_device_state newstate;
 
 	/* 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)) ||
 		(!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);
-		if (qe->chan->cdr)
+		if (qe->chan->cdr) {
 			ast_cdr_busy(qe->chan->cdr);
+		}
 		tmp->stillgoing = 0;
 		(*busies)++;
 		return 0;
 	}
 
-	if (!qe->parent->ringinuse && (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);
-		if (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;
-		return 0;
-	}
+	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);
+			if (qe->chan->cdr) {
+				ast_cdr_busy(qe->chan->cdr);
+			}
+			tmp->stillgoing = 0;
+			return 0;
+		}
+	}
+
 	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);
-		if (qe->chan->cdr)
+		if (qe->chan->cdr) {
 			ast_cdr_busy(qe->chan->cdr);
+		}
 		tmp->stillgoing = 0;
 		(*busies)++;
 		return 0;
@@ -3056,8 +3117,9 @@
 	/* Request the peer */
 	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 (qe->chan->cdr)
+		if (qe->chan->cdr) {
 			ast_cdr_busy(qe->chan->cdr);
+		}
 		tmp->stillgoing = 0;	
 
 		ao2_lock(qe->parent);
@@ -3396,6 +3458,18 @@
 	}
 	ast_queue_log(qe->parent->name, qe->chan->uniqueid, membername, "RINGNOANSWER", "%d", rnatime);
 	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 (!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",
@@ -4707,8 +4781,9 @@
 		else
 			ast_moh_stop(qe->chan);
 		/* 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);
+		}
 		/* Make sure channels are compatible */
 		res = ast_channel_make_compatible(qe->chan, peer);
 		if (res < 0) {
@@ -4788,10 +4863,11 @@
 				if (mixmonapp) {
 					ast_debug(1, "Starting MixMonitor as requested.\n");
 					if (!monitorfilename) {
-						if (qe->chan->cdr)
+						if (qe->chan->cdr) {
 							ast_copy_string(tmpid, qe->chan->cdr->uniqueid, sizeof(tmpid));
-						else
+						} else {
 							snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random());
+						}
 					} else {
 						const char *m = monitorfilename;
 						for (p = tmpid2; p < tmpid2 + sizeof(tmpid2) - 1; p++, m++) {
@@ -4858,12 +4934,13 @@
 					
 					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 */
-					if (qe->chan->cdr)
+					if (qe->chan->cdr) {
 						ast_set_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED);
+					}
 					pbx_exec(qe->chan, mixmonapp, mixmonargs);
-					if (qe->chan->cdr)
+					if (qe->chan->cdr) {
 						ast_clear_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED);
-
+					}
 				} else {
 					ast_log(LOG_WARNING, "Asked to run MixMonitor on this call, but cannot find the MixMonitor app!\n");
 				}
@@ -5180,7 +5257,10 @@
 		ao2_lock(q);
 		if ((mem = ao2_find(q->members, &tmpmem, OBJ_POINTER))) {
 			/* 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_unlock(q);
 				queue_t_unref(q, "Interface wasn't dynamic, expiring temporary reference");
@@ -5355,35 +5435,34 @@
 	int foundinterface = 0, foundqueue = 0;
 	struct call_queue *q;
 	struct member *mem;
-	struct ao2_iterator queue_iter;
-
-	if (penalty < 0) {
+	char rtpenalty[80];
+
+	if (penalty < 0 && !negative_penalty_invalid) {
 		ast_log(LOG_ERROR, "Invalid penalty (%d)\n", penalty);
 		return RESULT_FAILURE;
 	}
 
-	queue_iter = ao2_iterator_init(queues, 0);
-	while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
+	if ((q = load_realtime_queue(queuename))) {
+		foundqueue++;
 		ao2_lock(q);
-		if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
-			foundqueue++;
-			if ((mem = interface_exists(q, interface))) {
-				foundinterface++;
+		if ((mem = interface_exists(q, interface))) {
+			foundinterface++;
+			if (!mem->realtime) {
 				mem->penalty = penalty;
-				
-				ast_queue_log(q->name, "NONE", interface, "PENALTY", "%d", penalty);
-				manager_event(EVENT_FLAG_AGENT, "QueueMemberPenalty",
-					"Queue: %s\r\n"
-					"Location: %s\r\n"
-					"Penalty: %d\r\n",
-					q->name, mem->interface, penalty);
-				ao2_ref(mem, -1);
-			}
+			} else {
+				sprintf(rtpenalty,"%i", penalty);
+				update_realtime_member_field(mem, q->name, "penalty", rtpenalty);
+			}
+			ast_queue_log(q->name, "NONE", interface, "PENALTY", "%d", penalty);
+			manager_event(EVENT_FLAG_AGENT, "QueueMemberPenalty",
+				"Queue: %s\r\n"
+				"Location: %s\r\n"
+				"Penalty: %d\r\n",
+				q->name, mem->interface, penalty);
+			ao2_ref(mem, -1);
 		}
 		ao2_unlock(q);
-		queue_t_unref(q, "Done with iterator");
-	}
-	ao2_iterator_destroy(&queue_iter);
+	}
 
 	if (foundinterface) {
 		return RESULT_SUCCESS;
@@ -6157,31 +6236,37 @@
 	return 0;
 }
 
-/*! 
+/*!
  * \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
 */
-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;
 	struct member *m;
 	struct ao2_iterator mem_iter;
 	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)) {
 		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))) {
+	AST_STANDARD_APP_ARGS(args, data);
+
+	if ((q = load_realtime_queue(args.queuename))) {
 		ao2_lock(q);
-		if (!strcasecmp(option, "logged")) {
+		if (!strcasecmp(args.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 */
@@ -6191,7 +6276,7 @@
 				ao2_ref(m, -1);
 			}
 			ao2_iterator_destroy(&mem_iter);
-		} else if (!strcasecmp(option, "free")) {
+		} else if (!strcasecmp(args.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 */
@@ -6201,7 +6286,7 @@
 				ao2_ref(m, -1);
 			}
 			ao2_iterator_destroy(&mem_iter);
-		} else if (!strcasecmp(option, "ready")) {
+		} else if (!strcasecmp(args.option, "ready")) {
 			time_t now;
 			time(&now);
 			mem_iter = ao2_iterator_init(q->members, 0);
@@ -6214,22 +6299,104 @@
 				ao2_ref(m, -1);
 			}
 			ao2_iterator_destroy(&mem_iter);
-		} else /* must be "count" */
+		} else if (!strcasecmp(args.option, "count") || ast_strlen_zero(args.option)) {
 			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);
 		queue_t_unref(q, "Done with temporary reference in QUEUE_MEMBER()");
-	} else
-		ast_log(LOG_WARNING, "queue %s was not found\n", data);
+	} else {
+		ast_log(LOG_WARNING, "queue %s was not found\n", args.queuename);
+	}
 
 	snprintf(buf, len, "%d", count);
 
 	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)
- * \retval number of members 
- * \retval -1 on error 
+ * \retval number of members
+ * \retval -1 on error
 */
 static int queue_function_qac_dep(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
 {
@@ -6435,7 +6602,8 @@
 
 static struct ast_custom_function queuemembercount_function = {
 	.name = "QUEUE_MEMBER",
-	.read = queue_function_qac,
+	.read = queue_function_mem_read,
+	.write = queue_function_mem_write,
 };
 
 static struct ast_custom_function queuemembercount_dep = {
@@ -6517,8 +6685,9 @@
 {
 	const char *general_val = NULL;
 	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);
+	}
 	autofill_default = 0;
 	if ((general_val = ast_variable_retrieve(cfg, "general", "autofill")))
 		autofill_default = ast_true(general_val);
@@ -6533,6 +6702,9 @@
 	shared_lastcall = 0;
 	if ((general_val = ast_variable_retrieve(cfg, "general", "shared_lastcall")))
 		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

Modified: trunk/configs/queues.conf.sample
URL: http://svnview.digium.com/svn/asterisk/trunk/configs/queues.conf.sample?view=diff&rev=325483&r1=325482&r2=325483
==============================================================================
--- trunk/configs/queues.conf.sample (original)
+++ trunk/configs/queues.conf.sample Wed Jun 29 01:39:26 2011
@@ -60,6 +60,10 @@
 ; The default value is yes.
 ;
 ;shared_lastcall=no
+;
+; Negative_penalty_invalid will treat members with a negative penalty as logged off
+;
+;negative_penalty_invalid = no
 ;
 ;[markq]
 ;
@@ -195,6 +199,10 @@
 ; yes: Member will be paused only in the queue where the timeout took place
 ; all: Memeber will be paused in all queues he/she is a member
 ;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)
 ;
@@ -459,6 +467,9 @@
 ; uncomment this option. (Note: only the SIP channel driver currently is able
 ; 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
 ;
 ; If you wish to have a delay before the member is connected to the caller (or




More information about the svn-commits mailing list