Index: apps/app_queue.c =================================================================== --- apps/app_queue.c (revision 81597) +++ apps/app_queue.c (working copy) @@ -25,6 +25,8 @@ * \arg Config in \ref Config_qu queues.conf * * \par Development notes + * \note 2007-09-05: Call limit per member added by Proformatique (written by Richard Braun ). + * * \note 2004-11-25: Persistent Dynamic Members added by: * NetNation Communications (www.netnation.com) * Kevin Lindsay @@ -167,7 +169,7 @@ static char *app_aqm = "AddQueueMember" ; static char *app_aqm_synopsis = "Dynamically adds queue members" ; static char *app_aqm_descrip = -" AddQueueMember(queuename[,interface[,penalty[,options[,membername]]]]):\n" +" AddQueueMember(queuename[,interface[,penalty[,call-limit[,options[,membername]]]]]):\n" "Dynamically adds interface to an existing queue.\n" "If the interface is already in the queue it will return an error.\n" " This application sets the following channel variable upon completion:\n" @@ -316,6 +318,8 @@ char interface[80]; /*!< Technology/Location */ char membername[80]; /*!< Member name to use in queue logs */ int penalty; /*!< Are we a last resort? */ + int current_calls; /*!< Number of calls this member is servicing */ + int call_limit; /*!< Maximum number of calls this member can be servicing */ int calls; /*!< Number of calls serviced by this member */ int dynamic; /*!< Are we dynamically added? */ int realtime; /*!< Is this member realtime? */ @@ -597,12 +601,13 @@ "MemberName: %s\r\n" "Membership: %s\r\n" "Penalty: %d\r\n" + "CallLimit: %d\r\n" "CallsTaken: %d\r\n" "LastCall: %d\r\n" "Status: %d\r\n" "Paused: %d\r\n", q->name, cur->interface, cur->membername, cur->dynamic ? "dynamic" : cur->realtime ? "realtime" : "static", - cur->penalty, cur->calls, (int)cur->lastcall, cur->status, cur->paused); + cur->penalty, cur->call_limit, cur->calls, (int)cur->lastcall, cur->status, cur->paused); } } ast_mutex_unlock(&q->lock); @@ -691,12 +696,13 @@ statechange_queue(device, state); } -static struct member *create_queue_member(const char *interface, const char *membername, int penalty, int paused) +static struct member *create_queue_member(const char *interface, const char *membername, int penalty, int call_limit, int paused) { struct member *cur; if ((cur = ast_calloc(1, sizeof(*cur)))) { cur->penalty = penalty; + cur->call_limit = call_limit; cur->paused = paused; ast_copy_string(cur->interface, interface, sizeof(cur->interface)); ast_copy_string(cur->membername, membername, sizeof(cur->membername)); @@ -1023,10 +1029,11 @@ } } -static void rt_handle_member_record(struct call_queue *q, char *interface, const char *membername, const char *penalty_str, const char *paused_str) +static void rt_handle_member_record(struct call_queue *q, char *interface, const char *membername, const char *penalty_str, const char *call_limit_str, const char *paused_str) { struct member *m, *prev_m; int penalty = 0; + int call_limit = 0; int paused = 0; if (penalty_str) { @@ -1035,6 +1042,12 @@ penalty = 0; } + if (call_limit_str) { + call_limit = atoi(call_limit_str); + if (call_limit < 0) + call_limit = 0; + } + if (paused_str) { paused = atoi(paused_str); if (paused < 0) @@ -1046,9 +1059,9 @@ m && strcmp(m->interface, interface); prev_m = m, m = m->next); - /* Create a new one if not found, else update penalty */ + /* Create a new one if not found, else update penalty and call limit */ if (!m) { - if ((m = create_queue_member(interface, membername, penalty, paused))) { + if ((m = create_queue_member(interface, membername, penalty, call_limit, paused))) { m->dead = 0; m->realtime = 1; add_to_interfaces(interface); @@ -1064,6 +1077,7 @@ if (paused_str) m->paused = paused; m->penalty = penalty; + m->call_limit = call_limit; } } @@ -1186,6 +1200,7 @@ rt_handle_member_record(q, interface, S_OR(ast_variable_retrieve(member_config, interface, "membername"), interface), ast_variable_retrieve(member_config, interface, "penalty"), + ast_variable_retrieve(member_config, interface, "call_limit"), ast_variable_retrieve(member_config, interface, "paused")); } @@ -1309,6 +1324,7 @@ rt_handle_member_record(q, interface, S_OR(ast_variable_retrieve(member_config, interface, "membername"), interface), ast_variable_retrieve(member_config, interface, "penalty"), + ast_variable_retrieve(member_config, interface, "call_limit"), ast_variable_retrieve(member_config, interface, "paused")); } @@ -1653,12 +1669,13 @@ "MemberName: %s\r\n" "Membership: %s\r\n" "Penalty: %d\r\n" + "CallLimit: %d\r\n" "CallsTaken: %d\r\n" "LastCall: %d\r\n" "Status: %d\r\n" "Paused: %d\r\n", q->name, cur->interface, cur->membername, cur->dynamic ? "dynamic" : cur->realtime ? "realtime": "static", - cur->penalty, cur->calls, (int)cur->lastcall, cur->status, cur->paused); + cur->penalty, cur->call_limit, cur->calls, (int)cur->lastcall, cur->status, cur->paused); } } ast_mutex_unlock(&q->lock); @@ -2726,6 +2743,12 @@ ast_log(LOG_WARNING, "Announcement file '%s' is unavailable, continuing anyway...\n", qe->parent->sound_callerannounce); } + /* Don't allow more than call_limit calls per member */ + member->current_calls++; + if (member->call_limit && member->current_calls >= member->call_limit) { + set_member_paused(NULL, member->interface, 1); + } + /* Begin Monitoring */ if (qe->parent->monfmt && *qe->parent->monfmt) { if (!qe->parent->montype) { @@ -2824,8 +2847,8 @@ /* if setinterfacevar is defined, make member variables available to the channel */ /* use pbx_builtin_setvar to set a load of variables with one call */ if (qe->parent->setinterfacevar) { - snprintf(interfacevar,sizeof(interfacevar), "MEMBERINTERFACE=%s|MEMBERNAME=%s|MEMBERCALLS=%d|MEMBERLASTCALL=%ld|MEMBERPENALTY=%d|MEMBERDYNAMIC=%d|MEMBERREALTIME=%d", - member->interface, member->membername, member->calls, (long)member->lastcall, member->penalty, member->dynamic, member->realtime); + snprintf(interfacevar,sizeof(interfacevar), "MEMBERINTERFACE=%s|MEMBERNAME=%s|MEMBERCALLS=%d|MEMBERLASTCALL=%ld|MEMBERPENALTY=%d|MEMBERCALLLIMIT=%d|MEMBERDYNAMIC=%d|MEMBERREALTIME=%d", + member->interface, member->membername, member->calls, (long)member->lastcall, member->penalty, member->call_limit, member->dynamic, member->realtime); pbx_builtin_setvar(qe->chan, interfacevar); } @@ -2983,6 +3006,11 @@ if (bridge != AST_PBX_NO_HANGUP_PEER) ast_hangup(peer); update_queue(qe->parent, member, callcompletedinsl); + /* Unpause agent if it was paused */ + if (member->call_limit && member->current_calls >= member->call_limit) { + set_member_paused(NULL, member->interface, 0); + } + member->current_calls--; res = bridge ? bridge : 1; } out: @@ -3021,7 +3049,7 @@ /* Dump all members in a specific queue to the database * - * / = ;;[|...] + * / = ;;;[|...] * */ static void dump_queue_members(struct call_queue *pm_queue) @@ -3040,9 +3068,9 @@ if (!cur_member->dynamic) continue; - res = snprintf(value + value_len, sizeof(value) - value_len, "%s;%d;%d;%s%s", - cur_member->interface, cur_member->penalty, cur_member->paused, cur_member->membername, - cur_member->next ? "|" : ""); + res = snprintf(value + value_len, sizeof(value) - value_len, "%s;%d;%d;%d;%s%s", + cur_member->interface, cur_member->penalty, cur_member->call_limit, cur_member->paused, + cur_member->membername, cur_member->next ? "|" : ""); if (res != strlen(value + value_len)) { ast_log(LOG_WARNING, "Could not create persistent member string, out of space\n"); break; @@ -3114,7 +3142,7 @@ } -static int add_to_queue(const char *queuename, const char *interface, const char *membername, int penalty, int paused, int dump) +static int add_to_queue(const char *queuename, const char *interface, const char *membername, int penalty, int call_limit, int paused, int dump) { struct call_queue *q; struct member *new_member; @@ -3130,7 +3158,7 @@ ast_mutex_lock(&q->lock); if (interface_exists(q, interface) == NULL) { add_to_interfaces(interface); - if ((new_member = create_queue_member(interface, membername, penalty, paused))) { + if ((new_member = create_queue_member(interface, membername, penalty, call_limit, paused))) { new_member->dynamic = 1; new_member->next = q->members; q->members = new_member; @@ -3141,13 +3169,14 @@ "MemberName: %s\r\n" "Membership: %s\r\n" "Penalty: %d\r\n" + "CallLimit: %d\r\n" "CallsTaken: %d\r\n" "LastCall: %d\r\n" "Status: %d\r\n" "Paused: %d\r\n", q->name, new_member->interface, new_member->membername, "dynamic", - new_member->penalty, new_member->calls, (int) new_member->lastcall, + new_member->penalty, new_member->call_limit, new_member->calls, (int) new_member->lastcall, new_member->status, new_member->paused); if (dump) @@ -3221,6 +3250,8 @@ char *membername; char *penalty_tok; int penalty = 0; + char *call_limit_tok; + int call_limit; char *paused_tok; int paused = 0; struct ast_db_entry *db_tree; @@ -3265,6 +3296,7 @@ interface = strsep(&member, ";"); penalty_tok = strsep(&member, ";"); + call_limit_tok = strsep(&member, ";"); paused_tok = strsep(&member, ";"); membername = strsep(&member, ";"); @@ -3278,6 +3310,15 @@ break; } + if (!call_limit_tok) { + ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (call limit)\n", queue_name); + break; + } + call_limit = strtol(call_limit_tok, NULL, 10); + if (errno == ERANGE) { + ast_log(LOG_WARNING, "Error converting call limit: %s: Out of range.\n", call_limit_tok); + break; + } if (!paused_tok) { ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (paused)\n", queue_name); break; @@ -3290,9 +3331,9 @@ if (ast_strlen_zero(membername)) membername = interface; - ast_debug(1, "Reload Members: Queue: %s Member: %s Name: %s Penalty: %d Paused: %d\n", queue_name, interface, membername, penalty, paused); + ast_debug(1, "Reload Members: Queue: %s Member: %s Name: %s Penalty: %d Call limit: %d Paused: %d\n", queue_name, interface, membername, penalty, call_limit, paused); - if (add_to_queue(queue_name, interface, membername, penalty, paused, 0) == RES_OUTOFMEMORY) { + if (add_to_queue(queue_name, interface, membername, penalty, call_limit, paused, 0) == RES_OUTOFMEMORY) { ast_log(LOG_ERROR, "Out of Memory when reloading persistent queue member\n"); break; } @@ -3431,13 +3472,15 @@ AST_APP_ARG(queuename); AST_APP_ARG(interface); AST_APP_ARG(penalty); + AST_APP_ARG(call_limit); AST_APP_ARG(options); AST_APP_ARG(membername); ); int penalty = 0; + int call_limit = 0; if (ast_strlen_zero(data)) { - ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename[|[interface]|[penalty][|options][|membername]])\n"); + ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename[,interface[,penalty[,call-limit[,options[,membername]]]]])\n"); return -1; } @@ -3459,11 +3502,18 @@ } } + if (!ast_strlen_zero(args.call_limit)) { + if ((sscanf(args.call_limit, "%d", &call_limit) != 1) || call_limit < 0) { + ast_log(LOG_WARNING, "CallLimit '%s' is invalid, must be an integer >= 0\n", args.call_limit); + call_limit = 0; + } + } + if (ast_strlen_zero(args.membername)) args.membername = args.interface; - switch (add_to_queue(args.queuename, args.interface, args.membername, penalty, 0, queue_persistent_members)) { + switch (add_to_queue(args.queuename, args.interface, args.membername, penalty, call_limit, 0, queue_persistent_members)) { case RES_OKAY: ast_queue_log(args.queuename, chan->uniqueid, args.interface, "ADDMEMBER", "%s", ""); ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", args.interface, args.queuename); @@ -3994,10 +4044,12 @@ char *interface; char *membername; int penalty; + int call_limit; struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 }; AST_DECLARE_APP_ARGS(args, AST_APP_ARG(interface); AST_APP_ARG(penalty); + AST_APP_ARG(call_limit); AST_APP_ARG(membername); ); @@ -4076,6 +4128,16 @@ } else penalty = 0; + if(!ast_strlen_zero(args.call_limit)) { + tmp = args.call_limit; + while (*tmp && *tmp < 33) tmp++; + call_limit = atoi(tmp); + if (call_limit < 0) { + call_limit = 0; + } + } else + call_limit = 0; + if (!ast_strlen_zero(args.membername)) { membername = args.membername; while (*membername && *membername < 33) membername++; @@ -4089,7 +4151,7 @@ } } - newm = create_queue_member(interface, membername, penalty, cur ? cur->paused : 0); + newm = create_queue_member(interface, membername, penalty, call_limit, cur ? cur->paused : 0); if (cur) { /* Delete it now */ @@ -4219,6 +4281,8 @@ ast_str_set(&out, 0, " %s", mem->interface); if (mem->penalty) ast_str_append(&out, 0, " with penalty %d", mem->penalty); + if (mem->call_limit) + ast_str_append(&out, 0, " with call limit %d", mem->call_limit); ast_str_append(&out, 0, "%s%s%s (%s)", mem->dynamic ? " (dynamic)" : "", mem->realtime ? " (realtime)" : "", @@ -4413,6 +4477,7 @@ "Location: %s\r\n" "Membership: %s\r\n" "Penalty: %d\r\n" + "CallLimit: %d\r\n" "CallsTaken: %d\r\n" "LastCall: %d\r\n" "Status: %d\r\n" @@ -4420,7 +4485,7 @@ "%s" "\r\n", q->name, mem->interface, mem->dynamic ? "dynamic" : "static", - mem->penalty, mem->calls, (int)mem->lastcall, mem->status, mem->paused, idText); + mem->penalty, mem->call_limit, mem->calls, (int)mem->lastcall, mem->status, mem->paused, idText); } } /* List Queue Entries */ @@ -4457,12 +4522,13 @@ static int manager_add_queue_member(struct mansession *s, const struct message *m) { - const char *queuename, *interface, *penalty_s, *paused_s, *membername; - int paused, penalty = 0; + const char *queuename, *interface, *penalty_s, *call_limit_s, *paused_s, *membername; + int paused, penalty = 0, call_limit = 0; queuename = astman_get_header(m, "Queue"); interface = astman_get_header(m, "Interface"); penalty_s = astman_get_header(m, "Penalty"); + call_limit_s = astman_get_header(m, "CallLimit"); paused_s = astman_get_header(m, "Paused"); membername = astman_get_header(m, "MemberName"); @@ -4481,6 +4547,11 @@ else if (sscanf(penalty_s, "%d", &penalty) != 1) penalty = 0; + if (ast_strlen_zero(call_limit_s)) + call_limit = 0; + else if (sscanf(call_limit_s, "%d", &call_limit) != 1) + call_limit = 0; + if (ast_strlen_zero(paused_s)) paused = 0; else @@ -4489,7 +4560,7 @@ if (ast_strlen_zero(membername)) membername = interface; - switch (add_to_queue(queuename, interface, membername, penalty, paused, queue_persistent_members)) { + switch (add_to_queue(queuename, interface, membername, penalty, call_limit, paused, queue_persistent_members)) { case RES_OKAY: ast_queue_log(queuename, "MANAGER", interface, "ADDMEMBER", "%s", ""); astman_send_ack(s, m, "Added interface to queue"); @@ -4586,16 +4657,18 @@ static int handle_queue_add_member(int fd, int argc, char *argv[]) { char *queuename, *interface, *membername; - int penalty; + int penalty, call_limit; - if ((argc != 6) && (argc != 8) && (argc != 10)) { + if ((argc != 6) && (argc != 8) && (argc != 10) && (argc != 12)) { return RESULT_SHOWUSAGE; } else if (strcmp(argv[4], "to")) { return RESULT_SHOWUSAGE; - } else if ((argc == 8) && strcmp(argv[6], "penalty")) { + } else if ((argc >= 8) && strcmp(argv[6], "penalty")) { return RESULT_SHOWUSAGE; - } else if ((argc == 10) && strcmp(argv[8], "as")) { + } else if ((argc >= 10) && strcmp(argv[8], "call-limit")) { return RESULT_SHOWUSAGE; + } else if ((argc == 12) && strcmp(argv[10], "as")) { + return RESULT_SHOWUSAGE; } queuename = argv[5]; @@ -4615,12 +4688,26 @@ } if (argc >= 10) { - membername = argv[9]; + if (sscanf(argv[9], "%d", &call_limit) == 1) { + if (call_limit < 0) { + ast_cli(fd, "Call limit must be >= 0\n"); + call_limit = 0; + } + } else { + ast_cli(fd, "Call limit must be an integer >= 0\n"); + call_limit = 0; + } } else { + call_limit = 0; + } + + if (argc >= 12) { + membername = argv[11]; + } else { membername = interface; } - switch (add_to_queue(queuename, interface, membername, penalty, 0, queue_persistent_members)) { + switch (add_to_queue(queuename, interface, membername, penalty, call_limit, 0, queue_persistent_members)) { case RES_OKAY: ast_queue_log(queuename, "CLI", interface, "ADDMEMBER", "%s", ""); ast_cli(fd, "Added interface '%s' to queue '%s'\n", interface, queuename); @@ -4641,7 +4728,7 @@ static char *complete_queue_add_member(const char *line, const char *word, int pos, int state) { - /* 0 - queue; 1 - add; 2 - member; 3 - ; 4 - to; 5 - ; 6 - penalty; 7 - ; 8 - as; 9 - */ + /* 0 - queue; 1 - add; 2 - member; 3 - ; 4 - to; 5 - ; 6 - penalty; 7 - ; 8 - call-limit; 9 - ; 10 - as; 11 - */ switch (pos) { case 3: /* Don't attempt to complete name of interface (infinite possibilities) */ return NULL; @@ -4651,7 +4738,7 @@ return complete_queue(line, word, pos, state); case 6: /* only one possible match, "penalty" */ return state == 0 ? ast_strdup("penalty") : NULL; - case 7: + case 7: case 9: if (state < 100) { /* 0-99 */ char *num; if ((num = ast_malloc(3))) { @@ -4661,9 +4748,11 @@ } else { return NULL; } - case 8: /* only one possible match, "as" */ + case 8: /* only one possible match, "call-limit" */ + return state == 0 ? ast_strdup("call-limit") : NULL; + case 10: /* only one possible match, "as" */ return state == 0 ? ast_strdup("as") : NULL; - case 9: /* Don't attempt to complete name of member (infinite possibilities) */ + case 11: /* Don't attempt to complete name of member (infinite possibilities) */ return NULL; default: return NULL; @@ -4739,7 +4828,7 @@ " Provides summary information on a specified queue.\n"; static const char qam_cmd_usage[] = -"Usage: queue add member to [penalty ]\n"; +"Usage: queue add member to [penalty [call-limit [as ]]]\n"; static const char qrm_cmd_usage[] = "Usage: queue remove member from \n";