Skip to content
Snippets Groups Projects
app_queue.c 37.7 KiB
Newer Older
  • Learn to ignore specific revisions
  • 	if (!data) {
    		ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename|optional interface)\n");
    		return -1;
    	}
    	
    	LOCAL_USER_ADD(u); // not sure if we need this, but better be safe than sorry ;-)
    	
    	/* Parse our arguments XXX Check for failure XXX */
    	strncpy(info, (char *)data, strlen((char *)data) + AST_MAX_EXTENSION-1);
    	queuename = info;
    	if (queuename) {
    		interface = strchr(queuename, '|');
    		if (interface) {
    			*interface = '\0';
    			interface++;
    		}
    		else
    			interface = chan->name ;
    	}
    
    	if( ( q = queues) != NULL )
    	{
    		while( q && ( res != 0 ) && (!found) ) 
    		{
    			ast_pthread_mutex_lock(&q->lock);
    			if( strcmp( q->name, queuename) == 0 )
    			{
    				// found queue, try to enable interface
    				found=1 ;
    
    				if( interface_exists( q, interface ) == NULL )
    				{
    					save = q->members ;
    					q->members = create_queue_node( interface ) ;
    
    					if( q->members != NULL )
    						q->members->next = save ;
    					else
    						q->members = save ;
    
    					ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", interface, queuename);
    					res = 0 ;
    				}
    				else
    					ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': "
    						"Already there\n", interface, queuename);
    			}
    
    			ast_pthread_mutex_unlock(&q->lock);
    			q = q->next;
    		}
    	}
    
    	if( ! found )
    		ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", queuename);
    
    	LOCAL_USER_REMOVE(u);
    	return res;
    }
    
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int queue_exec(struct ast_channel *chan, void *data)
    {
    	int res=-1;
    	struct localuser *u;
    	char *queuename;
    	char info[512];
    	char *options = NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char *url = NULL;
    	char *announceoverride = NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	
    	/* Our queue entry */
    	struct queue_ent qe;
    	
    	if (!data) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_log(LOG_WARNING, "Queue requires an argument (queuename|optional timeout|optional URL)\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    	}
    	
    	LOCAL_USER_ADD(u);
    	
    	/* Parse our arguments XXX Check for failure XXX */
    	strncpy(info, (char *)data, strlen((char *)data) + AST_MAX_EXTENSION-1);
    	queuename = info;
    	if (queuename) {
    		options = strchr(queuename, '|');
    		if (options) {
    			*options = '\0';
    			options++;
    
    Mark Spencer's avatar
    Mark Spencer committed
    			url = strchr(options, '|');
    			if (url) {
    				*url = '\0';
    				url++;
    				announceoverride = strchr(url, '|');
    				if (announceoverride) {
    					*announceoverride = '\0';
    					announceoverride++;
    				}
    			}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	printf("queue: %s, options: %s, url: %s, announce: %s\n",
    		queuename, options, url, announceoverride);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Setup our queue entry */
    	memset(&qe, 0, sizeof(qe));
    	qe.chan = chan;
    	qe.start = time(NULL);
    	if (!join_queue(queuename, &qe)) {
    		/* Start music on hold */
    		ast_moh_start(chan, qe.moh);
    		for (;;) {
    			res = wait_our_turn(&qe);
    			/* If they hungup, return immediately */
    			if (res < 0) {
    				if (option_verbose > 2) {
    					ast_verbose(VERBOSE_PREFIX_3 "User disconnected while waiting their turn\n");
    					res = -1;
    				}
    				break;
    			}
    			if (!res)
    				break;
    			if (valid_exit(&qe, res))
    				break;
    		}
    		if (!res) {
    			for (;;) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    				res = try_calling(&qe, options, announceoverride, url);
    
    Mark Spencer's avatar
    Mark Spencer committed
    				if (res)
    					break;
    				res = wait_a_bit(&qe);
    				if (res < 0) {
    					if (option_verbose > 2) {
    						ast_verbose(VERBOSE_PREFIX_3 "User disconnected when they almost made it\n");
    						res = -1;
    					}
    					break;
    				}
    				if (res && valid_exit(&qe, res))
    					break;
    			}
    		}
    		/* Don't allow return code > 0 */
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (res > 0 && res != AST_PBX_KEEPALIVE) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			res = 0;	
    
    Mark Spencer's avatar
    Mark Spencer committed
    			ast_moh_stop(chan);
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    		leave_queue(&qe);
    	} else {
    		ast_log(LOG_WARNING, "Unable to join queue '%s'\n", queuename);
    		res =  0;
    	}
    	LOCAL_USER_REMOVE(u);
    	return res;
    }
    
    static void reload_queues(void)
    {
    	struct ast_call_queue *q, *ql, *qn;
    	struct ast_config *cfg;
    	char *cat, *tmp;
    	struct ast_variable *var;
    	struct member *prev, *cur;
    	int new;
    	cfg = ast_load("queues.conf");
    	if (!cfg) {
    		ast_log(LOG_NOTICE, "No call queueing config file, so no call queues\n");
    		return;
    	}
    	ast_pthread_mutex_lock(&qlock);
    	/* Mark all queues as dead for the moment */
    	q = queues;
    	while(q) {
    		q->dead = 1;
    		q = q->next;
    	}
    	/* Chug through config file */
    	cat = ast_category_browse(cfg, NULL);
    	while(cat) {
    		if (strcasecmp(cat, "general")) {
    			/* Look for an existing one */
    			q = queues;
    			while(q) {
    				if (!strcmp(q->name, cat))
    					break;
    				q = q->next;
    			}
    			if (!q) {
    				/* Make one then */
    				q = malloc(sizeof(struct ast_call_queue));
    				if (q) {
    					/* Initialize it */
    					memset(q, 0, sizeof(struct ast_call_queue));
    
    Mark Spencer's avatar
    Mark Spencer committed
    					ast_pthread_mutex_init(&q->lock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    					strncpy(q->name, cat, sizeof(q->name));
    					new = 1;
    				} else new = 0;
    			} else
    					new = 0;
    			if (q) {
    				if (!new) 
    					ast_pthread_mutex_lock(&q->lock);
    				/* Re-initialize the queue */
    				q->dead = 0;
    				q->retry = 0;
    				q->timeout = -1;
    				q->maxlen = 0;
    				free_members(q);
    				strcpy(q->moh, "");
    				strcpy(q->announce, "");
    				strcpy(q->context, "");
    				prev = NULL;
    				var = ast_variable_browse(cfg, cat);
    				while(var) {
    					if (!strcasecmp(var->name, "member")) {
    						/* Add a new member */
    						cur = malloc(sizeof(struct member));
    						if (cur) {
    							memset(cur, 0, sizeof(struct member));
    							strncpy(cur->tech, var->value, sizeof(cur->tech) - 1);
    
    							if ((tmp = strchr(cur->tech, ','))) {
    								*tmp = '\0';
    								tmp++;
    								cur->penalty = atoi(tmp);
    								if (cur->penalty < 0)
    									cur->penalty = 0;
    							}
    
    Mark Spencer's avatar
    Mark Spencer committed
    							if ((tmp = strchr(cur->tech, '/')))
    								*tmp = '\0';
    							if ((tmp = strchr(var->value, '/'))) {
    								tmp++;
    								strncpy(cur->loc, tmp, sizeof(cur->loc) - 1);
    
    								if ((tmp = strchr(cur->loc, ',')))
    									*tmp = '\0';
    
    Mark Spencer's avatar
    Mark Spencer committed
    							} else
    								ast_log(LOG_WARNING, "No location at line %d of queue.conf\n", var->lineno);
    							if (prev)
    								prev->next = cur;
    							else
    								q->members = cur;
    							prev = cur;
    						}
    					} else if (!strcasecmp(var->name, "music")) {
    						strncpy(q->moh, var->value, sizeof(q->moh) - 1);
    					} else if (!strcasecmp(var->name, "announce")) {
    						strncpy(q->announce, var->value, sizeof(q->announce) - 1);
    					} else if (!strcasecmp(var->name, "context")) {
    						strncpy(q->context, var->value, sizeof(q->context) - 1);
    					} else if (!strcasecmp(var->name, "timeout")) {
    						q->timeout = atoi(var->value);
    					} else if (!strcasecmp(var->name, "retry")) {
    						q->retry = atoi(var->value);
    					} else if (!strcasecmp(var->name, "maxlen")) {
    						q->maxlen = 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;
    						}
    
    Mark Spencer's avatar
    Mark Spencer committed
    					} else {
    						ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s at line %d of queue.conf\n", cat, var->name, var->lineno);
    					}
    					var = var->next;
    				}
    				if (q->retry < 1)
    					q->retry = DEFAULT_RETRY;
    				if (q->timeout < 0)
    					q->timeout = DEFAULT_TIMEOUT;
    				if (q->maxlen < 0)
    					q->maxlen = 0;
    				if (!new) 
    					ast_pthread_mutex_unlock(&q->lock);
    				if (new) {
    					q->next = queues;
    					queues = q;
    				}
    			}
    		}
    		cat = ast_category_browse(cfg, cat);
    	}
    
    	ast_destroy(cfg);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	q = queues;
    	ql = NULL;
    	while(q) {
    		qn = q->next;
    		if (q->dead) {
    			if (ql)
    				ql->next = q->next;
    			else
    				queues = q->next;
    			if (!q->count) {
    				free(q);
    			} else
    				ast_log(LOG_WARNING, "XXX Leaking a litttle memory :( XXX\n");
    		} else
    			ql = q;
    		q = qn;
    	}
    	ast_pthread_mutex_unlock(&qlock);
    }
    
    static int queues_show(int fd, int argc, char **argv)
    {
    	struct ast_call_queue *q;
    	struct queue_ent *qe;
    	struct member *mem;
    	int pos;
    	time_t now;
    	char max[80];
    
    Mark Spencer's avatar
    Mark Spencer committed
    	
    	time(&now);
    	if (argc != 2)
    		return RESULT_SHOWUSAGE;
    	q = queues;
    	if (!q) {	
    		ast_cli(fd, "No queues.\n");
    		return RESULT_SUCCESS;
    	}
    	while(q) {
    		ast_pthread_mutex_lock(&q->lock);
    		if (q->maxlen)
    			snprintf(max, sizeof(max), "%d", q->maxlen);
    		else
    			strcpy(max, "unlimited");
    
    		ast_cli(fd, "%-12.12s has %d calls (max %s) in '%s' strategy\n", q->name, q->count, max, int2strat(q->strategy));
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (q->members) {
    			ast_cli(fd, "   Members: \n");
    
    			for (mem = q->members; mem; mem = mem->next) {
    				if (mem->penalty)
    					snprintf(max, sizeof(max), " with penalty %d", mem->penalty);
    				else
    					strcpy(max, "");
    
    				if (mem->calls) {
    					snprintf(calls, sizeof(calls), " has taken %d calls (last was %ld secs ago)",
    							mem->calls, time(NULL) - mem->lastcall);
    				} else
    					strcpy(calls, " has taken no calls yet");
    				ast_cli(fd, "      %s/%s%s%s\n", mem->tech, mem->loc, max, calls);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		} else
    			ast_cli(fd, "   No Members\n");
    		if (q->head) {
    			pos = 1;
    			ast_cli(fd, "   Callers: \n");
    			for (qe = q->head; qe; qe = qe->next) 
    				ast_cli(fd, "      %d. %s (wait: %d:%02.2d)\n", pos++, qe->chan->name,
    								(now - qe->start) / 60, (now - qe->start) % 60);
    		} else
    			ast_cli(fd, "   No Callers\n");
    		ast_cli(fd, "\n");
    		ast_pthread_mutex_unlock(&q->lock);
    		q = q->next;
    	}
    	return RESULT_SUCCESS;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    /* JDG: callback to display queues status in manager */
    static int manager_queues_show( struct mansession *s, struct message *m )
    {
    	char *a[] = { "show", "queues" };
    	return queues_show( s->fd, 2, a );
    } /* /JDG */
    
    
    
    /* Dump queue status */
    static int manager_queues_status( struct mansession *s, struct message *m )
    {
    	time_t now;
    	int pos;
    	struct ast_call_queue *q;
    	struct queue_ent *qe;
    	astman_send_ack(s, "Queue status will follow");
    	time(&now);
    	q = queues;
    	while(q) {
    		ast_pthread_mutex_lock(&q->lock);
    		ast_cli(s->fd, "Event: QueueParams\r\n"
    					"Queue: %s\r\n"
    					"Max: %d\r\n"
    					"Calls: %d\r\n"
    					"\r\n",
    						q->name, q->maxlen, q->count);
    #if 0
    		/* Do we care about queue members? */					
    		for (mem = q->members; mem; mem = mem->next) 
    			ast_cli(fd, "      %s/%s\n", mem->tech, mem->loc);
    #endif			
    		pos = 1;
    		for (qe = q->head; qe; qe = qe->next) 
    			ast_cli(s->fd, "Event: QueueMember\r\n"
    				"Queue: %s\r\n"
    				"Position: %d\r\n"
    				"Channel: %s\r\n"
    				"CallerID: %s\r\n"
    				"Wait: %ld\r\n"
    				"\r\n", 
    
    Mark Spencer's avatar
    Mark Spencer committed
    					q->name, pos++, qe->chan->name, (qe->chan->callerid ? qe->chan->callerid : ""), now - qe->start);
    
    		ast_pthread_mutex_unlock(&q->lock);
    		q = q->next;
    	}
    	return RESULT_SUCCESS;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static char show_queues_usage[] = 
    "Usage: show queues\n"
    "       Provides summary information on call queues.\n";
    
    static struct ast_cli_entry cli_show_queues = {
    	{ "show", "queues", NULL }, queues_show, 
    	"Show status of queues", show_queues_usage, NULL };
    
    int unload_module(void)
    {
    	STANDARD_HANGUP_LOCALUSERS;
    	ast_cli_unregister(&cli_show_queues);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	ast_manager_unregister( "Queues" );
    
    	ast_manager_unregister( "QueueStatus" );
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return ast_unregister_application(app);
    }
    
    int load_module(void)
    {
    	int res;
    	res = ast_register_application(app, queue_exec, synopsis, descrip);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (!res) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_cli_register(&cli_show_queues);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_manager_register( "Queues", 0, manager_queues_show, "Queues" );
    
    		ast_manager_register( "QueueStatus", 0, manager_queues_status, "Queue Status" );
    
    
    		// [PHM 06/26/03]
    		ast_register_application(app_aqm, aqm_exec, app_aqm_synopsis, app_aqm_descrip) ;
    		ast_register_application(app_rqm, rqm_exec, app_rqm_synopsis, app_rqm_descrip) ;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	reload_queues();
    	return res;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    int reload(void)
    {
    	reload_queues();
    	return 0;
    }
    
    char *description(void)
    {
    	return tdesc;
    }
    
    int usecount(void)
    {
    	int res;
    	STANDARD_USECOUNT(res);
    	return res;
    }
    
    char *key()
    {
    	return ASTERISK_GPL_KEY;
    }