Convert struct member to use refcounts (closes issue #10199)

git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/1.4@82155 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
Tilghman Lesher
2007-09-10 18:02:02 +00:00
parent a13b91f49b
commit 4913548485

View File

@@ -92,6 +92,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/astdb.h" #include "asterisk/astdb.h"
#include "asterisk/devicestate.h" #include "asterisk/devicestate.h"
#include "asterisk/stringfields.h" #include "asterisk/stringfields.h"
#include "asterisk/astobj2.h"
enum { enum {
QUEUE_STRATEGY_RINGALL = 0, QUEUE_STRATEGY_RINGALL = 0,
@@ -324,7 +325,6 @@ struct member {
time_t lastcall; /*!< When last successful call was hungup */ time_t lastcall; /*!< When last successful call was hungup */
unsigned int dead:1; /*!< Used to detect members deleted in realtime */ unsigned int dead:1; /*!< Used to detect members deleted in realtime */
unsigned int delme:1; /*!< Flag to delete entry on reload */ unsigned int delme:1; /*!< Flag to delete entry on reload */
struct member *next; /*!< Next member */
}; };
struct member_interface { struct member_interface {
@@ -396,7 +396,7 @@ struct call_queue {
int memberdelay; /*!< Seconds to delay connecting member to caller */ int memberdelay; /*!< Seconds to delay connecting member to caller */
int autofill; /*!< Ignore the head call status and ring an available agent */ int autofill; /*!< Ignore the head call status and ring an available agent */
struct member *members; /*!< Head of the list of members */ struct ao2_container *members; /*!< Head of the list of members */
int membercount; /*!< Number of members in queue */ int membercount; /*!< Number of members in queue */
struct queue_ent *head; /*!< Head of the list of callers */ struct queue_ent *head; /*!< Head of the list of callers */
AST_LIST_ENTRY(call_queue) list; /*!< Next call queue */ AST_LIST_ENTRY(call_queue) list; /*!< Next call queue */
@@ -481,28 +481,38 @@ enum queue_member_status {
static enum queue_member_status get_member_status(struct call_queue *q, int max_penalty) static enum queue_member_status get_member_status(struct call_queue *q, int max_penalty)
{ {
struct member *member; struct member *member;
struct ao2_iterator i;
enum queue_member_status result = QUEUE_NO_MEMBERS; enum queue_member_status result = QUEUE_NO_MEMBERS;
ast_mutex_lock(&q->lock); ast_mutex_lock(&q->lock);
for (member = q->members; member; member = member->next) { i = ao2_iterator_init(q->members, 0);
if (max_penalty && (member->penalty > max_penalty)) while ((member = ao2_iterator_next(&i))) {
if (max_penalty && (member->penalty > max_penalty)) {
ao2_ref(member, -1);
continue; continue;
}
if (member->paused) continue; if (member->paused) {
ao2_ref(member, -1);
continue;
}
switch (member->status) { switch (member->status) {
case AST_DEVICE_INVALID: case AST_DEVICE_INVALID:
/* nothing to do */ /* nothing to do */
ao2_ref(member, -1);
break; break;
case AST_DEVICE_UNAVAILABLE: case AST_DEVICE_UNAVAILABLE:
result = QUEUE_NO_REACHABLE_MEMBERS; result = QUEUE_NO_REACHABLE_MEMBERS;
ao2_ref(member, -1);
break; break;
default: default:
ast_mutex_unlock(&q->lock); ast_mutex_unlock(&q->lock);
ao2_ref(member, -1);
return QUEUE_NORMAL; return QUEUE_NORMAL;
} }
} }
ast_mutex_unlock(&q->lock); ast_mutex_unlock(&q->lock);
return result; return result;
} }
@@ -517,6 +527,7 @@ static void *changethread(void *data)
struct call_queue *q; struct call_queue *q;
struct statechange *sc = data; struct statechange *sc = data;
struct member *cur; struct member *cur;
struct ao2_iterator i;
struct member_interface *curint; struct member_interface *curint;
char *loc; char *loc;
char *technology; char *technology;
@@ -556,7 +567,8 @@ static void *changethread(void *data)
AST_LIST_LOCK(&queues); AST_LIST_LOCK(&queues);
AST_LIST_TRAVERSE(&queues, q, list) { AST_LIST_TRAVERSE(&queues, q, list) {
ast_mutex_lock(&q->lock); ast_mutex_lock(&q->lock);
for (cur = q->members; cur; cur = cur->next) { i = ao2_iterator_init(q->members, 0);
while ((cur = ao2_iterator_next(&i))) {
char *interface; char *interface;
char *slash_pos; char *slash_pos;
interface = ast_strdupa(cur->interface); interface = ast_strdupa(cur->interface);
@@ -564,13 +576,17 @@ static void *changethread(void *data)
if ((slash_pos = strchr(slash_pos + 1, '/'))) if ((slash_pos = strchr(slash_pos + 1, '/')))
*slash_pos = '\0'; *slash_pos = '\0';
if (strcasecmp(sc->dev, interface)) if (strcasecmp(sc->dev, interface)) {
ao2_ref(cur, -1);
continue; continue;
}
if (cur->status != sc->state) { if (cur->status != sc->state) {
cur->status = sc->state; cur->status = sc->state;
if (q->maskmemberstatus) if (q->maskmemberstatus) {
ao2_ref(cur, -1);
continue; continue;
}
manager_event(EVENT_FLAG_AGENT, "QueueMemberStatus", manager_event(EVENT_FLAG_AGENT, "QueueMemberStatus",
"Queue: %s\r\n" "Queue: %s\r\n"
@@ -585,6 +601,7 @@ static void *changethread(void *data)
q->name, cur->interface, cur->membername, cur->dynamic ? "dynamic" : cur->realtime ? "realtime" : "static", q->name, cur->interface, cur->membername, cur->dynamic ? "dynamic" : cur->realtime ? "realtime" : "static",
cur->penalty, cur->calls, (int)cur->lastcall, cur->status, cur->paused); cur->penalty, cur->calls, (int)cur->lastcall, cur->status, cur->paused);
} }
ao2_ref(cur, -1);
} }
ast_mutex_unlock(&q->lock); ast_mutex_unlock(&q->lock);
} }
@@ -623,7 +640,7 @@ static struct member *create_queue_member(const char *interface, const char *mem
{ {
struct member *cur; struct member *cur;
if ((cur = ast_calloc(1, sizeof(*cur)))) { if ((cur = ao2_alloc(sizeof(*cur), NULL))) {
cur->penalty = penalty; cur->penalty = penalty;
cur->paused = paused; cur->paused = paused;
ast_copy_string(cur->interface, interface, sizeof(cur->interface)); ast_copy_string(cur->interface, interface, sizeof(cur->interface));
@@ -647,6 +664,34 @@ static struct call_queue *alloc_queue(const char *queuename)
return q; return q;
} }
static int compress_char(const char c)
{
if (c < 32)
return 0;
else if (c > 96)
return c - 64;
else
return c - 32;
}
static int member_hash_fn(const void *obj, const int flags)
{
const struct member *mem = obj;
const char *chname = strchr(mem->interface, '/');
int ret = 0, i;
if (!chname)
chname = mem->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, *mem2 = obj2;
return strcmp(mem1->interface, mem2->interface) ? 0 : CMP_MATCH;
}
static void init_queue(struct call_queue *q) static void init_queue(struct call_queue *q)
{ {
int i; int i;
@@ -668,6 +713,7 @@ static void init_queue(struct call_queue *q)
q->context[0] = '\0'; q->context[0] = '\0';
q->monfmt[0] = '\0'; q->monfmt[0] = '\0';
q->periodicannouncefrequency = 0; q->periodicannouncefrequency = 0;
q->members = ao2_container_alloc(37, member_hash_fn, member_cmp_fn);
q->membercount = 0; q->membercount = 0;
ast_copy_string(q->sound_next, "queue-youarenext", sizeof(q->sound_next)); ast_copy_string(q->sound_next, "queue-youarenext", sizeof(q->sound_next));
ast_copy_string(q->sound_thereare, "queue-thereare", sizeof(q->sound_thereare)); ast_copy_string(q->sound_thereare, "queue-thereare", sizeof(q->sound_thereare));
@@ -729,9 +775,9 @@ static int interface_exists_global(const char *interface)
AST_LIST_LOCK(&queues); AST_LIST_LOCK(&queues);
AST_LIST_TRAVERSE(&queues, q, list) { AST_LIST_TRAVERSE(&queues, q, list) {
ast_mutex_lock(&q->lock); ast_mutex_lock(&q->lock);
for (mem = q->members; mem && !ret; mem = mem->next) { if ((mem = ao2_find(q->members, (char *)interface, 0))) {
if (!strcasecmp(interface, mem->interface)) ao2_ref(mem, -1);
ret = 1; ret = 1;
} }
ast_mutex_unlock(&q->lock); ast_mutex_unlock(&q->lock);
if (ret) if (ret)
@@ -930,7 +976,7 @@ static void queue_set_param(struct call_queue *q, const char *param, const char
static void rt_handle_member_record(struct call_queue *q, char *interface, const char *membername, const char *penalty_str, const char *paused_str) static void rt_handle_member_record(struct call_queue *q, char *interface, const char *membername, const char *penalty_str, const char *paused_str)
{ {
struct member *m, *prev_m; struct member *m;
int penalty = 0; int penalty = 0;
int paused = 0; int paused = 0;
@@ -947,9 +993,7 @@ static void rt_handle_member_record(struct call_queue *q, char *interface, const
} }
/* Find the member, or the place to put a new one. */ /* Find the member, or the place to put a new one. */
for (m = q->members, prev_m = NULL; m = ao2_find(q->members, interface, 0);
m && strcmp(m->interface, interface);
prev_m = m, m = m->next);
/* Create a new one if not found, else update penalty */ /* Create a new one if not found, else update penalty */
if (!m) { if (!m) {
@@ -957,11 +1001,7 @@ static void rt_handle_member_record(struct call_queue *q, char *interface, const
m->dead = 0; m->dead = 0;
m->realtime = 1; m->realtime = 1;
add_to_interfaces(interface); add_to_interfaces(interface);
if (prev_m) { ao2_link(q->members, m);
prev_m->next = m;
} else {
q->members = m;
}
q->membercount++; q->membercount++;
} }
} else { } else {
@@ -969,26 +1009,23 @@ static void rt_handle_member_record(struct call_queue *q, char *interface, const
if (paused_str) if (paused_str)
m->paused = paused; m->paused = paused;
m->penalty = penalty; m->penalty = penalty;
ao2_ref(m, -1);
} }
} }
static void free_members(struct call_queue *q, int all) static void free_members(struct call_queue *q, int all)
{ {
/* Free non-dynamic members */ /* Free non-dynamic members */
struct member *curm, *next, *prev = NULL; struct member *cur;
struct ao2_iterator i = ao2_iterator_init(q->members, 0);
for (curm = q->members; curm; curm = next) { while ((cur = ao2_iterator_next(&i))) {
next = curm->next; if (all || !cur->dynamic) {
if (all || !curm->dynamic) { ao2_unlink(q->members, cur);
if (prev) remove_from_interfaces(cur->interface);
prev->next = next;
else
q->members = next;
remove_from_interfaces(curm->interface);
q->membercount--; q->membercount--;
free(curm); }
} else ao2_ref(cur, -1);
prev = curm;
} }
} }
@@ -996,6 +1033,7 @@ static void destroy_queue(struct call_queue *q)
{ {
free_members(q, 1); free_members(q, 1);
ast_mutex_destroy(&q->lock); ast_mutex_destroy(&q->lock);
ao2_ref(q->members, -1);
free(q); free(q);
} }
@@ -1006,7 +1044,8 @@ static struct call_queue *find_queue_by_name_rt(const char *queuename, struct as
{ {
struct ast_variable *v; struct ast_variable *v;
struct call_queue *q; struct call_queue *q;
struct member *m, *prev_m, *next_m; struct member *m;
struct ao2_iterator i;
char *interface = NULL; char *interface = NULL;
char *tmp, *tmp_name; char *tmp, *tmp_name;
char tmpbuf[64]; /* Must be longer than the longest queue param name. */ char tmpbuf[64]; /* Must be longer than the longest queue param name. */
@@ -1085,9 +1124,12 @@ static struct call_queue *find_queue_by_name_rt(const char *queuename, struct as
/* Temporarily set realtime members dead so we can detect deleted ones. /* Temporarily set realtime members dead so we can detect deleted ones.
* Also set the membercount correctly for realtime*/ * Also set the membercount correctly for realtime*/
for (m = q->members; m; m = m->next, q->membercount++) { i = ao2_iterator_init(q->members, 0);
while ((m = ao2_iterator_next(&i))) {
q->membercount++;
if (m->realtime) if (m->realtime)
m->dead = 1; m->dead = 1;
ao2_ref(m, -1);
} }
while ((interface = ast_category_browse(member_config, interface))) { while ((interface = ast_category_browse(member_config, interface))) {
@@ -1098,25 +1140,16 @@ static struct call_queue *find_queue_by_name_rt(const char *queuename, struct as
} }
/* Delete all realtime members that have been deleted in DB. */ /* Delete all realtime members that have been deleted in DB. */
m = q->members; i = ao2_iterator_init(q->members, 0);
prev_m = NULL; while ((m = ao2_iterator_next(&i))) {
while (m) {
next_m = m->next;
if (m->dead) { if (m->dead) {
if (prev_m) { ao2_unlink(q->members, m);
prev_m->next = next_m;
} else {
q->members = next_m;
}
ast_mutex_unlock(&q->lock); ast_mutex_unlock(&q->lock);
remove_from_interfaces(m->interface); remove_from_interfaces(m->interface);
ast_mutex_lock(&q->lock); ast_mutex_lock(&q->lock);
q->membercount--; q->membercount--;
free(m);
} else {
prev_m = m;
} }
m = next_m; ao2_ref(m, -1);
} }
ast_mutex_unlock(&q->lock); ast_mutex_unlock(&q->lock);
@@ -1146,8 +1179,9 @@ static int update_realtime_member_field(struct member *mem, const char *queue_na
static void update_realtime_members(struct call_queue *q) static void update_realtime_members(struct call_queue *q)
{ {
struct ast_config *member_config = NULL; struct ast_config *member_config = NULL;
struct member *m, *prev_m, *next_m; struct member *m;
char *interface = NULL; char *interface = NULL;
struct ao2_iterator i;
member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", q->name , NULL); member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", q->name , NULL);
if (!member_config) { if (!member_config) {
@@ -1160,9 +1194,11 @@ static void update_realtime_members(struct call_queue *q)
ast_mutex_lock(&q->lock); ast_mutex_lock(&q->lock);
/* Temporarily set realtime members dead so we can detect deleted ones.*/ /* Temporarily set realtime members dead so we can detect deleted ones.*/
for (m = q->members; m; m = m->next) { i = ao2_iterator_init(q->members, 0);
while ((m = ao2_iterator_next(&i))) {
if (m->realtime) if (m->realtime)
m->dead = 1; m->dead = 1;
ao2_ref(m, -1);
} }
while ((interface = ast_category_browse(member_config, interface))) { while ((interface = ast_category_browse(member_config, interface))) {
@@ -1173,25 +1209,16 @@ static void update_realtime_members(struct call_queue *q)
} }
/* Delete all realtime members that have been deleted in DB. */ /* Delete all realtime members that have been deleted in DB. */
m = q->members; i = ao2_iterator_init(q->members, 0);
prev_m = NULL; while ((m = ao2_iterator_next(&i))) {
while (m) {
next_m = m->next;
if (m->dead) { if (m->dead) {
if (prev_m) { ao2_unlink(q->members, m);
prev_m->next = next_m;
} else {
q->members = next_m;
}
ast_mutex_unlock(&q->lock); ast_mutex_unlock(&q->lock);
remove_from_interfaces(m->interface); remove_from_interfaces(m->interface);
ast_mutex_lock(&q->lock); ast_mutex_lock(&q->lock);
q->membercount--; q->membercount--;
free(m);
} else {
prev_m = m;
} }
m = next_m; ao2_ref(m, -1);
} }
ast_mutex_unlock(&q->lock); ast_mutex_unlock(&q->lock);
} }
@@ -1541,13 +1568,17 @@ static void hangupcalls(struct callattempt *outgoing, struct ast_channel *except
static int update_status(struct call_queue *q, struct member *member, int status) static int update_status(struct call_queue *q, struct member *member, int status)
{ {
struct member *cur; struct member *cur;
struct ao2_iterator i;
/* Since a reload could have taken place, we have to traverse the list to /* Since a reload could have taken place, we have to traverse the list to
be sure it's still valid */ be sure it's still valid */
ast_mutex_lock(&q->lock); ast_mutex_lock(&q->lock);
for (cur = q->members; cur; cur = cur->next) { i = ao2_iterator_init(q->members, 0);
if (member != cur) while ((cur = ao2_iterator_next(&i))) {
if (member != cur) {
ao2_ref(cur, -1);
continue; continue;
}
cur->status = status; cur->status = status;
if (!q->maskmemberstatus) { if (!q->maskmemberstatus) {
@@ -1564,6 +1595,7 @@ static int update_status(struct call_queue *q, struct member *member, int status
q->name, cur->interface, cur->membername, cur->dynamic ? "dynamic" : cur->realtime ? "realtime": "static", q->name, cur->interface, cur->membername, cur->dynamic ? "dynamic" : cur->realtime ? "realtime": "static",
cur->penalty, cur->calls, (int)cur->lastcall, cur->status, cur->paused); cur->penalty, cur->calls, (int)cur->lastcall, cur->status, cur->paused);
} }
ao2_ref(cur, -1);
} }
ast_mutex_unlock(&q->lock); ast_mutex_unlock(&q->lock);
return 0; return 0;
@@ -1597,16 +1629,13 @@ static int compare_weight(struct call_queue *rq, struct member *member)
continue; continue;
ast_mutex_lock(&q->lock); ast_mutex_lock(&q->lock);
if (q->count && q->members) { if (q->count && q->members) {
for (mem = q->members; mem; mem = mem->next) { if ((mem = ao2_find(q->members, member->interface, 0))) {
if (strcmp(mem->interface, member->interface))
continue;
ast_log(LOG_DEBUG, "Found matching member %s in queue '%s'\n", mem->interface, q->name); ast_log(LOG_DEBUG, "Found matching member %s in queue '%s'\n", mem->interface, q->name);
if (q->weight > rq->weight) { if (q->weight > rq->weight) {
ast_log(LOG_DEBUG, "Queue '%s' (weight %d, calls %d) is preferred over '%s' (weight %d, calls %d)\n", q->name, q->weight, q->count, rq->name, rq->weight, rq->count); ast_log(LOG_DEBUG, "Queue '%s' (weight %d, calls %d) is preferred over '%s' (weight %d, calls %d)\n", q->name, q->weight, q->count, rq->name, rq->weight, rq->count);
found = 1; found = 1;
break;
} }
ao2_ref(mem, -1);
} }
} }
ast_mutex_unlock(&q->lock); ast_mutex_unlock(&q->lock);
@@ -2199,7 +2228,8 @@ static int is_our_turn(struct queue_ent *qe)
ast_log(LOG_DEBUG, "Even though there are %d available members, the strategy is ringall so only the head call is allowed in\n", avl); ast_log(LOG_DEBUG, "Even though there are %d available members, the strategy is ringall so only the head call is allowed in\n", avl);
avl = 1; avl = 1;
} else { } else {
for (cur = qe->parent->members; cur; cur = cur->next) { struct ao2_iterator i = ao2_iterator_init(qe->parent->members, 0);
while ((cur = ao2_iterator_next(&i))) {
switch (cur->status) { switch (cur->status) {
case AST_DEVICE_NOT_INUSE: case AST_DEVICE_NOT_INUSE:
case AST_DEVICE_UNKNOWN: case AST_DEVICE_UNKNOWN:
@@ -2207,6 +2237,7 @@ static int is_our_turn(struct queue_ent *qe)
avl++; avl++;
break; break;
} }
ao2_ref(cur, -1);
} }
} }
@@ -2294,20 +2325,9 @@ static int wait_our_turn(struct queue_ent *qe, int ringing, enum queue_result *r
static int update_queue(struct call_queue *q, struct member *member, int callcompletedinsl) static int update_queue(struct call_queue *q, struct member *member, int callcompletedinsl)
{ {
struct member *cur;
/* Since a reload could have taken place, we have to traverse the list to
be sure it's still valid */
ast_mutex_lock(&q->lock); ast_mutex_lock(&q->lock);
cur = q->members; time(&member->lastcall);
while (cur) { member->calls++;
if (member == cur) {
time(&cur->lastcall);
cur->calls++;
break;
}
cur = cur->next;
}
q->callscompleted++; q->callscompleted++;
if (callcompletedinsl) if (callcompletedinsl)
q->callscompletedinsl++; q->callscompletedinsl++;
@@ -2405,6 +2425,7 @@ static int try_calling(struct queue_ent *qe, const char *options, char *announce
char vars[2048]; char vars[2048];
int forwardsallowed = 1; int forwardsallowed = 1;
int callcompletedinsl; int callcompletedinsl;
struct ao2_iterator memi;
memset(&bridge_config, 0, sizeof(bridge_config)); memset(&bridge_config, 0, sizeof(bridge_config));
time(&now); time(&now);
@@ -2452,13 +2473,13 @@ static int try_calling(struct queue_ent *qe, const char *options, char *announce
ast_log(LOG_DEBUG, "%s is trying to call a queue member.\n", ast_log(LOG_DEBUG, "%s is trying to call a queue member.\n",
qe->chan->name); qe->chan->name);
ast_copy_string(queuename, qe->parent->name, sizeof(queuename)); ast_copy_string(queuename, qe->parent->name, sizeof(queuename));
cur = qe->parent->members;
if (!ast_strlen_zero(qe->announce)) if (!ast_strlen_zero(qe->announce))
announce = qe->announce; announce = qe->announce;
if (!ast_strlen_zero(announceoverride)) if (!ast_strlen_zero(announceoverride))
announce = announceoverride; announce = announceoverride;
for (; cur; cur = cur->next) { memi = ao2_iterator_init(qe->parent->members, 0);
while ((cur = ao2_iterator_next(&memi))) {
struct callattempt *tmp = ast_calloc(1, sizeof(*tmp)); struct callattempt *tmp = ast_calloc(1, sizeof(*tmp));
if (!tmp) { if (!tmp) {
@@ -2468,7 +2489,7 @@ static int try_calling(struct queue_ent *qe, const char *options, char *announce
goto out; goto out;
} }
tmp->stillgoing = -1; tmp->stillgoing = -1;
tmp->member = cur; /* Never directly dereference! Could change on reload */ tmp->member = cur;
tmp->oldstatus = cur->status; tmp->oldstatus = cur->status;
tmp->lastcall = cur->lastcall; tmp->lastcall = cur->lastcall;
ast_copy_string(tmp->interface, cur->interface, sizeof(tmp->interface)); ast_copy_string(tmp->interface, cur->interface, sizeof(tmp->interface));
@@ -2484,6 +2505,7 @@ static int try_calling(struct queue_ent *qe, const char *options, char *announce
if (outgoing->chan && (outgoing->chan->_state == AST_STATE_UP)) if (outgoing->chan && (outgoing->chan->_state == AST_STATE_UP))
break; break;
} else { } else {
ao2_ref(cur, -1);
free(tmp); free(tmp);
} }
} }
@@ -2798,13 +2820,16 @@ static int wait_a_bit(struct queue_ent *qe)
static struct member *interface_exists(struct call_queue *q, const char *interface) static struct member *interface_exists(struct call_queue *q, const char *interface)
{ {
struct member *mem; struct member *mem;
struct ao2_iterator i;
if (!q) if (!q)
return NULL; return NULL;
for (mem = q->members; mem; mem = mem->next) { i = ao2_iterator_init(q->members, 0);
while ((mem = ao2_iterator_next(&i))) {
if (!strcasecmp(interface, mem->interface)) if (!strcasecmp(interface, mem->interface))
return mem; return mem;
ao2_ref(mem, -1);
} }
return NULL; return NULL;
@@ -2822,19 +2847,25 @@ static void dump_queue_members(struct call_queue *pm_queue)
char value[PM_MAX_LEN]; char value[PM_MAX_LEN];
int value_len = 0; int value_len = 0;
int res; int res;
struct ao2_iterator i;
memset(value, 0, sizeof(value)); memset(value, 0, sizeof(value));
if (!pm_queue) if (!pm_queue)
return; return;
for (cur_member = pm_queue->members; cur_member; cur_member = cur_member->next) { i = ao2_iterator_init(pm_queue->members, 0);
if (!cur_member->dynamic) while ((cur_member = ao2_iterator_next(&i))) {
if (!cur_member->dynamic) {
ao2_ref(cur_member, -1);
continue; continue;
}
res = snprintf(value + value_len, sizeof(value) - value_len, "%s%s;%d;%d;%s",
value_len ? "|" : "", cur_member->interface, cur_member->penalty, cur_member->paused, cur_member->membername);
ao2_ref(cur_member, -1);
res = snprintf(value + value_len, sizeof(value) - value_len, "%s;%d;%d;%s%s",
cur_member->interface, cur_member->penalty, cur_member->paused, cur_member->membername,
cur_member->next ? "|" : "");
if (res != strlen(value + value_len)) { if (res != strlen(value + value_len)) {
ast_log(LOG_WARNING, "Could not create persistent member string, out of space\n"); ast_log(LOG_WARNING, "Could not create persistent member string, out of space\n");
break; break;
@@ -2853,7 +2884,7 @@ static void dump_queue_members(struct call_queue *pm_queue)
static int remove_from_queue(const char *queuename, const char *interface) static int remove_from_queue(const char *queuename, const char *interface)
{ {
struct call_queue *q; struct call_queue *q;
struct member *last_member, *look; struct member *mem;
int res = RES_NOSUCHQUEUE; int res = RES_NOSUCHQUEUE;
AST_LIST_LOCK(&queues); AST_LIST_LOCK(&queues);
@@ -2864,28 +2895,15 @@ static int remove_from_queue(const char *queuename, const char *interface)
continue; continue;
} }
if ((last_member = interface_exists(q, interface))) { if ((mem = ao2_find(q->members, (char *)interface, OBJ_UNLINK))) {
if ((look = q->members) == last_member) { q->membercount--;
q->members = last_member->next;
q->membercount--;
} else {
while (look != NULL) {
if (look->next == last_member) {
look->next = last_member->next;
q->membercount--;
break;
} else {
look = look->next;
}
}
}
manager_event(EVENT_FLAG_AGENT, "QueueMemberRemoved", manager_event(EVENT_FLAG_AGENT, "QueueMemberRemoved",
"Queue: %s\r\n" "Queue: %s\r\n"
"Location: %s\r\n" "Location: %s\r\n"
"MemberName: %s\r\n", "MemberName: %s\r\n",
q->name, last_member->interface, last_member->membername); q->name, mem->interface, mem->membername);
free(last_member); ao2_ref(mem, -1);
if (queue_persistent_members) if (queue_persistent_members)
dump_queue_members(q); dump_queue_members(q);
@@ -2909,7 +2927,7 @@ static int remove_from_queue(const char *queuename, const char *interface)
static int add_to_queue(const char *queuename, const char *interface, const char *membername, int penalty, int paused, int dump) static int add_to_queue(const char *queuename, const char *interface, const char *membername, int penalty, int paused, int dump)
{ {
struct call_queue *q; struct call_queue *q;
struct member *new_member; struct member *new_member, *old_member;
int res = RES_NOSUCHQUEUE; int res = RES_NOSUCHQUEUE;
/* \note Ensure the appropriate realtime queue is loaded. Note that this /* \note Ensure the appropriate realtime queue is loaded. Note that this
@@ -2920,12 +2938,11 @@ static int add_to_queue(const char *queuename, const char *interface, const char
AST_LIST_LOCK(&queues); AST_LIST_LOCK(&queues);
ast_mutex_lock(&q->lock); ast_mutex_lock(&q->lock);
if (interface_exists(q, interface) == NULL) { if ((old_member = interface_exists(q, interface)) == NULL) {
add_to_interfaces(interface); add_to_interfaces(interface);
if ((new_member = create_queue_member(interface, membername, penalty, paused))) { if ((new_member = create_queue_member(interface, membername, penalty, paused))) {
new_member->dynamic = 1; new_member->dynamic = 1;
new_member->next = q->members; ao2_link(q->members, new_member);
q->members = new_member;
q->membercount++; q->membercount++;
manager_event(EVENT_FLAG_AGENT, "QueueMemberAdded", manager_event(EVENT_FLAG_AGENT, "QueueMemberAdded",
"Queue: %s\r\n" "Queue: %s\r\n"
@@ -2950,6 +2967,7 @@ static int add_to_queue(const char *queuename, const char *interface, const char
res = RES_OUTOFMEMORY; res = RES_OUTOFMEMORY;
} }
} else { } else {
ao2_ref(old_member, -1);
res = RES_EXISTS; res = RES_EXISTS;
} }
ast_mutex_unlock(&q->lock); ast_mutex_unlock(&q->lock);
@@ -2993,6 +3011,7 @@ static int set_member_paused(const char *queuename, const char *interface, int p
"MemberName: %s\r\n" "MemberName: %s\r\n"
"Paused: %d\r\n", "Paused: %d\r\n",
q->name, mem->interface, mem->membername, paused); q->name, mem->interface, mem->membername, paused);
ao2_ref(mem, -1);
} }
} }
ast_mutex_unlock(&q->lock); ast_mutex_unlock(&q->lock);
@@ -3635,6 +3654,7 @@ static int queue_function_qac(struct ast_channel *chan, char *cmd, char *data, c
struct call_queue *q; struct call_queue *q;
struct ast_module_user *lu; struct ast_module_user *lu;
struct member *m; struct member *m;
struct ao2_iterator i;
buf[0] = '\0'; buf[0] = '\0';
@@ -3655,11 +3675,13 @@ static int queue_function_qac(struct ast_channel *chan, char *cmd, char *data, c
AST_LIST_UNLOCK(&queues); AST_LIST_UNLOCK(&queues);
if (q) { if (q) {
for (m = q->members; m; m = m->next) { i = ao2_iterator_init(q->members, 0);
while ((m = ao2_iterator_next(&i))) {
/* Count the agents who are logged in and presently answering calls */ /* Count the agents who are logged in and presently answering calls */
if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) { if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
count++; count++;
} }
ao2_ref(m, -1);
} }
ast_mutex_unlock(&q->lock); ast_mutex_unlock(&q->lock);
} else } else
@@ -3733,8 +3755,9 @@ static int queue_function_queuememberlist(struct ast_channel *chan, char *cmd, c
if (q) { if (q) {
int buflen = 0, count = 0; int buflen = 0, count = 0;
struct ao2_iterator i = ao2_iterator_init(q->members, 0);
for (m = q->members; m; m = m->next) { while ((m = ao2_iterator_next(&i))) {
/* strcat() is always faster than printf() */ /* strcat() is always faster than printf() */
if (count++) { if (count++) {
strncat(buf + buflen, ",", len - buflen - 1); strncat(buf + buflen, ",", len - buflen - 1);
@@ -3747,6 +3770,7 @@ static int queue_function_queuememberlist(struct ast_channel *chan, char *cmd, c
ast_log(LOG_WARNING, "Truncating list\n"); ast_log(LOG_WARNING, "Truncating list\n");
break; break;
} }
ao2_ref(m, -1);
} }
ast_mutex_unlock(&q->lock); ast_mutex_unlock(&q->lock);
} else } else
@@ -3802,7 +3826,8 @@ static int reload_queues(void)
struct ast_config *cfg; struct ast_config *cfg;
char *cat, *tmp; char *cat, *tmp;
struct ast_variable *var; struct ast_variable *var;
struct member *prev, *cur, *newm, *next; struct member *cur, *newm;
struct ao2_iterator i;
int new; int new;
const char *general_val = NULL; const char *general_val = NULL;
char parse[80]; char parse[80];
@@ -3862,10 +3887,12 @@ static int reload_queues(void)
/* Re-initialize the queue, and clear statistics */ /* Re-initialize the queue, and clear statistics */
init_queue(q); init_queue(q);
clear_queue(q); clear_queue(q);
for (cur = q->members; cur; cur = cur->next) { i = ao2_iterator_init(q->members, 0);
while ((cur = ao2_iterator_next(&i))) {
if (!cur->dynamic) { if (!cur->dynamic) {
cur->delme = 1; cur->delme = 1;
} }
ao2_ref(cur, -1);
} }
for (var = ast_variable_browse(cfg, cat); var; var = var->next) { for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
if (!strcasecmp(var->name, "member")) { if (!strcasecmp(var->name, "member")) {
@@ -3892,54 +3919,35 @@ static int reload_queues(void)
membername = interface; membername = interface;
/* Find the old position in the list */ /* Find the old position in the list */
for (prev = NULL, cur = q->members; cur; prev = cur, cur = cur->next) { cur = ao2_find(q->members, interface, OBJ_UNLINK);
if (!strcmp(cur->interface, interface)) {
break;
}
}
newm = create_queue_member(interface, membername, penalty, cur ? cur->paused : 0); newm = create_queue_member(interface, membername, penalty, cur ? cur->paused : 0);
ao2_link(q->members, newm);
if (cur) { if (cur)
/* Delete it now */ ao2_ref(cur, -1);
newm->next = cur->next; else {
if (prev) {
prev->next = newm;
} else {
q->members = newm;
}
free(cur);
} else {
/* Add them to the master int list if necessary */ /* Add them to the master int list if necessary */
add_to_interfaces(interface); add_to_interfaces(interface);
newm->next = q->members; q->membercount++;
q->members = newm;
} }
q->membercount++;
} else { } else {
queue_set_param(q, var->name, var->value, var->lineno, 1); queue_set_param(q, var->name, var->value, var->lineno, 1);
} }
} }
/* Free remaining members marked as delme */ /* Free remaining members marked as delme */
for (prev = NULL, cur = q->members; i = ao2_iterator_init(q->members, 0);
cur; while ((cur = ao2_iterator_next(&i))) {
cur = next) { if (! cur->delme) {
next = cur->next; ao2_ref(cur, -1);
if (!cur->delme) {
prev = cur;
continue; continue;
} }
if (prev)
prev->next = next;
else
q->members = next;
remove_from_interfaces(cur->interface); remove_from_interfaces(cur->interface);
q->membercount--; q->membercount--;
free(cur); ao2_unlink(q->members, cur);
ao2_ref(cur, -1);
} }
if (q->strategy == QUEUE_STRATEGY_ROUNDROBIN) if (q->strategy == QUEUE_STRATEGY_ROUNDROBIN)
@@ -3962,10 +3970,12 @@ static int reload_queues(void)
ast_log(LOG_DEBUG, "XXX Leaking a little memory :( XXX\n"); ast_log(LOG_DEBUG, "XXX Leaking a little memory :( XXX\n");
} else { } else {
ast_mutex_lock(&q->lock); ast_mutex_lock(&q->lock);
for (cur = q->members; cur; cur = cur->next) { i = ao2_iterator_init(q->members, 0);
if(cur->dynamic) while ((cur = ao2_iterator_next(&i))) {
if (cur->dynamic)
q->membercount++; q->membercount++;
cur->status = ast_device_state(cur->interface); cur->status = ast_device_state(cur->interface);
ao2_ref(cur, -1);
} }
ast_mutex_unlock(&q->lock); ast_mutex_unlock(&q->lock);
} }
@@ -3987,6 +3997,7 @@ static int __queues_show(struct mansession *s, int manager, int fd, int argc, ch
size_t max_left; size_t max_left;
float sl = 0; float sl = 0;
char *term = manager ? "\r\n" : "\n"; char *term = manager ? "\r\n" : "\n";
struct ao2_iterator i;
time(&now); time(&now);
if (argc == 2) if (argc == 2)
@@ -4045,12 +4056,13 @@ static int __queues_show(struct mansession *s, int manager, int fd, int argc, ch
else else
ast_cli(fd, "%-12.12s has %d calls (max %s) in '%s' strategy (%ds holdtime), W:%d, C:%d, A:%d, SL:%2.1f%% within %ds%s", ast_cli(fd, "%-12.12s has %d calls (max %s) in '%s' strategy (%ds holdtime), W:%d, C:%d, A:%d, SL:%2.1f%% within %ds%s",
q->name, q->count, max_buf, int2strat(q->strategy), q->holdtime, q->weight, q->callscompleted, q->callsabandoned,sl,q->servicelevel, term); q->name, q->count, max_buf, int2strat(q->strategy), q->holdtime, q->weight, q->callscompleted, q->callsabandoned,sl,q->servicelevel, term);
if (q->members) { if (ao2_container_count(q->members)) {
if (s) if (s)
astman_append(s, " Members: %s", term); astman_append(s, " Members: %s", term);
else else
ast_cli(fd, " Members: %s", term); ast_cli(fd, " Members: %s", term);
for (mem = q->members; mem; mem = mem->next) { i = ao2_iterator_init(q->members, 0);
while ((mem = ao2_iterator_next(&i))) {
max_buf[0] = '\0'; max_buf[0] = '\0';
max = max_buf; max = max_buf;
max_left = sizeof(max_buf); max_left = sizeof(max_buf);
@@ -4072,6 +4084,7 @@ static int __queues_show(struct mansession *s, int manager, int fd, int argc, ch
astman_append(s, " %s%s%s", mem->interface, max_buf, term); astman_append(s, " %s%s%s", mem->interface, max_buf, term);
else else
ast_cli(fd, " %s%s%s", mem->interface, max_buf, term); ast_cli(fd, " %s%s%s", mem->interface, max_buf, term);
ao2_ref(mem, -1);
} }
} else if (s) } else if (s)
astman_append(s, " No Members%s", term); astman_append(s, " No Members%s", term);
@@ -4166,6 +4179,7 @@ static int manager_queues_status(struct mansession *s, const struct message *m)
struct queue_ent *qe; struct queue_ent *qe;
float sl = 0; float sl = 0;
struct member *mem; struct member *mem;
struct ao2_iterator i;
astman_send_ack(s, m, "Queue status will follow"); astman_send_ack(s, m, "Queue status will follow");
time(&now); time(&now);
@@ -4194,7 +4208,8 @@ static int manager_queues_status(struct mansession *s, const struct message *m)
q->name, q->maxlen, q->count, q->holdtime, q->callscompleted, q->name, q->maxlen, q->count, q->holdtime, q->callscompleted,
q->callsabandoned, q->servicelevel, sl, q->weight, idText); q->callsabandoned, q->servicelevel, sl, q->weight, idText);
/* List Queue Members */ /* List Queue Members */
for (mem = q->members; mem; mem = mem->next) { i = ao2_iterator_init(q->members, 0);
while ((mem = ao2_iterator_next(&i))) {
if (ast_strlen_zero(memberfilter) || !strcmp(mem->interface, memberfilter)) { if (ast_strlen_zero(memberfilter) || !strcmp(mem->interface, memberfilter)) {
astman_append(s, "Event: QueueMember\r\n" astman_append(s, "Event: QueueMember\r\n"
"Queue: %s\r\n" "Queue: %s\r\n"
@@ -4210,6 +4225,7 @@ static int manager_queues_status(struct mansession *s, const struct message *m)
q->name, mem->interface, mem->dynamic ? "dynamic" : "static", q->name, mem->interface, mem->dynamic ? "dynamic" : "static",
mem->penalty, mem->calls, (int)mem->lastcall, mem->status, mem->paused, idText); mem->penalty, mem->calls, (int)mem->lastcall, mem->status, mem->paused, idText);
} }
ao2_ref(mem, -1);
} }
/* List Queue Entries */ /* List Queue Entries */
pos = 1; pos = 1;
@@ -4474,6 +4490,7 @@ static char *complete_queue_remove_member(const char *line, const char *word, in
int which = 0; int which = 0;
struct call_queue *q; struct call_queue *q;
struct member *m; struct member *m;
struct ao2_iterator i;
/* 0 - queue; 1 - remove; 2 - member; 3 - <member>; 4 - from; 5 - <queue> */ /* 0 - queue; 1 - remove; 2 - member; 3 - <member>; 4 - from; 5 - <queue> */
if (pos > 5 || pos < 3) if (pos > 5 || pos < 3)
@@ -4488,10 +4505,14 @@ static char *complete_queue_remove_member(const char *line, const char *word, in
if (!AST_LIST_EMPTY(&queues)) { /* XXX unnecessary ? the traverse does that for us */ if (!AST_LIST_EMPTY(&queues)) { /* XXX unnecessary ? the traverse does that for us */
AST_LIST_TRAVERSE(&queues, q, list) { AST_LIST_TRAVERSE(&queues, q, list) {
ast_mutex_lock(&q->lock); ast_mutex_lock(&q->lock);
for (m = q->members ; m ; m = m->next) { i = ao2_iterator_init(q->members, 0);
while ((m = ao2_iterator_next(&i))) {
if (++which > state) { if (++which > state) {
char *tmp;
ast_mutex_unlock(&q->lock); ast_mutex_unlock(&q->lock);
return ast_strdup(m->interface); tmp = ast_strdup(m->interface);
ao2_ref(m, -1);
return tmp;
} }
} }
ast_mutex_unlock(&q->lock); ast_mutex_unlock(&q->lock);