mirror of
				https://github.com/asterisk/asterisk.git
				synced 2025-10-26 06:26:41 +00:00 
			
		
		
		
	add realtime support to app_queue for static members/agents (bug #4037)
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@5821 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
		
							
								
								
									
										673
									
								
								apps/app_queue.c
									
									
									
									
									
								
							
							
						
						
									
										673
									
								
								apps/app_queue.c
									
									
									
									
									
								
							| @@ -250,6 +250,7 @@ struct member { | |||||||
| 	int status;			/* Status of queue member */ | 	int status;			/* Status of queue member */ | ||||||
| 	int paused;			/* Are we paused (not accepting calls)? */ | 	int paused;			/* Are we paused (not accepting calls)? */ | ||||||
| 	time_t lastcall;		/* When last successful call was hungup */ | 	time_t lastcall;		/* When last successful call was hungup */ | ||||||
|  | 	int dead;			/* Used to detect members deleted in realtime */ | ||||||
| 	struct member *next;		/* Next member */ | 	struct member *next;		/* Next member */ | ||||||
| }; | }; | ||||||
|  |  | ||||||
| @@ -276,6 +277,7 @@ struct ast_call_queue { | |||||||
| 		unsigned int announceholdtime:2; | 		unsigned int announceholdtime:2; | ||||||
| 		unsigned int strategy:3; | 		unsigned int strategy:3; | ||||||
| 		unsigned int maskmemberstatus:1; | 		unsigned int maskmemberstatus:1; | ||||||
|  | 		unsigned int realtime:1; | ||||||
| 	int announcefrequency;          /* How often to announce their position */ | 	int announcefrequency;          /* How often to announce their position */ | ||||||
| 	int roundingseconds;            /* How many seconds do we round to? */ | 	int roundingseconds;            /* How many seconds do we round to? */ | ||||||
| 	int holdtime;                   /* Current avg holdtime, based on recursive boxcar filter */ | 	int holdtime;                   /* Current avg holdtime, based on recursive boxcar filter */ | ||||||
| @@ -336,7 +338,7 @@ static char *int2strat(int strategy) | |||||||
| 	return "<unknown>"; | 	return "<unknown>"; | ||||||
| } | } | ||||||
|  |  | ||||||
| static int strat2int(char *strategy) | static int strat2int(const char *strategy) | ||||||
| { | { | ||||||
| 	int x; | 	int x; | ||||||
| 	for (x=0;x<sizeof(strategies) / sizeof(strategies[0]);x++) { | 	for (x=0;x<sizeof(strategies) / sizeof(strategies[0]);x++) { | ||||||
| @@ -473,68 +475,439 @@ static int statechange_queue(const char *dev, int state, void *ign) | |||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static struct member *create_queue_member(char *interface, int penalty, int paused) | ||||||
|  | { | ||||||
|  | 	struct member *cur; | ||||||
|  | 	 | ||||||
|  | 	/* Add a new member */ | ||||||
|  |  | ||||||
|  | 	cur = malloc(sizeof(struct member)); | ||||||
|  |  | ||||||
|  | 	if (cur) { | ||||||
|  | 		memset(cur, 0, sizeof(struct member)); | ||||||
|  | 		cur->penalty = penalty; | ||||||
|  | 		cur->paused = paused; | ||||||
|  | 		ast_copy_string(cur->interface, interface, sizeof(cur->interface)); | ||||||
|  | 		if (!strchr(cur->interface, '/')) | ||||||
|  | 			ast_log(LOG_WARNING, "No location at interface '%s'\n", interface); | ||||||
|  | 		cur->status = ast_device_state(interface); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return cur; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static struct ast_call_queue *alloc_queue(const char *queuename) | ||||||
|  | { | ||||||
|  | 	struct ast_call_queue *q; | ||||||
|  |  | ||||||
|  | 	q = malloc(sizeof(*q)); | ||||||
|  | 	if (q) { | ||||||
|  | 		memset(q, 0, sizeof(*q)); | ||||||
|  | 		ast_mutex_init(&q->lock); | ||||||
|  | 		ast_copy_string(q->name, queuename, sizeof(q->name)); | ||||||
|  | 	} | ||||||
|  | 	return q; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void init_queue(struct ast_call_queue *q) | ||||||
|  | { | ||||||
|  | 	q->dead = 0; | ||||||
|  | 	q->retry = DEFAULT_RETRY; | ||||||
|  | 	q->timeout = -1; | ||||||
|  | 	q->maxlen = 0; | ||||||
|  | 	q->announcefrequency = 0; | ||||||
|  | 	q->announceholdtime = 0; | ||||||
|  | 	q->roundingseconds = 0; /* Default - don't announce seconds */ | ||||||
|  | 	q->servicelevel = 0; | ||||||
|  | 	q->moh[0] = '\0'; | ||||||
|  | 	q->announce[0] = '\0'; | ||||||
|  | 	q->context[0] = '\0'; | ||||||
|  | 	q->monfmt[0] = '\0'; | ||||||
|  | 	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_calls, "queue-callswaiting", sizeof(q->sound_calls)); | ||||||
|  | 	ast_copy_string(q->sound_holdtime, "queue-holdtime", sizeof(q->sound_holdtime)); | ||||||
|  | 	ast_copy_string(q->sound_minutes, "queue-minutes", sizeof(q->sound_minutes)); | ||||||
|  | 	ast_copy_string(q->sound_seconds, "queue-seconds", sizeof(q->sound_seconds)); | ||||||
|  | 	ast_copy_string(q->sound_thanks, "queue-thankyou", sizeof(q->sound_thanks)); | ||||||
|  | 	ast_copy_string(q->sound_lessthan, "queue-less-than", sizeof(q->sound_lessthan)); | ||||||
|  | 	ast_copy_string(q->sound_reporthold, "queue-reporthold", sizeof(q->sound_reporthold)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void clear_queue(struct ast_call_queue *q) | ||||||
|  | { | ||||||
|  | 	q->holdtime = 0; | ||||||
|  | 	q->callscompleted = 0; | ||||||
|  | 	q->callsabandoned = 0; | ||||||
|  | 	q->callscompletedinsl = 0; | ||||||
|  | 	q->wrapuptime = 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Configure a queue parameter. | ||||||
|  |    For error reporting, line number is passed for .conf static configuration. | ||||||
|  |    For Realtime queues, linenum is -1. | ||||||
|  |    The failunknown flag is set for config files (and static realtime) to show | ||||||
|  |    errors for unknown parameters. It is cleared for dynamic realtime to allow | ||||||
|  |    extra fields in the tables. */ | ||||||
|  | static void queue_set_param(struct ast_call_queue *q, const char *param, const char *val, int linenum, int failunknown) | ||||||
|  | { | ||||||
|  | 	if (!strcasecmp(param, "music") || !strcasecmp(param, "musiconhold")) { | ||||||
|  | 		ast_copy_string(q->moh, val, sizeof(q->moh)); | ||||||
|  | 	} else if (!strcasecmp(param, "announce")) { | ||||||
|  | 		ast_copy_string(q->announce, val, sizeof(q->announce)); | ||||||
|  | 	} else if (!strcasecmp(param, "context")) { | ||||||
|  | 		ast_copy_string(q->context, val, sizeof(q->context)); | ||||||
|  | 	} else if (!strcasecmp(param, "timeout")) { | ||||||
|  | 		q->timeout = atoi(val); | ||||||
|  | 		if (q->timeout < 0) | ||||||
|  | 			q->timeout = DEFAULT_TIMEOUT; | ||||||
|  | 	} else if (!strcasecmp(param, "monitor-join")) { | ||||||
|  | 		q->monjoin = ast_true(val); | ||||||
|  | 	} else if (!strcasecmp(param, "monitor-format")) { | ||||||
|  | 		ast_copy_string(q->monfmt, val, sizeof(q->monfmt)); | ||||||
|  | 	} else if (!strcasecmp(param, "queue-youarenext")) { | ||||||
|  | 		ast_copy_string(q->sound_next, val, sizeof(q->sound_next)); | ||||||
|  | 	} else if (!strcasecmp(param, "queue-thereare")) { | ||||||
|  | 		ast_copy_string(q->sound_thereare, val, sizeof(q->sound_thereare)); | ||||||
|  | 	} else if (!strcasecmp(param, "queue-callswaiting")) { | ||||||
|  | 		ast_copy_string(q->sound_calls, val, sizeof(q->sound_calls)); | ||||||
|  | 	} else if (!strcasecmp(param, "queue-holdtime")) { | ||||||
|  | 		ast_copy_string(q->sound_holdtime, val, sizeof(q->sound_holdtime)); | ||||||
|  | 	} else if (!strcasecmp(param, "queue-minutes")) { | ||||||
|  | 		ast_copy_string(q->sound_minutes, val, sizeof(q->sound_minutes)); | ||||||
|  | 	} else if (!strcasecmp(param, "queue-seconds")) { | ||||||
|  | 		ast_copy_string(q->sound_seconds, val, sizeof(q->sound_seconds)); | ||||||
|  | 	} else if (!strcasecmp(param, "queue-lessthan")) { | ||||||
|  | 		ast_copy_string(q->sound_lessthan, val, sizeof(q->sound_lessthan)); | ||||||
|  | 	} else if (!strcasecmp(param, "queue-thankyou")) { | ||||||
|  | 		ast_copy_string(q->sound_thanks, val, sizeof(q->sound_thanks)); | ||||||
|  | 	} else if (!strcasecmp(param, "queue-reporthold")) { | ||||||
|  | 		ast_copy_string(q->sound_reporthold, val, sizeof(q->sound_reporthold)); | ||||||
|  | 	} else if (!strcasecmp(param, "announce-frequency")) { | ||||||
|  | 		q->announcefrequency = atoi(val); | ||||||
|  | 	} else if (!strcasecmp(param, "announce-round-seconds")) { | ||||||
|  | 		q->roundingseconds = atoi(val); | ||||||
|  | 		if (q->roundingseconds>60 || q->roundingseconds<0) { | ||||||
|  | 			if (linenum >= 0) { | ||||||
|  | 				ast_log(LOG_WARNING, "'%s' isn't a valid value for %s " | ||||||
|  | 					"using 0 instead for queue '%s' at line %d of queues.conf\n", | ||||||
|  | 					val, param, q->name, linenum); | ||||||
|  | 			} else { | ||||||
|  | 				ast_log(LOG_WARNING, "'%s' isn't a valid value for %s " | ||||||
|  | 					"using 0 instead for queue '%s'\n", val, param, q->name); | ||||||
|  | 			} | ||||||
|  | 			q->roundingseconds=0; | ||||||
|  | 		} | ||||||
|  | 	} else if (!strcasecmp(param, "announce-holdtime")) { | ||||||
|  | 		if (!strcasecmp(val, "once")) | ||||||
|  | 			q->announceholdtime = ANNOUNCEHOLDTIME_ONCE; | ||||||
|  | 		else if (ast_true(val)) | ||||||
|  | 			q->announceholdtime = ANNOUNCEHOLDTIME_ALWAYS; | ||||||
|  | 		else | ||||||
|  | 			q->announceholdtime = 0; | ||||||
|  | 	} else if (!strcasecmp(param, "retry")) { | ||||||
|  | 		q->retry = atoi(val); | ||||||
|  | 		if (q->retry < 0) | ||||||
|  | 			q->retry = DEFAULT_RETRY; | ||||||
|  | 	} else if (!strcasecmp(param, "wrapuptime")) { | ||||||
|  | 		q->wrapuptime = atoi(val); | ||||||
|  | 	} else if (!strcasecmp(param, "maxlen")) { | ||||||
|  | 		q->maxlen = atoi(val); | ||||||
|  | 		if (q->maxlen < 0) | ||||||
|  | 			q->maxlen = 0; | ||||||
|  | 	} 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 = 0; | ||||||
|  | 		} | ||||||
|  | 	} else if (!strcasecmp(param, "joinempty")) { | ||||||
|  | 		if (!strcasecmp(val, "strict")) | ||||||
|  | 			q->joinempty = QUEUE_EMPTY_STRICT; | ||||||
|  | 		else if (ast_true(val)) | ||||||
|  | 			q->joinempty = QUEUE_EMPTY_NORMAL; | ||||||
|  | 		else | ||||||
|  | 			q->joinempty = 0; | ||||||
|  | 	} else if (!strcasecmp(param, "leavewhenempty")) { | ||||||
|  | 		if (!strcasecmp(val, "strict")) | ||||||
|  | 			q->leavewhenempty = QUEUE_EMPTY_STRICT; | ||||||
|  | 		else if (ast_true(val)) | ||||||
|  | 			q->leavewhenempty = QUEUE_EMPTY_NORMAL; | ||||||
|  | 		else | ||||||
|  | 			q->leavewhenempty = 0; | ||||||
|  | 	} else if (!strcasecmp(param, "eventmemberstatus")) { | ||||||
|  | 		q->maskmemberstatus = !ast_true(val); | ||||||
|  | 	} else if (!strcasecmp(param, "eventwhencalled")) { | ||||||
|  | 		q->eventwhencalled = ast_true(val); | ||||||
|  | 	} else if (!strcasecmp(param, "reportholdtime")) { | ||||||
|  | 		q->reportholdtime = ast_true(val); | ||||||
|  | 	} else if (!strcasecmp(param, "memberdelay")) { | ||||||
|  | 		q->memberdelay = atoi(val); | ||||||
|  | 	} else if (!strcasecmp(param, "weight")) { | ||||||
|  | 		q->weight = atoi(val); | ||||||
|  | 		if (q->weight) | ||||||
|  | 			use_weight++; | ||||||
|  | 		/* With Realtime queues, if the last queue using weights is deleted in realtime, | ||||||
|  | 		   we will not see any effect on use_weight until next reload. */ | ||||||
|  | 	} else if (!strcasecmp(param, "timeoutrestart")) { | ||||||
|  | 		q->timeoutrestart = ast_true(val); | ||||||
|  | 	} else if(failunknown) { | ||||||
|  | 		if (linenum >= 0) { | ||||||
|  | 			ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s at line %d of queues.conf\n", | ||||||
|  | 				q->name, param, linenum); | ||||||
|  | 		} else { | ||||||
|  | 			ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s\n", q->name, param); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void rt_handle_member_record(struct ast_call_queue *q, char *interface, const char *penalty_str) | ||||||
|  | { | ||||||
|  | 	struct member *m, *prev_m; | ||||||
|  | 	int penalty = 0; | ||||||
|  |  | ||||||
|  | 	if(penalty_str) { | ||||||
|  | 		penalty = atoi(penalty_str); | ||||||
|  | 		if(penalty < 0) | ||||||
|  | 			penalty = 0; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/* Find the member, or the place to put a new one. */ | ||||||
|  | 	prev_m = NULL; | ||||||
|  | 	m = q->members; | ||||||
|  | 	while (m && strcmp(m->interface, interface)) { | ||||||
|  | 		prev_m = m; | ||||||
|  | 		m = m->next; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/* Create a new one if not found, else update penalty */ | ||||||
|  | 	if (!m) { | ||||||
|  | 		m = create_queue_member(interface, penalty, 0); | ||||||
|  | 		if (m) { | ||||||
|  | 			m->dead = 0; | ||||||
|  | 			if (prev_m) { | ||||||
|  | 				prev_m->next = m; | ||||||
|  | 			} else { | ||||||
|  | 				q->members = m; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} else { | ||||||
|  | 		m->dead = 0;	/* Do not delete this one. */ | ||||||
|  | 		m->penalty = penalty; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* Reload a single queue via realtime. Return the queue, or NULL if it doesn't exist. | ||||||
|  |    Should be called with the global qlock locked. | ||||||
|  |    When found, the queue is returned with q->lock locked. */ | ||||||
|  | static struct ast_call_queue *reload_queue_rt(const char *queuename, struct ast_variable *queue_vars, struct ast_config *member_config) | ||||||
|  | { | ||||||
|  | 	struct ast_variable *v; | ||||||
|  | 	struct ast_call_queue *q, *prev_q; | ||||||
|  | 	struct member *m, *prev_m, *next_m; | ||||||
|  | 	char *interface; | ||||||
|  | 	char *tmp, *tmp_name; | ||||||
|  | 	char tmpbuf[64];	/* Must be longer than the longest queue param name. */ | ||||||
|  |  | ||||||
|  | 	/* Find the queue in the in-core list (we will create a new one if not found). */ | ||||||
|  | 	q = queues; | ||||||
|  | 	prev_q = NULL; | ||||||
|  | 	while (q) { | ||||||
|  | 		if (!strcasecmp(q->name, queuename)) { | ||||||
|  | 			break; | ||||||
|  | 		} | ||||||
|  | 		q = q->next; | ||||||
|  | 		prev_q = q; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/* Static queues override realtime. */ | ||||||
|  | 	if (q) { | ||||||
|  | 		ast_mutex_lock(&q->lock); | ||||||
|  | 		if (!q->realtime) { | ||||||
|  | 			if (q->dead) { | ||||||
|  | 				ast_mutex_unlock(&q->lock); | ||||||
|  | 				return NULL; | ||||||
|  | 			} else { | ||||||
|  | 				return q; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/* Check if queue is defined in realtime. */ | ||||||
|  | 	if (!queue_vars) { | ||||||
|  | 		/* Delete queue from in-core list if it has been deleted in realtime. */ | ||||||
|  | 		if (q) { | ||||||
|  | 			/* Hmm, can't seem to distinguish a DB failure from a not | ||||||
|  | 			   found condition... So we might delete an in-core queue | ||||||
|  | 			   in case of DB failure. */ | ||||||
|  | 			ast_log(LOG_DEBUG, "Queue %s not found in realtime.\n", queuename); | ||||||
|  |  | ||||||
|  | 			q->dead = 1; | ||||||
|  | 			/* Delete if unused (else will be deleted when last caller leaves). */ | ||||||
|  | 			if (!q->count) { | ||||||
|  | 				/* Delete. */ | ||||||
|  | 				if (!prev_q) { | ||||||
|  | 					queues = q->next; | ||||||
|  | 				} else { | ||||||
|  | 					prev_q->next = q->next; | ||||||
|  | 				} | ||||||
|  | 				ast_mutex_unlock(&q->lock); | ||||||
|  | 				free(q); | ||||||
|  | 			} else | ||||||
|  | 				ast_mutex_unlock(&q->lock); | ||||||
|  | 		} | ||||||
|  | 		return NULL; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/* Create a new queue if an in-core entry does not exist yet. */ | ||||||
|  | 	if (!q) { | ||||||
|  | 		q = alloc_queue(queuename); | ||||||
|  | 		if (!q) | ||||||
|  | 			return NULL; | ||||||
|  | 		ast_mutex_lock(&q->lock); | ||||||
|  | 		clear_queue(q); | ||||||
|  | 		q->realtime = 1; | ||||||
|  | 		q->next = queues; | ||||||
|  | 		queues = q; | ||||||
|  | 	} | ||||||
|  | 	init_queue(q);		/* Ensure defaults for all parameters not set explicitly. */ | ||||||
|  |  | ||||||
|  | 	v = queue_vars; | ||||||
|  | 	memset(tmpbuf, 0, sizeof(tmpbuf)); | ||||||
|  | 	while(v) { | ||||||
|  | 		/* Convert to dashes `-' from underscores `_' as the latter are more SQL friendly. */ | ||||||
|  | 		if((tmp = strchr(v->name, '_')) != NULL) { | ||||||
|  | 			ast_copy_string(tmpbuf, v->name, sizeof(tmpbuf)); | ||||||
|  | 			tmp_name = tmpbuf; | ||||||
|  | 			tmp = tmp_name; | ||||||
|  | 			while((tmp = strchr(tmp, '_')) != NULL) | ||||||
|  | 				*tmp++ = '-'; | ||||||
|  | 		} else | ||||||
|  | 			tmp_name = v->name; | ||||||
|  | 		queue_set_param(q, tmp_name, v->value, -1, 0); | ||||||
|  | 		v = v->next; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/* Temporarily set members dead so we can detect deleted ones. */ | ||||||
|  | 	m = q->members; | ||||||
|  | 	while (m) { | ||||||
|  | 		m->dead = 1; | ||||||
|  | 		m = m->next; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	interface = ast_category_browse(member_config, NULL); | ||||||
|  | 	while (interface) { | ||||||
|  | 		rt_handle_member_record(q, interface, ast_variable_retrieve(member_config, interface, "penalty")); | ||||||
|  | 		interface = ast_category_browse(member_config, interface); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/* Delete all realtime members that have been deleted in DB. */ | ||||||
|  | 	m = q->members; | ||||||
|  | 	prev_m = NULL; | ||||||
|  | 	while (m) { | ||||||
|  | 		next_m = m->next; | ||||||
|  | 		if (m->dead) { | ||||||
|  | 			if (prev_m) { | ||||||
|  | 				prev_m->next = next_m; | ||||||
|  | 			} else { | ||||||
|  | 				q->members = next_m; | ||||||
|  | 			} | ||||||
|  | 			free(m); | ||||||
|  | 		} else { | ||||||
|  | 			prev_m = m; | ||||||
|  | 		} | ||||||
|  | 		m = next_m; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return q; | ||||||
|  | } | ||||||
|  |  | ||||||
| static int join_queue(char *queuename, struct queue_ent *qe, enum queue_result *reason) | static int join_queue(char *queuename, struct queue_ent *qe, enum queue_result *reason) | ||||||
| { | { | ||||||
|  | 	struct ast_variable *queue_vars = NULL; | ||||||
|  | 	struct ast_config *member_config = NULL; | ||||||
| 	struct ast_call_queue *q; | 	struct ast_call_queue *q; | ||||||
| 	struct queue_ent *cur, *prev = NULL; | 	struct queue_ent *cur, *prev = NULL; | ||||||
| 	int res = -1; | 	int res = -1; | ||||||
| 	int pos = 0; | 	int pos = 0; | ||||||
| 	int inserted = 0; | 	int inserted = 0; | ||||||
|  | 	enum queue_member_status stat; | ||||||
|  |  | ||||||
|  | 	/* Load from realtime before taking the global qlock, to avoid blocking all | ||||||
|  | 	   queue operations while waiting for the DB. | ||||||
|  |  | ||||||
|  | 	   This will be two separate database transactions, so we might | ||||||
|  | 	   see queue parameters as they were before another process | ||||||
|  | 	   changed the queue and member list as it was after the change. | ||||||
|  | 	   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, NULL); | ||||||
|  | 	if(queue_vars) | ||||||
|  | 		member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", queuename, NULL); | ||||||
|  |  | ||||||
| 	ast_mutex_lock(&qlock); | 	ast_mutex_lock(&qlock); | ||||||
| 	for (q = queues; q; q = q->next) { | 	q = reload_queue_rt(queuename, queue_vars, member_config); | ||||||
| 		if (!strcasecmp(q->name, queuename)) { | 	/* Note: If found, reload_queue_rt() returns with q->lock locked. */ | ||||||
| 			enum queue_member_status stat; | 	if(member_config) | ||||||
| 			/* This is our one */ | 		ast_config_destroy(member_config); | ||||||
| 			ast_mutex_lock(&q->lock); | 	if(queue_vars) | ||||||
| 			stat = get_member_status(q); | 		ast_variables_destroy(queue_vars); | ||||||
| 			if (!q->joinempty && (stat == QUEUE_NO_MEMBERS)) |  | ||||||
| 				*reason = QUEUE_JOINEMPTY; | 	if (!q) { | ||||||
| 			else if ((q->joinempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) | 		ast_mutex_unlock(&qlock); | ||||||
| 				*reason = QUEUE_JOINUNAVAIL; | 		return res; | ||||||
| 			else if (q->maxlen && (q->count >= q->maxlen)) | 	} | ||||||
| 				*reason = QUEUE_FULL; |  | ||||||
| 			else { | 	/* This is our one */ | ||||||
| 				/* There's space for us, put us at the right position inside | 	stat = get_member_status(q); | ||||||
| 				 * the queue.  | 	if (!q->joinempty && (stat == QUEUE_NO_MEMBERS)) | ||||||
| 				 * Take into account the priority of the calling user */ | 		*reason = QUEUE_JOINEMPTY; | ||||||
| 				inserted = 0; | 	else if ((q->joinempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) | ||||||
| 				prev = NULL; | 		*reason = QUEUE_JOINUNAVAIL; | ||||||
| 				cur = q->head; | 	else if (q->maxlen && (q->count >= q->maxlen)) | ||||||
| 				while(cur) { | 		*reason = QUEUE_FULL; | ||||||
| 					/* We have higher priority than the current user, enter | 	else { | ||||||
| 					 * before him, after all the other users with priority | 		/* There's space for us, put us at the right position inside | ||||||
| 					 * higher or equal to our priority. */ | 		 * the queue.  | ||||||
| 					if ((!inserted) && (qe->prio > cur->prio)) { | 		 * Take into account the priority of the calling user */ | ||||||
| 						insert_entry(q, prev, qe, &pos); | 		inserted = 0; | ||||||
| 						inserted = 1; | 		prev = NULL; | ||||||
| 					} | 		cur = q->head; | ||||||
| 					cur->pos = ++pos; | 		while(cur) { | ||||||
| 					prev = cur; | 			/* We have higher priority than the current user, enter | ||||||
| 					cur = cur->next; | 			 * before him, after all the other users with priority | ||||||
| 				} | 			 * higher or equal to our priority. */ | ||||||
| 				/* No luck, join at the end of the queue */ | 			if ((!inserted) && (qe->prio > cur->prio)) { | ||||||
| 				if (!inserted) | 				insert_entry(q, prev, qe, &pos); | ||||||
| 					insert_entry(q, prev, qe, &pos); | 				inserted = 1; | ||||||
| 				ast_copy_string(qe->moh, q->moh, sizeof(qe->moh)); | 			} | ||||||
| 				ast_copy_string(qe->announce, q->announce, sizeof(qe->announce)); | 			cur->pos = ++pos; | ||||||
| 				ast_copy_string(qe->context, q->context, sizeof(qe->context)); | 			prev = cur; | ||||||
| 				q->count++; | 			cur = cur->next; | ||||||
| 				res = 0; | 		} | ||||||
| 				manager_event(EVENT_FLAG_CALL, "Join",  | 		/* No luck, join at the end of the queue */ | ||||||
| 					"Channel: %s\r\nCallerID: %s\r\nCallerIDName: %s\r\nQueue: %s\r\nPosition: %d\r\nCount: %d\r\n", | 		if (!inserted) | ||||||
| 					qe->chan->name,  | 			insert_entry(q, prev, qe, &pos); | ||||||
| 					qe->chan->cid.cid_num ? qe->chan->cid.cid_num : "unknown", | 		ast_copy_string(qe->moh, q->moh, sizeof(qe->moh)); | ||||||
| 					qe->chan->cid.cid_name ? qe->chan->cid.cid_name : "unknown", | 		ast_copy_string(qe->announce, q->announce, sizeof(qe->announce)); | ||||||
| 					q->name, qe->pos, q->count ); | 		ast_copy_string(qe->context, q->context, sizeof(qe->context)); | ||||||
|  | 		q->count++; | ||||||
|  | 		res = 0; | ||||||
|  | 		manager_event(EVENT_FLAG_CALL, "Join",  | ||||||
|  | 			      "Channel: %s\r\nCallerID: %s\r\nCallerIDName: %s\r\nQueue: %s\r\nPosition: %d\r\nCount: %d\r\n", | ||||||
|  | 			      qe->chan->name,  | ||||||
|  | 			      qe->chan->cid.cid_num ? qe->chan->cid.cid_num : "unknown", | ||||||
|  | 			      qe->chan->cid.cid_name ? qe->chan->cid.cid_name : "unknown", | ||||||
|  | 			      q->name, qe->pos, q->count ); | ||||||
| #if 0 | #if 0 | ||||||
| ast_log(LOG_NOTICE, "Queue '%s' Join, Channel '%s', Position '%d'\n", q->name, qe->chan->name, qe->pos ); | ast_log(LOG_NOTICE, "Queue '%s' Join, Channel '%s', Position '%d'\n", q->name, qe->chan->name, qe->pos ); | ||||||
| #endif | #endif | ||||||
| 			} |  | ||||||
| 			ast_mutex_unlock(&q->lock); |  | ||||||
| 			break; |  | ||||||
| 		} |  | ||||||
| 	} | 	} | ||||||
|  | 	ast_mutex_unlock(&q->lock); | ||||||
| 	ast_mutex_unlock(&qlock); | 	ast_mutex_unlock(&qlock); | ||||||
| 	return res; | 	return res; | ||||||
| } | } | ||||||
| @@ -1793,27 +2166,6 @@ static struct member * interface_exists(struct ast_call_queue *q, char *interfac | |||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| static struct member *create_queue_node(char *interface, int penalty, int paused) |  | ||||||
| { |  | ||||||
| 	struct member *cur; |  | ||||||
| 	 |  | ||||||
| 	/* Add a new member */ |  | ||||||
|  |  | ||||||
| 	cur = malloc(sizeof(struct member)); |  | ||||||
|  |  | ||||||
| 	if (cur) { |  | ||||||
| 		memset(cur, 0, sizeof(struct member)); |  | ||||||
| 		cur->penalty = penalty; |  | ||||||
| 		cur->paused = paused; |  | ||||||
| 		ast_copy_string(cur->interface, interface, sizeof(cur->interface)); |  | ||||||
| 		if (!strchr(cur->interface, '/')) |  | ||||||
| 			ast_log(LOG_WARNING, "No location at interface '%s'\n", interface); |  | ||||||
| 		cur->status = ast_device_state(interface); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return cur; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /* Dump all members in a specific queue to the databse | /* Dump all members in a specific queue to the databse | ||||||
|  * |  * | ||||||
|  * <pm_family>/<queuename> = <interface>;<penalty>;<paused>[|...] |  * <pm_family>/<queuename> = <interface>;<penalty>;<paused>[|...] | ||||||
| @@ -1909,7 +2261,7 @@ static int add_to_queue(char *queuename, char *interface, int penalty, int pause | |||||||
| 		ast_mutex_lock(&q->lock); | 		ast_mutex_lock(&q->lock); | ||||||
| 		if (!strcmp(q->name, queuename)) { | 		if (!strcmp(q->name, queuename)) { | ||||||
| 			if (interface_exists(q, interface) == NULL) { | 			if (interface_exists(q, interface) == NULL) { | ||||||
| 				new_member = create_queue_node(interface, penalty, paused); | 				new_member = create_queue_member(interface, penalty, paused); | ||||||
|  |  | ||||||
| 				if (new_member != NULL) { | 				if (new_member != NULL) { | ||||||
| 					new_member->dynamic = 1; | 					new_member->dynamic = 1; | ||||||
| @@ -2540,12 +2892,15 @@ static void reload_queues(void) | |||||||
| 	struct member *prev, *cur; | 	struct member *prev, *cur; | ||||||
| 	int new; | 	int new; | ||||||
| 	char *general_val = NULL; | 	char *general_val = NULL; | ||||||
|  | 	char interface[80]; | ||||||
|  | 	int penalty; | ||||||
| 	 | 	 | ||||||
| 	cfg = ast_config_load("queues.conf"); | 	cfg = ast_config_load("queues.conf"); | ||||||
| 	if (!cfg) { | 	if (!cfg) { | ||||||
| 		ast_log(LOG_NOTICE, "No call queueing config file (queues.conf), so no call queues\n"); | 		ast_log(LOG_NOTICE, "No call queueing config file (queues.conf), so no call queues\n"); | ||||||
| 		return; | 		return; | ||||||
| 	} | 	} | ||||||
|  | 	memset(interface, 0, sizeof(interface)); | ||||||
| 	ast_mutex_lock(&qlock); | 	ast_mutex_lock(&qlock); | ||||||
| 	use_weight=0; | 	use_weight=0; | ||||||
| 	/* Mark all queues as dead for the moment */ | 	/* Mark all queues as dead for the moment */ | ||||||
| @@ -2557,7 +2912,12 @@ static void reload_queues(void) | |||||||
| 	/* Chug through config file */ | 	/* Chug through config file */ | ||||||
| 	cat = ast_category_browse(cfg, NULL); | 	cat = ast_category_browse(cfg, NULL); | ||||||
| 	while(cat) { | 	while(cat) { | ||||||
| 		if (strcasecmp(cat, "general")) {	/* Define queue */ | 		if (!strcasecmp(cat, "general")) {	 | ||||||
|  | 			/* Initialize global settings */ | ||||||
|  | 			queue_persistent_members = 0; | ||||||
|  | 			if ((general_val = ast_variable_retrieve(cfg, "general", "persistentmembers"))) | ||||||
|  | 				queue_persistent_members = ast_true(general_val); | ||||||
|  | 		} else {	/* Define queue */ | ||||||
| 			/* Look for an existing one */ | 			/* Look for an existing one */ | ||||||
| 			q = queues; | 			q = queues; | ||||||
| 			while(q) { | 			while(q) { | ||||||
| @@ -2567,47 +2927,17 @@ static void reload_queues(void) | |||||||
| 			} | 			} | ||||||
| 			if (!q) { | 			if (!q) { | ||||||
| 				/* Make one then */ | 				/* Make one then */ | ||||||
| 				q = malloc(sizeof(struct ast_call_queue)); | 				q = alloc_queue(cat); | ||||||
| 				if (q) { | 				new = 1; | ||||||
| 					/* Initialize it */ |  | ||||||
| 					memset(q, 0, sizeof(struct ast_call_queue)); |  | ||||||
| 					ast_mutex_init(&q->lock); |  | ||||||
| 					ast_copy_string(q->name, cat, sizeof(q->name)); |  | ||||||
| 					new = 1; |  | ||||||
| 				} else new = 0; |  | ||||||
| 			} else | 			} else | ||||||
| 					new = 0; | 				new = 0; | ||||||
| 			if (q) { | 			if (q) { | ||||||
| 				if (!new)  | 				if (!new) | ||||||
| 					ast_mutex_lock(&q->lock); | 					ast_mutex_lock(&q->lock); | ||||||
| 				/* Re-initialize the queue */ | 				/* Re-initialize the queue, and clear statistics */ | ||||||
| 				q->dead = 0; | 				init_queue(q); | ||||||
| 				q->retry = DEFAULT_RETRY; | 				clear_queue(q); | ||||||
| 				q->timeout = -1; |  | ||||||
| 				q->maxlen = 0; |  | ||||||
| 				q->announcefrequency = 0; |  | ||||||
| 				q->announceholdtime = 0; |  | ||||||
| 				q->roundingseconds = 0; /* Default - don't announce seconds */ |  | ||||||
| 				q->holdtime = 0; |  | ||||||
| 				q->callscompleted = 0; |  | ||||||
| 				q->callsabandoned = 0; |  | ||||||
| 				q->callscompletedinsl = 0; |  | ||||||
| 				q->servicelevel = 0; |  | ||||||
| 				q->wrapuptime = 0; |  | ||||||
| 				free_members(q, 0); | 				free_members(q, 0); | ||||||
| 				q->moh[0] = '\0'; |  | ||||||
| 				q->announce[0] = '\0'; |  | ||||||
| 				q->context[0] = '\0'; |  | ||||||
| 				q->monfmt[0] = '\0'; |  | ||||||
| 				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_calls, "queue-callswaiting", sizeof(q->sound_calls)); |  | ||||||
| 				ast_copy_string(q->sound_holdtime, "queue-holdtime", sizeof(q->sound_holdtime)); |  | ||||||
| 				ast_copy_string(q->sound_minutes, "queue-minutes", sizeof(q->sound_minutes)); |  | ||||||
| 				ast_copy_string(q->sound_seconds, "queue-seconds", sizeof(q->sound_seconds)); |  | ||||||
| 				ast_copy_string(q->sound_thanks, "queue-thankyou", sizeof(q->sound_thanks)); |  | ||||||
| 				ast_copy_string(q->sound_lessthan, "queue-less-than", sizeof(q->sound_lessthan)); |  | ||||||
| 				ast_copy_string(q->sound_reporthold, "queue-reporthold", sizeof(q->sound_reporthold)); |  | ||||||
| 				prev = q->members; | 				prev = q->members; | ||||||
| 				if (prev) { | 				if (prev) { | ||||||
| 					/* find the end of any dynamic members */ | 					/* find the end of any dynamic members */ | ||||||
| @@ -2618,123 +2948,29 @@ static void reload_queues(void) | |||||||
| 				while(var) { | 				while(var) { | ||||||
| 					if (!strcasecmp(var->name, "member")) { | 					if (!strcasecmp(var->name, "member")) { | ||||||
| 						/* Add a new member */ | 						/* Add a new member */ | ||||||
| 						cur = malloc(sizeof(struct member)); | 						ast_copy_string(interface, var->value, sizeof(interface)); | ||||||
| 						if (cur) { | 						if ((tmp = strchr(interface, ','))) { | ||||||
| 							memset(cur, 0, sizeof(struct member)); | 							*tmp = '\0'; | ||||||
| 							ast_copy_string(cur->interface, var->value, sizeof(cur->interface)); | 							tmp++; | ||||||
| 							if ((tmp = strchr(cur->interface, ','))) { | 							penalty = atoi(tmp); | ||||||
| 								*tmp = '\0'; | 							if (penalty < 0) { | ||||||
| 								tmp++; | 								penalty = 0; | ||||||
| 								cur->penalty = atoi(tmp); |  | ||||||
| 								if (cur->penalty < 0) |  | ||||||
| 									cur->penalty = 0; |  | ||||||
| 							} | 							} | ||||||
| 							if (!strchr(cur->interface, '/')) | 						} else | ||||||
| 								ast_log(LOG_WARNING, "No location at line %d of queue.conf\n", var->lineno); | 							penalty = 0; | ||||||
|  | 						cur = create_queue_member(interface, penalty, 0); | ||||||
|  | 						if (cur) { | ||||||
| 							if (prev) | 							if (prev) | ||||||
| 								prev->next = cur; | 								prev->next = cur; | ||||||
| 							else | 							else | ||||||
| 								q->members = cur; | 								q->members = cur; | ||||||
| 							prev = cur; | 							prev = cur; | ||||||
| 						} | 						} | ||||||
| 					} else if (!strcasecmp(var->name, "music") || !strcasecmp(var->name, "musiconhold")) { |  | ||||||
| 						ast_copy_string(q->moh, var->value, sizeof(q->moh)); |  | ||||||
| 					} else if (!strcasecmp(var->name, "announce")) { |  | ||||||
| 						ast_copy_string(q->announce, var->value, sizeof(q->announce)); |  | ||||||
| 					} else if (!strcasecmp(var->name, "context")) { |  | ||||||
| 						ast_copy_string(q->context, var->value, sizeof(q->context)); |  | ||||||
| 					} else if (!strcasecmp(var->name, "timeout")) { |  | ||||||
| 						q->timeout = atoi(var->value); |  | ||||||
| 					} else if (!strcasecmp(var->name, "monitor-join")) { |  | ||||||
| 						q->monjoin = ast_true(var->value); |  | ||||||
| 					} else if (!strcasecmp(var->name, "monitor-format")) { |  | ||||||
| 						ast_copy_string(q->monfmt, var->value, sizeof(q->monfmt)); |  | ||||||
| 					} else if (!strcasecmp(var->name, "queue-youarenext")) { |  | ||||||
| 						ast_copy_string(q->sound_next, var->value, sizeof(q->sound_next)); |  | ||||||
| 					} else if (!strcasecmp(var->name, "queue-thereare")) { |  | ||||||
| 						ast_copy_string(q->sound_thereare, var->value, sizeof(q->sound_thereare)); |  | ||||||
| 					} else if (!strcasecmp(var->name, "queue-callswaiting")) { |  | ||||||
| 						ast_copy_string(q->sound_calls, var->value, sizeof(q->sound_calls)); |  | ||||||
| 					} else if (!strcasecmp(var->name, "queue-holdtime")) { |  | ||||||
| 						ast_copy_string(q->sound_holdtime, var->value, sizeof(q->sound_holdtime)); |  | ||||||
| 					} else if (!strcasecmp(var->name, "queue-minutes")) { |  | ||||||
| 						ast_copy_string(q->sound_minutes, var->value, sizeof(q->sound_minutes)); |  | ||||||
| 					} else if (!strcasecmp(var->name, "queue-seconds")) { |  | ||||||
| 						ast_copy_string(q->sound_seconds, var->value, sizeof(q->sound_seconds)); |  | ||||||
| 					} else if (!strcasecmp(var->name, "queue-lessthan")) { |  | ||||||
| 						ast_copy_string(q->sound_lessthan, var->value, sizeof(q->sound_lessthan)); |  | ||||||
| 					} else if (!strcasecmp(var->name, "queue-thankyou")) { |  | ||||||
| 						ast_copy_string(q->sound_thanks, var->value, sizeof(q->sound_thanks)); |  | ||||||
| 					} else if (!strcasecmp(var->name, "queue-reporthold")) { |  | ||||||
| 						ast_copy_string(q->sound_reporthold, var->value, sizeof(q->sound_reporthold)); |  | ||||||
| 					} else if (!strcasecmp(var->name, "announce-frequency")) { |  | ||||||
| 						q->announcefrequency = atoi(var->value); |  | ||||||
| 					} else if (!strcasecmp(var->name, "announce-round-seconds")) { |  | ||||||
| 						q->roundingseconds = atoi(var->value); |  | ||||||
| 						if(q->roundingseconds>60 || q->roundingseconds<0) { |  | ||||||
| 							ast_log(LOG_WARNING, "'%s' isn't a valid value for queue-rounding-seconds using 0 instead at line %d of queue.conf\n", var->value, var->lineno); |  | ||||||
| 							q->roundingseconds=0; |  | ||||||
| 						} |  | ||||||
| 					} else if (!strcasecmp(var->name, "announce-holdtime")) { |  | ||||||
| 						if (!strcasecmp(var->value, "once")) |  | ||||||
| 							q->announceholdtime = ANNOUNCEHOLDTIME_ONCE; |  | ||||||
| 						else if (ast_true(var->value)) |  | ||||||
| 							q->announceholdtime = ANNOUNCEHOLDTIME_ALWAYS; |  | ||||||
| 						else |  | ||||||
| 							q->announceholdtime = 0; |  | ||||||
| 					} else if (!strcasecmp(var->name, "retry")) { |  | ||||||
| 						q->retry = atoi(var->value); |  | ||||||
| 					} else if (!strcasecmp(var->name, "wrapuptime")) { |  | ||||||
| 						q->wrapuptime = atoi(var->value); |  | ||||||
| 					} else if (!strcasecmp(var->name, "maxlen")) { |  | ||||||
| 						q->maxlen = atoi(var->value); |  | ||||||
| 					} else if (!strcasecmp(var->name, "servicelevel")) { |  | ||||||
| 						q->servicelevel= atoi(var->value); |  | ||||||
| 					} else if (!strcasecmp(var->name, "strategy")) { |  | ||||||
| 						q->strategy = strat2int(var->value); |  | ||||||
| 						if (q->strategy < 0) { |  | ||||||
| 							ast_log(LOG_WARNING, "'%s' isn't a valid strategy, using ringall instead\n", var->value); |  | ||||||
| 							q->strategy = 0; |  | ||||||
| 						} |  | ||||||
| 					} else if (!strcasecmp(var->name, "joinempty")) { |  | ||||||
| 						if (!strcasecmp(var->value, "strict")) |  | ||||||
| 							q->joinempty = QUEUE_EMPTY_STRICT; |  | ||||||
| 						else if (ast_true(var->value)) |  | ||||||
| 							q->joinempty = QUEUE_EMPTY_NORMAL; |  | ||||||
| 						else |  | ||||||
| 							q->joinempty = 0; |  | ||||||
| 					} else if (!strcasecmp(var->name, "leavewhenempty")) { |  | ||||||
| 						if (!strcasecmp(var->value, "strict")) |  | ||||||
| 							q->leavewhenempty = QUEUE_EMPTY_STRICT; |  | ||||||
| 						else if (ast_true(var->value)) |  | ||||||
| 							q->leavewhenempty = QUEUE_EMPTY_NORMAL; |  | ||||||
| 						else |  | ||||||
| 							q->leavewhenempty = 0; |  | ||||||
| 					} else if (!strcasecmp(var->name, "eventmemberstatus")) { |  | ||||||
| 						q->maskmemberstatus = !ast_true(var->value); |  | ||||||
| 					} else if (!strcasecmp(var->name, "eventwhencalled")) { |  | ||||||
| 						q->eventwhencalled = ast_true(var->value); |  | ||||||
| 					} else if (!strcasecmp(var->name, "reportholdtime")) { |  | ||||||
| 						q->reportholdtime = ast_true(var->value); |  | ||||||
| 					} else if (!strcasecmp(var->name, "memberdelay")) { |  | ||||||
| 						q->memberdelay = atoi(var->value); |  | ||||||
| 					} else if (!strcasecmp(var->name, "weight")) { |  | ||||||
| 						q->weight = atoi(var->value); |  | ||||||
| 						if (q->weight) |  | ||||||
| 							use_weight++; |  | ||||||
| 					} else if (!strcasecmp(var->name, "timeoutrestart")) { |  | ||||||
| 						q->timeoutrestart = ast_true(var->value); |  | ||||||
| 					} else { | 					} else { | ||||||
| 						ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s at line %d of queue.conf\n", cat, var->name, var->lineno); | 						queue_set_param(q, var->name, var->value, var->lineno, 1); | ||||||
| 					} | 					} | ||||||
| 					var = var->next; | 					var = var->next; | ||||||
| 				} | 				} | ||||||
| 				if (q->retry < 0) |  | ||||||
| 					q->retry = DEFAULT_RETRY; |  | ||||||
| 				if (q->timeout < 0) |  | ||||||
| 					q->timeout = DEFAULT_TIMEOUT; |  | ||||||
| 				if (q->maxlen < 0) |  | ||||||
| 					q->maxlen = 0; |  | ||||||
| 				if (!new)  | 				if (!new)  | ||||||
| 					ast_mutex_unlock(&q->lock); | 					ast_mutex_unlock(&q->lock); | ||||||
| 				if (new) { | 				if (new) { | ||||||
| @@ -2742,11 +2978,6 @@ static void reload_queues(void) | |||||||
| 					queues = q; | 					queues = q; | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 		} else {	 |  | ||||||
| 			/* Initialize global settings */ |  | ||||||
| 			queue_persistent_members = 0; |  | ||||||
| 			if ((general_val = ast_variable_retrieve(cfg, "general", "persistentmembers"))) |  | ||||||
| 			    queue_persistent_members = ast_true(general_val); |  | ||||||
| 		} | 		} | ||||||
| 		cat = ast_category_browse(cfg, cat); | 		cat = ast_category_browse(cfg, cat); | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -34,4 +34,6 @@ | |||||||
| ;sippeers => odbc,asterisk | ;sippeers => odbc,asterisk | ||||||
| ;voicemail => odbc,asterisk | ;voicemail => odbc,asterisk | ||||||
| ;extensions => odbc,asterisk | ;extensions => odbc,asterisk | ||||||
|  | ;queues => odbc,asterisk | ||||||
|  | ;queue_members => odbc,asterisk | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user