Index: apps/app_queue.c =================================================================== --- apps/app_queue.c (revision 66584) +++ apps/app_queue.c (working copy) @@ -3062,6 +3062,80 @@ return found ? RESULT_SUCCESS : RESULT_FAILURE; } +/* \brief Sets members penalty, if queuename=NULL we set member penalty in all the queues. */ +static int set_member_penalty(char *queuename, char *interface, int penalty) +{ + int foundinterface = 0, foundqueue = 0; + struct call_queue *q; + struct member *mem; + + AST_LIST_LOCK(&queues); + AST_LIST_TRAVERSE(&queues, q, list) { + if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) { + foundqueue++; + ast_mutex_lock(&q->lock); + if ((mem = interface_exists(q, interface))) { + foundinterface++; + mem->penalty = penalty; + + ast_queue_log(q->name, "NONE", interface, "PENALTY", "%d", penalty); + manager_event(EVENT_FLAG_AGENT, "QueueMemberPenalty", + "Queue: %s\r\n" + "Location: %s\r\n" + "Penalty: %d\r\n", + q->name, mem->interface, penalty); + + } + ast_mutex_unlock(&q->lock); + } + } + AST_LIST_UNLOCK(&queues); + + if (foundinterface) { + return RESULT_SUCCESS; + } else if (foundqueue) { + ast_log (LOG_ERROR, "Invalid queuename\n"); + } else { + ast_log (LOG_ERROR, "Invalid interface\n"); + } + + return RESULT_FAILURE; +} + +/* \brief Gets members penalty. + * + * \return Return the members penalty or RESULT_FAILURE on error. */ +static int get_member_penalty(char *queuename, char *interface) +{ + int foundqueue = 0, penalty; + struct call_queue *q; + struct member *mem; + + AST_LIST_LOCK(&queues); + AST_LIST_TRAVERSE(&queues, q, list) { + if (!strcasecmp(q->name, queuename)) { + foundqueue++; + ast_mutex_lock(&q->lock); + if ((mem = interface_exists(q, interface))) { + penalty = mem->penalty; + ast_mutex_unlock(&q->lock); + AST_LIST_UNLOCK(&queues); + return penalty; + } + ast_mutex_unlock(&q->lock); + } + } + AST_LIST_UNLOCK(&queues); + + /* some useful debuging */ + if (foundqueue) + ast_log (LOG_ERROR, "Invalid queuename\n"); + else + ast_log (LOG_ERROR, "Invalid interface\n"); + + return RESULT_FAILURE; +} + /* Reload dynamic queue members persisted into the astdb */ static void reload_queue_members(void) { @@ -3910,6 +3984,91 @@ return 0; } +/*! \brief Dialplan function QUEUE_MEMBER_PENALTY() + * Gets the members penalty. */ +static int queue_function_memberpenalty_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len) { + struct ast_module_user *lu; + int penalty; + + /* Make sure the returned value on error is NULL. */ + buf[0] = '\0'; + + if (ast_strlen_zero(data)) { + ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(,)\n"); + return -1; + } + + lu = ast_module_user_add(chan); + + AST_DECLARE_APP_ARGS(args, + AST_APP_ARG(queuename); + AST_APP_ARG(interface); + ); + + AST_STANDARD_APP_ARGS(args, data); + + if (args.argc < 2) { + ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(,)\n"); + ast_module_user_remove(lu); + return -1; + } + + penalty = get_member_penalty (args.queuename, args.interface); + + if (penalty >= 0) /* remember that buf is already '\0' */ + sprintf (buf, "%d", penalty); + + ast_module_user_remove(lu); + return 0; +} + +/*! Dialplan function QUEUE_MEMBER_PENALTY() + * Sets the members penalty. */ +static int queue_function_memberpenalty_write(struct ast_channel *chan, const char *cmd, char *data, const char *value) { + struct ast_module_user *lu; + int penalty; + + if (ast_strlen_zero(data)) { + ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(,)\n"); + return -1; + } + + lu = ast_module_user_add(chan); + + AST_DECLARE_APP_ARGS(args, + AST_APP_ARG(queuename); + AST_APP_ARG(interface); + ); + + AST_STANDARD_APP_ARGS(args, data); + + if (args.argc < 2) { + ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(,)\n"); + ast_module_user_remove(lu); + return -1; + } + + penalty = atoi(value); + if (penalty < 0) { + ast_log(LOG_ERROR, "Invalid penalty\n"); + ast_module_user_remove(lu); + return -1; + } + + if (ast_strlen_zero(args.interface)) { + ast_log (LOG_ERROR, " parameter can't be null\n"); + ast_module_user_remove(lu); + return -1; + } + + /* if queuename = NULL then penalty will be set for interface in all the queues. */ + set_member_penalty(args.queuename, args.interface, penalty); + + ast_module_user_remove(lu); + + return 0; +} + static struct ast_custom_function queuevar_function = { .name = "QUEUE_VARIABLES", .synopsis = "Return Queue information in variables", @@ -3955,6 +4114,16 @@ .read = queue_function_queuememberlist, }; +static struct ast_custom_function queuememberpenalty_function = { + .name = "QUEUE_MEMBER_PENALTY", + .synopsis = "Gets or sets queue members penalty.", + .syntax = "QUEUE_MEMBER_PENALTY(,)", + .desc = +"Gets or sets queue members penalty\n", + .read = queue_function_memberpenalty_read, + .write = queue_function_memberpenalty_write, +}; + static int reload_queues(void) { struct call_queue *q; @@ -4541,6 +4710,31 @@ return 0; } +static int manager_queue_member_penalty(struct mansession *s, const struct message *m) +{ + const char *queuename, *interface, *penalty_s; + int penalty; + + interface = astman_get_header(m, "Interface"); + penalty_s = astman_get_header(m, "Penalty"); + /* Optional - if not supplied, set the penalty value for the given Interface in all queues */ + queuename = astman_get_header(m, "Queue"); + + if (ast_strlen_zero(interface) || ast_strlen_zero(penalty_s)) { + astman_send_error(s, m, "Need 'Interface' and 'Penalty' parameters."); + return 0; + } + + penalty = atoi(penalty_s); + + if (set_member_penalty((char *)queuename, (char *)interface, penalty)) + astman_send_error(s, m, "Invalid interface or queuename"); + else + astman_send_ack(s, m, "Interface penalty set successfully"); + + return 0; +} + static int handle_queue_add_member(int fd, int argc, char *argv[]) { char *queuename, *interface, *membername; @@ -4692,6 +4886,76 @@ return NULL; } +static int handle_queue_set_member_penalty(int fd, int argc, char *argv[]) +{ + char *queuename, *interface; + int penalty = 0; + + if (argc != 8) { + return RESULT_SHOWUSAGE; + } else if (strcmp(argv[5], "from")) { + return RESULT_SHOWUSAGE; + } + + queuename = argv[6]; + interface = argv[4]; + penalty = atoi(argv[7]); + + if (penalty < 0) { + ast_cli(fd, "Invalid penalty (%d)\n", penalty); + return -1; + } + + switch (set_member_penalty(queuename, interface, penalty)) { + case RESULT_SUCCESS: + ast_cli(fd, "Set penalty on interface '%s' from queue '%s'\n", interface, queuename); + return RESULT_SUCCESS; + case RESULT_FAILURE: + ast_cli(fd, "Failed to set penalty on interface '%s' from queue '%s'\n", interface, queuename); + return RESULT_FAILURE; + default: + return RESULT_FAILURE; + } +} + +static char *complete_queue_set_member_penalty(const char *line, const char *word, int pos, int state) +{ + int which = 0; + struct call_queue *q; + struct member *m; + + /* 0 - queue; 1 - set; 2 - member; 3 - penalty; 4 - ; 5 - from; 6 - ; 7 - */ + if ((pos > 7) || (pos < 4)) { + return NULL; + } + if (pos == 5) { + if (state == 0) { + return ast_strdup("from"); + } else { + return NULL; + } + } + + if (pos == 6) { + /* No need to duplicate code */ + return complete_queue(line, word, pos, state); + } + + /* We need to lock queues before reading it? */ + AST_LIST_TRAVERSE(&queues, q, list) { + ast_mutex_lock(&q->lock); + for (m = q->members ; m ; m = m->next) { + if (++which > state) { + ast_mutex_unlock(&q->lock); + return ast_strdup(m->interface); + } + } + ast_mutex_unlock(&q->lock); + } + + return NULL; +} + static const char queue_show_usage[] = "Usage: queue show\n" " Provides summary information on a specified queue.\n"; @@ -4702,6 +4966,9 @@ static const char qrm_cmd_usage[] = "Usage: queue remove member from \n"; +static const char qsmp_cmd_usage[] = +"Usage: queue set member penalty from \n"; + static struct ast_cli_entry cli_queue[] = { { { "queue", "show", NULL }, queue_show, "Show status of a specified queue", @@ -4714,6 +4981,10 @@ { { "queue", "remove", "member", NULL }, handle_queue_remove_member, "Removes a channel from a specified queue", qrm_cmd_usage, complete_queue_remove_member, NULL }, + + { { "queue", "set", "member", "penalty", NULL }, + handle_queue_set_member_penalty, "Set penalty for a channel of a specified queue", + qsmp_cmd_usage, complete_queue_set_member_penalty, NULL }, }; static int unload_module(void) @@ -4737,6 +5008,7 @@ res |= ast_manager_unregister("QueueRemove"); res |= ast_manager_unregister("QueuePause"); res |= ast_manager_unregister("QueueLog"); + res |= ast_manager_unregister("QueuePenalty"); res |= ast_unregister_application(app_aqm); res |= ast_unregister_application(app_rqm); res |= ast_unregister_application(app_pqm); @@ -4747,6 +5019,7 @@ res |= ast_custom_function_unregister(&queuemembercount_function); res |= ast_custom_function_unregister(&queuememberlist_function); res |= ast_custom_function_unregister(&queuewaitingcount_function); + res |= ast_custom_function_unregister(&queuememberpenalty_function); ast_devstate_del(statechange_queue, NULL); ast_module_user_hangup_all(); @@ -4784,10 +5057,12 @@ 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("QueueLog", EVENT_FLAG_AGENT, manager_queue_log_custom, "Adds custom entry in queue_log"); + res |= ast_manager_register("QueuePenalty", EVENT_FLAG_AGENT, manager_queue_member_penalty, "Set the penalty for a queue member"); res |= ast_custom_function_register(&queuevar_function); res |= ast_custom_function_register(&queuemembercount_function); res |= ast_custom_function_register(&queuememberlist_function); res |= ast_custom_function_register(&queuewaitingcount_function); + res |= ast_custom_function_register(&queuememberpenalty_function); res |= ast_devstate_add(statechange_queue, NULL); return res;