[svn-commits] irroot: branch irroot/distrotech-customers-trunk r347942 - in /team/irroot/di...

SVN commits to the Digium repositories svn-commits at lists.digium.com
Mon Dec 12 08:37:20 CST 2011


Author: irroot
Date: Mon Dec 12 08:37:14 2011
New Revision: 347942

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=347942
Log:
Make queue log indicate if ADDMEMBER is paused for AMI and realtime.

* Add parameter to queue log ADDMEMBER to indicate if the member is
paused.

(closes issue ASTERISK-18645)
Reported by: garlew
Patches:
      paused.diff (License #5337) patch uploaded by garlew
Tested by: rmudgett, garlew

Review: https://reviewboard.asterisk.org/r/1469/
........

Merged revisions 345285 from http://svn.asterisk.org/svn/asterisk/branches/1.8
........

Merged revisions 345290 from http://svn.asterisk.org/svn/asterisk/branches/10
........

Merged revisions 345317 from http://svn.asterisk.org/svn/asterisk/trunk

Modified:
    team/irroot/distrotech-customers-trunk/   (props changed)
    team/irroot/distrotech-customers-trunk/apps/app_mixmonitor.c
    team/irroot/distrotech-customers-trunk/apps/app_queue.c
    team/irroot/distrotech-customers-trunk/apps/app_sayunixtime.c
    team/irroot/distrotech-customers-trunk/channels/chan_sip.c

Propchange: team/irroot/distrotech-customers-trunk/
------------------------------------------------------------------------------
    automerge = *

Propchange: team/irroot/distrotech-customers-trunk/
            ('svnmerge-integrated' removed)

Modified: team/irroot/distrotech-customers-trunk/apps/app_mixmonitor.c
URL: http://svnview.digium.com/svn/asterisk/team/irroot/distrotech-customers-trunk/apps/app_mixmonitor.c?view=diff&rev=347942&r1=347941&r2=347942
==============================================================================
--- team/irroot/distrotech-customers-trunk/apps/app_mixmonitor.c (original)
+++ team/irroot/distrotech-customers-trunk/apps/app_mixmonitor.c Mon Dec 12 08:37:14 2011
@@ -165,6 +165,54 @@
 			<para>This action may be used to mute a MixMonitor recording.</para>
 		</description>
 	</manager>
+	<manager name="MixMonitor" language="en_US">
+		<synopsis>
+			Record a call and mix the audio during the recording.  Use of StopMixMonitor is required
+			to guarantee the audio file is available for processing during dialplan execution.
+		</synopsis>
+		<syntax>
+			<xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
+			<parameter name="Channel" required="true">
+				<para>Used to specify the channel to record.</para>
+			</parameter>
+			<parameter name="File">
+				<para>Is the name of the file created in the monitor spool directory.
+				Defaults to the same name as the channel (with slashes replaced with dashes).
+				This argument is optional if you specify to record unidirectional audio with
+				either the r(filename) or t(filename) options in the options field. If
+				neither MIXMONITOR_FILENAME or this parameter is set, the mixed stream won't
+				be recorded.</para>
+			</parameter>
+			<parameter name="options">
+				<para>Options that apply to the MixMonitor in the same way as they
+				would apply if invoked from the MixMonitor application. For a list of
+				available options, see the documentation for the mixmonitor application. </para>
+			</parameter>
+		</syntax>
+		<description>
+			<para>This action records the audio on the current channel to the specified file.</para>
+			<variablelist>
+				<variable name="MIXMONITOR_FILENAME">
+					<para>Will contain the filename used to record the mixed stream.</para>
+				</variable>
+			</variablelist>
+		</description>
+	</manager>
+	<manager name="StopMixMonitor" language="en_US">
+		<synopsis>
+			Stop recording a call through MixMonitor, and free the recording's file handle.
+		</synopsis>
+		<syntax>
+			<xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
+			<parameter name="Channel" required="true">
+				<para>The name of the channel monitored.</para>
+			</parameter>
+		</syntax>
+		<description>
+			<para>This action stops the audio recording that was started with the <literal>MixMonitor</literal>
+			action on the current channel.</para>
+		</description>
+	</manager>
 
  ***/
 
@@ -852,6 +900,95 @@
 	return AMI_SUCCESS;
 }
 
+static int manager_mixmonitor(struct mansession *s, const struct message *m)
+{
+	struct ast_channel *c = NULL;
+
+	const char *name = astman_get_header(m, "Channel");
+	const char *id = astman_get_header(m, "ActionID");
+	const char *file = astman_get_header(m, "File");
+	const char *options = astman_get_header(m, "Options");
+
+	int res;
+	char args[PATH_MAX] = "";
+	if (ast_strlen_zero(name)) {
+		astman_send_error(s, m, "No channel specified");
+		return AMI_SUCCESS;
+	}
+
+	c = ast_channel_get_by_name(name);
+
+	if (!c) {
+		astman_send_error(s, m, "No such channel");
+		return AMI_SUCCESS;
+	}
+
+	strcpy(args, file);
+	strcat(args, ",");
+	strcat(args, options);
+
+	ast_channel_lock(c);
+	res = mixmonitor_exec(c, args);
+	ast_channel_unlock(c);
+
+	if (res) {
+		astman_send_error(s, m, "Could not start monitoring channel");
+		return AMI_SUCCESS;
+	}
+
+	astman_append(s, "Response: Success\r\n");
+
+	if (!ast_strlen_zero(id)) {
+		astman_append(s, "ActionID: %s\r\n", id);
+	}
+
+	astman_append(s, "\r\n");
+
+	c = ast_channel_unref(c);
+
+	return AMI_SUCCESS;
+}
+
+static int manager_stop_mixmonitor(struct mansession *s, const struct message *m)
+{
+	struct ast_channel *c = NULL;
+
+	const char *name = astman_get_header(m, "Channel");
+	const char *id = astman_get_header(m, "ActionID");
+
+	int res;
+	if (ast_strlen_zero(name)) {
+		astman_send_error(s, m, "No channel specified");
+		return AMI_SUCCESS;
+	}
+
+	c = ast_channel_get_by_name(name);
+
+	if (!c) {
+		astman_send_error(s, m, "No such channel");
+		return AMI_SUCCESS;
+	}
+
+	res = stop_mixmonitor_exec(c, NULL);
+
+	if (res) {
+		astman_send_error(s, m, "Could not stop monitoring channel");
+		return AMI_SUCCESS;
+	}
+
+	astman_append(s, "Response: Success\r\n");
+
+	if (!ast_strlen_zero(id)) {
+		astman_append(s, "ActionID: %s\r\n", id);
+	}
+
+	astman_append(s, "\r\n");
+
+	c = ast_channel_unref(c);
+
+	return AMI_SUCCESS;
+}
+
 static struct ast_cli_entry cli_mixmonitor[] = {
 	AST_CLI_DEFINE(handle_cli_mixmonitor, "Execute a MixMonitor command")
 };
@@ -864,7 +1001,9 @@
 	res = ast_unregister_application(stop_app);
 	res |= ast_unregister_application(app);
 	res |= ast_manager_unregister("MixMonitorMute");
-	
+	res |= ast_manager_unregister("MixMonitor");
+	res |= ast_manager_unregister("StopMixMonitor");
+
 	return res;
 }
 
@@ -876,6 +1015,8 @@
 	res = ast_register_application_xml(app, mixmonitor_exec);
 	res |= ast_register_application_xml(stop_app, stop_mixmonitor_exec);
 	res |= ast_manager_register_xml("MixMonitorMute", 0, manager_mute_mixmonitor);
+	res |= ast_manager_register_xml("MixMonitor", 0, manager_mixmonitor);
+	res |= ast_manager_register_xml("StopMixMonitor", 0, manager_stop_mixmonitor);
 
 	return res;
 }

Modified: team/irroot/distrotech-customers-trunk/apps/app_queue.c
URL: http://svnview.digium.com/svn/asterisk/team/irroot/distrotech-customers-trunk/apps/app_queue.c?view=diff&rev=347942&r1=347941&r2=347942
==============================================================================
--- team/irroot/distrotech-customers-trunk/apps/app_queue.c (original)
+++ team/irroot/distrotech-customers-trunk/apps/app_queue.c Mon Dec 12 08:37:14 2011
@@ -25,17 +25,18 @@
  * \arg Config in \ref Config_qu queues.conf
  *
  * \par Development notes
- * \note 2011-11-01: Made application more thread safe
+ * \note 2011-11-01: Reworked to seperate structures make more thread safe
  *		Distrotech PTY (LTD) (www.distrotech.co.za)
  *		Gregory Nietsky (irroot) <gregory at distrotech.co.za>
  *
  *		Split away from locking queues/queue to having locks per
- *		Queue/Member/Device.
+ *		Queue Data/Member/Device.
  *		Added device state struct to mange device states shared
  *		for multiple members sharing same device.
+ *		Added queue data struct to mange queue data.
  *
  *		Made all functions work with realtime/dynamic/static members.
- *		Added missing CLI/AMI functions for handling callinuse.
+ *		Added missing CLI/AMI functions for handling callinuse (ignorebusy).
  *
  * \note 2004-11-25: Persistent Dynamic Members added by:
  *             NetNation Communications (www.netnation.com)
@@ -117,25 +118,22 @@
  * \par Please read before modifying this file.
  * There are locks which are regularly used
  * throughout this file, the lock
- * for each individual queue, the individual member lock ,
+ * for each individual queue, queue data, the individual member lock ,
  * and the device state lock.
  * there are container locks for the queues list, the member
- * list on each queue and the devices container.
+ * list on each queue the devices container and rules container.
+ * in the queue data struct there is linked lists for the entries in queue.
  * Please be extra careful to always lock in the following order
  *
- * 1) queues container lock (ao2 functions that lock the container)
- * 2) queue lock
- * 3) queue member lock
- * 4) member lock
- * 5) devices container lock
- * 6) member device lock
+ * 1) container lock first
+ * 2) container element
+ *
+ * Never call a function on a container while a element is locked
+ *
  * This order has sort of "evolved" over the lifetime of this
  * application, but it is now in place this way, so please adhere
  * to this order!
  *
- * the only elements that do not require a read lock (ie will not change)
- * the queue name and members container, the member interface and
- * device state_interface as they are never altered after been linked.
  */
 
 /*** DOCUMENTATION
@@ -270,7 +268,6 @@
 			<ref type="application">UnpauseQueueMember</ref>
 			<ref type="function">QUEUE_VARIABLES</ref>
 			<ref type="function">QUEUE_MEMBER</ref>
-			<ref type="function">QUEUE_MEMBER_COUNT</ref>
 			<ref type="function">QUEUE_EXISTS</ref>
 			<ref type="function">QUEUE_WAITING_COUNT</ref>
 			<ref type="function">QUEUE_MEMBER_LIST</ref>
@@ -312,7 +309,6 @@
 			<ref type="application">UnpauseQueueMember</ref>
 			<ref type="function">QUEUE_VARIABLES</ref>
 			<ref type="function">QUEUE_MEMBER</ref>
-			<ref type="function">QUEUE_MEMBER_COUNT</ref>
 			<ref type="function">QUEUE_EXISTS</ref>
 			<ref type="function">QUEUE_WAITING_COUNT</ref>
 			<ref type="function">QUEUE_MEMBER_LIST</ref>
@@ -349,7 +345,6 @@
 			<ref type="application">UnpauseQueueMember</ref>
 			<ref type="function">QUEUE_VARIABLES</ref>
 			<ref type="function">QUEUE_MEMBER</ref>
-			<ref type="function">QUEUE_MEMBER_COUNT</ref>
 			<ref type="function">QUEUE_EXISTS</ref>
 			<ref type="function">QUEUE_WAITING_COUNT</ref>
 			<ref type="function">QUEUE_MEMBER_LIST</ref>
@@ -393,7 +388,6 @@
 			<ref type="application">UnpauseQueueMember</ref>
 			<ref type="function">QUEUE_VARIABLES</ref>
 			<ref type="function">QUEUE_MEMBER</ref>
-			<ref type="function">QUEUE_MEMBER_COUNT</ref>
 			<ref type="function">QUEUE_EXISTS</ref>
 			<ref type="function">QUEUE_WAITING_COUNT</ref>
 			<ref type="function">QUEUE_MEMBER_LIST</ref>
@@ -434,7 +428,6 @@
 			<ref type="application">UnpauseQueueMember</ref>
 			<ref type="function">QUEUE_VARIABLES</ref>
 			<ref type="function">QUEUE_MEMBER</ref>
-			<ref type="function">QUEUE_MEMBER_COUNT</ref>
 			<ref type="function">QUEUE_EXISTS</ref>
 			<ref type="function">QUEUE_WAITING_COUNT</ref>
 			<ref type="function">QUEUE_MEMBER_LIST</ref>
@@ -465,7 +458,6 @@
 			<ref type="application">UnpauseQueueMember</ref>
 			<ref type="function">QUEUE_VARIABLES</ref>
 			<ref type="function">QUEUE_MEMBER</ref>
-			<ref type="function">QUEUE_MEMBER_COUNT</ref>
 			<ref type="function">QUEUE_EXISTS</ref>
 			<ref type="function">QUEUE_WAITING_COUNT</ref>
 			<ref type="function">QUEUE_MEMBER_LIST</ref>
@@ -519,7 +511,6 @@
 			<ref type="application">UnpauseQueueMember</ref>
 			<ref type="function">QUEUE_VARIABLES</ref>
 			<ref type="function">QUEUE_MEMBER</ref>
-			<ref type="function">QUEUE_MEMBER_COUNT</ref>
 			<ref type="function">QUEUE_EXISTS</ref>
 			<ref type="function">QUEUE_WAITING_COUNT</ref>
 			<ref type="function">QUEUE_MEMBER_LIST</ref>
@@ -575,34 +566,6 @@
 			<ref type="application">UnpauseQueueMember</ref>
 			<ref type="function">QUEUE_VARIABLES</ref>
 			<ref type="function">QUEUE_MEMBER</ref>
-			<ref type="function">QUEUE_MEMBER_COUNT</ref>
-			<ref type="function">QUEUE_EXISTS</ref>
-			<ref type="function">QUEUE_WAITING_COUNT</ref>
-			<ref type="function">QUEUE_MEMBER_LIST</ref>
-			<ref type="function">QUEUE_MEMBER_PENALTY</ref>
-		</see-also>
-	</function>
-	<function name="QUEUE_MEMBER_COUNT" language="en_US">
-		<synopsis>
-			Count number of members answering a queue.
-		</synopsis>
-		<syntax>
-			<parameter name="queuename" required="true" />
-		</syntax>
-		<description>
-			<para>Returns the number of members currently associated with the specified <replaceable>queuename</replaceable>.</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>
-			<ref type="application">QueueLog</ref>
-			<ref type="application">AddQueueMember</ref>
-			<ref type="application">RemoveQueueMember</ref>
-			<ref type="application">PauseQueueMember</ref>
-			<ref type="application">UnpauseQueueMember</ref>
-			<ref type="function">QUEUE_VARIABLES</ref>
-			<ref type="function">QUEUE_MEMBER</ref>
-			<ref type="function">QUEUE_MEMBER_COUNT</ref>
 			<ref type="function">QUEUE_EXISTS</ref>
 			<ref type="function">QUEUE_WAITING_COUNT</ref>
 			<ref type="function">QUEUE_MEMBER_LIST</ref>
@@ -949,22 +912,20 @@
                                                      The default value of 15 provides backwards compatibility */
 #define MAX_QUEUE_BUCKETS 53
 
-#define	RES_OKAY	0		/*!< Action completed */
-#define	RES_EXISTS	(-1)		/*!< Entry already exists */
-#define	RES_OUTOFMEMORY	(-2)		/*!< Out of memory */
-#define	RES_NOSUCHQUEUE	(-3)		/*!< No such queue */
-#define RES_NOT_DYNAMIC (-4)		/*!< Member is not dynamic */
-#define RES_ERROR	(-5)		/*!< Member is mis configured */
+enum member_result {
+	RES_OKAY = 0,			/*!< Action completed */
+	RES_EXISTS = -1,		/*!< Entry already exists */
+	RES_OUTOFMEMORY	= -2,		/*!< Out of memory */
+	RES_NOSUCHQUEUE	= -3,		/*!< No such queue */
+	RES_NOT_DYNAMIC = -4,		/*!< Member is not dynamic */
+	RES_ERROR = -5,			/*!< Member is mis configured */
+};
+
 static char *app = "Queue";
-
 static char *app_aqm = "AddQueueMember" ;
-
 static char *app_rqm = "RemoveQueueMember" ;
-
 static char *app_pqm = "PauseQueueMember" ;
-
 static char *app_upqm = "UnpauseQueueMember" ;
-
 static char *app_ql = "QueueLog" ;
 
 /*! \brief Persistent Members astdb family */
@@ -1037,14 +998,15 @@
 
 struct callattempt {
 	struct ast_channel *chan;                  /*! Channel called */
-	int stillgoing;                            /*! This attempt is valid and active */
 	int metric;                                /*! Metric calculated according to strategy */
 	struct member *member;                     /*! Member assosiated with this attempt */
 	struct ast_party_connected_line connected; /*! Saved connected party info from an AST_CONTROL_CONNECTED_LINE. */
+	unsigned int stillgoing:1;                 /*! This attempt is valid and active */
 	unsigned int reserved:1;                   /*! Is this attempt been attempted*/
 	unsigned int active:1;                     /*! Is this attempt active in a call*/
 	unsigned int pending_connected_update:1;   /*! TRUE if caller id is not available for connected line*/
 	unsigned int dial_callerid_absent:1;       /*! TRUE if caller id is not available for connected line */
+	unsigned int watching:1;                   /*! TRUE if callattempt is been watched */
 	struct ast_aoc_decoded *aoc_s_rate_list;
 };
 
@@ -1188,7 +1150,6 @@
 	int strategy:4;
 	unsigned int maskmemberstatus:1;
 	unsigned int realtime:1;
-	unsigned int found:1;
 	unsigned int relativeperiodicannounce:1;
 	unsigned int autopausebusy:1;
 	unsigned int autopauseunavail:1;
@@ -1248,7 +1209,6 @@
 static void pm_load_member_config(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);
 
 /*! \brief sets the QUEUESTATUS channel variable */
@@ -1330,9 +1290,13 @@
 static int queue_cmp_cb(void *obj, void *arg, int flags)
 {
 	const struct call_queue *q = obj, *q2 = arg;
-	const char *name = (flags & OBJ_POINTER) ? q2->name : arg;
-
-	return !strcasecmp(q->name, name) ? CMP_MATCH | CMP_STOP : 0;
+	const char *name = (arg && (flags & OBJ_POINTER)) ? q2->name : arg;
+
+	if (!ast_strlen_zero(name) && !strcasecmp(q->name, name)) {
+		return CMP_MATCH | CMP_STOP;
+	} else {
+		return 0;
+	}
 }
 
 /*!
@@ -1356,9 +1320,14 @@
 {
 	const struct queue_data *d = obj, *d2 = arg;
 	const char *name = arg;
-	int qhash = (flags & OBJ_POINTER) ? d2->qhash : ast_str_case_hash(name);
-
-	return  (d->qhash == qhash) ? CMP_MATCH | CMP_STOP : 0;
+	int nhash = (ast_strlen_zero(name)) ? 0 : ast_str_case_hash(name);
+	int qhash = (arg && (flags & OBJ_POINTER)) ? d2->qhash : nhash;
+
+	if (qhash && (d->qhash == qhash)) {
+		return CMP_MATCH | CMP_STOP;
+	} else {
+		return 0;
+	}
 }
 
 /*! \brief Set channel variables of queue */
@@ -1416,7 +1385,6 @@
 			/* if im active and may not place calls when INUSE im actually BUSY */
 			if ((s->reserved || s->active) && !m->callinuse) {
 				ret = AST_DEVICE_BUSY;
-				break;
 			}
 			break;
 		case AST_DEVICE_NOT_INUSE:
@@ -1444,8 +1412,6 @@
 	struct member *member;
 	struct ao2_iterator mem_iter;
 	struct call_queue *q = qe->parent;
-	int max_penalty = qe->max_penalty;
-	int min_penalty = qe->min_penalty;
 	enum empty_conditions conditions;
 
 	conditions = (join) ? q->joinempty : q->leavewhenempty;
@@ -1457,9 +1423,9 @@
 	mem_iter = ao2_iterator_init(q->data->members, 0);
 	while((member = ao2_iterator_next(&mem_iter))) {
 		ao2_lock(member);
-		if ((max_penalty && (member->penalty > max_penalty)) || (min_penalty && (member->penalty < min_penalty))) {
+		if ((qe->max_penalty && (member->penalty > qe->max_penalty)) || (qe->min_penalty && (member->penalty < qe->min_penalty))) {
 			if (conditions & QUEUE_EMPTY_PENALTY) {
-				ast_debug(4, "%s is unavailable because his penalty is not between %d and %d\n", member->membername, min_penalty, max_penalty);
+				ast_debug(4, "%s is unavailable because his penalty is not between %d and %d\n", member->membername, qe->min_penalty, qe->max_penalty);
 				ao2_unlock(member);
 				ao2_ref(member, -1);
 				continue;
@@ -1778,9 +1744,13 @@
 {
 	const struct member *mem1 = obj1;
 	const struct member *mem2 = obj2;
-	const char *arg = (flags & OBJ_POINTER) ? mem2->interface : obj2;
-
-	return strcasecmp(mem1->interface, arg) ? 0 : CMP_MATCH | CMP_STOP;
+	const char *iface = (obj2 && (flags & OBJ_POINTER)) ? mem2->interface : obj2;
+
+	if (!ast_strlen_zero(iface) && !strcasecmp(mem1->interface, iface)) {
+		return CMP_MATCH | CMP_STOP;
+	} else {
+		return 0;
+	}
 }
 
 /*!
@@ -1790,9 +1760,9 @@
 {
 	const struct member *mem1 = obj1;
 	const struct member *mem2 = arg;
-	const char *uniqueid = (flags & OBJ_POINTER) ? mem2->rt_uniqueid : arg;
-
-	if (mem1->realtime && !mem1->dead &&
+	const char *uniqueid = (arg && (flags & OBJ_POINTER)) ? mem2->rt_uniqueid : arg;
+
+	if (mem1->realtime && !mem1->dead && !ast_strlen_zero(uniqueid) &&
 	    !strcasecmp(mem1->rt_uniqueid, uniqueid)) {
 		return CMP_MATCH | CMP_STOP;
 	}
@@ -1823,13 +1793,13 @@
 	const struct call_queue *q = data;
 
 	if (m->realtime && m->dead) {
-		ao2_lock(m);
-		if (ast_strlen_zero(m->membername) || !log_membername_as_agent) {
+		if (!log_membername_as_agent) {
 			ast_queue_log(q->name, "REALTIME", m->interface, "REMOVEMEMBER", "%s", "");
 		} else {
+			ao2_lock(m);
 			ast_queue_log(q->name, "REALTIME", m->membername, "REMOVEMEMBER", "%s", "");
-		}
-		ao2_unlock(m);
+			ao2_unlock(m);
+		}
 		return CMP_MATCH;
 	}
 	return 0;
@@ -1868,9 +1838,13 @@
 {
 	const struct mem_state *d = obj;
 	const struct mem_state *d2 = arg;
-	const char *iface = (flags & OBJ_POINTER) ? d2->state_interface : arg;
-
-	return !strcasecmp(d->state_interface, iface) ? CMP_MATCH | CMP_STOP : 0;
+	const char *iface = (arg && (flags & OBJ_POINTER)) ? d2->state_interface : arg;
+
+	if (!ast_strlen_zero(iface) && !strcasecmp(d->state_interface, iface)) {
+		return CMP_MATCH | CMP_STOP;
+	} else {
+		return 0;
+	}
 }
 
 /*!
@@ -1922,9 +1896,13 @@
 {
 	const struct rule_list *rl = obj;
 	const struct rule_list *rl2 = arg;
-	const char *name = (flags & OBJ_POINTER) ? rl2->name : arg;
-
-	return !strcasecmp(rl->name, name) ? CMP_MATCH | CMP_STOP : 0;
+	const char *name = (arg && (flags & OBJ_POINTER)) ? rl2->name : arg;
+
+	if (!ast_strlen_zero(name) && !strcasecmp(rl->name, name)) {
+		return CMP_MATCH | CMP_STOP;
+	} else {
+		return 0;
+	}
 }
 
 /*!
@@ -1939,6 +1917,21 @@
 	return ast_str_case_hash(interface);
 }
 
+/*!
+ * \brief ao2 callback to find callattempt been watched
+ */
+static int callattempt_watched_cb(void *obj, void *arg, const int flags)
+{
+	const struct callattempt *c = obj;
+	const struct callattempt *c1 = arg;
+	const struct member *mem = (arg) ? c1->member : NULL;
+	const char *interface = (arg && (flags & OBJ_POINTER)) ? mem->interface : arg;
+
+	if (!arg || ast_strlen_zero(interface) || !strcasecmp(interface, c->member->interface)) {
+		return (c->stillgoing && c->chan) ? CMP_MATCH : 0;
+	}
+	return 0;
+}
 
 /*!
  * \brief ao2 callback to obtain the callattempt with best metric
@@ -2002,7 +1995,6 @@
 	q->autopause = QUEUE_AUTOPAUSE_OFF;
 	q->timeoutpriority = TIMEOUT_PRIORITY_APP;
 	q->autopausedelay = 0;
-	q->found = 1;
 
 	ast_string_field_set(q, sound_next, "queue-youarenext");
 	ast_string_field_set(q, sound_thereare, "queue-thereare");
@@ -2376,6 +2368,11 @@
 	char *rt_uniqueid = NULL, *st_dev = NULL;
 	int dead = 0;
 
+	if (ast_strlen_zero(interface)) {
+		ast_log(AST_LOG_ERROR, "Interface was not specified !!\n");
+		return RES_ERROR;
+	}
+
 	if (!(m = ao2_find(q->data->members, interface, OBJ_KEY))) {
 		if (!(m = ao2_alloc(sizeof(*m), remove_queue_member))) {
 			return RES_OUTOFMEMORY;
@@ -2473,7 +2470,7 @@
 		}
 	}
 
-	if (!dead && !m->device && (!(m->device = create_member_state(st_dev)))) {
+	if (!dead && !m->device && !(m->device = create_member_state(st_dev))) {
 		dead = 1;
 	}
 
@@ -2482,7 +2479,7 @@
 	}
 
 	/*check the uniqueness of the RT uniqueid */
-	if (link && (memtype & QUEUE_ADD_MEMBER_REALTIME) && !dead) {
+	if (!dead && link && (memtype & QUEUE_ADD_MEMBER_REALTIME)) {
 		if (ast_strlen_zero(rt_uniqueid)) {
 			ast_log(LOG_WARNING, "Realtime field uniqueid is empty for member %s\n", S_OR(m->membername, interface));
 			dead = 1;
@@ -2503,10 +2500,10 @@
 		int status = get_device_status(m);
 
 		/* i have just been born */
-		if ((ast_strlen_zero(m->membername) || !log_membername_as_agent)) {
-			ast_queue_log(q->name, source, m->interface, "ADDMEMBER", "%s", "");
+		if (!log_membername_as_agent) {
+			ast_queue_log(q->name, source, m->interface, "ADDMEMBER", "%s", m->paused ? "PAUSED" : "");
 		} else {
-			ast_queue_log(q->name, source, m->membername, "ADDMEMBER", "%s", "");
+			ast_queue_log(q->name, source, m->membername, "ADDMEMBER", "%s", m->paused ? "PAUSED" : "");
 		}
 		manager_event(EVENT_FLAG_AGENT, "QueueMemberAdded",
 			"Queue: %s\r\n"
@@ -2533,7 +2530,7 @@
 		}
 		if (!link) {
 			/* thee was a config error remove this member from container now*/
-			if ((ast_strlen_zero(m->membername) || !log_membername_as_agent)) {
+			if (!log_membername_as_agent) {
 				ast_queue_log(q->name, source, m->interface, "REMOVEMEMBER", "%s", "");
 			} else {
 				ast_queue_log(q->name, source, m->membername, "REMOVEMEMBER", "%s", "");
@@ -2647,83 +2644,41 @@
 
 	ast_string_field_set(q, name, queuename);
 	q->realtime = rt;
-	q->weight = 0;
-	q->found = 0;
+
+	/* Ensure defaults for all parameters not set explicitly. */
+	init_queue(q);
 
 	return q;
 }
 
-/*!
- * \brief Reload a single queue via realtime.
+/*
  *
- * Check for statically defined queue first, check if deleted RT queue,
- * check for new RT queue, if queue vars are not defined init them with defaults.
- * reload RT queue vars, set RT queue members dead and reload them, return finished queue.
- * \retval the queue,
- * \retval NULL if it doesn't exist.
-*/
-static struct call_queue *load_realtime_queue(const char *queuename, struct ast_flags *mask)
-{
+ */
+static struct call_queue *config_call_queue(struct call_queue *oldq, struct ast_variable *queue_vars, const char *queuename, int reload_members)
+{
+	int prev_weight = 0;
 	struct ast_variable *v;
+	char *tmp;
 	const char *tmp_name;
-	char *tmp;
 	char tmpbuf[64];	/* Must be longer than the longest queue param name. */
-	int prev_weight = 0;
-	struct ast_variable *queue_vars;
-	struct call_queue *q, *oldq;
-	int found;
-	int reload_queue = (mask) ? ast_test_flag(mask, QUEUE_RELOAD_PARAMETERS) : 0;
-	int reload_members = (mask) ? ast_test_flag(mask, QUEUE_RELOAD_MEMBER) : 0;
-	int reload_realtime = (mask) ? ast_test_flag(mask, QUEUE_RELOAD_REALTIME) : 0;
-
-	if ((q = ao2_t_find(queues, queuename, OBJ_KEY, "Look for queue in memory first")) &&
-	    (!q->realtime || !reload_queue)) {
-		if (reload_members) {
-			rt_load_member_config(q);
-		}
-		return q;
-	}
-
-	if (!reload_realtime && q && !ast_tvzero(q->reload) && ast_tvdiff_sec(ast_tvnow(), q->reload)) {
-		ast_log(AST_LOG_WARNING, "Not reloading queue for next %ld Seconds\n", (long)ast_tvdiff_sec(q->reload, ast_tvnow()));
-		return q;
-	}
-
-	/*! \note The queue is recreated and the existing queue will not change and any
-		users holding a ref to the queue will have no changes applied. */
-
-	/* Check if queue is defined in realtime. */
-	if (!(queue_vars = ast_load_realtime("queues", "name", queuename, SENTINEL))) {
-		/* Delete queue from in-core list if it has been deleted in realtime. */
-		if (q) {
-			/*! \note Hmm, can't seem to distinguish a DB failure from a not
-			   found condition... So we might delete an in-core queue
-			   in case of DB failure. */
-			ast_debug(1, "Queue %s not found in realtime.\n", queuename);
-			ao2_t_unlink(queues, q, "Unused; removing from container");
-			ao2_t_ref(q, -1, "Queue is dead; can't return it");
-		}
-		return NULL;
-	}
+	struct call_queue *q;
 
 	/* Create a new queue if an in-core entry does not exist yet. */
-	if (!q && (!(q = alloc_queue(queuename, 1)))) {
+	if (!oldq && !(q = alloc_queue(queuename, 1))) {
 		ast_variables_destroy(queue_vars);
 		return NULL;
-	} else {
-		found = q->found;
-		prev_weight = q->weight ? 1 : 0;
-
-		oldq = q;
+	} else if (oldq) {
+		prev_weight = oldq->weight ? 1 : 0;
+		/*! \note The queue is recreated and the existing queue will not change and any
+		 *users holding a ref to the queue will have no changes applied.
+		 */
 		if (!(q = alloc_queue(queuename, 1))) {
 			/* i could not allocate new structure return the old one*/
 			ast_log(AST_LOG_WARNING, "Failed to assign new queue object returning unchanged object\n");
 			ast_variables_destroy(queue_vars);
-			return oldq;
-		}
-	}
-
-	init_queue(q);		/* Ensure defaults for all parameters not set explicitly. */
+			return NULL;
+		}
+	}
 
 	memset(tmpbuf, 0, sizeof(tmpbuf));
 	for (v = queue_vars; v; v = v->next) {
@@ -2753,7 +2708,6 @@
 	}
 
 	/* update the use_weight value if the queue's has gained or lost a weight */
-	/* Other cases will end up with the proper value for use_weight */
 	if (!q->weight && prev_weight) {
 		ast_atomic_fetchadd_int(&use_weight, -1);
 	} else if (q->weight && !prev_weight) {
@@ -2761,7 +2715,7 @@
 	}
 
 	/* add persistent members to new queue*/
-	if (!found && queue_persistent_members) {
+	if (!oldq && queue_persistent_members) {
 		pm_load_member_config(q);
 	}
 
@@ -2779,7 +2733,61 @@
 	} else {
 		ao2_link(queues, q);
 	}
+
 	return q;
+}
+
+/*!
+ * \brief Reload a single queue via realtime.
+ *
+ * Check for statically defined queue first, check if deleted RT queue,
+ * check for new RT queue, if queue vars are not defined init them with defaults.
+ * reload RT queue vars, set RT queue members dead and reload them, return finished queue.
+ * \retval the queue,
+ * \retval NULL if it doesn't exist.
+*/
+static struct call_queue *load_realtime_queue(const char *queuename, struct ast_flags *mask)
+{
+	struct ast_variable *queue_vars;
+	struct call_queue *oldq;
+
+	int reload_queue = (mask) ? ast_test_flag(mask, QUEUE_RELOAD_PARAMETERS) : 0;
+	int reload_members = (mask) ? ast_test_flag(mask, QUEUE_RELOAD_MEMBER) : 0;
+	int reload_realtime = (mask) ? ast_test_flag(mask, QUEUE_RELOAD_REALTIME) : 0;
+
+	/* return if im not realtime or not reloading the queue possibly checking members */
+	if ((oldq = ao2_t_find(queues, queuename, OBJ_KEY, "Look for queue in memory first")) &&
+	    (!oldq->realtime || !(reload_queue || reload_realtime))) {
+		if (reload_members) {
+			rt_load_member_config(oldq);
+		}
+		return oldq;
+	} else if (!oldq && !(reload_queue || reload_realtime)) {
+		ast_debug(1, "Not loading queue %s at this time\n",queuename);
+		return NULL;
+	}
+
+	/* if im reloading realtime (CLI/AMI) i ignore cache timer */
+	if (!reload_realtime && oldq && !ast_tvzero(oldq->reload) &&
+	    (ast_tvcmp(ast_tvnow(), oldq->reload) < 0)) {
+		ast_debug(1, "Not reloading queue %s for next %ld Seconds\n", oldq->name, (long)ast_tvdiff_sec(oldq->reload, ast_tvnow()));
+		return oldq;
+	}
+
+	/* Check if queue is defined in realtime if im reloading */
+	if (!(queue_vars = ast_load_realtime("queues", "name", queuename, SENTINEL))) {
+		/* Delete queue from in-core list if it has been deleted in realtime.
+		 * ! \note On DB failure the queue will be removed as i cant distinguish a DB failure
+		 */
+		if (oldq) {
+			ast_debug(1, "Queue %s not found in realtime.\n", queuename);
+			ao2_t_unlink(queues, oldq, "Unused; removing from container");
+			ao2_t_ref(oldq, -1, "Queue is dead; can't return it");
+		}
+		return NULL;
+	}
+
+	return config_call_queue(oldq, queue_vars, queuename, reload_members);
 }
 
 static int update_realtime_member_field(struct member *mem, const char *queue_name, const char *field, const char *value)
@@ -2799,18 +2807,23 @@
 
 static void load_all_realtime_queues(struct ast_flags *mask)
 {
-	char *queuename;
+	const char *queuename;
 	struct ast_config *cfg;
 	struct call_queue *queue;
+	struct ast_category *cat;
+	struct ast_variable *var;
 
 	/* load realtime queues. */
 	if ((cfg = ast_load_realtime_multientry("queues", "name LIKE", "%", SENTINEL))) {
 		for (queuename = ast_category_browse(cfg, NULL);
 				!ast_strlen_zero(queuename);
 				queuename = ast_category_browse(cfg, queuename)) {
-			if ((queue = load_realtime_queue(queuename, mask))) {
-				ao2_ref(queue, -1);
-			}
+			cat = ast_category_get(cfg, queuename);
+			var = ast_category_detach_variables(cat);
+			queue = ao2_find(queues, queuename, OBJ_KEY);
+			queue = config_call_queue(queue, var, queuename, 1);
+			ao2_ref(queue, -1);
+
 		}
 		ast_config_destroy(cfg);
 	}
@@ -2818,36 +2831,34 @@
 
 static int join_queue(char *queuename, struct queue_ent *qe, enum queue_result *reason, int position)
 {
-	struct call_queue *q;
 	struct queue_ent *cur;
 	int res = -1;
 	int pos = 0;
 	int inserted = 0;
 	struct ast_flags qflags = {QUEUE_RELOAD_PARAMETERS | QUEUE_RELOAD_MEMBER};
 
-	if (!(q = load_realtime_queue(queuename, &qflags))) {
+	/*obtain a ref for the queue reload realtime settings/members*/
+	if (!(qe->parent = load_realtime_queue(queuename, &qflags))) {
 		return res;
 	}
 
-	qe->parent = q;
-
 	/* This is our one */
-	if ((get_member_status(qe, 1))) {
+	if (get_member_status(qe, 1)) {
 		*reason = QUEUE_JOINEMPTY;
-		ao2_t_ref(q, -1, "Done with realtime queue");
+		ao2_t_ref(qe->parent, -1, "Done with realtime queue");
 		return res;
 	}
 
-	ao2_lock(q->data);
-	if ((*reason == QUEUE_UNKNOWN && q->maxlen && (q->data->count >= q->maxlen)) ||
+	ao2_lock(qe->parent->data);
+	if ((*reason == QUEUE_UNKNOWN && qe->parent->maxlen && (qe->parent->data->count >= qe->parent->maxlen)) ||
 	    (*reason != QUEUE_UNKNOWN)) {
-		ao2_unlock(q->data);
+		ao2_unlock(qe->parent->data);
 		*reason = QUEUE_FULL;
-		ao2_t_ref(q, -1, "Done with realtime queue");
+		ao2_t_ref(qe->parent, -1, "Done with realtime queue");
 
 		return res;
 	}
-	ao2_unlock(q->data);
+	ao2_unlock(qe->parent->data);
 
 
 	/* There's space for us, put us at the right position inside
@@ -2855,12 +2866,12 @@
 	 * Take into account the priority of the calling user */
 	inserted = 0;
 
-	AST_LIST_LOCK(q->data->head);
-	AST_LIST_TRAVERSE_SAFE_BEGIN(q->data->head, cur, next) {
+	AST_LIST_LOCK(qe->parent->data->head);
+	AST_LIST_TRAVERSE_SAFE_BEGIN(qe->parent->data->head, cur, next) {
 		/* We have higher priority than the current user, enter
 		 * before him, after all the other users with priority
 		 * higher or equal to our priority. */
-		if ((!inserted) && qe && (qe->prio > cur->prio)) {
+		if (!inserted && qe && (qe->prio > cur->prio)) {
 			AST_LIST_INSERT_BEFORE_CURRENT(qe, next);
 			insert_entry(qe, &pos);
 			inserted = 1;
@@ -2883,20 +2894,20 @@
 
 	/* No luck, join at the end of the queue */
 	if (!inserted && qe) {
-		AST_LIST_INSERT_TAIL(q->data->head, qe, next);
+		AST_LIST_INSERT_TAIL(qe->parent->data->head, qe, next);
 		insert_entry(qe, &pos);
 	}
-	AST_LIST_UNLOCK(q->data->head);
+	AST_LIST_UNLOCK(qe->parent->data->head);
 
 	/* pass a ref to the queue rules or this queue*/
 	qe->pr = NULL;
-	if ((qe->rules = ao2_find(rules, q->defaultrule, 0))) {
+	if ((qe->rules = ao2_find(rules, qe->parent->defaultrule, 0))) {
 		int time = 0;
 		ao2_callback_data(qe->rules->rules, OBJ_NODATA | OBJ_MULTIPLE, get_best_rule_cb, &time, &qe->pr);
 	}
 
 	res = 0;
-	ao2_lock(q->data);
+	ao2_lock(qe->parent->data);
 	ast_manager_event(qe->chan, EVENT_FLAG_CALL, "Join",
 		"Channel: %s\r\n"
 		"CallerIDNum: %s\r\n"
@@ -2912,9 +2923,9 @@
 		S_COR(qe->chan->caller.id.name.valid, qe->chan->caller.id.name.str, "unknown"),
 		S_COR(qe->chan->connected.id.number.valid, qe->chan->connected.id.number.str, "unknown"),/* XXX somewhere else it is <unknown> */
 		S_COR(qe->chan->connected.id.name.valid, qe->chan->connected.id.name.str, "unknown"),
-		q->name, qe->pos, q->data->count, qe->chan->uniqueid );
-	ao2_unlock(q->data);
-	ast_debug(1, "Queue '%s' Join, Channel '%s', Position '%d'\n", q->name, qe->chan->name, qe->pos );
+		qe->parent->name, qe->pos, qe->parent->data->count, qe->chan->uniqueid );
+	ao2_unlock(qe->parent->data);
+	ast_debug(1, "Queue '%s' Join, Channel '%s', Position '%d'\n", qe->parent->name, qe->chan->name, qe->pos );
 
 	return res;
 }
@@ -3307,43 +3318,35 @@
 {
 	struct call_queue *q;
 	struct member *mem;
-	int found = 0;
 	int count;
-	int rcount;
+	int res = 0;
 	struct ao2_iterator queue_iter;
-
-	ao2_lock(rq->data);
-	rcount = rq->data->count;
-	ao2_unlock(rq->data);
 
 	queue_iter = ao2_iterator_init(queues, 0);
 	while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
-		if (q == rq) { /* don't check myself, could deadlock */
-			ao2_t_ref(q, -1, "Done with iterator");
+		/* don't check myself or queues with  lower weight*/
+		if ((q == rq) || (q->weight <= rq->weight)) {
+			ao2_ref(q, -1);
 			continue;
 		}
-
-		if (count && (mem = interface_exists(q, interface))) {
-			ao2_lock(q->data);
-			count = q->data->count;
-			ao2_unlock(q->data);
-
-			ao2_lock(mem);
-			ast_debug(1, "Found matching member %s in queue '%s'\n", mem->interface, q->name);
-			if (q->weight > rq->weight && count >= num_available_members(q)) {
-				ast_debug(1, "Queue '%s' (weight %d, calls %d) is preferred over '%s' (weight %d, calls %d)\n", q->name, q->weight, count, rq->name, rq->weight, rcount);
-				found = 1;
-			}
-			ao2_unlock(mem);
+		ao2_lock(q->data);
+		count = q->data->count;
+		ao2_unlock(q->data);
+		if (count && (mem = interface_exists(q, interface)) &&
+		    (count >= num_available_members(q))) {
 			ao2_ref(mem, -1);
+			ast_debug(1, "Queue '%s' (weight %d, calls %d) is preferred over '%s' (weight %d)\n",
+					q->name, q->weight, count, rq->name, rq->weight);
+			ao2_ref(q, -1);
+			res = 1;
+			break;
+		} else if (mem) {
+			ao2_ref(mem, -1);
 		}
 		ao2_ref(q, -1);
-		if (found) {
-			break;
-		}
 	}
 	ao2_iterator_destroy(&queue_iter);
-	return found;
+	return res;
 }
 
 /*! \brief common hangup actions */
@@ -3392,15 +3395,15 @@
 	return vars;
 }
 
-/*! 
+/*!
  * \brief Part 2 of ring_one
  *
  * Does error checking before attempting to request a channel and call a member.
  * This function is only called from ring_one().
  * Failure can occur if:
  * - Priority by another queue
+ * - Member is paused
  * - Wrapup time not expired
- * - Member is paused
  * - Member on call / or is not available for a call
  * - Channel cannot be created by driver
  * - Channel cannot be called by driver
@@ -3415,7 +3418,6 @@
 	char tech[256];
 	char *location;
 	const char *macrocontext, *macroexten;
-	struct mem_state *s;
 	int dstat;
 
 	/* we cannot take this call there is a more urgent call we qualify for */
@@ -3430,8 +3432,7 @@
 	}
 
 	ao2_lock(tmp->member);
-	s = tmp->member->device;
-
+	/* im paused i cannot take this call */
 	if (tmp->member->paused) {
 		ao2_unlock(tmp->member);
 		ast_debug(1, "%s paused, can't receive call\n", tmp->member->interface);
@@ -3473,6 +3474,7 @@
 
 	/* mark device and call entry reserved */
 	if (!tmp->reserved) {
+		struct mem_state *s = tmp->member->device;
 		ao2_lock(s);
 		s->reserved++;
 		ao2_unlock(s);
@@ -3488,17 +3490,16 @@
 	}
 
 	/* 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 (!(tmp->chan = ast_request(tech, qe->chan->nativeformats, qe->chan, location, &status))) {
 		if (qe->chan->cdr) {
 			ast_cdr_busy(qe->chan->cdr);
 		}
 		tmp->stillgoing = 0;
 
 		set_queue_member_status(tmp->member);
-		ao2_lock(qe->parent->data->members);
+		ao2_lock(qe->parent->data);
 		qe->parent->data->rrpos++;
-		ao2_unlock(qe->parent->data->members);
+		ao2_unlock(qe->parent->data);
 		qe->linpos++;
 
 		(*busies)++;
@@ -3838,11 +3839,9 @@
 	ao2_unlock(call->member);
 
 	if (qe->parent->autopause != QUEUE_AUTOPAUSE_OFF && pause) {
-		if (qe->parent->autopausedelay > 0) {
-			if (!ast_tvzero(call->member->lastcall) &&
-			    (ast_tvdiff_sec(ast_tvnow(), call->member->lastcall) < qe->parent->autopausedelay)) {
-				return;
-			}
+		if ((qe->parent->autopausedelay > 0) && !ast_tvzero(call->member->lastcall) &&
+		    (ast_tvdiff_sec(ast_tvnow(), call->member->lastcall) < qe->parent->autopausedelay)) {
+			return;
 		}
 		if (qe->parent->autopause == QUEUE_AUTOPAUSE_ON) {
 			ao2_lock(call->member);
@@ -3898,8 +3897,7 @@
 	struct ast_channel *in = qe->chan;
 	long starttime = 0;
 	long endtime = 0;
-	struct ao2_iterator aiter;
-	struct ao2_container *calls;
+	struct ao2_iterator aiter, *calls;
 #ifdef HAVE_EPOLL
 	struct callattempt *epollo;
 #endif
@@ -3924,8 +3922,6 @@
 	ao2_iterator_destroy(&aiter);
 #endif
 
-	calls = ao2_container_alloc(MAX_QUEUE_BUCKETS, callattempt_hash_fn, NULL);
-
 	while (*to && !peer) {
 		int numlines, retry, pos = 1;
 		struct ast_channel *watchers[AST_MAX_WATCHERS];
@@ -3937,13 +3933,9 @@
 			while ((o = ao2_iterator_next(&aiter))) {
 				if (o->stillgoing) {	/* Keep track of important channels */
 					stillgoing = 1;
-					if (o->chan) {
-						if (pos < AST_MAX_WATCHERS) {
-							watchers[pos++] = o->chan;
-						}
-						if (!retry) {
-							ao2_link(calls, o);
-						}
+					if (o->chan && !o->watching && (pos < AST_MAX_WATCHERS)) {
+						watchers[pos++] = o->chan;
+						o->watching = 1;
 					}
 				}

[... 665 lines stripped ...]



More information about the svn-commits mailing list