[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