[asterisk-commits] irroot: branch irroot/distrotech-customers-1.8 r342562 - in /team/irroot/dist...
SVN commits to the Asterisk project
asterisk-commits at lists.digium.com
Thu Oct 27 10:10:06 CDT 2011
Author: irroot
Date: Thu Oct 27 10:10:01 2011
New Revision: 342562
URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=342562
Log:
Work on app_queue RB1538
Modified:
team/irroot/distrotech-customers-1.8/ (props changed)
team/irroot/distrotech-customers-1.8/apps/app_queue.c
Propchange: team/irroot/distrotech-customers-1.8/
------------------------------------------------------------------------------
automerge = *
Modified: team/irroot/distrotech-customers-1.8/apps/app_queue.c
URL: http://svnview.digium.com/svn/asterisk/team/irroot/distrotech-customers-1.8/apps/app_queue.c?view=diff&rev=342562&r1=342561&r2=342562
==============================================================================
--- team/irroot/distrotech-customers-1.8/apps/app_queue.c (original)
+++ team/irroot/distrotech-customers-1.8/apps/app_queue.c Thu Oct 27 10:10:01 2011
@@ -846,6 +846,19 @@
<description>
</description>
</manager>
+ <manager name="QueueIgnoreBusy" language="en_US">
+ <synopsis>
+ Set interface to allow multiple calls
+ </synopsis>
+ <syntax>
+ <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
+ <parameter name="Interface" required="true" />
+ <parameter name="IgnoreBusy" required="true" />
+ <parameter name="Queue" required="true" />
+ </syntax>
+ <description>
+ </description>
+ </manager>
***/
enum {
@@ -895,6 +908,9 @@
{ QUEUE_AUTOPAUSE_ON, "yes" },
{ QUEUE_AUTOPAUSE_ALL,"all" },
};
+
+
+static struct ast_taskprocessor *devicestate_tps;
#define DEFAULT_RETRY 5
#define DEFAULT_TIMEOUT 15
@@ -1012,6 +1028,7 @@
unsigned int pending_connected_update:1;
/*! TRUE if caller id is not available for connected line */
unsigned int dial_callerid_absent:1;
+ unsigned int reserved:1;
struct ast_aoc_decoded *aoc_s_rate_list;
};
@@ -1057,13 +1074,12 @@
char membername[80]; /*!< Member name to use in queue logs */
int penalty; /*!< Are we a last resort? */
int calls; /*!< Number of calls serviced by this member */
- int dynamic; /*!< Are we dynamically added? */
time_t lastcall; /*!< When last successful call was hungup */
struct call_queue *lastqueue; /*!< Last queue we received a call */
unsigned int realtime:1; /*!< Is this member realtime? */
unsigned int paused:1; /*!< Are we paused (not accepting calls)? */
unsigned int dead:1; /*!< Used to detect members deleted in realtime */
- unsigned int delme:1; /*!< Flag to delete entry on reload */
+ unsigned int dynamic:1; /*!< Are we dynamically added? */
unsigned int ignorebusy:1; /*!< Flag to ignore member if the status is not available */
char rt_uniqueid[80]; /*!< Unique id of realtime member entry */
struct mem_state *device; /*!< Device information */
@@ -1211,7 +1227,9 @@
static struct ao2_container *queues;
static struct ao2_container *devices;
-static void update_realtime_members(struct call_queue *q);
+static void dump_queue_members(struct call_queue *pm_queue);
+static void pm_load_member_config(struct call_queue *q);
+static char *queue_refshow(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
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);
@@ -1284,15 +1302,11 @@
static int queue_cmp_cb(void *obj, void *arg, int flags)
{
- struct call_queue *q = obj;
-
- if (flags & OBJ_POINTER) {
- struct call_queue *q2 = arg;
- return !strcasecmp(q->name, q2->name) ? CMP_MATCH | CMP_STOP : 0;
- } else {
- char *name = arg;
- return !strcasecmp(q->name, name) ? CMP_MATCH | CMP_STOP : 0;
- }
+ const struct call_queue *q = obj;
+ const struct call_queue *q2 = arg;
+ const char *name = (flags & OBJ_POINTER) ? q2->name : arg;
+
+ return !strcasecmp(q->name, name) ? CMP_MATCH | CMP_STOP : 0;
}
static int device_hash_cb(const void *obj, const int flags)
@@ -1304,14 +1318,11 @@
static int device_cmp_cb(void *obj, void *arg, int flags)
{
- struct mem_state *d = obj;
- if (flags & OBJ_POINTER) {
- struct mem_state *d2 = arg;
- return !strcasecmp(d->state_interface, d2->state_interface) ? CMP_MATCH | CMP_STOP : 0;
- } else {
- char *iface = arg;
- return !strcasecmp(d->state_interface, iface) ? CMP_MATCH | CMP_STOP : 0;
- }
+ 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;
}
#ifdef REF_DEBUG_ONLY_QUEUES
@@ -1470,12 +1481,13 @@
/*! \brief send a QueueMemberStatus manager_event
*/
-static void update_status(struct mem_state *s)
+static int update_status(void *data)
{
struct ao2_iterator qiter;
struct ao2_iterator miter;
struct call_queue *q;
struct member *m;
+ struct mem_state *s = data;
qiter = ao2_iterator_init(queues, 0);
while ((q = ao2_iterator_next(&qiter))) {
@@ -1488,11 +1500,12 @@
miter = ao2_iterator_init(q->members, 0);
while((m = ao2_iterator_next(&miter))) {
ao2_lock(m->device);
- if (strcmp(m->device->state_interface, s->state_interface)) {
+ if (strcasecmp(m->device->state_interface, s->state_interface)) {
ao2_unlock(m->device);
ao2_ref(m, -1);
continue;
}
+ ao2_unlock(m->device);
manager_event(EVENT_FLAG_AGENT, "QueueMemberStatus",
"Queue: %s\r\n"
"Location: %s\r\n"
@@ -1507,14 +1520,16 @@
q->name, m->interface, m->membername, s->state_interface, m->dynamic ? "dynamic" : m->realtime ? "realtime" : "static",
m->penalty, m->calls, (int)m->lastcall, s->status, m->paused
);
- ao2_unlock(m->device);
ao2_ref(m, -1);
}
ao2_iterator_destroy(&miter);
ao2_unlock(q);
- queue_t_unref(q, "Done with iterator");
+ ao2_ref(q, -1);
}
ao2_iterator_destroy(&qiter);
+ ao2_ref(s, -1);
+
+ return 0;
}
/*! \brief callback used when device state changes*/
@@ -1522,7 +1537,7 @@
{
enum ast_device_state state;
const char *device;
- struct mem_state *s;
+ struct mem_state *s, *tmp;
state = ast_event_get_ie_uint(event, AST_EVENT_IE_STATE);
device = ast_event_get_ie_str(event, AST_EVENT_IE_DEVICE);
@@ -1534,9 +1549,16 @@
if ((s = ao2_find(devices, (char *)device, 0))) {
ao2_lock(s);
- s->status = state;
+ if (s->status != state) {
+ s->status = state;
+ if ((tmp = ao2_alloc(sizeof(*tmp), NULL))) {
+ memcpy(tmp, s, sizeof(*tmp));
+ if (ast_taskprocessor_push(devicestate_tps, update_status, tmp)) {
+ ao2_ref(tmp, -1);
+ }
+ }
+ }
ao2_unlock(s);
- update_status(s);
ao2_ref(s, -1);
ast_debug(1, "Device '%s' changed to state '%d' (%s)\n", device, state, ast_devstate2str(state));
} else {
@@ -1578,7 +1600,7 @@
static int extension_state_cb(char *context, char *exten, enum ast_extension_states state, void *data)
{
- struct mem_state *s;
+ struct mem_state *s, *tmp;
char *device;
int status = extensionstate2devicestate(state);
@@ -1589,9 +1611,16 @@
if ((s = ao2_find(devices, device, 0))) {
ao2_lock(s);
- s->status = status;
+ if (s->status != status) {
+ s->status = status;
+ if ((tmp = ao2_alloc(sizeof(*tmp), NULL))) {
+ memcpy(tmp, s, sizeof(*tmp));
+ if (ast_taskprocessor_push(devicestate_tps, update_status, tmp)) {
+ ao2_ref(tmp, -1);
+ }
+ }
+ }
ao2_unlock(s);
- update_status(s);
ao2_ref(s, -1);
ast_debug(1, "Extension '%s@%s' changed to state '%d' (%s)\n", exten, context, status, ast_devstate2str(status));
} else {
@@ -1619,33 +1648,30 @@
static struct mem_state *create_member_state(const char *state_interface) {
struct mem_state *state;
- char *dev_int;
- char *device;
char *exten;
char *context;
-
- dev_int = ast_strdupa(state_interface);
+ char *device;
+
/* ref will be held for each shared member and one ref for container */
- if ((state = ao2_find(devices, dev_int, 0))) {
+ if ((state = ao2_find(devices, (char *)state_interface, 0))) {
return state;
} else if (!(state = ao2_alloc(sizeof(*state), NULL))) {
return NULL;
}
state->reserved = 0;
- if (!strncmp(dev_int, "hint:", 5)) {
- context = ast_strdupa(dev_int);
+ if (!strncasecmp(state_interface, "hint:", 5)) {
+ context = ast_strdupa(state_interface);
exten = strsep(&context, "@") + 5;
-
if (context) {
- ast_copy_string(state->state_interface, dev_int, sizeof(state->state_interface));
+ ast_copy_string(state->state_interface, state_interface, sizeof(state->state_interface));
} else {
- asprintf(&device, "%s at default", dev_int);
+ asprintf(&device, "%s at default", state_interface);
ast_copy_string(state->state_interface, device, sizeof(state->state_interface));
}
- state->status = extensionstate2devicestate(ast_extension_state(NULL, context, exten));
+ state->status = extensionstate2devicestate(ast_extension_state(NULL, S_OR(context, "default"), exten));
} else {
- ast_copy_string(state->state_interface, dev_int, sizeof(state->state_interface));
+ ast_copy_string(state->state_interface, state_interface, sizeof(state->state_interface));
state->status = ast_device_state(state->state_interface);
}
ao2_link(devices, state);
@@ -1653,8 +1679,37 @@
return state;
}
+/*! \brief Set current state of member if needed*/
+static void set_queue_member_status(struct member *m)
+{
+ int status;
+ struct mem_state *s = m->device;
+ struct mem_state *tmp;
+
+ ao2_lock(s);
+ if (!strncasecmp(s->state_interface, "hint:", 5)) {
+ char *context = ast_strdupa(s->state_interface);
+ char *exten = strsep(&context, "@") + 5;
+ status = extensionstate2devicestate(ast_extension_state(NULL, S_OR(context, "default"), exten));
+ } else {
+ status = ast_device_state(s->state_interface);
+ }
+
+ if (s->status != status) {
+ s->status = status;
+ if ((tmp = ao2_alloc(sizeof(*tmp), NULL))) {
+ memcpy(tmp, s, sizeof(*tmp));
+ if (ast_taskprocessor_push(devicestate_tps, update_status, tmp)) {
+ ao2_ref(tmp, -1);
+ }
+ }
+ }
+ ao2_unlock(s);
+}
+
/*! \brief allocate space for new queue member and set fields based on parameters passed */
-static struct member *create_queue_member(const char *interface, const char *membername, int penalty, int paused, const char *state_interface)
+static struct member *create_queue_member(const char *interface, const char *membername, int penalty,
+ int paused, const char *state_interface, int ignorebusy)
{
struct member *cur;
const char* state_int;
@@ -1672,6 +1727,7 @@
cur->penalty = penalty;
cur->paused = paused;
+ cur->ignorebusy = ignorebusy;
ast_copy_string(cur->interface, interface, sizeof(cur->interface));
if (!ast_strlen_zero(membername)) {
ast_copy_string(cur->membername, membername, sizeof(cur->membername));
@@ -1700,38 +1756,35 @@
static int member_hash_fn(const void *obj, const int flags)
{
const struct member *mem = obj;
- const char *chname = strchr(mem->interface, '/');
+ const char *interface = mem->interface;
+ const char *chname = strchr(interface, '/');
int ret = 0, i;
- if (!chname)
- chname = mem->interface;
- for (i = 0; i < 5 && chname[i]; i++)
+
+ if (!chname) {
+ chname = interface;
+ }
+ for (i = 0; i < 5 && chname[i]; i++) {
ret += compress_char(chname[i]) << (i * 6);
+ }
return ret;
}
static int member_cmp_fn(void *obj1, void *obj2, int flags)
{
- struct member *mem1 = obj1;
-
- if (flags & OBJ_POINTER) {
- struct member *mem2 = obj2;
- return strcasecmp(mem1->interface, mem2->interface) ? 0 : CMP_MATCH | CMP_STOP;
- } else {
- char *arg = obj2;
- return strcasecmp(mem1->interface, arg) ? 0 : CMP_MATCH | CMP_STOP;
- }
+ 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;
}
static int member_cmp_uniqueid_fn(void *obj1, void *arg, int flags)
{
- struct member *mem1 = obj1;
- if (flags & OBJ_POINTER) {
- struct member *mem2 = arg;
- return strcasecmp(mem1->rt_uniqueid, mem2->rt_uniqueid) ? 0 : CMP_MATCH | CMP_STOP;
- } else {
- char *uniqueid = arg;
- return strcasecmp(mem1->rt_uniqueid, uniqueid) ? 0 : CMP_MATCH | CMP_STOP;
- }
+ const struct member *mem1 = obj1;
+ const struct member *mem2 = arg;
+ const char *uniqueid = (flags & OBJ_POINTER) ? mem2->rt_uniqueid : arg;
+
+ return strcasecmp(mem1->rt_uniqueid, uniqueid) ? 0 : CMP_MATCH | CMP_STOP;
}
/*!
@@ -2169,65 +2222,90 @@
static void rt_handle_member_record(struct call_queue *q, char *interface, struct ast_config *member_config)
{
struct member *m;
+ struct ast_variable *v;
int penalty = 0;
int paused = 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);
- const char *penalty_str = ast_variable_retrieve(member_config, interface, "penalty");
- const char *paused_str = ast_variable_retrieve(member_config, interface, "paused");
+ const char *rt_uniqueid = NULL;
+ const char *membername = NULL;
+ const char *state_interface = NULL;
+
+ for (v = ast_variable_browse(member_config, interface); v; v = v->next) {
+ if (!strcasecmp(v->name, "uniqueid")) {
+ rt_uniqueid = ast_strdupa(v->value);
+ } else if (!strcasecmp(v->name, "membername")) {
+ membername = ast_strdupa(v->value);
+ } else if (!strcasecmp(v->name, "state_interface")) {
+ state_interface = ast_strdupa(v->value);
+ } else if (!strcasecmp(v->name, "penalty")) {
+ if ((sscanf(v->value, "%30d", &penalty) != 1) || (!negative_penalty_invalid && penalty < 0)) {
+ penalty = 0;
+ }
+ } else if (!strcasecmp(v->name, "paused")) {
+ paused = abs(ast_true(v->value));
+ } else if (!strcasecmp(v->name, "ignorebusy")) {
+ ignorebusy = abs(ast_true(v->value));
+ }
+ }
if (ast_strlen_zero(rt_uniqueid)) {
ast_log(LOG_WARNING, "Realtime field uniqueid is empty for member %s\n", S_OR(membername, "NULL"));
return;
}
- if (penalty_str) {
- penalty = atoi(penalty_str);
- if ((penalty < 0) && negative_penalty_invalid) {
- return;
- } else if (penalty < 0) {
- penalty = 0;
- }
- }
-
- if (paused_str) {
- paused = atoi(paused_str);
- if (paused < 0) {
- paused = 0;
- }
- }
-
- if ((config_val = ast_variable_retrieve(member_config, interface, "ignorebusy"))) {
- ignorebusy = ast_true(config_val);
- } else {
- ignorebusy = 1;
- }
-
- /* Find member by realtime uniqueid and update */
- if ((m = ao2_callback(q->members, 0, member_cmp_uniqueid_fn, (char *)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 (strcmp(state_interface, m->device->state_interface)) {
- /* we may be the last ref [outside container] unlink if so */
+ /* negative_penalty_invalid is set and i have a invalid penalty ignoring this member */
+ if (penalty < 0) {
+ return;
+ }
+
+ if (ast_strlen_zero(state_interface)) {
+ state_interface = ast_strdupa(interface);
+ }
+
+ if (ast_strlen_zero(membername)) {
+ membername = ast_strdupa(interface);
+ }
+
+ /*existing member could be static or dynamic now realtime*/
+ if (!(m = ao2_callback(q->members, 0, member_cmp_uniqueid_fn, (char *)rt_uniqueid))) {
+ if ((m = interface_exists(q, interface))) {
+ m->realtime = 1;
+ m->dynamic = 0;
+ ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid));
+ }
+ }
+
+ if (m) {
+ m->dead = 0;
+ if (!strcasecmp(m->interface, interface)) {
+ ast_copy_string(m->interface, interface, sizeof(m->interface));
+ }
+ if (!strcasecmp(m->membername, membername)) {
+ ast_copy_string(m->membername, membername, sizeof(m->membername));
+ }
+ m->penalty = penalty;
+ m->ignorebusy = ignorebusy;
+ m->paused = paused;
+ /* create or update the device state entry */
+ ao2_lock(m->device);
+ if (strcasecmp(state_interface, m->device->state_interface)) {
+ ao2_unlock(m->device);
if (ao2_ref(m->device, -1) == 2) {
ao2_unlink(devices, m->device);
}
- m->device = create_member_state(state_interface);
- }
- m->penalty = penalty;
- m->ignorebusy = ignorebusy;
+ /* if i cant allocate the device state mark member dead */
+ if (!(m->device = create_member_state(state_interface))) {
+ ast_log(AST_LOG_ERROR, "Error creating the member state device %s for %s in queue %s\n",
+ state_interface, interface , q->name);
+ m->dead = 1;
+ }
+ } else {
+ ao2_unlock(m->device);
+ }
ao2_ref(m, -1);
- } else if ((m = create_queue_member(interface, membername, penalty, paused, state_interface))) {
+ } else if ((m = create_queue_member(interface, membername, penalty, paused, state_interface, ignorebusy))) {
m->dead = 0;
m->realtime = 1;
- m->ignorebusy = ignorebusy;
ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid));
if (!log_membername_as_agent) {
ast_queue_log(q->name, "REALTIME", m->interface, "ADDMEMBER", "%s", "");
@@ -2236,6 +2314,8 @@
}
ao2_link(q->members, m);
ao2_ref(m, -1);
+ } else {
+ ast_log(AST_LOG_ERROR, "Error creating the member %s in queue %s\n", interface , q->name);
}
}
@@ -2282,6 +2362,62 @@
ast_string_field_set(q, name, queuename);
}
return q;
+}
+
+
+static void rt_load_member_config(struct call_queue *q)
+{
+ struct ast_config *member_config;
+ struct member *m;
+ struct ao2_iterator mem_iter;
+ char *interface = NULL;
+
+ if (!ast_check_realtime("queue_members") ||
+ (!(member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", q->name, SENTINEL)))) {
+ /*This queue doesn't have realtime members*/
+ ast_debug(3, "Queue %s has no realtime members defined. No need for update\n", q->name);
+ return;
+ }
+
+ /* Temporarily set realtime and persistent members dead so we can detect deleted ones. */
+ mem_iter = ao2_iterator_init(q->members, 0);
+ while ((m = ao2_iterator_next(&mem_iter))) {
+ if (m->realtime) {
+ m->dead = 1;
+ } else if (queue_persistent_members && m->dynamic) {
+ m->dead = 1;
+ }
+ ao2_ref(m, -1);
+ }
+ ao2_iterator_destroy(&mem_iter);
+
+ while ((interface = ast_category_browse(member_config, interface))) {
+ rt_handle_member_record(q, interface, member_config);
+ }
+
+ /* mark persistent alive */
+ if (queue_persistent_members) {
+ pm_load_member_config(q);
+ }
+
+ /* Delete all realtime/dynamic members that have been deleted in DB. */
+ mem_iter = ao2_iterator_init(q->members, 0);
+ while ((m = ao2_iterator_next(&mem_iter))) {
+ if (m->dead) {
+ if (m->realtime && (ast_strlen_zero(m->membername) || !log_membername_as_agent)) {
+ ast_queue_log(q->name, "REALTIME", m->interface, "REMOVEMEMBER", "%s", "");
+ } else if (m->realtime) {
+ ast_queue_log(q->name, "REALTIME", m->membername, "REMOVEMEMBER", "%s", "");
+ }
+ ao2_unlink(q->members, m);
+ }
+ ao2_ref(m, -1);
+ }
+ ao2_iterator_destroy(&mem_iter);
+
+ if (queue_persistent_members) {
+ dump_queue_members(q);
+ }
}
/*!
@@ -2294,16 +2430,14 @@
* \retval NULL if it doesn't exist.
* \note Should be called with the "queues" container locked.
*/
-static struct call_queue *find_queue_by_name_rt(const char *queuename, struct ast_variable *queue_vars, struct ast_config *member_config)
+static struct call_queue *find_queue_by_name_rt(const char *queuename)
{
struct ast_variable *v;
struct call_queue *q;
- struct member *m;
- struct ao2_iterator mem_iter;
- char *interface = NULL;
const char *tmp_name;
char *tmp;
char tmpbuf[64]; /* Must be longer than the longest queue param name. */
+ struct ast_variable *queue_vars;
/* Static queues override realtime. */
if ((q = ao2_t_find(queues, (char *)queuename, 0, "Check if static queue exists"))) {
@@ -2319,12 +2453,9 @@
return q;
}
}
- } else if (!member_config) {
- /* Not found in the list, and it's not realtime ... */
- return NULL;
}
/* Check if queue is defined in realtime. */
- if (!queue_vars) {
+ 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
@@ -2345,6 +2476,7 @@
if (!q) {
struct ast_variable *tmpvar = NULL;
if (!(q = alloc_queue(queuename))) {
+ ast_variables_destroy(queue_vars);
return NULL;
}
ao2_lock(q);
@@ -2389,36 +2521,9 @@
* should set the realtime column to NULL, not blank. */
queue_set_param(q, tmp_name, v->value, -1, 0);
}
-
- /* Temporarily set realtime members dead so we can detect deleted ones. */
- mem_iter = ao2_iterator_init(q->members, 0);
- while ((m = ao2_iterator_next(&mem_iter))) {
- if (m->realtime) {
- m->dead = 1;
- }
- ao2_ref(m, -1);
- }
- ao2_iterator_destroy(&mem_iter);
-
- while ((interface = ast_category_browse(member_config, interface))) {
- rt_handle_member_record(q, interface, member_config);
- }
-
- /* Delete all realtime members that have been deleted in DB. */
- mem_iter = ao2_iterator_init(q->members, 0);
- while ((m = ao2_iterator_next(&mem_iter))) {
- if (m->dead) {
- if (ast_strlen_zero(m->membername) || !log_membername_as_agent) {
- ast_queue_log(q->name, "REALTIME", m->interface, "REMOVEMEMBER", "%s", "");
- } else {
- ast_queue_log(q->name, "REALTIME", m->membername, "REMOVEMEMBER", "%s", "");
- }
- ao2_unlink(q->members, m);
- }
- ao2_ref(m, -1);
- }
- ao2_iterator_destroy(&mem_iter);
-
+ ast_variables_destroy(queue_vars);
+
+ rt_load_member_config(q);
ao2_unlock(q);
return q;
@@ -2427,8 +2532,6 @@
/*! \note Returns a reference to the loaded realtime queue. */
static struct call_queue *load_realtime_queue(const char *queuename)
{
- struct ast_variable *queue_vars;
- struct ast_config *member_config = NULL;
struct call_queue *q;
int prev_weight = 0;
@@ -2445,26 +2548,14 @@
Thus we might see an empty member list when a queue is
deleted. In practise, this is unlikely to cause a problem. */
- queue_vars = ast_load_realtime("queues", "name", queuename, SENTINEL);
- if (queue_vars) {
- member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", queuename, SENTINEL);
- if (!member_config) {
- ast_log(LOG_ERROR, "no queue_members defined in your config (extconfig.conf).\n");
- ast_variables_destroy(queue_vars);
- return NULL;
- }
- }
if (q) {
prev_weight = q->weight ? 1 : 0;
queue_t_unref(q, "Need to find realtime queue");
}
- q = find_queue_by_name_rt(queuename, queue_vars, member_config);
- ast_config_destroy(member_config);
- ast_variables_destroy(queue_vars);
-
- /* update the use_weight value if the queue's has gained or lost a weight */
- if (q) {
+ if ((q = find_queue_by_name_rt(queuename))) {
+ /* 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);
}
@@ -2472,9 +2563,10 @@
ast_atomic_fetchadd_int(&use_weight, +1);
}
}
- /* Other cases will end up with the proper value for use_weight */
} else {
- update_realtime_members(q);
+ ao2_lock(q);
+ rt_load_member_config(q);
+ ao2_unlock(q);
}
return q;
}
@@ -2483,60 +2575,34 @@
{
int ret = -1;
- if (ast_strlen_zero(mem->rt_uniqueid))
- return ret;
-
- if ((ast_update_realtime("queue_members", "uniqueid", mem->rt_uniqueid, field, value, SENTINEL)) > 0)
+ if (ast_strlen_zero(mem->rt_uniqueid)) {
+ return ret;
+ }
+
+ if ((ast_update_realtime("queue_members", "uniqueid", mem->rt_uniqueid, field, value, SENTINEL)) > 0) {
ret = 0;
+ }
return ret;
}
-
-static void update_realtime_members(struct call_queue *q)
-{
- struct ast_config *member_config = NULL;
- struct member *m;
- char *interface = NULL;
- struct ao2_iterator mem_iter;
-
- if (!(member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", q->name , SENTINEL))) {
- /*This queue doesn't have realtime members*/
- ast_debug(3, "Queue %s has no realtime members defined. No need for update\n", q->name);
- return;
- }
-
- ao2_lock(q);
-
- /* Temporarily set realtime members dead so we can detect deleted ones.*/
- mem_iter = ao2_iterator_init(q->members, 0);
- while ((m = ao2_iterator_next(&mem_iter))) {
- if (m->realtime)
- m->dead = 1;
- ao2_ref(m, -1);
- }
- ao2_iterator_destroy(&mem_iter);
-
- while ((interface = ast_category_browse(member_config, interface))) {
- rt_handle_member_record(q, interface, member_config);
- }
-
- /* Delete all realtime members that have been deleted in DB. */
- mem_iter = ao2_iterator_init(q->members, 0);
- while ((m = ao2_iterator_next(&mem_iter))) {
- if (m->dead) {
- if (ast_strlen_zero(m->membername) || !log_membername_as_agent) {
- ast_queue_log(q->name, "REALTIME", m->interface, "REMOVEMEMBER", "%s", "");
- } else {
- ast_queue_log(q->name, "REALTIME", m->membername, "REMOVEMEMBER", "%s", "");
- }
- ao2_unlink(q->members, m);
- }
- ao2_ref(m, -1);
- }
- ao2_iterator_destroy(&mem_iter);
- ao2_unlock(q);
- ast_config_destroy(member_config);
+static void load_all_realtime_queues(void) {
+ char *queuename;
+ struct ast_config *cfg;
+ struct call_queue *queue;
+
+ /* load realtime queues. */
+ if (ast_check_realtime("queues") &&
+ (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))) {
+ queue_unref(queue);
+ }
+ }
+ ast_config_destroy(cfg);
+ }
}
static int join_queue(char *queuename, struct queue_ent *qe, enum queue_result *reason, int position)
@@ -2922,10 +2988,12 @@
*/
static void callattempt_free(struct callattempt *doomed)
{
- ao2_lock(doomed->member->device);
- doomed->member->device->reserved--;
- ao2_unlock(doomed->member->device);
if (doomed->member) {
+ if (doomed->reserved) {
+ ao2_lock(doomed->member->device);
+ doomed->member->device->reserved--;
+ ao2_unlock(doomed->member->device);
+ }
ao2_ref(doomed->member, -1);
}
ast_party_connected_line_free(&doomed->connected);
@@ -3141,20 +3209,6 @@
return 0;
}
- if (!qe->parent->ringinuse || !tmp->member->ignorebusy) {
- ao2_lock(s);
- if ((s->reserved > 1) || ((s->status != AST_DEVICE_NOT_INUSE) && (s->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;
- ao2_unlock(s);
- return 0;
- }
- ao2_unlock(s);
- }
-
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) {
@@ -3165,15 +3219,42 @@
return 0;
}
+ if (!qe->parent->ringinuse || !tmp->member->ignorebusy) {
+ ao2_lock(s);
+ if ((s->reserved) || ((s->status != AST_DEVICE_NOT_INUSE) && (s->status != AST_DEVICE_UNKNOWN))) {
+ ao2_unlock(s);
+ 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;
+ (*busies)++;
+ return 0;
+ }
+ ao2_unlock(s);
+ }
+
+ /* mark device and call entry reserved */
+ if (!tmp->reserved) {
+ ao2_lock(s);
+ s->reserved++;
+ ao2_unlock(s);
+ tmp->reserved = 1;
+ }
+
ast_copy_string(tech, tmp->interface, sizeof(tech));
- if ((location = strchr(tech, '/')))
+ if ((location = strchr(tech, '/'))) {
*location++ = '\0';
- else
+ } else {
location = "";
+ }
/* 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 */
+ /* something is wrong here check status*/
+ set_queue_member_status(tmp->member);
+
if (qe->chan->cdr) {
ast_cdr_busy(qe->chan->cdr);
}
@@ -3256,6 +3337,9 @@
/* Place the call, but don't wait on the answer */
if ((res = ast_call(tmp->chan, location, 0))) {
+ /* something is wrong here check status*/
+ set_queue_member_status(tmp->member);
+
/* Again, keep going even if there's an error */
ast_debug(1, "ast call on peer returned %d\n", res);
ast_verb(3, "Couldn't call %s\n", tmp->interface);
@@ -3293,7 +3377,6 @@
}
ast_channel_unlock(tmp->chan);
ast_channel_unlock(qe->chan);
-
return 1;
}
@@ -4657,12 +4740,7 @@
ast_channel_unlock(qe->chan);
tmp->stillgoing = -1;
- tmp->member = cur;/* Place the reference for cur into callattempt. */
- ao2_lock(tmp->member->device);
- tmp->member->device->reserved++;
- ao2_unlock(tmp->member->device);
- tmp->lastcall = cur->lastcall;
- tmp->lastqueue = cur->lastqueue;
+ tmp->reserved = 0;
ast_copy_string(tmp->interface, cur->interface, sizeof(tmp->interface));
/* Special case: If we ring everyone, go ahead and ring them, otherwise
just calculate their metric for the appropriate strategy */
@@ -4670,12 +4748,18 @@
/* Put them in the list of outgoing thingies... We're ready now.
XXX If we're forcibly removed, these outgoing calls won't get
hung up XXX */
+ tmp->member = cur;/* Place the reference for cur into callattempt. */
+ tmp->lastcall = cur->lastcall;
+ tmp->lastqueue = cur->lastqueue;
+
tmp->q_next = outgoing;
- outgoing = tmp;
+ outgoing = tmp;
+
/* If this line is up, don't try anybody else */
if (outgoing->chan && (outgoing->chan->_state == AST_STATE_UP))
break;
} else {
+ ao2_ref(cur, -1);
callattempt_free(tmp);
}
}
@@ -5256,7 +5340,7 @@
/*! \brief Dump all members in a specific queue to the database
*
- * <pm_family>/<queuename> = <interface>;<penalty>;<paused>;<state_interface>[|...]
+ * <pm_family>/<queuename> = <interface>;<penalty>;<paused>;<state_interface>;<ignorebusy>[|...]
*/
static void dump_queue_members(struct call_queue *pm_queue)
{
@@ -5273,14 +5357,17 @@
mem_iter = ao2_iterator_init(pm_queue->members, 0);
while ((cur_member = ao2_iterator_next(&mem_iter))) {
- if (!cur_member->dynamic) {
+ if (!cur_member->dynamic || cur_member->dead) {
ao2_ref(cur_member, -1);
continue;
}
- res = snprintf(value + value_len, sizeof(value) - value_len, "%s%s;%d;%d;%s;%s",
- value_len ? "|" : "", cur_member->interface, cur_member->penalty, cur_member->paused, cur_member->membername, cur_member->device->state_interface);
-
+ ao2_lock(cur_member->device);
+ res = snprintf(value + value_len, sizeof(value) - value_len, "%s%s;%d;%d;%s;%s;%d",
+ value_len ? "|" : "", cur_member->interface, cur_member->penalty, cur_member->paused,
+ cur_member->membername, cur_member->device->state_interface, cur_member->ignorebusy);
+
+ ao2_unlock(cur_member->device);
ao2_ref(cur_member, -1);
if (res != strlen(value + value_len)) {
@@ -5347,28 +5434,22 @@
return res;
}
-/*! \brief Add member to queue
+/*! \brief Add member to queue Must be called with q locked and ref held
* \retval RES_NOT_DYNAMIC when they aren't a RT member
* \retval RES_NOSUCHQUEUE queue does not exist
* \retval RES_OKAY added member from queue
* \retval RES_EXISTS queue exists but no members
* \retval RES_OUT_OF_MEMORY queue exists but not enough memory to create member
*/
-static int add_to_queue(const char *queuename, const char *interface, const char *membername, int penalty, int paused, int dump, const char *state_interface)
-{
- struct call_queue *q;
+static int add_to_queue(struct call_queue *q, const char *interface, const char *membername, int penalty, int paused, int dump,
+ const char *state_interface, int ignorebusy)
+{
struct member *new_member, *old_member;
int res = RES_NOSUCHQUEUE;
- /*! \note Ensure the appropriate realtime queue is loaded. Note that this
- * short-circuits if the queue is already in memory. */
- if (!(q = load_realtime_queue(queuename))) {
- return res;
- }
-
- ao2_lock(q);
- if ((old_member = interface_exists(q, interface)) == NULL) {
- if ((new_member = create_queue_member(interface, membername, penalty, paused, state_interface))) {
+ if (!(old_member = interface_exists(q, interface))) {
+ if ((new_member = create_queue_member(interface, membername, penalty, paused,
+ state_interface, ignorebusy))) {
new_member->dynamic = 1;
ao2_link(q->members, new_member);
ao2_lock(new_member->device);
@@ -5382,11 +5463,12 @@
"CallsTaken: %d\r\n"
"LastCall: %d\r\n"
"Status: %d\r\n"
- "Paused: %d\r\n",
+ "Paused: %d\r\n"
+ "IgnoreBusy: %d\r\n",
q->name, new_member->interface, new_member->membername, state_interface,
"dynamic",
new_member->penalty, new_member->calls, (int) new_member->lastcall,
- new_member->device->status, new_member->paused);
+ new_member->device->status, new_member->paused, new_member->ignorebusy);
ao2_unlock(new_member->device);
ao2_ref(new_member, -1);
@@ -5404,11 +5486,71 @@
ao2_ref(old_member, -1);
res = RES_EXISTS;
}
- ao2_unlock(q);
- queue_t_unref(q, "Expiring temporary reference");
return res;
}
+
+static int do_set_member_penalty_paused(struct call_queue *q, struct member *mem, int pause, int value, const char *reason)
+{
+ if (pause) {
+ if (mem->paused == value) {
+ ast_debug(1, "%spausing already-%spaused member %s queue %s\n", (mem->paused) ? "" : "un",
+ (mem->paused) ? "" : "un", mem->membername, q->name);
+ return 0;
+ }
+
+ mem->paused = (value) ? 1 : 0;
+ if ((mem->realtime) && (update_realtime_member_field(mem, q->name, "paused", (mem->paused) ? "1" : "0"))) {
+ ast_log(LOG_WARNING, "Failed %spausing realtime member %s queue %s\n",
+ (mem->paused) ? "" : "un", mem->membername, q->name);
+ return -1;
+ }
+
+ ast_queue_log(q->name, "NONE", mem->membername, (mem->paused) ? "PAUSE" : "UNPAUSE", "%s", S_OR(reason, ""));
+
+ if (!ast_strlen_zero(reason)) {
+ manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
+ "Queue: %s\r\n"
+ "Location: %s\r\n"
+ "MemberName: %s\r\n"
+ "Paused: %d\r\n"
+ "Reason: %s\r\n",
+ q->name, mem->interface, mem->membername, mem->paused, reason);
+ } else {
+ manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
+ "Queue: %s\r\n"
+ "Location: %s\r\n"
+ "MemberName: %s\r\n"
+ "Paused: %d\r\n",
+ q->name, mem->interface, mem->membername, mem->paused);
+ }
+ } else {
+ mem->penalty = value;
+ if (mem->realtime) {
+ char *rtpenalty;
+ asprintf(&rtpenalty,"%i", mem->penalty);
+ if (update_realtime_member_field(mem, q->name, "penalty", rtpenalty)) {
+ ast_log(LOG_WARNING, "Failed setting penalty %d on member %s queue %s\n", mem->penalty,
+ mem->membername, q->name);
+ return -1;
+ }
+ }
+
+ ast_queue_log(q->name, "NONE", mem->interface, "PENALTY", "%d", mem->penalty);
+ manager_event(EVENT_FLAG_AGENT, "QueueMemberPenalty",
+ "Queue: %s\r\n"
+ "Location: %s\r\n"
+ "Penalty: %d\r\n",
+ q->name, mem->interface, mem->penalty);
+ }
+
+ if (queue_persistent_members) {
+ dump_queue_members(q);
+ }
+
+ return 0;
+}
+
static int set_member_paused(const char *queuename, const char *interface, const char *reason, int paused)
{
@@ -5417,60 +5559,42 @@
struct member *mem;
struct ao2_iterator queue_iter;
+ if (!ast_strlen_zero(queuename)) {
+ if ((q = load_realtime_queue(queuename))) {
+ ao2_lock(q);
+ if ((mem = interface_exists(q, interface))) {
+ found = do_set_member_penalty_paused(q, mem, 1, paused, reason);
+ ao2_ref(mem, -1);
+ ao2_unlock(q);
+ ao2_ref(q, -1);
+ return (found) ? RESULT_FAILURE : RESULT_SUCCESS;
+ } else {
+ ao2_unlock(q);
+ ao2_ref(q, -1);
+ return RESULT_FAILURE;
+ }
+ } else {
+ ast_log (LOG_ERROR, "Invalid queuename\n");
+ return RESULT_FAILURE;
+ }
+ }
+
+ load_all_realtime_queues();
+
/* Special event for when all queues are paused - individual events still generated */
/* XXX In all other cases, we use the membername, but since this affects all queues, we cannot */
- if (ast_strlen_zero(queuename))
- ast_queue_log("NONE", "NONE", interface, (paused ? "PAUSEALL" : "UNPAUSEALL"), "%s", "");
+ ast_queue_log("NONE", "NONE", interface, (paused ? "PAUSEALL" : "UNPAUSEALL"), "%s", "");
queue_iter = ao2_iterator_init(queues, 0);
while ((q = ao2_t_iterator_next(&queue_iter, "Iterate over queues"))) {
ao2_lock(q);
- if ((ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) &&
- (mem = interface_exists(q, interface))) {
- if (mem->paused == paused) {
- ast_debug(1, "%spausing already-%spaused queue member %s:%s\n", (paused ? "" : "un"), (paused ? "" : "un"), q->name, interface);
- }
-
- if ((mem->realtime) && (update_realtime_member_field(mem, q->name, "paused", paused ? "1" : "0"))) {
- ast_log(LOG_WARNING, "Failed %spausing realtime queue member %s:%s\n", (paused ? "" : "un"), q->name, interface);
- ao2_ref(mem, -1);
- ao2_unlock(q);
- queue_t_unref(q, "Done with iterator");
- continue;
- }
- found++;
- mem->paused = paused;
-
- if (queue_persistent_members)
- dump_queue_members(q);
-
- ast_queue_log(q->name, "NONE", mem->membername, (paused ? "PAUSE" : "UNPAUSE"), "%s", S_OR(reason, ""));
-
- if (!ast_strlen_zero(reason)) {
- manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
- "Queue: %s\r\n"
- "Location: %s\r\n"
- "MemberName: %s\r\n"
- "Paused: %d\r\n"
- "Reason: %s\r\n",
- q->name, mem->interface, mem->membername, paused, reason);
- } else {
- manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
- "Queue: %s\r\n"
- "Location: %s\r\n"
- "MemberName: %s\r\n"
- "Paused: %d\r\n",
- q->name, mem->interface, mem->membername, paused);
+ mem = interface_exists(q, interface);
+ if (mem) {
+ if (!do_set_member_penalty_paused(q, mem, 1, paused, reason)) {
+ found ++;
}
ao2_ref(mem, -1);
}
-
- if (!ast_strlen_zero(queuename) && !strcasecmp(queuename, q->name)) {
- ao2_unlock(q);
- queue_t_unref(q, "Done with iterator");
- break;
- }
-
ao2_unlock(q);
queue_t_unref(q, "Done with iterator");
}
@@ -5482,42 +5606,54 @@
/* \brief Sets members penalty, if queuename=NULL we set member penalty in all the queues. */
static int set_member_penalty(const char *queuename, const char *interface, int penalty)
{
- int foundinterface = 0, foundqueue = 0;
+ int foundinterface = 0;
struct call_queue *q;
struct member *mem;
- char rtpenalty[80];
+ struct ao2_iterator queue_iter;
if (penalty < 0 && !negative_penalty_invalid) {
ast_log(LOG_ERROR, "Invalid penalty (%d)\n", penalty);
return RESULT_FAILURE;
}
- if ((q = load_realtime_queue(queuename))) {
- foundqueue++;
+ if (!ast_strlen_zero(queuename)) {
+ if ((q = load_realtime_queue(queuename))) {
+ ao2_lock(q);
+ if ((mem = interface_exists(q, interface))) {
+ do_set_member_penalty_paused(q, mem, 0, penalty, NULL);
+ ao2_ref(mem, -1);
+ ao2_unlock(q);
+ ao2_ref(q, -1);
+ return RESULT_SUCCESS;
+ } else {
+ ao2_unlock(q);
+ ao2_ref(q, -1);
+ return RESULT_FAILURE;
+ }
+ } else {
+ ast_log (LOG_ERROR, "Invalid queuename\n");
+ return RESULT_FAILURE;
+ }
+ }
+
+ load_all_realtime_queues();
+
+ queue_iter = ao2_iterator_init(queues, 0);
+ while ((q = ao2_t_iterator_next(&queue_iter, "Iterate over queues"))) {
ao2_lock(q);
[... 1030 lines stripped ...]
More information about the asterisk-commits
mailing list