diff -Nurp asterisk-1.2.13.orig/apps/app_queue.c asterisk-1.2.13/apps/app_queue.c --- asterisk-1.2.13.orig/apps/app_queue.c 2006-10-03 22:14:13.000000000 +0200 +++ asterisk-1.2.13/apps/app_queue.c 2006-12-12 17:56:43.000000000 +0100 @@ -43,6 +43,8 @@ * - Failout timer passed as optional app parameter * - Optional monitoring of calls, started when call is answered * + * \note 2006-12-12: Call limit per member added by Proformatique (written by Richard Braun ). + * * Patch Version 1.07 2003-12-24 01 * * Added servicelevel statistic by Michiel Betel @@ -153,7 +155,7 @@ static char *descrip = static char *app_aqm = "AddQueueMember" ; static char *app_aqm_synopsis = "Dynamically adds queue members" ; static char *app_aqm_descrip = -" AddQueueMember(queuename[|interface[|penalty[|options]]]):\n" +" AddQueueMember(queuename[|[interface][|[penalty][|[call_limit][|options]]]]):\n" "Dynamically adds interface to an existing queue.\n" "If the interface is already in the queue and there exists an n+101 priority\n" "then it will then jump to this priority. Otherwise it will return an error\n" @@ -293,6 +295,8 @@ struct queue_ent { struct member { char interface[80]; /*!< Technology/Location */ 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 status; /*!< Status of queue member */ @@ -439,7 +443,10 @@ static enum queue_member_status get_memb ast_mutex_lock(&q->lock); for (member = q->members; member; member = member->next) { - if (member->paused) continue; + if (member->paused) { + result = QUEUE_NO_REACHABLE_MEMBERS; + continue; + } switch (member->status) { case AST_DEVICE_INVALID: @@ -528,12 +535,14 @@ static void *changethread(void *data) "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" "Paused: %d\r\n", q->name, cur->interface, cur->dynamic ? "dynamic" : "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); @@ -566,7 +575,7 @@ static int statechange_queue(const char return 0; } -static struct member *create_queue_member(char *interface, int penalty, int paused) +static struct member *create_queue_member(char *interface, int penalty, int call_limit, int paused) { struct member *cur; @@ -577,6 +586,7 @@ static struct member *create_queue_membe if (cur) { memset(cur, 0, sizeof(struct member)); cur->penalty = penalty; + cur->call_limit = call_limit; cur->paused = paused; ast_copy_string(cur->interface, interface, sizeof(cur->interface)); if (!strchr(cur->interface, '/')) @@ -851,10 +861,11 @@ static void queue_set_param(struct call_ } } -static void rt_handle_member_record(struct call_queue *q, char *interface, const char *penalty_str) +static void rt_handle_member_record(struct call_queue *q, char *interface, const char *penalty_str, + const char *call_limit_str) { struct member *m, *prev_m; - int penalty = 0; + int penalty = 0, call_limit = 0; if(penalty_str) { penalty = atoi(penalty_str); @@ -862,6 +873,12 @@ static void rt_handle_member_record(stru penalty = 0; } + if(call_limit_str) { + call_limit = atoi(call_limit_str); + if(call_limit < 0) + call_limit = 0; + } + /* Find the member, or the place to put a new one. */ prev_m = NULL; m = q->members; @@ -870,9 +887,9 @@ static void rt_handle_member_record(stru 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) { - m = create_queue_member(interface, penalty, 0); + m = create_queue_member(interface, penalty, call_limit, 0); if (m) { m->dead = 0; add_to_interfaces(interface); @@ -885,6 +902,7 @@ static void rt_handle_member_record(stru } else { m->dead = 0; /* Do not delete this one. */ m->penalty = penalty; + m->call_limit = call_limit; } } @@ -1033,7 +1051,8 @@ static struct call_queue *find_queue_by_ interface = ast_category_browse(member_config, NULL); while (interface) { - rt_handle_member_record(q, interface, ast_variable_retrieve(member_config, interface, "penalty")); + rt_handle_member_record(q, interface, ast_variable_retrieve(member_config, interface, "penalty"), + ast_variable_retrieve(member_config, interface, "call_limit")); interface = ast_category_browse(member_config, interface); } @@ -1423,12 +1442,14 @@ static int update_status(struct call_que "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" "Paused: %d\r\n", q->name, cur->interface, cur->dynamic ? "dynamic" : "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); } break; } @@ -2150,6 +2171,8 @@ static int calc_metric(struct call_queue return 0; } +static int set_member_paused(char *queuename, char *interface, int paused); + static int try_calling(struct queue_ent *qe, const char *options, char *announceoverride, const char *url, int *go_on) { struct member *cur; @@ -2374,6 +2397,11 @@ static int try_calling(struct queue_ent ast_hangup(peer); return -1; } + /* 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) { monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME"); @@ -2450,6 +2478,11 @@ static int try_calling(struct queue_ent if (bridge != AST_PBX_NO_HANGUP_PEER) ast_hangup(peer); update_queue(qe->parent, member); + /* 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: @@ -2480,7 +2513,7 @@ static struct member *interface_exists(s /* Dump all members in a specific queue to the databse * - * / = ;;[|...] + * / = ;;;[|...] * */ static void dump_queue_members(struct call_queue *pm_queue) @@ -2499,9 +2532,9 @@ static void dump_queue_members(struct ca if (!cur_member->dynamic) continue; - res = snprintf(value + value_len, sizeof(value) - value_len, "%s;%d;%d%s", - cur_member->interface, cur_member->penalty, cur_member->paused, - cur_member->next ? "|" : ""); + res = snprintf(value + value_len, sizeof(value) - value_len, "%s;%d;%d;%d%s", + cur_member->interface, cur_member->penalty, cur_member->call_limit, + cur_member->paused, cur_member->next ? "|" : ""); if (res != strlen(value + value_len)) { ast_log(LOG_WARNING, "Could not create persistent member string, out of space\n"); break; @@ -2564,7 +2597,7 @@ static int remove_from_queue(char *queue return res; } -static int add_to_queue(char *queuename, char *interface, int penalty, int paused, int dump) +static int add_to_queue(char *queuename, char *interface, int penalty, int call_limit, int paused, int dump) { struct call_queue *q; struct member *new_member; @@ -2582,7 +2615,7 @@ static int add_to_queue(char *queuename, add_to_interfaces(interface); - new_member = create_queue_member(interface, penalty, paused); + new_member = create_queue_member(interface, penalty, call_limit, paused); if (new_member != NULL) { new_member->dynamic = 1; @@ -2593,12 +2626,14 @@ static int add_to_queue(char *queuename, "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" "Paused: %d\r\n", q->name, new_member->interface, new_member->dynamic ? "dynamic" : "static", - new_member->penalty, new_member->calls, (int)new_member->lastcall, new_member->status, new_member->paused); + new_member->penalty, new_member->call_limit, new_member->calls, + (int)new_member->lastcall, new_member->status, new_member->paused); if (dump) dump_queue_members(q); @@ -2668,6 +2703,8 @@ static void reload_queue_members(void) char *interface; char *penalty_tok; int penalty = 0; + char *call_limit_tok; + int call_limit = 0; char *paused_tok; int paused = 0; struct ast_db_entry *db_tree; @@ -2710,6 +2747,7 @@ static void reload_queue_members(void) interface = strsep(&member, ";"); penalty_tok = strsep(&member, ";"); + call_limit_tok = strsep(&member, ";"); paused_tok = strsep(&member, ";"); if (!penalty_tok) { @@ -2722,6 +2760,16 @@ static void reload_queue_members(void) break; } + if (!call_limit_tok) { + ast_log(LOG_WARNING, "Error parsing persisent 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; @@ -2733,9 +2781,10 @@ static void reload_queue_members(void) } if (option_debug) - ast_log(LOG_DEBUG, "Reload Members: Queue: %s Member: %s Penalty: %d Paused: %d\n", queue_name, interface, penalty, paused); + ast_log(LOG_DEBUG, "Reload Members: Queue: %s Member: %s Penalty: %d Call Limit: %d" + " Paused: %d\n", queue_name, interface, penalty, call_limit, paused); - if (add_to_queue(queue_name, interface, penalty, paused, 0) == RES_OUTOFMEMORY) { + if (add_to_queue(queue_name, interface, penalty, call_limit, paused, 0) == RES_OUTOFMEMORY) { ast_log(LOG_ERROR, "Out of Memory when reloading persistent queue member\n"); break; } @@ -2938,12 +2987,14 @@ static int aqm_exec(struct ast_channel * AST_APP_ARG(queuename); AST_APP_ARG(interface); AST_APP_ARG(penalty); + AST_APP_ARG(call_limit); AST_APP_ARG(options); ); int penalty = 0; + int call_limit = 0; if (ast_strlen_zero(data)) { - ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename[|[interface]|[penalty][|options]])\n"); + ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename[|[interface][|[penalty][|[call_limit][|options]]]])\n"); return -1; } @@ -2971,13 +3022,20 @@ static int aqm_exec(struct ast_channel * } } + if (!ast_strlen_zero(args.call_limit)) { + if ((sscanf(args.call_limit, "%d", &call_limit) != 1) || call_limit < 0) { + ast_log(LOG_WARNING, "Call Limit '%s' is invalid, must be an integer >= 0\n", args.call_limit); + call_limit = 0; + } + } + if (args.options) { if (strchr(args.options, 'j')) priority_jump = 1; } - switch (add_to_queue(args.queuename, args.interface, penalty, 0, queue_persistent_members)) { + switch (add_to_queue(args.queuename, args.interface, penalty, call_limit, 0, queue_persistent_members)) { case RES_OKAY: ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", args.interface, args.queuename); pbx_builtin_setvar_helper(chan, "AQMSTATUS", "ADDED"); @@ -3310,7 +3368,7 @@ static void reload_queues(void) int new; char *general_val = NULL; char interface[80]; - int penalty; + int penalty, call_limit; cfg = ast_config_load("queues.conf"); if (!cfg) { @@ -3365,14 +3423,27 @@ static void reload_queues(void) /* Add a new member */ ast_copy_string(interface, var->value, sizeof(interface)); if ((tmp = strchr(interface, ','))) { + char *ptr; + if ((ptr = strchr(tmp + 1, ','))) { + *ptr = '\0'; + ptr++; + call_limit = atoi(ptr); + if (call_limit < 0) { + call_limit = 0; + } + } else + call_limit = 0; + *tmp = '\0'; tmp++; penalty = atoi(tmp); if (penalty < 0) { penalty = 0; } - } else + } else { penalty = 0; + call_limit = 0; + } /* Find the old position in the list */ for (prev = NULL, cur = q->members; cur; prev = cur, cur = cur->next) { @@ -3381,7 +3452,8 @@ static void reload_queues(void) } } - newm = create_queue_member(interface, penalty, cur ? cur->paused : 0); + newm = create_queue_member(interface, penalty, call_limit, + cur ? cur->paused : 0); if (cur) { /* Delete it now */ @@ -3525,6 +3597,8 @@ static int __queues_show(int manager, in max_left = sizeof(max_buf); if (mem->penalty) ast_build_string(&max, &max_left, " with penalty %d", mem->penalty); + if (mem->call_limit) + ast_build_string(&max, &max_left, " with call limit %d", mem->call_limit); if (mem->dynamic) ast_build_string(&max, &max_left, " (dynamic)"); if (mem->paused) @@ -3644,6 +3718,7 @@ static int manager_queues_status( struct "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" @@ -3651,7 +3726,8 @@ static int manager_queues_status( struct "%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 */ @@ -3687,12 +3763,13 @@ static int manager_queues_status( struct static int manager_add_queue_member(struct mansession *s, struct message *m) { - char *queuename, *interface, *penalty_s, *paused_s; - int paused, penalty = 0; + char *queuename, *interface, *penalty_s, *call_limit_s, *paused_s; + 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, "Call Limit"); paused_s = astman_get_header(m, "Paused"); if (ast_strlen_zero(queuename)) { @@ -3711,12 +3788,18 @@ static int manager_add_queue_member(stru 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 paused = abs(ast_true(paused_s)); - switch (add_to_queue(queuename, interface, penalty, paused, queue_persistent_members)) { + switch (add_to_queue(queuename, interface, penalty, call_limit, paused, queue_persistent_members)) { case RES_OKAY: astman_send_ack(s, m, "Added interface to queue"); break; @@ -3791,21 +3874,34 @@ static int manager_pause_queue_member(st static int handle_add_queue_member(int fd, int argc, char *argv[]) { - char *queuename, *interface; - int penalty; + char *queuename, *interface, *penalty_s, *call_limit_s; + int penalty, call_limit; - if ((argc != 6) && (argc != 8)) { + penalty_s = NULL; + call_limit_s = NULL; + if ((argc != 6) && (argc != 8) && (argc != 10)) { return RESULT_SHOWUSAGE; } else if (strcmp(argv[4], "to")) { return RESULT_SHOWUSAGE; - } else if ((argc == 8) && strcmp(argv[6], "penalty")) { - return RESULT_SHOWUSAGE; + } else if ((argc == 8) || (argc == 10)) { + if (strcmp(argv[6], "penalty") == 0) { + penalty_s = argv[7]; + } else if (strcmp(argv[6], "call-limit") == 0) { + call_limit_s = argv[7]; + } else + return RESULT_SHOWUSAGE; + if (argc == 10) { + if ((call_limit_s == NULL) && (strcmp(argv[8], "call-limit") == 0)) { + call_limit_s = argv[9]; + } else + return RESULT_SHOWUSAGE; + } } queuename = argv[5]; interface = argv[3]; - if (argc == 8) { - if (sscanf(argv[7], "%d", &penalty) == 1) { + if (penalty_s) { + if (sscanf(penalty_s, "%d", &penalty) == 1) { if (penalty < 0) { ast_cli(fd, "Penalty must be >= 0\n"); penalty = 0; @@ -3818,7 +3914,21 @@ static int handle_add_queue_member(int f penalty = 0; } - switch (add_to_queue(queuename, interface, penalty, 0, queue_persistent_members)) { + if (call_limit_s) { + if (sscanf(call_limit_s, "%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; + } + + switch (add_to_queue(queuename, interface, penalty, call_limit, 0, queue_persistent_members)) { case RES_OKAY: ast_cli(fd, "Added interface '%s' to queue '%s'\n", interface, queuename); return RESULT_SUCCESS; @@ -3838,7 +3948,7 @@ static int handle_add_queue_member(int f 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 - */ + /* 0 - add; 1 - queue; 2 - member; 3 - ; 4 - to; 5 - ; 6 - penalty|call-limit; 7 - ; 8 - call-limit; 9 - */ switch (pos) { case 3: /* Don't attempt to complete name of member (infinite possibilities) */ @@ -3854,11 +3964,16 @@ static char *complete_add_queue_member(c return complete_queue(line, word, pos, state); case 6: if (state == 0) { - return strdup("penalty"); + if (word && word[0] == 'c') + return strdup("call-limit"); + else + return strdup("penalty"); + } else if ((state == 1) && !(word && ((word[0] == 'c') || (word[0] == 'p')))) { + return strdup("call-limit"); } else { return NULL; } - case 7: + case 7: case 9: if (state < 100) { /* 0-99 */ char *num = malloc(3); if (num) { @@ -3868,6 +3983,12 @@ static char *complete_add_queue_member(c } else { return NULL; } + case 8: + if (state == 0) { + return strdup("call-limit"); + } else { + return NULL; + } default: return NULL; } @@ -3959,7 +4080,7 @@ static struct ast_cli_entry cli_show_que "Show status of a specified queue", show_queue_usage, complete_queue }; static char aqm_cmd_usage[] = -"Usage: add queue member to [penalty ]\n"; +"Usage: add queue member to [penalty ] [call_limit ]\n"; static struct ast_cli_entry cli_add_queue_member = { { "add", "queue", "member", NULL }, handle_add_queue_member,