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

SVN commits to the Digium repositories svn-commits at lists.digium.com
Thu Oct 27 09:59:38 CDT 2011


Author: irroot
Date: Thu Oct 27 09:59:34 2011
New Revision: 342558

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=342558
Log:
Work on app_queue RB1538

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

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

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=342558&r1=342557&r2=342558
==============================================================================
--- team/irroot/distrotech-customers-trunk/apps/app_queue.c (original)
+++ team/irroot/distrotech-customers-trunk/apps/app_queue.c Thu Oct 27 09:59:34 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
@@ -1058,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 */
@@ -1212,7 +1227,8 @@
 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 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);
 
@@ -1466,12 +1482,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))) {
@@ -1489,6 +1506,7 @@
 				ao2_ref(m, -1);
 				continue;
 			}
+			ao2_unlock(m->device);
 			manager_event(EVENT_FLAG_AGENT, "QueueMemberStatus",
 				"Queue: %s\r\n"
 				"Location: %s\r\n"
@@ -1503,14 +1521,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*/
@@ -1518,7 +1538,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);
@@ -1532,11 +1552,14 @@
 		ao2_lock(s);
 		if (s->status != state) {
 			s->status = state;
-			ao2_unlock(s);
-			update_status(s);
-		} else {
-			ao2_unlock(s);
-		}
+			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);
 		ao2_ref(s, -1);
 		ast_debug(1, "Device '%s' changed to state '%d' (%s)\n", device, state, ast_devstate2str(state));
 	} else {
@@ -1578,7 +1601,7 @@
 
 static int extension_state_cb(const char *context, const char *exten, enum ast_extension_states state, void *data)
 {
-	struct mem_state *s;
+	struct mem_state *s, *tmp;
 	char *device;
 	int status = extensionstate2devicestate(state);
 
@@ -1591,11 +1614,14 @@
 		ao2_lock(s);
 		if (s->status != status) {
 			s->status = status;
-			ao2_unlock(s);
-			update_status(s);
-		} else {
-			ao2_unlock(s);
-		}
+			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);
 		ao2_ref(s, -1);
 		ast_debug(1, "Extension '%s@%s' changed to state '%d' (%s)\n", exten, context, status, ast_devstate2str(status));
 	} else {
@@ -1659,7 +1685,9 @@
 {
 	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;
@@ -1670,12 +1698,19 @@
 
 	if (s->status != status) {
 		s->status = status;
-		update_status(s);
-	}
+		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;
@@ -1693,6 +1728,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));
@@ -2187,69 +2223,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;
-		}
+	/* 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)) {
-			/* we may be the last ref [outside container] unlink if so */
 			ao2_unlock(m->device);
 			if (ao2_ref(m->device, -1) == 2) {
 				ao2_unlink(devices, m->device);
 			}
-			m->device = create_member_state(state_interface);
+			/* 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);
 		}
-		m->penalty = penalty;
-		m->ignorebusy = ignorebusy;
 		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", "");
@@ -2258,6 +2315,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);
 	}
 }
 
@@ -2304,6 +2363,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);
+	}
 }
 
 /*!
@@ -2316,16 +2431,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, queuename, OBJ_KEY, "Check if static queue exists"))) {
@@ -2341,12 +2454,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
@@ -2367,6 +2477,7 @@
 	if (!q) {
 		struct ast_variable *tmpvar = NULL;
 		if (!(q = alloc_queue(queuename))) {
+			ast_variables_destroy(queue_vars);
 			return NULL;
 		}
 		ao2_lock(q);
@@ -2411,36 +2522,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;
@@ -2449,8 +2533,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;
 
@@ -2467,26 +2549,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);
 			}
@@ -2494,9 +2564,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;
 }
@@ -2505,62 +2576,16 @@
 {
 	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;
@@ -2568,7 +2593,8 @@
 	struct call_queue *queue;
 
 	/* load realtime queues. */
-	if (ast_check_realtime("queues") && (cfg = ast_load_realtime_multientry("queues", "name LIKE", "%", SENTINEL))) {
+	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)) {
@@ -3196,18 +3222,7 @@
 
 	if (!qe->parent->ringinuse || !tmp->member->ignorebusy) {
 		ao2_lock(s);
-		if (!s->reserved && ((s->status != AST_DEVICE_NOT_INUSE) || (s->status == AST_DEVICE_UNKNOWN))) {
-			int oldstate = s->status;
-			set_queue_member_status(tmp->member);
-			if (oldstate != s->status) {
-				ast_log(LOG_WARNING, "Member %s On Queue %s Had Incorect Status Was %s Now %s\n", s->state_interface,
-						qe->parent->name, ast_devstate2str(oldstate), ast_devstate2str(s->status));
-			}
-		}
 		if ((s->reserved) || ((s->status != AST_DEVICE_NOT_INUSE) && (s->status != AST_DEVICE_UNKNOWN))) {
-			if ((s->status != AST_DEVICE_NOT_INUSE) && (s->status != AST_DEVICE_UNKNOWN)) {
-				set_queue_member_status(tmp->member);
-			}
 			ao2_unlock(s);
 			ast_debug(1, "%s in use, can't receive call\n", tmp->interface);
 			if (qe->chan->cdr) {
@@ -3220,7 +3235,8 @@
 		ao2_unlock(s);
 	}
 
-	if (!tmp->reserved) { 
+	/* mark device and call entry reserved */
+	if (!tmp->reserved) {
 		ao2_lock(s);
 		s->reserved++;
 		ao2_unlock(s);
@@ -3228,18 +3244,17 @@
 	}
 
 	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*/
-		ao2_lock(s);
 		set_queue_member_status(tmp->member);
-		ao2_unlock(s);
 
 		if (qe->chan->cdr) {
 			ast_cdr_busy(qe->chan->cdr);
@@ -3324,9 +3339,7 @@
 	/* Place the call, but don't wait on the answer */
 	if ((res = ast_call(tmp->chan, location, 0))) {
 		/* something is wrong here check status*/
-		ao2_lock(s);
 		set_queue_member_status(tmp->member);
-		ao2_unlock(s);
 
 		/* Again, keep going even if there's an error */
 		ast_debug(1, "ast call on peer returned %d\n", res);
@@ -3363,10 +3376,8 @@
 			qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
 		ast_verb(3, "Called %s\n", tmp->interface);
 	}
-
 	ast_channel_unlock(tmp->chan);
 	ast_channel_unlock(qe->chan);
-
 	return 1;
 }
 
@@ -5330,7 +5341,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)
 {
@@ -5347,14 +5358,15 @@
 
 	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;
 		}
 
 		ao2_lock(cur_member->device);
-		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);
+		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);
@@ -5423,28 +5435,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);
@@ -5458,11 +5464,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);
@@ -5480,8 +5487,6 @@
 		ao2_ref(old_member, -1);
 		res = RES_EXISTS;
 	}
-	ao2_unlock(q);
-	queue_t_unref(q, "Expiring temporary reference");
 
 	return res;
 }
@@ -5490,23 +5495,19 @@
 {
 	if (pause) {
 		if (mem->paused == value) {
-			ast_debug(1, "%spausing already-%spaused member %s queue %s\n", (value ? "" : "un"), (value ? "" : "un"),
-					mem->membername, q->name);
+			ast_debug(1, "%spausing already-%spaused member %s queue %s\n", (mem->paused) ? "" : "un",
+					(mem->paused) ? "" : "un", mem->membername, q->name);
 			return 0;
 		}
 
-		if ((mem->realtime) && (update_realtime_member_field(mem, q->name, "paused", value ? "1" : "0"))) {
-			ast_log(LOG_WARNING, "Failed %spausing realtime member %s queue %s\n", (value ? "" : "un"),
-					mem->membername, q->name);
+		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;
 		}
-		mem->paused = (value) ? 1 : 0;
-
-		if (queue_persistent_members) {
-			dump_queue_members(q);
-		}
-
-		ast_queue_log(q->name, "NONE", mem->membername, (value) ? "PAUSE" : "UNPAUSE", "%s", S_OR(reason, ""));
+
+		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",
@@ -5525,16 +5526,16 @@
 					q->name, mem->interface, mem->membername, mem->paused);
 		}
 	} else {
+		mem->penalty = value;
 		if (mem->realtime) {
 			char *rtpenalty;
-			asprintf(&rtpenalty,"%i", value);
+			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", value,
+				ast_log(LOG_WARNING, "Failed setting penalty %d on member %s queue %s\n", mem->penalty,
 					mem->membername, q->name);
 				return -1;
 			}
 		}
-		mem->penalty = value;
 
 		ast_queue_log(q->name, "NONE", mem->interface, "PENALTY", "%d", mem->penalty);
 		manager_event(EVENT_FLAG_AGENT, "QueueMemberPenalty",
@@ -5543,6 +5544,11 @@
 					"Penalty: %d\r\n",
 			q->name, mem->interface, mem->penalty);
 	}
+
+	if (queue_persistent_members) {
+		dump_queue_members(q);
+	}
+
 	return 0;
 }
 
@@ -5689,11 +5695,7 @@
 	return RESULT_FAILURE;
 }
 
-/*! \brief Reload dynamic queue members persisted into the astdb */
-static void reload_queue_members(void)
-{
-	char *cur_ptr;
-	const char *queue_name;
+static void pm_load_member_config(struct call_queue *q) {
 	char *member;
 	char *interface;
 	char *membername = NULL;
@@ -5702,10 +5704,67 @@
 	int penalty = 0;
 	char *paused_tok;
 	int paused = 0;
+	char *ignorebusy_tok;
+	int ignorebusy = 0;
+	char queue_data[PM_MAX_LEN];
+	char *cur_ptr;
+	struct member *m;
+
+	if (ast_db_get(pm_family, q->name, queue_data, PM_MAX_LEN)) {
+		return;
+	}
+
+	cur_ptr = queue_data;
+	while ((member = strsep(&cur_ptr, ",|"))) {
+		if (ast_strlen_zero(member)) {
+			continue;
+		}
+
+		interface = strsep(&member, ";");
+		penalty_tok = strsep(&member, ";");
+		paused_tok = strsep(&member, ";");
+		membername = strsep(&member, ";");
+		state_interface = strsep(&member, ";");
+		ignorebusy_tok = strsep(&member, ";");
+
+		m = interface_exists(q, interface);
+		if (m && !m->dynamic) {
+			ast_log(LOG_WARNING, "Interface %s in queue %s exists as a %s member", interface, q->name,
+						(m->realtime) ? "realtime" : "static");
+			ao2_ref(m, -1);
+			continue;
+		} else if (m) {
+			m->dead = 0;
+			ao2_ref(m, -1);
+			continue;
+		}
+
+		if (!ast_strlen_zero(penalty_tok)) {
+			penalty = atoi(penalty_tok);
+		}
+		if (!ast_strlen_zero(paused_tok)) {
+			paused = ast_true(paused_tok) ? 1 : 0;
+		}
+		if (!ast_strlen_zero(ignorebusy_tok)) {
+			ignorebusy = ast_true(ignorebusy_tok) ? 1 : 0;
+		}
+
+		ast_debug(1, "Reload Members: Queue: %s  Member: %s  Name: %s  Penalty: %d  Paused: %d\n", q->name, interface, membername, penalty, paused);
+
+		if (add_to_queue(q, interface, membername, penalty, paused, 0, state_interface, ignorebusy) == RES_OUTOFMEMORY) {
+			ast_log(LOG_ERROR, "Out of Memory when reloading persistent queue member\n");
+			continue;
+		}
+	}
+}
+
+/*! \brief Reload dynamic queue members persisted into the astdb */
+static void reload_queue_members(void)
+{
+	const char *queue_name;
 	struct ast_db_entry *db_tree;
 	struct ast_db_entry *entry;
 	struct call_queue *cur_queue;
-	char queue_data[PM_MAX_LEN];
 
 	/* Each key in 'pm_family' is the name of a queue */
 	db_tree = ast_db_gettree(pm_family, NULL);
@@ -5725,51 +5784,12 @@
 			ast_log(LOG_WARNING, "Error loading persistent queue: '%s': it does not exist\n", queue_name);
 			ast_db_del(pm_family, queue_name);
 			continue;
-		}
-
-		if (ast_db_get(pm_family, queue_name, queue_data, PM_MAX_LEN)) {
-			queue_t_unref(cur_queue, "Expire reload reference");
-			continue;
-		}
-
-		cur_ptr = queue_data;
-		while ((member = strsep(&cur_ptr, ",|"))) {
-			if (ast_strlen_zero(member))
-				continue;
-
-			interface = strsep(&member, ";");
-			penalty_tok = strsep(&member, ";");
-			paused_tok = strsep(&member, ";");
-			membername = strsep(&member, ";");
-			state_interface = strsep(&member, ";");
-
-			if (!penalty_tok) {
-				ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (penalty)\n", queue_name);
-				break;
-			}
-			penalty = strtol(penalty_tok, NULL, 10);
-			if (errno == ERANGE) {
-				ast_log(LOG_WARNING, "Error converting penalty: %s: Out of range.\n", penalty_tok);
-				break;
-			}
-			
-			if (!paused_tok) {
-				ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (paused)\n", queue_name);
-				break;
-			}
-			paused = strtol(paused_tok, NULL, 10);
-			if ((errno == ERANGE) || paused < 0 || paused > 1) {
-				ast_log(LOG_WARNING, "Error converting paused: %s: Expected 0 or 1.\n", paused_tok);
-				break;
-			}
-
-			ast_debug(1, "Reload Members: Queue: %s  Member: %s  Name: %s  Penalty: %d  Paused: %d\n", queue_name, interface, membername, penalty, paused);
-			
-			if (add_to_queue(queue_name, interface, membername, penalty, paused, 0, state_interface) == RES_OUTOFMEMORY) {
-				ast_log(LOG_ERROR, "Out of Memory when reloading persistent queue member\n");
-				break;
-			}
-		}
+		} else {
+			ao2_lock(cur_queue);
+			pm_load_member_config(cur_queue);
+			ao2_unlock(cur_queue);
+		}
+
 		queue_t_unref(cur_queue, "Expire reload reference");
 	}
 
@@ -5925,6 +5945,7 @@
 /*! \brief AddQueueMember application */
 static int aqm_exec(struct ast_channel *chan, const char *data)
 {
+	struct call_queue *q;
 	int res=-1;
 	char *parse, *temppos = NULL;
 	AST_DECLARE_APP_ARGS(args,
@@ -5934,6 +5955,7 @@
 		AST_APP_ARG(options);
 		AST_APP_ARG(membername);
 		AST_APP_ARG(state_interface);
+		AST_APP_ARG(ignorebusy);
 	);
 	int penalty = 0;
 
@@ -5953,14 +5975,20 @@
 			*temppos = '\0';
 	}
 
-	if (!ast_strlen_zero(args.penalty)) {
-		if ((sscanf(args.penalty, "%30d", &penalty) != 1) || penalty < 0) {
-			ast_log(LOG_WARNING, "Penalty '%s' is invalid, must be an integer >= 0\n", args.penalty);
-			penalty = 0;
-		}
-	}
-
-	switch (add_to_queue(args.queuename, args.interface, args.membername, penalty, 0, queue_persistent_members, args.state_interface)) {
+	if (!ast_strlen_zero(args.penalty) &&
+		((sscanf(args.penalty, "%30d", &penalty) != 1) || (penalty < 0 && !negative_penalty_invalid))) {
+		ast_log(LOG_WARNING, "Penalty '%s' is invalid, must be an integer >= 0\n", args.penalty);
+		penalty = 0;
+	}
+
+	if (!(q = load_realtime_queue(args.queuename))) {
+		ast_log(AST_LOG_ERROR, "Could not find queue %s\n", args.queuename);
+		return res;
+	}
+
+	ao2_lock(q);
+	switch (add_to_queue(q, args.interface, args.membername, penalty, 0, queue_persistent_members,
+			args.state_interface, abs(ast_true(args.ignorebusy)))) {
 	case RES_OKAY:
 		if (ast_strlen_zero(args.membername) || !log_membername_as_agent) {
 			ast_queue_log(args.queuename, chan->uniqueid, args.interface, "ADDMEMBER", "%s", "");
@@ -5985,6 +6013,8 @@
 		ast_log(LOG_ERROR, "Out of memory adding interface %s to queue %s\n", args.interface, args.queuename);
 		break;
 	}
+	ao2_unlock(q);
+	ao2_ref(q, -1);
 
 	return res;
 }
@@ -6297,7 +6327,9 @@
 		}
 
 		/* If using dynamic realtime members, we should regenerate the member list for this queue */
-		update_realtime_members(qe.parent);
+		ao2_lock(qe.parent);
+		rt_load_member_config(qe.parent);
+		ao2_unlock(qe.parent);
 		/* OK, we didn't get anybody; wait for 'retry' seconds; may get a digit to exit with */
 		res = wait_a_bit(&qe);
 		if (res)
@@ -6529,7 +6561,6 @@
 	int memvalue;
 	struct call_queue *q;
 	struct member *m;
-	char rtvalue[80];
 
 	AST_DECLARE_APP_ARGS(args,
 		AST_APP_ARG(queuename);
@@ -6554,7 +6585,10 @@
 		return -1;
 	}
 
-	memvalue = atoi(value);
+	if (sscanf(value, "%30d", &memvalue) != 1) {
+		ast_log(AST_LOG_ERROR, "Failed to read value from %s\n", value);
+		return -1;
+	}
 
 	if (!strcasecmp(args.option, "penalty")) {
 		/* if queuename = NULL then penalty will be set for interface in all the queues.*/
@@ -6571,12 +6605,10 @@
 	} else if (!ast_strlen_zero(args.queuename) && (q = load_realtime_queue(args.queuename))) {
 		ao2_lock(q);
 		if ((m = interface_exists(q, args.interface))) {
-			sprintf(rtvalue, "%s",(memvalue <= 0) ? "0" : "1");
 			if (!strcasecmp(args.option, "ignorebusy")) {
+				m->ignorebusy = (memvalue) ? 1 : 0;
 				if (m->realtime) {
-					update_realtime_member_field(m, q->name, args.option, rtvalue);
-				} else {
-					m->ignorebusy = (memvalue <= 0) ? 0 : 1;
+					update_realtime_member_field(m, q->name, args.option, value);
 				}
 			} else {
 				ast_log(LOG_ERROR, "Invalid option, only penalty , paused or ignorebusy are valid\n");
@@ -6933,16 +6965,16 @@
  */
 static void reload_single_member(const char *memberdata, struct call_queue *q)
 {
-	char *membername, *interface, *state_interface, *tmp;
+	char *membername, *interface, *state_interface;
 	char *parse;
 	struct member *cur, *newm;
-	struct member tmpmem;
-	int penalty;
+	int penalty, ignorebusy;
 	AST_DECLARE_APP_ARGS(args,
 		AST_APP_ARG(interface);
 		AST_APP_ARG(penalty);
 		AST_APP_ARG(membername);
 		AST_APP_ARG(state_interface);
+		AST_APP_ARG(ignorebusy);
 	);
 
 	if (ast_strlen_zero(memberdata)) {
@@ -6952,19 +6984,23 @@
 
 	/* Add a new member */
 	parse = ast_strdupa(memberdata);
-				
+
 	AST_STANDARD_APP_ARGS(args, parse);
 
 	interface = args.interface;
-	if (!ast_strlen_zero(args.penalty)) {
-		tmp = args.penalty;
-		ast_strip(tmp);
-		penalty = atoi(tmp);
-		if (penalty < 0) {
-			penalty = 0;
-		}
+
+	if (!ast_strlen_zero(args.penalty) &&
+		((sscanf(args.penalty, "%30d", &penalty) != 1) || (penalty < 0 && !negative_penalty_invalid))) {
+		penalty = 0;
 	} else {
 		penalty = 0;
+	}
+
+	/* negative_penalty_invalid is set and i have a invalid penalty ignoring this member */
+	if (penalty < 0) {
+		ast_log(LOG_WARNING, "Negative penalty implies invalid member %s on queue %s!\n",
+				interface, q->name);
+		return;
 	}
 
 	if (!ast_strlen_zero(args.membername)) {
@@ -6981,25 +7017,30 @@
 		state_interface = interface;
 	}
 
-	/* Find the old position in the list */
-	ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
-	cur = ao2_find(q->members, &tmpmem, OBJ_POINTER | OBJ_UNLINK);
-	if ((newm = create_queue_member(interface, membername, penalty, cur ? cur->paused : 0, state_interface))) {
+	if (!ast_strlen_zero(args.ignorebusy)) {
+		ignorebusy = abs(ast_true(args.ignorebusy));
+	} else {
+		ignorebusy = 0;
+	}
+
+	/* remove member if found*/
+	if ((cur = interface_exists(q, interface))) {
+		ao2_unlink(q->members, cur);
+		ao2_ref(cur, -1);
+	}
+
+	if ((newm = create_queue_member(interface, membername, penalty, cur ? cur->paused : 0,
+			state_interface, ignorebusy))) {
 		ao2_link(q->members, newm);
 		ao2_ref(newm, -1);
 	}
-	newm = NULL;
-
-	if (cur) {
-		ao2_ref(cur, -1);
-	}
 }
 
 static int mark_member_dead(void *obj, void *arg, int flags)
 {
 	struct member *member = obj;
 	if (!member->dynamic) {
-		member->delme = 1;
+		member->dead = 1;
 	}
 	return 0;
 }
@@ -7008,7 +7049,7 @@
 {
 	struct member *member = obj;
 
-	return (member->delme) ? CMP_MATCH : 0;
+	return (member->dead) ? CMP_MATCH : 0;
 }
 
 /*! \brief Reload information pertaining to a particular queue
@@ -7024,45 +7065,34 @@
  */
 static void reload_single_queue(struct ast_config *cfg, struct ast_flags *mask, const char *queuename)
 {
-	int new;
 	struct call_queue *q = NULL;
 	/*We're defining a queue*/
 	const char *tmpvar;
 	const int queue_reload = ast_test_flag(mask, QUEUE_RELOAD_PARAMETERS);
 	const int member_reload = ast_test_flag(mask, QUEUE_RELOAD_MEMBER);
-	int prev_weight = 0;
+	int prev_weight = 0, new = 0;
 	struct ast_variable *var;
-		if (!(q = ao2_t_find(queues, queuename, OBJ_KEY, "Find queue for reload"))) {
-		if (queue_reload) {
-			/* Make one then */
-			if (!(q = alloc_queue(queuename))) {
-				return;
-			}
+
+	if (!(q = ao2_t_find(queues, queuename, OBJ_KEY, "Find queue for reload"))) {
+		/* Make one then */
+		if (queue_reload && (q = alloc_queue(queuename))) {
+			new =1;
 		} else {
 			/* Since we're not reloading queues, this means that we found a queue
 			 * in the configuration file which we don't know about yet. Just return.
+			 * or we could not alloc a new queue
 			 */
 			return;
 		}
-		new = 1;
-	} else {
-		new = 0;
-	}
-	
-	if (!new) {
+	} else if (!q->found) {
 		ao2_lock(q);
 		prev_weight = q->weight ? 1 : 0;
-	}
-	/* Check if we already found a queue with this name in the config file */

[... 439 lines stripped ...]



More information about the svn-commits mailing list