mirror of
				https://github.com/asterisk/asterisk.git
				synced 2025-10-31 10:47:18 +00:00 
			
		
		
		
	Merge in-access updates for queue priorities (bug #1821)
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@3288 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
		
							
								
								
									
										175
									
								
								apps/app_queue.c
									
									
									
									
									
								
							
							
						
						
									
										175
									
								
								apps/app_queue.c
									
									
									
									
									
								
							| @@ -7,6 +7,8 @@ | ||||
|  * | ||||
|  * Mark Spencer <markster@linux-support.net> | ||||
|  * | ||||
|  * 2004-06-04: Priorities in queues added by inAccess Networks (work funded by Hellas On Line (HOL) www.hol.gr). | ||||
|  * | ||||
|  * These features added by David C. Troy <dave@toad.net>: | ||||
|  *    - Per-queue holdtime calculation | ||||
|  *    - Estimated holdtime announcement | ||||
| @@ -154,7 +156,8 @@ struct queue_ent { | ||||
| 	char moh[80];			/* Name of musiconhold to be used */ | ||||
| 	char announce[80];		/* Announcement to play for member when call is answered */ | ||||
| 	char context[80];		/* Context when user exits queue */ | ||||
| 	int pos;			/* Where we are in the queue */ | ||||
| 	int pos;					/* Where we are in the queue */ | ||||
| 	int prio;					/* Our priority */ | ||||
| 	int last_pos_said;              /* Last position we told the user */ | ||||
| 	time_t last_pos;                /* Last time we told the user their position */ | ||||
| 	int opos;			/* Where we started in the queue */ | ||||
| @@ -166,12 +169,12 @@ struct queue_ent { | ||||
| }; | ||||
|  | ||||
| struct member { | ||||
| 	char tech[80];			/* Technology */ | ||||
| 	char loc[256];			/* Location */ | ||||
| 	int penalty;			/* Are we a last resort? */ | ||||
| 	int calls; | ||||
| 	int dynamic;			/* Are we dynamically added? */ | ||||
| 	time_t lastcall;		/* When last successful call was hungup */ | ||||
| 	char tech[80];				/* Technology */ | ||||
| 	char loc[256];				/* Location */ | ||||
| 	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 member *next;		/* Next member */ | ||||
| }; | ||||
|  | ||||
| @@ -238,12 +241,35 @@ static int strat2int(char *strategy) | ||||
| 	return -1; | ||||
| } | ||||
|  | ||||
| /* Insert the 'new' entry after the 'prev' entry of queue 'q' */ | ||||
| static inline void insert_entry(struct ast_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; | ||||
| } | ||||
|  | ||||
| static int join_queue(char *queuename, struct queue_ent *qe) | ||||
| { | ||||
| 	struct ast_call_queue *q; | ||||
| 	struct queue_ent *cur, *prev = NULL; | ||||
| 	int res = -1; | ||||
| 	int pos = 0; | ||||
| 	int inserted = 0; | ||||
|  | ||||
| 	ast_mutex_lock(&qlock); | ||||
| 	q = queues; | ||||
| 	while(q) { | ||||
| @@ -251,32 +277,35 @@ static int join_queue(char *queuename, struct queue_ent *qe) | ||||
| 			/* This is our one */ | ||||
| 			ast_mutex_lock(&q->lock); | ||||
| 			if (q->members && (!q->maxlen || (q->count < q->maxlen))) { | ||||
| 				/* There's space for us, put us at the end */ | ||||
| 				/* There's space for us, put us at the right position inside | ||||
| 				 * the queue.  | ||||
| 				 * Take into account the priority of the calling user */ | ||||
| 				inserted = 0; | ||||
| 				prev = NULL; | ||||
| 				cur = q->head; | ||||
| 				while(cur) { | ||||
| 					/* 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); | ||||
| 						inserted = 1; | ||||
| 					} | ||||
| 					cur->pos = ++pos; | ||||
| 					prev = cur; | ||||
| 					cur = cur->next; | ||||
| 				} | ||||
| 				if (prev) | ||||
| 					prev->next = qe; | ||||
| 				else | ||||
| 					q->head = qe; | ||||
| 				/* Fix additional pointers and | ||||
| 				  information  */ | ||||
| 				qe->next = NULL; | ||||
| 				qe->parent = q; | ||||
| 				qe->pos = ++pos; | ||||
| 				qe->opos = pos; | ||||
| 				/* No luck, join at the end of the queue */ | ||||
| 				if (!inserted) | ||||
| 					insert_entry(q, prev, qe, &pos); | ||||
| 				strncpy(qe->moh, q->moh, sizeof(qe->moh)); | ||||
| 				strncpy(qe->announce, q->announce, sizeof(qe->announce)); | ||||
| 				strncpy(qe->context, q->context, sizeof(qe->context)); | ||||
| 				q->count++; | ||||
| 				res = 0; | ||||
| 				manager_event(EVENT_FLAG_CALL, "Join",  | ||||
|         	                                               	"Channel: %s\r\nCallerID: %s\r\nQueue: %s\r\nPosition: %d\r\nCount: %d\r\n", | ||||
| 	                                                       	qe->chan->name, (qe->chan->callerid ? qe->chan->callerid : "unknown"), q->name, qe->pos, q->count ); | ||||
| 					"Channel: %s\r\nCallerID: %s\r\nQueue: %s\r\nPosition: %d\r\nCount: %d\r\n", | ||||
| 					qe->chan->name, (qe->chan->callerid ? qe->chan->callerid : "unknown"), q->name, qe->pos, q->count ); | ||||
| #if 0 | ||||
| ast_log(LOG_NOTICE, "Queue '%s' Join, Channel '%s', Position '%d'\n", q->name, qe->chan->name, qe->pos ); | ||||
| #endif | ||||
| @@ -448,8 +477,8 @@ static void leave_queue(struct queue_ent *qe) | ||||
|  | ||||
| 			/* Take us out of the queue */ | ||||
| 			manager_event(EVENT_FLAG_CALL, "Leave", | ||||
|                                  "Channel: %s\r\nQueue: %s\r\nCount: %d\r\n", | ||||
| 				 qe->chan->name, q->name,  q->count); | ||||
| 				"Channel: %s\r\nQueue: %s\r\nCount: %d\r\n", | ||||
| 				qe->chan->name, q->name,  q->count); | ||||
| #if 0 | ||||
| ast_log(LOG_NOTICE, "Queue '%s' Leave, Channel '%s'\n", q->name, qe->chan->name ); | ||||
| #endif | ||||
| @@ -565,13 +594,16 @@ static int ring_one(struct queue_ent *qe, struct localuser *outgoing) | ||||
| 				} | ||||
| 			} else { | ||||
| 				/* Ring just the best channel */ | ||||
| 				ast_log(LOG_DEBUG, "Trying '%s/%s' with metric %d\n", best->tech, best->numsubst, best->metric); | ||||
| 				if (option_debug) | ||||
| 					ast_log(LOG_DEBUG, "Trying '%s/%s' with metric %d\n",  | ||||
| 									best->tech, best->numsubst, best->metric); | ||||
| 				ring_entry(qe, best); | ||||
| 			} | ||||
| 		} | ||||
| 	} while (best && !best->chan); | ||||
| 	if (!best) { | ||||
| 		ast_log(LOG_DEBUG, "Nobody left to try ringing in queue\n"); | ||||
| 		if (option_debug) | ||||
| 			ast_log(LOG_DEBUG, "Nobody left to try ringing in queue\n"); | ||||
| 		return 0; | ||||
| 	} | ||||
| 	return 1; | ||||
| @@ -784,6 +816,26 @@ static struct localuser *wait_for_answer(struct queue_ent *qe, struct localuser | ||||
| 	 | ||||
| } | ||||
|  | ||||
| static int is_our_turn(struct queue_ent *qe) | ||||
| { | ||||
| 	struct queue_ent *ch; | ||||
| 	int res; | ||||
|  | ||||
| 	/* Atomically read the parent head -- does not need a lock */ | ||||
| 	ch = qe->parent->head; | ||||
| 	/* If we are now at the top of the head, break out */ | ||||
| 	if (ch == qe) { | ||||
| 		if (option_debug) | ||||
| 			ast_log(LOG_DEBUG, "It's our turn (%s).\n", qe->chan->name); | ||||
| 		res = 1; | ||||
| 	} else { | ||||
| 		if (option_debug) | ||||
| 			ast_log(LOG_DEBUG, "It's not our turn (%s).\n", qe->chan->name); | ||||
| 		res = 0; | ||||
| 	} | ||||
| 	return res; | ||||
| } | ||||
|  | ||||
| static int wait_our_turn(struct queue_ent *qe, int ringing) | ||||
| { | ||||
| 	struct queue_ent *ch; | ||||
| @@ -796,8 +848,11 @@ static int wait_our_turn(struct queue_ent *qe, int ringing) | ||||
| 		ch = qe->parent->head; | ||||
|  | ||||
| 		/* If we are now at the top of the head, break out */ | ||||
| 		if (qe == ch) | ||||
| 		if (ch == qe) { | ||||
| 			if (option_debug) | ||||
| 				ast_log(LOG_DEBUG, "It's our turn (%s).\n", qe->chan->name); | ||||
| 			break; | ||||
| 		} | ||||
|  | ||||
| 		/* If we have timed out, break out */ | ||||
| 		if ( qe->queuetimeout ) { | ||||
| @@ -810,7 +865,6 @@ static int wait_our_turn(struct queue_ent *qe, int ringing) | ||||
| 		if (qe->parent->announcefrequency && !ringing) | ||||
| 			say_position(qe); | ||||
|  | ||||
|  | ||||
| 		/* Wait a second before checking again */ | ||||
| 		res = ast_waitfordigit(qe->chan, RECHECK * 1000); | ||||
| 		if (res) | ||||
| @@ -919,6 +973,9 @@ static int try_calling(struct queue_ent *qe, char *options, char *announceoverri | ||||
| 	struct ast_bridge_config config; | ||||
| 	/* Hold the lock while we setup the outgoing calls */ | ||||
| 	ast_mutex_lock(&qe->parent->lock); | ||||
| 	if (option_debug) | ||||
| 		ast_log(LOG_DEBUG, "%s is trying to call a queue member.\n",  | ||||
| 							qe->chan->name); | ||||
| 	strncpy(queuename, qe->parent->name, sizeof(queuename) - 1); | ||||
| 	time(&now); | ||||
| 	cur = qe->parent->members; | ||||
| @@ -952,10 +1009,12 @@ static int try_calling(struct queue_ent *qe, char *options, char *announceoverri | ||||
| 			if ((strchr(options, 'n')) && (now - qe->start >= qe->parent->timeout)) | ||||
| 				*go_on = 1; | ||||
| 		} | ||||
| 		if (url) { | ||||
| 			ast_log(LOG_DEBUG, "Queue with URL=%s_\n", url); | ||||
| 		} else  | ||||
| 			ast_log(LOG_DEBUG, "Simple queue (no URL)\n"); | ||||
| 		if (option_debug) { | ||||
| 			if (url) | ||||
| 				ast_log(LOG_DEBUG, "Queue with URL=%s_\n", url); | ||||
| 			else  | ||||
| 				ast_log(LOG_DEBUG, "Simple queue (no URL)\n"); | ||||
| 		} | ||||
|  | ||||
| 		tmp->member = cur;		/* Never directly dereference!  Could change on reload */ | ||||
| 		strncpy(tmp->tech, cur->tech, sizeof(tmp->tech)-1); | ||||
| @@ -1009,6 +1068,8 @@ static int try_calling(struct queue_ent *qe, char *options, char *announceoverri | ||||
| 				/* Nobody answered, next please? */ | ||||
| 				res=0; | ||||
| 		} | ||||
| 		if (option_debug) | ||||
| 			ast_log(LOG_DEBUG, "%s: Nobody answered.\n", qe->chan->name); | ||||
| 		goto out; | ||||
| 	} | ||||
| 	if (peer) { | ||||
| @@ -1078,7 +1139,8 @@ static int try_calling(struct queue_ent *qe, char *options, char *announceoverri | ||||
| 		/* Drop out of the queue at this point, to prepare for next caller */ | ||||
| 		leave_queue(qe);			 | ||||
|  		if( url && !ast_strlen_zero(url) && ast_channel_supports_html(peer) ) { | ||||
|  			ast_log(LOG_DEBUG, "app_queue: sendurl=%s.\n", url); | ||||
| 			if (option_debug) | ||||
| 	 			ast_log(LOG_DEBUG, "app_queue: sendurl=%s.\n", url); | ||||
|  			ast_channel_sendurl( peer, url ); | ||||
|  		} | ||||
| 		ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "CONNECT", "%ld", (long)time(NULL) - qe->start); | ||||
| @@ -1382,12 +1444,13 @@ static int queue_exec(struct ast_channel *chan, void *data) | ||||
| 	char *options = NULL; | ||||
| 	char *url = NULL; | ||||
| 	char *announceoverride = NULL; | ||||
| 	char *user_priority; | ||||
| 	int prio; | ||||
| 	char *queuetimeoutstr = NULL; | ||||
|  | ||||
| 	/* whether to exit Queue application after the timeout hits */ | ||||
| 	int go_on = 0; | ||||
|  | ||||
|  | ||||
|  | ||||
| 	/* Our queue entry */ | ||||
| 	struct queue_ent qe; | ||||
| 	 | ||||
| @@ -1424,29 +1487,49 @@ static int queue_exec(struct ast_channel *chan, void *data) | ||||
| 						qe.queuetimeout = atoi(queuetimeoutstr); | ||||
| 					} else { | ||||
| 						qe.queuetimeout = 0; | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	/* Get the priority from the variable ${QUEUE_PRIO} */ | ||||
| 	user_priority = pbx_builtin_getvar_helper(chan, "QUEUE_PRIO"); | ||||
| 	if (user_priority) { | ||||
| 		if (sscanf(user_priority, "%d", &prio) == 1) { | ||||
| 			if (option_debug) | ||||
| 				ast_log(LOG_DEBUG, "%s: Got priority %d from ${QUEUE_PRIO}.\n", | ||||
| 								chan->name, prio); | ||||
| 		} else { | ||||
| 			ast_log(LOG_WARNING, "${QUEUE_PRIO}: Invalid value (%s), channel %s.\n", | ||||
| 							user_priority, chan->name); | ||||
| 			prio = 0; | ||||
| 		} | ||||
| 	} else { | ||||
| 		if (option_debug) | ||||
| 			ast_log(LOG_DEBUG, "NO QUEUE_PRIO variable found. Using default.\n"); | ||||
| 		prio = 0; | ||||
| 	} | ||||
|  | ||||
| 	if (options) { | ||||
| 		if (strchr(options, 'r')) { | ||||
| 			ringing = 1; | ||||
| 	  	} | ||||
|         } | ||||
|  | ||||
| 	printf("queue: %s, options: %s, url: %s, announce: %s, timeout: %d\n", | ||||
| 		queuename, options, url, announceoverride, qe.queuetimeout); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| //	if (option_debug)  | ||||
| 		ast_log(LOG_DEBUG, "queue: %s, options: %s, url: %s, announce: %s, timeout: %d, priority: %d\n", | ||||
| 				queuename, options, url, announceoverride, qe.queuetimeout, (int)prio); | ||||
|  | ||||
| 	qe.chan = chan; | ||||
| 	qe.start = time(NULL); | ||||
| 	qe.prio = (int)prio; | ||||
| 	qe.last_pos_said = 0; | ||||
| 	qe.last_pos = 0; | ||||
| 	if (!join_queue(queuename, &qe)) { | ||||
| 		ast_queue_log(queuename, chan->uniqueid, "NONE", "ENTERQUEUE", "%s|%s", url ? url : "", chan->callerid ? chan->callerid : ""); | ||||
| 		/* Start music on hold */ | ||||
| check_turns: | ||||
| 		if (ringing) { | ||||
| 			ast_indicate(chan, AST_CONTROL_RINGING); | ||||
| 		} else {               | ||||
| @@ -1532,7 +1615,15 @@ static int queue_exec(struct ast_channel *chan, void *data) | ||||
| 					res = 0; | ||||
| 					break; | ||||
| 				} | ||||
|  | ||||
| 				/* Since this is a priority queue and  | ||||
| 				 * it is not sure that we are still at the head | ||||
| 				 * of the queue, go and check for our turn again. | ||||
| 				 */ | ||||
| 				if (!is_our_turn(&qe)) { | ||||
| 					ast_log(LOG_DEBUG, "Darn priorities, going back in queue (%s)!\n", | ||||
| 								qe.chan->name); | ||||
| 					goto check_turns; | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		/* Don't allow return code > 0 */ | ||||
| @@ -1733,7 +1824,7 @@ static void reload_queues(void) | ||||
| 			if (!q->count) { | ||||
| 				free(q); | ||||
| 			} else | ||||
| 				ast_log(LOG_WARNING, "XXX Leaking a litttle memory :( XXX\n"); | ||||
| 				ast_log(LOG_WARNING, "XXX Leaking a little memory :( XXX\n"); | ||||
| 		} else | ||||
| 			ql = q; | ||||
| 		q = qn; | ||||
| @@ -1809,8 +1900,8 @@ static int __queues_show(int fd, int argc, char **argv, int queue_show) | ||||
| 			pos = 1; | ||||
| 			ast_cli(fd, "   Callers: \n"); | ||||
| 			for (qe = q->head; qe; qe = qe->next)  | ||||
| 				ast_cli(fd, "      %d. %s (wait: %ld:%2.2ld)\n", pos++, qe->chan->name, | ||||
| 								(long)(now - qe->start) / 60, (long)(now - qe->start) % 60); | ||||
| 				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_cli(fd, "\n"); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user