[prev in list] [next in list] [prev in thread] [next in thread] 

List:       asterisk-biz
Subject:    Re: [asterisk-biz] Looking for a highly qualified Asterisk
From:       Danny Ashworth <dan () bluehost ! com>
Date:       2010-01-22 21:21:24
Message-ID: AE437043-3559-4FFD-9C7C-E1A9A5EAF1CE () bluehost ! com
[Download RAW message or body]

Jean,

We have had many responses.  I have attached the 1.4 patch code.  Last  
version we used the queuegroups patch on was 1.4.19.1.  We are  
currently using digium's 1.6.0.6 with one patch that was yanked out of  
the 1.6.0.19 version to attempt a fix to an issue with non-ringall  
groups.  I would need a bid from you so I can make a decision and get  
this done ASAP.
["custom_queue.patch" (custom_queue.patch)]

Index: apps/app_queue.c
===================================================================
--- apps/app_queue.c	(revision 114901)
+++ apps/app_queue.c	(working copy)
@@ -340,6 +340,7 @@
 	time_t expire;                      /*!< When this entry should expire (time out of \
queue) */  struct ast_channel *chan;           /*!< Our channel */
 	struct queue_ent *next;             /*!< The next queue entry */
+	AST_LIST_ENTRY(queue_ent) list;
 };
 
 struct member {
@@ -433,10 +434,21 @@
 	 *       in, so this can not simply be replaced with ao2_container_count(). 
 	 */
 	int membercount;
-	struct queue_ent *head;             /*!< Head of the list of callers */
+	int group_found;                    /*!< How to tell if a group has been specified \
for a queue */ +	int group_strategy_found;			/*!< How to tell if a queue's group \
specifies the strategy to use */ +	struct queue_group *group;          /*!< To which \
group does this queue belong */  AST_LIST_ENTRY(call_queue) list;    /*!< Next call \
queue */  };
 
+struct queue_group {
+	AST_LIST_HEAD(, queue_ent) callers;
+	AST_LIST_ENTRY(queue_group) list;
+	char name[80];
+	int unused;                         /*!< If a group is removed from configuration, \
this is how we detect it */ +};
+
+static AST_LIST_HEAD_STATIC(groups, queue_group);
+
 static AST_LIST_HEAD_STATIC(queues, call_queue);
 
 static int set_member_paused(const char *queuename, const char *interface, int \
paused); @@ -496,26 +508,6 @@
 	return -1;
 }
 
-/*! \brief Insert the 'new' entry after the 'prev' entry of queue 'q' */
-static inline void insert_entry(struct call_queue *q, struct queue_ent *prev, struct \
                queue_ent *new, int *pos)
-{
-	struct queue_ent *cur;
-
-	if (!q || !new)
-		return;
-	if (prev) {
-		cur = prev->next;
-		prev->next = new;
-	} else {
-		cur = q->head;
-		q->head = new;
-	}
-	new->next = cur;
-	new->parent = q;
-	new->pos = ++(*pos);
-	new->opos = *pos;
-}
-
 enum queue_member_status {
 	QUEUE_NO_MEMBERS,
 	QUEUE_NO_REACHABLE_MEMBERS,
@@ -802,6 +794,8 @@
 {
 	int i;
 
+	q->group_found = 0;
+	q->group_strategy_found = 0;
 	q->dead = 0;
 	q->retry = DEFAULT_RETRY;
 	q->timeout = -1;
@@ -941,6 +935,124 @@
 	AST_LIST_UNLOCK(&interfaces);
 }
 
+static void parse_member(struct call_queue *q, char *data)
+{
+	char parse[80];
+	char *interface;
+	char *membername = NULL;
+	int penalty;
+	AST_DECLARE_APP_ARGS(args,
+		AST_APP_ARG(interface);
+		AST_APP_ARG(penalty);
+		AST_APP_ARG(membername);
+	);
+	struct member *newm = NULL, *cur = NULL;
+	struct member tmpmem;
+	char *tmp;
+
+	/* Add a new member */
+	ast_copy_string(parse, data, sizeof(parse));
+			
+	AST_NONSTANDARD_APP_ARGS(args, parse, ',');
+
+	interface = args.interface;
+	if (!ast_strlen_zero(args.penalty)) {
+		tmp = args.penalty;
+		while (*tmp && *tmp < 33) tmp++;
+		penalty = atoi(tmp);
+		if (penalty < 0) {
+			penalty = 0;
+		}
+	} else
+		penalty = 0;
+
+	if (!ast_strlen_zero(args.membername)) {
+		membername = args.membername;
+		while (*membername && *membername < 33) membername++;
+	} else
+		membername = 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);
+
+	newm = create_queue_member(interface, membername, penalty, cur ? cur->paused : 0);
+	ao2_link(q->members, newm);
+	ao2_ref(newm, -1);
+	newm = NULL;
+
+	if (cur)
+		ao2_ref(cur, -1);
+	else {
+		/* Add them to the master int list if necessary */
+		add_to_interfaces(interface);
+		q->membercount++;
+	}
+}
+static void set_strategy(struct call_queue *q, const char *strategy)
+{
+	q->strategy = strat2int(strategy);
+	if (q->strategy < 0) {
+		ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall \
instead\n", +			strategy, q->name);
+		q->strategy = QUEUE_STRATEGY_RINGALL;
+	}
+}
+static void parse_queuegroup(struct call_queue *q, const char *groupname)
+{
+	struct ast_config *groupcfg = NULL;
+	struct ast_variable *var = NULL, *realtimevar = NULL;
+	struct queue_group *group = NULL;
+
+	/* As with other queue-related configurations, static trumps realtime.
+	 * In other words, if we find the specified group in the config file, then
+	 * we use it. We only look in realtime if we can't find the group in the config \
file +	 */
+	if (!(groupcfg = ast_config_load("queuegroups.conf"))) {
+		if (!(realtimevar = ast_load_realtime("queue_groups", "name", groupname, NULL))) {
+			ast_log(LOG_WARNING, "Group %s was specified for queue %s but could not find \
definition. Ignoring!\n", groupname, q->name); +			return;
+		}
+	}
+
+	for (var = realtimevar ? realtimevar : ast_variable_browse(groupcfg, groupname); \
var; var = var->next) { +		if (!strcasecmp(var->name, "member")) {
+			parse_member(q, var->value);
+		} else if (!strcasecmp(var->name, "strategy")) {
+			set_strategy(q, var->value);
+			q->group_strategy_found = 1;
+		} else {
+			ast_log(LOG_WARNING, "Invalid queuegroup option '%s' for queuegroup '%s'\n", \
var->name, groupname); +		}
+	}
+
+	if (groupcfg) {
+		ast_config_destroy(groupcfg);
+	} else {
+		ast_variables_destroy(realtimevar);
+	}
+
+	AST_LIST_TRAVERSE(&groups, group, list) {
+		if (!strcasecmp(group->name, groupname)) {
+			group->unused = 0;
+			q->group = group;
+			q->group_found = 1;
+			return;
+		}
+	}
+
+	/* This is a new group */
+	if (!(group = ast_calloc(1, sizeof (*group)))) {
+		/* OH SHIT */
+	}
+	ast_copy_string(group->name, groupname, sizeof(group->name));
+	AST_LIST_HEAD_INIT(&group->callers);
+	group->unused = 0;
+	q->group = group;
+	q->group_found = 1;
+	AST_LIST_INSERT_HEAD(&groups, group, list);
+}
+
 /*! \brief Configure a queue parameter.
 \par
    For error reporting, line number is passed for .conf static configuration.
@@ -1046,11 +1158,10 @@
 	} else if (!strcasecmp(param, "servicelevel")) {
 		q->servicelevel= atoi(val);
 	} else if (!strcasecmp(param, "strategy")) {
-		q->strategy = strat2int(val);
-		if (q->strategy < 0) {
-			ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall \
                instead\n",
-				val, q->name);
-			q->strategy = QUEUE_STRATEGY_RINGALL;
+		if (!q->group_strategy_found){
+			set_strategy(q, val);
+		} else {
+			ast_log(LOG_WARNING, "Ignoring strategy parameter for queue %s since its group \
specifies a strategy already\n", q->name);  }
 	} else if (!strcasecmp(param, "joinempty")) {
 		if (!strcasecmp(val, "strict"))
@@ -1086,6 +1197,12 @@
 		   we will not see any effect on use_weight until next reload. */
 	} else if (!strcasecmp(param, "timeoutrestart")) {
 		q->timeoutrestart = ast_true(val);
+	} else if (!strcasecmp(param, "group")) {
+		if (!q->group_found) {
+			parse_queuegroup(q, val);
+		} else {
+			ast_log(LOG_WARNING, "Ignoring group setting %s for queue %s since a group was \
already specified\n", val, q->name); +		}
 	} else if (failunknown) {
 		if (linenum >= 0) {
 			ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s at line %d of \
queues.conf\n", @@ -1406,7 +1523,7 @@
 static int join_queue(char *queuename, struct queue_ent *qe, enum queue_result \
*reason)  {
 	struct call_queue *q;
-	struct queue_ent *cur, *prev = NULL;
+	struct queue_ent *cur;
 	int res = -1;
 	int pos = 0;
 	int inserted = 0;
@@ -1431,23 +1548,33 @@
 		 * the queue.
 		 * Take into account the priority of the calling user */
 		inserted = 0;
-		prev = NULL;
-		cur = q->head;
-		while (cur) {
+		AST_LIST_LOCK(&q->group->callers);
+		AST_LIST_TRAVERSE_SAFE_BEGIN(&q->group->callers, cur, list) {
 			/* We have higher priority than the current user, enter
 			 * before him, after all the other users with priority
 			 * higher or equal to our priority. */
 			if ((!inserted) && (qe->prio > cur->prio)) {
-				insert_entry(q, prev, qe, &pos);
+				AST_LIST_INSERT_BEFORE_CURRENT(&q->group->callers, qe, list);
+				if (option_debug > 2)
+					ast_log(LOG_DEBUG, "Inserted %s into group %s, address %p\n", qe->chan->name, \
q->group->name, q->group); +				qe->parent = q;
+				qe->pos = ++pos;
+				qe->opos = pos;
 				inserted = 1;
 			}
 			cur->pos = ++pos;
-			prev = cur;
-			cur = cur->next;
 		}
+		AST_LIST_TRAVERSE_SAFE_END;
 		/* No luck, join at the end of the queue */
-		if (!inserted)
-			insert_entry(q, prev, qe, &pos);
+		if (!inserted) {
+			AST_LIST_INSERT_TAIL(&q->group->callers, qe, list);
+			if (option_debug > 2)
+				ast_log(LOG_DEBUG, "Inserted %s into group %s, address %p\n", qe->chan->name, \
q->group->name, q->group); +			qe->parent = q;
+			qe->pos = ++pos;
+			qe->opos = pos;
+		}
+		AST_LIST_UNLOCK(&q->group->callers);
 		ast_copy_string(qe->moh, q->moh, sizeof(qe->moh));
 		ast_copy_string(qe->announce, q->announce, sizeof(qe->announce));
 		ast_copy_string(qe->context, q->context, sizeof(qe->context));
@@ -1649,10 +1776,10 @@
 	ast_mutex_lock(&q->lock);
 
 	prev = NULL;
-	for (cur = q->head; cur; cur = cur->next) {
+	AST_LIST_LOCK(&q->group->callers);
+	AST_LIST_TRAVERSE_SAFE_BEGIN(&q->group->callers, cur, list) {
 		if (cur == qe) {
 			q->count--;
-
 			/* Take us out of the queue */
 			manager_event(EVENT_FLAG_CALL, "Leave",
 				"Channel: %s\r\nQueue: %s\r\nCount: %d\r\nUniqueid: %s\r\n",
@@ -1660,16 +1787,14 @@
 			if (option_debug)
 				ast_log(LOG_DEBUG, "Queue '%s' Leave, Channel '%s'\n", q->name, qe->chan->name \
);  /* Take us out of the queue */
-			if (prev)
-				prev->next = cur->next;
-			else
-				q->head = cur->next;
+			AST_LIST_REMOVE_CURRENT(&q->group->callers, list);
 		} else {
 			/* Renumber the people after us in the queue based on a new count */
 			cur->pos = ++pos;
-			prev = cur;
 		}
 	}
+	AST_LIST_TRAVERSE_SAFE_END;
+	AST_LIST_UNLOCK(&q->group->callers);
 	ast_mutex_unlock(&q->lock);
 
 	if (q->dead && !q->count) {	
@@ -2335,7 +2460,7 @@
 
 	if (!qe->parent->autofill) {
 		/* Atomically read the parent head -- does not need a lock */
-		ch = qe->parent->head;
+		ch = AST_LIST_FIRST(&qe->parent->group->callers);
 		/* If we are now at the top of the head, break out */
 		if (ch == qe) {
 			if (option_debug)
@@ -2351,8 +2476,6 @@
 		/* This needs a lock. How many members are available to be served? */
 		ast_mutex_lock(&qe->parent->lock);
 			
-		ch = qe->parent->head;
-	
 		if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
 			if (option_debug)
 				ast_log(LOG_DEBUG, "Even though there may be multiple members available, the \
strategy is ringall so only the head call is allowed in\n"); @@ -2378,11 +2501,16 @@
 		if (option_debug)
 			ast_log(LOG_DEBUG, "There are %d available members.\n", avl);
 	
-		while ((idx < avl) && (ch) && (ch != qe)) {
-			if (!ch->pending)
+		AST_LIST_LOCK(&qe->parent->group->callers);
+		AST_LIST_TRAVERSE(&qe->parent->group->callers, ch, list) {
+			if (idx >= avl || ch == qe) {
+				break;
+			}
+			if (!ch->pending) {
 				idx++;
-			ch = ch->next;			
+			}
 		}
+		AST_LIST_UNLOCK(&qe->parent->group->callers);
 	
 		/* If the queue entry is within avl [the number of available members] calls from \
the top ... */  if (ch && idx < avl) {
@@ -4110,30 +4238,26 @@
 	.read = queue_function_queuememberlist,
 };
 
+
+
 static int reload_queues(void)
 {
 	struct call_queue *q;
 	struct ast_config *cfg;
-	char *cat, *tmp;
+	struct member *cur;
+	char *cat;
 	struct ast_variable *var;
-	struct member *cur, *newm;
 	struct ao2_iterator mem_iter;
+	struct queue_group *group = NULL;
 	int new;
 	const char *general_val = NULL;
-	char parse[80];
-	char *interface;
-	char *membername = NULL;
-	int penalty;
-	AST_DECLARE_APP_ARGS(args,
-		AST_APP_ARG(interface);
-		AST_APP_ARG(penalty);
-		AST_APP_ARG(membername);
-	);
 	
+	
 	if (!(cfg = ast_config_load("queues.conf"))) {
 		ast_log(LOG_NOTICE, "No call queueing config file (queues.conf), so no call \
queues\n");  return 0;
 	}
+
 	AST_LIST_LOCK(&queues);
 	use_weight=0;
 	/* Mark all non-realtime queues as dead for the moment */
@@ -4143,6 +4267,10 @@
 			q->found = 0;
 		}
 	}
+	/* And mark the queue groups as unused */
+	AST_LIST_TRAVERSE(&groups, group, list) {
+		group->unused = 1;
+	}
 
 	/* Chug through config file */
 	cat = NULL;
@@ -4195,51 +4323,28 @@
 				}
 				for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
 					if (!strcasecmp(var->name, "member")) {
-						struct member tmpmem;
-						membername = NULL;
-
-						/* Add a new member */
-						ast_copy_string(parse, var->value, sizeof(parse));
-						
-						AST_NONSTANDARD_APP_ARGS(args, parse, ',');
-
-						interface = args.interface;
-						if (!ast_strlen_zero(args.penalty)) {
-							tmp = args.penalty;
-							while (*tmp && *tmp < 33) tmp++;
-							penalty = atoi(tmp);
-							if (penalty < 0) {
-								penalty = 0;
-							}
-						} else
-							penalty = 0;
-
-						if (!ast_strlen_zero(args.membername)) {
-							membername = args.membername;
-							while (*membername && *membername < 33) membername++;
-						}
-
-						/* 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);
-
-						newm = create_queue_member(interface, membername, penalty, cur ? cur->paused : \
                0);
-						ao2_link(q->members, newm);
-						ao2_ref(newm, -1);
-						newm = NULL;
-
-						if (cur)
-							ao2_ref(cur, -1);
-						else {
-							/* Add them to the master int list if necessary */
-							add_to_interfaces(interface);
-							q->membercount++;
-						}
+						parse_member(q, var->value);
 					} else {
 						queue_set_param(q, var->name, var->value, var->lineno, 1);
 					}
 				}
 
+				if (!q->group_found) {
+					/* No group was specified for this queue, so we need to
+					 * figure out if the queue used to be part of a group and if
+					 * so, was it a named one.
+					 */
+					if (!q->group || !ast_strlen_zero(q->group->name)) {
+						/* Either this is a new queue or the queue was in a group
+						 * but now is not
+						 */
+						group = ast_calloc(1, sizeof(*group));
+						*(group->name) = '\0';
+						AST_LIST_HEAD_INIT(&group->callers);
+						q->group = group;
+					}
+				}
+
 				/* Free remaining members marked as delme */
 				mem_iter = ao2_iterator_init(q->members, 0);
 				while ((cur = ao2_iterator_next(&mem_iter))) {
@@ -4286,6 +4391,13 @@
 	}
 	AST_LIST_TRAVERSE_SAFE_END;
 	AST_LIST_UNLOCK(&queues);
+	AST_LIST_TRAVERSE_SAFE_BEGIN(&groups, group, list) {
+		if (group->unused) {
+			AST_LIST_REMOVE_CURRENT(&groups, list);
+			free(group);
+		}
+	}
+	AST_LIST_TRAVERSE_SAFE_END;
 	return 1;
 }
 
@@ -4394,13 +4506,14 @@
 			astman_append(s, "   No Members%s", term);
 		else	
 			ast_cli(fd, "   No Members%s", term);
-		if (q->head) {
+		AST_LIST_LOCK(&q->group->callers);
+		if (!AST_LIST_EMPTY(&q->group->callers)) {
 			pos = 1;
 			if (s)
 				astman_append(s, "   Callers: %s", term);
 			else
 				ast_cli(fd, "   Callers: %s", term);
-			for (qe = q->head; qe; qe = qe->next) {
+			AST_LIST_TRAVERSE(&q->group->callers, qe, list) {
 				if (s)
 					astman_append(s, "      %d. %s (wait: %ld:%2.2ld, prio: %d)%s",
 						pos++, qe->chan->name, (long) (now - qe->start) / 60,
@@ -4414,6 +4527,7 @@
 			astman_append(s, "   No Callers%s", term);
 		else
 			ast_cli(fd, "   No Callers%s", term);
+		AST_LIST_UNLOCK(&q->group->callers);
 		if (s)
 			astman_append(s, "%s", term);
 		else
@@ -4534,7 +4648,8 @@
 			}
 			/* List Queue Entries */
 			pos = 1;
-			for (qe = q->head; qe; qe = qe->next) {
+			AST_LIST_LOCK(&q->group->callers);
+			AST_LIST_TRAVERSE(&q->group->callers, qe, list) {
 				astman_append(s, "Event: QueueEntry\r\n"
 					"Queue: %s\r\n"
 					"Position: %d\r\n"
@@ -4549,6 +4664,7 @@
 					S_OR(qe->chan->cid.cid_name, "unknown"),
 					(long) (now - qe->start), idText);
 			}
+			AST_LIST_UNLOCK(&q->group->callers);
 		}
 		ast_mutex_unlock(&q->lock);
 	}
@@ -4829,6 +4945,140 @@
 	return NULL;
 }
 
+static int handle_queue_group_show(int fd, int argc, char *argv[])
+{
+	char *groupname = NULL;
+	struct queue_group *group_iter;
+	struct call_queue *queue_iter;
+	struct queue_ent *qe;
+	int pos;
+	time_t now;
+	char *max;
+	char max_buf[80];
+	size_t max_left;
+	float sl = 0;
+
+	time(&now);
+
+	if (argc > 3)
+		groupname = argv[3];
+
+	AST_LIST_TRAVERSE(&groups, group_iter, list) {
+		if (ast_strlen_zero(groupname) || !strcasecmp(group_iter->name, groupname)) {
+			ast_cli(fd, "Groupname: %s\n", group_iter->name); 
+			ast_cli(fd, "Queues:\n");
+			AST_LIST_TRAVERSE(&queues, queue_iter, list) {
+				max_buf[0] = '\0';
+				max = max_buf;
+				max_left = sizeof(max_buf);
+				sl = 0;
+				if (queue_iter->callscompleted > 0)
+					sl = 100 * ((float) queue_iter->callscompletedinsl / (float) \
queue_iter->callscompleted); +				if (queue_iter->maxlen)
+					ast_build_string(&max, &max_left, "%d", queue_iter->maxlen);
+				else
+					ast_build_string(&max, &max_left, "unlimited");
+				if (!strcasecmp(queue_iter->group->name, group_iter->name)) {
+					ast_cli(fd, "%-12.12s has %d calls (max %s) in '%s' strategy (%ds holdtime), \
W:%d, C:%d, A:%d, S:%2.1f within %ds\n", +						queue_iter->name, queue_iter->count, \
max_buf, int2strat(queue_iter->strategy), queue_iter->holdtime, queue_iter->weight, \
queue_iter->callscompleted, queue_iter->callsabandoned,sl,queue_iter->servicelevel); \
+				} +			}
+			AST_LIST_LOCK(&group_iter->callers);
+			if (!AST_LIST_EMPTY(&group_iter->callers)) {
+				pos = 1;
+				ast_cli(fd, "   Callers: \n");
+				AST_LIST_TRAVERSE(&group_iter->callers, qe, list) {
+					ast_cli(fd, "      %d. %s (wait: %ld:%2.2ld, prio: %d)\n", pos++,
+						qe->chan->name, (long) (now - qe->start) / 60,
+						(long) (now - qe->start) % 60, qe->prio);
+				}
+			} else
+				ast_cli(fd, "   No Callers\n");
+			AST_LIST_UNLOCK(&group_iter->callers);
+		}
+	}
+
+	return RESULT_SUCCESS;
+}
+
+static char *complete_queue_group_show(const char *line, const char *word, int pos, \
int state) +{
+	struct queue_group *group;
+	char *ret = NULL;
+	int which = 0;
+	int wordlen = strlen(word);
+
+	if (pos != 3)
+		return NULL;
+	
+	AST_LIST_LOCK(&groups);
+	AST_LIST_TRAVERSE(&groups, group, list) {
+		if (!strncasecmp(word, group->name, wordlen) && ++which > state) {
+			ret = ast_strdup(group->name);	
+			break;
+		}
+	}
+	AST_LIST_UNLOCK(&groups);
+
+	return ret;
+}
+
+
+static int manager_queue_group_show(struct mansession *s, const struct message *m)
+{
+	const char *groupname = astman_get_header(m, "Group");
+	struct queue_group *group_iter;
+	struct call_queue *queue_iter;
+	struct queue_ent *qe;
+	int pos;
+	time_t now;
+	char *max;
+	char max_buf[80];
+	size_t max_left;
+	float sl = 0;
+
+	time(&now);
+
+	AST_LIST_TRAVERSE(&groups, group_iter, list) {
+		if (ast_strlen_zero(groupname) || !strcasecmp(group_iter->name, groupname)) {
+			astman_append(s, "Groupname: %s\r\n", group_iter->name); 
+			astman_append(s, "Queues:\r\n");
+			AST_LIST_TRAVERSE(&queues, queue_iter, list) {
+				max_buf[0] = '\0';
+				max = max_buf;
+				max_left = sizeof(max_buf);
+				sl = 0;
+				if (queue_iter->callscompleted > 0)
+					sl = 100 * ((float) queue_iter->callscompletedinsl / (float) \
queue_iter->callscompleted); +				if (queue_iter->maxlen)
+					ast_build_string(&max, &max_left, "%d", queue_iter->maxlen);
+				else
+					ast_build_string(&max, &max_left, "unlimited");
+
+				if (!strcasecmp(queue_iter->group->name, group_iter->name)) {
+					astman_append(s, "%-12.12s has %d calls (max %s) in '%s' strategy (%ds \
holdtime), W:%d, C:%d, A:%d, SL:%2.1f%% within %ds\r\n", +						queue_iter->name, \
queue_iter->count, max_buf, int2strat(queue_iter->strategy), queue_iter->holdtime, \
queue_iter->weight, +						queue_iter->callscompleted, \
queue_iter->callsabandoned,sl,queue_iter->servicelevel); +				}
+			}
+			AST_LIST_LOCK(&group_iter->callers);
+			if (!AST_LIST_EMPTY(&group_iter->callers)) {
+				pos = 1;
+				astman_append(s, "   Callers: \n");
+				AST_LIST_TRAVERSE(&group_iter->callers, qe, list) {
+					astman_append(s, "      %d. %s (wait: %ld:%2.2ld, prio: %d)\r\n", pos++,
+						qe->chan->name, (long) (now - qe->start) / 60,
+						(long) (now - qe->start) % 60, qe->prio);
+				}
+			} else
+				astman_append(s, "   No Callers\n");
+			AST_LIST_UNLOCK(&group_iter->callers);
+		}
+	}
+
+	return 0;
+}
+
 static char queue_show_usage[] =
 "Usage: queue show\n"
 "       Provides summary information on a specified queue.\n";
@@ -4839,6 +5089,11 @@
 static char qrm_cmd_usage[] =
 "Usage: queue remove member <channel> from <queue>\n";
 
+static char qgs_cmd_usage[] =
+"Usage: queue group show [groupname]\n"
+"       Shows information about group groupname. If no groupname is\n"
+"       specified, then all groups are shown\n";
+
 static struct ast_cli_entry cli_show_queue_deprecated = {
 	{ "show", "queue", NULL },
 	queue_show, NULL,
@@ -4871,6 +5126,10 @@
 	{ { "queue", "remove", "member", NULL },
 	handle_queue_remove_member, "Removes a channel from a specified queue",
 	qrm_cmd_usage, complete_queue_remove_member, &cli_remove_queue_member_deprecated },
+
+	{ { "queue", "group", "show", NULL },
+	handle_queue_group_show, "Show status of specified queue group",
+	qgs_cmd_usage, complete_queue_group_show },
 };
 
 static int unload_module(void)
@@ -4892,6 +5151,7 @@
 	res |= ast_manager_unregister("QueueAdd");
 	res |= ast_manager_unregister("QueueRemove");
 	res |= ast_manager_unregister("QueuePause");
+	res |= ast_manager_unregister("QueueGroupShow");
 	res |= ast_unregister_application(app_aqm);
 	res |= ast_unregister_application(app_rqm);
 	res |= ast_unregister_application(app_pqm);
@@ -4937,6 +5197,7 @@
 	res |= ast_manager_register("QueueAdd", EVENT_FLAG_AGENT, manager_add_queue_member, \
"Add interface to queue.");  res |= ast_manager_register("QueueRemove", \
EVENT_FLAG_AGENT, manager_remove_queue_member, "Remove interface from queue.");  res \
|= ast_manager_register("QueuePause", EVENT_FLAG_AGENT, manager_pause_queue_member, \
"Makes a queue member temporarily unavailable"); +	res |= \
ast_manager_register("QueueGroupShow", 0, manager_queue_group_show, "Queue group \
status");  res |= ast_custom_function_register(&queueagentcount_function);
 	res |= ast_custom_function_register(&queuemembercount_function);
 	res |= ast_custom_function_register(&queuememberlist_function);
Index: configs/queues.conf.sample
===================================================================
--- configs/queues.conf.sample	(revision 114901)
+++ configs/queues.conf.sample	(working copy)
@@ -278,6 +278,12 @@
 ;
 ; timeoutrestart = no
 ;
+; If a group is specified for a queue, then that group will be searched for in
+; queuegroups.conf. If a group is not found there, then it will be searched for in
+; realtime. For more information regarding queue groups, see queuegroups.conf.sample
+;
+; group = group1
+;
 ; Each member of this call queue is listed on a separate line in
 ; the form technology/dialstring.  "member" means a normal member of a
 ; queue.  An optional penalty may be specified after a comma, such that



Danny Ashworth
On Jan 21, 2010, at 5:45 PM, mtlvoip wrote:

> Hi,
> I'm interested to do that. can you send me your 1.4 patch code?
> tks,
> Jean
>
> --------------------------------------------------
> From: "Danny Ashworth" <dan@bluehost.com>
> Sent: Thursday, January 21, 2010 3:38 PM
> To: <asterisk-biz@lists.digium.com>
> Subject: [asterisk-biz] Looking for a highly qualified Asterisk  
> Programmer
>
>> We have a handful of asterisk projects that we need programmed and  
>> re-
>> programmed.  Our most pressing item is an update to a patch that we
>> paid to have created for 1.4.  The patch worked beautifully until we
>> upgraded to 1.6 and we now need someone to re-write the patch for
>> 1.6.  The basis of the patch is that if multiple agents are logged
>> into multiple queues and each of those queues have multiple holding
>> calls.  Asterisk will randomly decide which queue to pull the next
>> call from when the agent answers a call.  This is a problem because
>> the queue if pulls from may not be the queue with the call that has
>> been on hold the longest period of time.  Our patch made it so that
>> the agent would get the oldest call from all of the queues they are
>> logged into.  If you have interest in this project I can send you the
>> 1.4 patch code and we need it done ASAP and are willing to pay for
>> quick delivery.
>>
>> Danny Ashworth
>> Bluehost.com
>>
>> -- 
>> _____________________________________________________________________
>> -- Bandwidth and Colocation Provided by http://www.api-digital.com --
>>
>> asterisk-biz mailing list
>> To UNSUBSCRIBE or update options visit:
>>  http://lists.digium.com/mailman/listinfo/asterisk-biz
>>
>
> -- 
> _____________________________________________________________________
> -- Bandwidth and Colocation Provided by http://www.api-digital.com --
>
> asterisk-biz mailing list
> To UNSUBSCRIBE or update options visit:
>   http://lists.digium.com/mailman/listinfo/asterisk-biz



-- 
_____________________________________________________________________
-- Bandwidth and Colocation Provided by http://www.api-digital.com --

asterisk-biz mailing list
To UNSUBSCRIBE or update options visit:
   http://lists.digium.com/mailman/listinfo/asterisk-biz

[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic