From 7b52e19b6da65ecad9dd1968c97cd1cc14a58afe Mon Sep 17 00:00:00 2001 From: Mark Spencer Date: Wed, 28 Jul 2004 02:55:22 +0000 Subject: Unify queue add/remove from manager and CLI (bug #2125/2123) git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@3526 65c4cc65-6c06-0410-ace0-fbb531ad65f3 --- apps/app_queue.c | 533 ++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 394 insertions(+), 139 deletions(-) (limited to 'apps') diff --git a/apps/app_queue.c b/apps/app_queue.c index bd8e04830..2f9f3df29 100755 --- a/apps/app_queue.c +++ b/apps/app_queue.c @@ -80,6 +80,11 @@ static struct strategy { #define DEFAULT_TIMEOUT 15 #define RECHECK 1 /* Recheck every second to see we we're at the top yet */ +#define RES_OKAY 0 /* Action completed */ +#define RES_EXISTS (-1) /* Entry already exists */ +#define RES_OUTOFMEMORY (-2) /* Out of memory */ +#define RES_NOSUCHQUEUE (-3) /* No such queue */ + static char *tdesc = "True Call Queueing"; static char *app = "Queue"; @@ -277,8 +282,7 @@ static int join_queue(char *queuename, struct queue_ent *qe) int inserted = 0; ast_mutex_lock(&qlock); - q = queues; - while(q) { + for (q = queues; q; q = q->next) { if (!strcasecmp(q->name, queuename)) { /* This is our one */ ast_mutex_lock(&q->lock); @@ -319,7 +323,6 @@ ast_log(LOG_NOTICE, "Queue '%s' Join, Channel '%s', Position '%d'\n", q->name, q ast_mutex_unlock(&q->lock); break; } - q = q->next; } ast_mutex_unlock(&qlock); return res; @@ -349,8 +352,7 @@ static void destroy_queue(struct ast_call_queue *q) { struct ast_call_queue *cur, *prev = NULL; ast_mutex_lock(&qlock); - cur = queues; - while(cur) { + for (cur = queues; cur; cur = cur->next) { if (cur == q) { if (prev) prev->next = cur->next; @@ -359,7 +361,6 @@ static void destroy_queue(struct ast_call_queue *q) } else { prev = cur; } - cur = cur->next; } ast_mutex_unlock(&qlock); free_members(q, 1); @@ -1275,29 +1276,97 @@ static struct member * create_queue_node( char * interface, int penalty ) return( cur ) ; } +static int remove_from_queue(char *queuename, char *interface) +{ + struct ast_call_queue *q; + struct member *last_member, *look; + int res = RES_NOSUCHQUEUE; + + ast_mutex_lock(&qlock); + for (q = queues ; q ; q = q->next) { + ast_mutex_lock(&q->lock); + if (!strcmp(q->name, queuename)) { + if ((last_member = interface_exists(q, interface))) { + if ((look = q->members) == last_member) { + q->members = last_member->next; + } else { + while (look != NULL) { + if (look->next == last_member) { + look->next = last_member->next; + break; + } else { + look = look->next; + } + } + } + free(last_member); + res = RES_OKAY; + } else { + res = RES_EXISTS; + } + ast_mutex_unlock(&q->lock); + break; + } + ast_mutex_unlock(&q->lock); + } + ast_mutex_unlock(&qlock); + return res; +} + +static int add_to_queue(char *queuename, char *interface, int penalty) +{ + struct ast_call_queue *q; + struct member *new_member; + int res = RES_NOSUCHQUEUE; + + ast_mutex_lock(&qlock); + for (q = queues ; q ; q = q->next) { + ast_mutex_lock(&q->lock); + if (!strcmp(q->name, queuename)) { + if (interface_exists(q, interface) == NULL) { + new_member = create_queue_node(interface, penalty); + + if (new_member != NULL) { + new_member->dynamic = 1; + new_member->next = q->members; + q->members = new_member; + res = RES_OKAY; + } else { + res = RES_OUTOFMEMORY; + } + } else { + res = RES_EXISTS; + } + ast_mutex_unlock(&q->lock); + break; + } + ast_mutex_unlock(&q->lock); + } + ast_mutex_unlock(&qlock); + return res; +} static int rqm_exec(struct ast_channel *chan, void *data) { int res=-1; struct localuser *u; - char *queuename; - struct member * node ; - struct member * look ; - char info[512]; + char *info, *queuename; char tmpchan[256]=""; - char *interface=NULL; - struct ast_call_queue *q; - int found=0 ; + char *interface = NULL; if (!data) { - ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename|optional interface)\n"); + ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename[|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); + + info = ast_strdupa((char *)data); + if (!info) { + ast_log(LOG_ERROR, "Out of memory\n"); + return -1; + } + + LOCAL_USER_ADD(u); + queuename = info; if (queuename) { interface = strchr(queuename, '|'); @@ -1314,90 +1383,54 @@ static int rqm_exec(struct ast_channel *chan, void *data) } } - if( ( q = queues) != NULL ) - { - while( q && ( res != 0 ) && (!found) ) - { - ast_mutex_lock(&q->lock); - if( strcmp( q->name, queuename) == 0 ) - { - // found queue, try to remove interface - found=1 ; - - if( ( node = interface_exists( q, interface ) ) != NULL ) - { - if( ( look = q->members ) == node ) - { - // 1st - q->members = node->next; - } - else - { - while( look != NULL ) - if( look->next == node ) - { - look->next = node->next ; - break ; - } - else - look = look->next ; - } - - free( node ) ; - - ast_log(LOG_NOTICE, "Removed interface '%s' to queue '%s'\n", - interface, queuename); - res = 0 ; - } - else - { - ast_log(LOG_WARNING, "Unable to remove interface '%s' from queue '%s': " - "Not there\n", interface, queuename); - if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->callerid)) - { - chan->priority += 100; - res = 0 ; - } - } - } - - ast_mutex_unlock(&q->lock); - q = q->next; + switch (remove_from_queue(queuename, interface)) { + case RES_OKAY: + ast_log(LOG_NOTICE, "Removed interface '%s' from queue '%s'\n", interface, queuename); + res = 0; + break; + case RES_EXISTS: + ast_log(LOG_WARNING, "Unable to remove interface '%s' from queue '%s': Not there\n", interface, queuename); + if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->callerid)) { + chan->priority += 100; } - } - - if( ! found ) + res = 0; + break; + case RES_NOSUCHQUEUE: ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", queuename); + res = 0; + break; + case RES_OUTOFMEMORY: + ast_log(LOG_ERROR, "Out of memory\n"); + break; + } LOCAL_USER_REMOVE(u); return res; } - - static int aqm_exec(struct ast_channel *chan, void *data) { int res=-1; struct localuser *u; char *queuename; - char info[512]; + char *info; char tmpchan[512]=""; char *interface=NULL; char *penaltys=NULL; int penalty = 0; - struct ast_call_queue *q; - struct member *save; - int found=0 ; if (!data) { - ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename|optional interface|optional penalty)\n"); + ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename[|[interface][|penalty]])\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); + + info = ast_strdupa((char *)data); + if (!info) { + ast_log(LOG_ERROR, "Out of memory\n"); + return -1; + } + LOCAL_USER_ADD(u); + queuename = info; if (queuename) { interface = strchr(queuename, '|'); @@ -1408,11 +1441,11 @@ static int aqm_exec(struct ast_channel *chan, void *data) if (interface) { penaltys = strchr(interface, '|'); if (penaltys) { - *penaltys = 0; + *penaltys = '\0'; penaltys++; } } - if (!interface || !strlen(interface)) { + if (!interface || ast_strlen_zero(interface)) { strncpy(tmpchan, chan->name, sizeof(tmpchan) - 1); interface = strrchr(tmpchan, '-'); if (interface) @@ -1427,55 +1460,31 @@ static int aqm_exec(struct ast_channel *chan, void *data) } } - if( ( q = queues) != NULL ) - { - while( q && ( res != 0 ) && (!found) ) - { - ast_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, penalty ) ; - - if( q->members != NULL ) { - q->members->dynamic = 1; - 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); - if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->callerid)) - { - chan->priority += 100; - res = 0 ; - } - } - } - - ast_mutex_unlock(&q->lock); - q = q->next; + switch (add_to_queue(queuename, interface, penalty)) { + case RES_OKAY: + ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", interface, queuename); + res = 0; + break; + case RES_EXISTS: + ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': Already there\n", interface, queuename); + if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->callerid)) { + chan->priority += 100; } - } - - if( ! found ) + res = 0; + break; + case RES_NOSUCHQUEUE: ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", queuename); + res = 0; + break; + case RES_OUTOFMEMORY: + ast_log(LOG_ERROR, "Out of memory\n"); + break; + } LOCAL_USER_REMOVE(u); return res; } - static int queue_exec(struct ast_channel *chan, void *data) { int res=-1; @@ -1497,7 +1506,7 @@ static int queue_exec(struct ast_channel *chan, void *data) struct queue_ent qe; if (!data) { - ast_log(LOG_WARNING, "Queue requires an argument (queuename|optional timeout|optional URL)\n"); + ast_log(LOG_WARNING, "Queue requires an argument (queuename[|[timeout][|URL]])\n"); return -1; } @@ -1989,13 +1998,11 @@ static char *complete_queue(char *line, char *word, int pos, int state) int which=0; ast_mutex_lock(&qlock); - q = queues; - while(q) { + for (q = queues; q; q = q->next) { if (!strncasecmp(word, q->name, strlen(word))) { if (++which > state) break; } - q = q->next; } ast_mutex_unlock(&qlock); return q ? strdup(q->name) : NULL; @@ -2023,11 +2030,10 @@ static int manager_queues_status( struct mansession *s, struct message *m ) astman_send_ack(s, m, "Queue status will follow"); time(&now); ast_mutex_lock(&qlock); - q = queues; - if (id && !ast_strlen_zero(id)) { + if (!ast_strlen_zero(id)) { snprintf(idText,256,"ActionID: %s\r\n",id); } - while(q) { + for (q = queues; q; q = q->next) { ast_mutex_lock(&q->lock); /* List queue properties */ @@ -2075,12 +2081,241 @@ static int manager_queues_status( struct mansession *s, struct message *m ) "\r\n", q->name, pos++, qe->chan->name, (qe->chan->callerid ? qe->chan->callerid : ""), (long)(now - qe->start), idText); ast_mutex_unlock(&q->lock); - q = q->next; } ast_mutex_unlock(&qlock); return RESULT_SUCCESS; } +static int manager_add_queue_member(struct mansession *s, struct message *m) +{ + char *queuename, *interface, *penalty_s; + int penalty = 0; + + queuename = astman_get_header(m, "Queue"); + interface = astman_get_header(m, "Interface"); + penalty_s = astman_get_header(m, "Penalty"); + + if (ast_strlen_zero(queuename)) { + astman_send_error(s, m, "'Queue' not specified."); + return 0; + } + + if (ast_strlen_zero(interface)) { + astman_send_error(s, m, "'Interface' not specified."); + return 0; + } + + if (ast_strlen_zero(penalty_s)) + penalty = 0; + else if (sscanf(penalty_s, "%d", &penalty) != 1) { + penalty = 0; + } + + switch (add_to_queue(queuename, interface, penalty)) { + case RES_OKAY: + astman_send_ack(s, m, "Added interface to queue"); + break; + case RES_EXISTS: + astman_send_error(s, m, "Unable to add interface: Already there"); + break; + case RES_NOSUCHQUEUE: + astman_send_error(s, m, "Unable to add interface to queue: No such queue"); + break; + case RES_OUTOFMEMORY: + astman_send_error(s, m, "Out of memory"); + break; + } + return 0; +} + +static int manager_remove_queue_member(struct mansession *s, struct message *m) +{ + char *queuename, *interface; + + queuename = astman_get_header(m, "Queue"); + interface = astman_get_header(m, "Interface"); + + if (ast_strlen_zero(queuename) || ast_strlen_zero(interface)) { + astman_send_error(s, m, "Need 'Queue' and 'Interface' parameters."); + return 0; + } + + switch (remove_from_queue(queuename, interface)) { + case RES_OKAY: + astman_send_ack(s, m, "Removed interface from queue"); + break; + case RES_EXISTS: + astman_send_error(s, m, "Unable to remove interface: Not there"); + break; + case RES_NOSUCHQUEUE: + astman_send_error(s, m, "Unable to remove interface from queue: No such queue"); + break; + case RES_OUTOFMEMORY: + astman_send_error(s, m, "Out of memory"); + break; + } + return 0; +} + +static int handle_add_queue_member(int fd, int argc, char *argv[]) +{ + char *queuename, *interface; + int penalty; + + if ((argc != 6) && (argc != 8)) { + return RESULT_SHOWUSAGE; + } else if (strcmp(argv[4], "to")) { + return RESULT_SHOWUSAGE; + } else if ((argc == 8) && strcmp(argv[6], "priority")) { + return RESULT_SHOWUSAGE; + } + + queuename = argv[5]; + interface = argv[3]; + if (argc == 8) { + if (sscanf(argv[7], "%d", &penalty) == 1) { + if (penalty < 0) { + ast_cli(fd, "Penalty must be >= 0\n"); + penalty = 0; + } + } else { + ast_cli(fd, "Penalty must be an integer >= 0\n"); + penalty = 0; + } + } else { + penalty = 0; + } + + switch (add_to_queue(queuename, interface, penalty)) { + case RES_OKAY: + ast_cli(fd, "Added interface '%s' to queue '%s'\n", interface, queuename); + return RESULT_SUCCESS; + case RES_EXISTS: + ast_cli(fd, "Unable to add interface '%s' to queue '%s': Already there\n", interface, queuename); + return RESULT_FAILURE; + case RES_NOSUCHQUEUE: + ast_cli(fd, "Unable to add interface to queue '%s': No such queue\n", queuename); + return RESULT_FAILURE; + case RES_OUTOFMEMORY: + ast_cli(fd, "Out of memory\n"); + return RESULT_FAILURE; + default: + return RESULT_FAILURE; + } +} + +static char *complete_add_queue_member(char *line, char *word, int pos, int state) +{ + /* 0 - add; 1 - queue; 2 - member; 3 - ; 4 - to; 5 - ; 6 - penalty; 7 - */ + switch (pos) { + case 3: + /* Don't attempt to complete name of member (infinite possibilities) */ + return NULL; + case 4: + if (state == 0) { + return strdup("to"); + } else { + return NULL; + } + case 5: + /* No need to duplicate code */ + return complete_queue(line, word, pos, state); + case 6: + if (state == 0) { + return strdup("penalty"); + } else { + return NULL; + } + case 7: + if (state < 100) { /* 0-99 */ + char *num = malloc(3); + if (num) { + sprintf(num, "%d", state); + } + return num; + } else { + return NULL; + } + default: + return NULL; + } +} + +static int handle_remove_queue_member(int fd, int argc, char *argv[]) +{ + char *queuename, *interface; + + if (argc != 6) { + return RESULT_SHOWUSAGE; + } else if (strcmp(argv[4], "from")) { + return RESULT_SHOWUSAGE; + } + + queuename = argv[5]; + interface = argv[3]; + + switch (remove_from_queue(queuename, interface)) { + case RES_OKAY: + ast_cli(fd, "Removed interface '%s' from queue '%s'\n", interface, queuename); + return RESULT_SUCCESS; + case RES_EXISTS: + ast_cli(fd, "Unable to remove interface '%s' from queue '%s': Not there\n", interface, queuename); + return RESULT_FAILURE; + case RES_NOSUCHQUEUE: + ast_cli(fd, "Unable to remove interface from queue '%s': No such queue\n", queuename); + return RESULT_FAILURE; + case RES_OUTOFMEMORY: + ast_cli(fd, "Out of memory\n"); + return RESULT_FAILURE; + default: + return RESULT_FAILURE; + } +} + +static char *complete_remove_queue_member(char *line, char *word, int pos, int state) +{ + int which = 0; + struct ast_call_queue *q; + struct member *m; + + /* 0 - add; 1 - queue; 2 - member; 3 - ; 4 - to; 5 - */ + if ((pos > 5) || (pos < 3)) { + return NULL; + } + if (pos == 4) { + if (state == 0) { + return strdup("from"); + } else { + return NULL; + } + } + + if (pos == 5) { + /* No need to duplicate code */ + return complete_queue(line, word, pos, state); + } + + if (queues != NULL) { + for (q = queues ; q ; q = q->next) { + ast_mutex_lock(&q->lock); + for (m = q->members ; m ; m = m->next) { + if (++which > state) { + char *tmp = malloc(strlen(m->tech) + strlen(m->loc) + 2); + if (tmp) { + sprintf(tmp, "%s/%s", m->tech, m->loc); + } else { + ast_log(LOG_ERROR, "Out of memory\n"); + } + ast_mutex_unlock(&q->lock); + return tmp; + } + } + ast_mutex_unlock(&q->lock); + } + } + return NULL; +} + static char show_queues_usage[] = "Usage: show queues\n" " Provides summary information on call queues.\n"; @@ -2097,13 +2332,31 @@ static struct ast_cli_entry cli_show_queue = { { "show", "queue", NULL }, queue_show, "Show status of a specified queue", show_queue_usage, complete_queue }; +static char aqm_cmd_usage[] = +"Usage: add queue member to [penalty ]\n"; + +static struct ast_cli_entry cli_add_queue_member = { + { "add", "queue", "member", NULL }, handle_add_queue_member, + "Add a channel to a specified queue", aqm_cmd_usage, complete_add_queue_member }; + +static char rqm_cmd_usage[] = +"Usage: remove queue member from \n"; + +static struct ast_cli_entry cli_remove_queue_member = { + { "remove", "queue", "member", NULL }, handle_remove_queue_member, + "Removes a channel from a specified queue", rqm_cmd_usage, complete_remove_queue_member }; + int unload_module(void) { STANDARD_HANGUP_LOCALUSERS; ast_cli_unregister(&cli_show_queue); ast_cli_unregister(&cli_show_queues); - ast_manager_unregister( "Queues" ); - ast_manager_unregister( "QueueStatus" ); + ast_cli_unregister(&cli_add_queue_member); + ast_cli_unregister(&cli_remove_queue_member); + ast_manager_unregister("Queues"); + ast_manager_unregister("QueueStatus"); + ast_manager_unregister("QueueAdd"); + ast_manager_unregister("QueueRemove"); ast_unregister_application(app_aqm); ast_unregister_application(app_rqm); return ast_unregister_application(app); @@ -2116,10 +2369,12 @@ int load_module(void) if (!res) { ast_cli_register(&cli_show_queue); ast_cli_register(&cli_show_queues); + ast_cli_register(&cli_add_queue_member); + ast_cli_register(&cli_remove_queue_member); ast_manager_register( "Queues", 0, manager_queues_show, "Queues" ); ast_manager_register( "QueueStatus", 0, manager_queues_status, "Queue Status" ); - - // [PHM 06/26/03] + ast_manager_register( "QueueAdd", 0, manager_add_queue_member, "Add interface to queue." ); + ast_manager_register( "QueueRemove", 0, manager_remove_queue_member, "Remove interface from queue." ); 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) ; } -- cgit v1.2.3