diff options
Diffstat (limited to 'apps')
-rw-r--r-- | apps/app_chanspy.c | 16 | ||||
-rw-r--r-- | apps/app_dial.c | 6 | ||||
-rw-r--r-- | apps/app_disa.c | 10 | ||||
-rw-r--r-- | apps/app_dumpchan.c | 4 | ||||
-rw-r--r-- | apps/app_externalivr.c | 6 | ||||
-rw-r--r-- | apps/app_meetme.c | 2 | ||||
-rw-r--r-- | apps/app_queue.c | 239 |
7 files changed, 173 insertions, 110 deletions
diff --git a/apps/app_chanspy.c b/apps/app_chanspy.c index 4814f4ce5..354b9ea7d 100644 --- a/apps/app_chanspy.c +++ b/apps/app_chanspy.c @@ -693,9 +693,7 @@ static int channel_spy(struct ast_channel *chan, struct ast_autochan *spyee_auto } } - ast_channel_lock(chan); - ast_set_flag(ast_channel_flags(chan), AST_FLAG_END_DTMF_ONLY); - ast_channel_unlock(chan); + ast_channel_set_flag(chan, AST_FLAG_END_DTMF_ONLY); csth.volfactor = *volfactor; @@ -825,9 +823,7 @@ static int channel_spy(struct ast_channel *chan, struct ast_autochan *spyee_auto else ast_deactivate_generator(chan); - ast_channel_lock(chan); - ast_clear_flag(ast_channel_flags(chan), AST_FLAG_END_DTMF_ONLY); - ast_channel_unlock(chan); + ast_channel_clear_flag(chan, AST_FLAG_END_DTMF_ONLY); if (ast_test_flag(flags, OPTION_WHISPER | OPTION_BARGE | OPTION_DTMF_SWITCH_MODES)) { ast_audiohook_lock(&csth.whisper_audiohook); @@ -921,7 +917,7 @@ static int common_exec(struct ast_channel *chan, struct ast_flags *flags, if (ast_channel_state(chan) != AST_STATE_UP) ast_answer(chan); - ast_set_flag(ast_channel_flags(chan), AST_FLAG_SPYING); /* so nobody can spy on us while we are spying */ + ast_channel_set_flag(chan, AST_FLAG_SPYING); waitms = 100; @@ -934,7 +930,7 @@ static int common_exec(struct ast_channel *chan, struct ast_flags *flags, if (!res) res = ast_waitstream(chan, ""); else if (res < 0) { - ast_clear_flag(ast_channel_flags(chan), AST_FLAG_SPYING); + ast_channel_clear_flag(chan, AST_FLAG_SPYING); break; } if (!ast_strlen_zero(exitcontext)) { @@ -977,7 +973,7 @@ static int common_exec(struct ast_channel *chan, struct ast_flags *flags, res = ast_waitfordigit(chan, waitms); if (res < 0) { iter = ast_channel_iterator_destroy(iter); - ast_clear_flag(ast_channel_flags(chan), AST_FLAG_SPYING); + ast_channel_clear_flag(chan, AST_FLAG_SPYING); break; } if (!ast_strlen_zero(exitcontext)) { @@ -1196,7 +1192,7 @@ static int common_exec(struct ast_channel *chan, struct ast_flags *flags, } exit: - ast_clear_flag(ast_channel_flags(chan), AST_FLAG_SPYING); + ast_channel_clear_flag(chan, AST_FLAG_SPYING); ast_channel_setoption(chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0); diff --git a/apps/app_dial.c b/apps/app_dial.c index 79e2a9b0b..847a095f4 100644 --- a/apps/app_dial.c +++ b/apps/app_dial.c @@ -2855,7 +2855,7 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast ast_log(LOG_ERROR, "error streaming file '%s' to callee\n", opt_args[OPT_ARG_ANNOUNCE]); } - ast_set_flag(ast_channel_flags(peer), AST_FLAG_END_DTMF_ONLY); + ast_channel_set_flag(peer, AST_FLAG_END_DTMF_ONLY); while (ast_channel_stream(peer)) { int ms; @@ -2919,13 +2919,13 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast } ast_sched_runq(ast_channel_sched(peer)); } - ast_clear_flag(ast_channel_flags(peer), AST_FLAG_END_DTMF_ONLY); + ast_channel_clear_flag(peer, AST_FLAG_END_DTMF_ONLY); } if (chan && peer && ast_test_flag64(&opts, OPT_GOTO) && !ast_strlen_zero(opt_args[OPT_ARG_GOTO])) { /* chan and peer are going into the PBX; as such neither are considered * outgoing channels any longer */ - ast_clear_flag(ast_channel_flags(chan), AST_FLAG_OUTGOING); + ast_channel_clear_flag(chan, AST_FLAG_OUTGOING); ast_replace_subargument_delimiter(opt_args[OPT_ARG_GOTO]); ast_parseable_goto(chan, opt_args[OPT_ARG_GOTO]); diff --git a/apps/app_disa.c b/apps/app_disa.c index 8dc61ff64..cceb5541d 100644 --- a/apps/app_disa.c +++ b/apps/app_disa.c @@ -208,7 +208,7 @@ static int disa_exec(struct ast_channel *chan, const char *data) play_dialtone(chan, args.mailbox); - ast_set_flag(ast_channel_flags(chan), AST_FLAG_END_DTMF_ONLY); + ast_channel_set_flag(chan, AST_FLAG_END_DTMF_ONLY); for (;;) { /* if outa time, give em reorder */ @@ -224,7 +224,7 @@ static int disa_exec(struct ast_channel *chan, const char *data) } if (!(f = ast_read(chan))) { - ast_clear_flag(ast_channel_flags(chan), AST_FLAG_END_DTMF_ONLY); + ast_channel_clear_flag(chan, AST_FLAG_END_DTMF_ONLY); return -1; } @@ -232,7 +232,7 @@ static int disa_exec(struct ast_channel *chan, const char *data) if (f->data.uint32) ast_channel_hangupcause_set(chan, f->data.uint32); ast_frfree(f); - ast_clear_flag(ast_channel_flags(chan), AST_FLAG_END_DTMF_ONLY); + ast_channel_clear_flag(chan, AST_FLAG_END_DTMF_ONLY); return -1; } @@ -261,7 +261,7 @@ static int disa_exec(struct ast_channel *chan, const char *data) fp = fopen(args.passcode,"r"); if (!fp) { ast_log(LOG_WARNING,"DISA password file %s not found on chan %s\n",args.passcode,ast_channel_name(chan)); - ast_clear_flag(ast_channel_flags(chan), AST_FLAG_END_DTMF_ONLY); + ast_channel_clear_flag(chan, AST_FLAG_END_DTMF_ONLY); return -1; } pwline[0] = 0; @@ -357,7 +357,7 @@ static int disa_exec(struct ast_channel *chan, const char *data) } } - ast_clear_flag(ast_channel_flags(chan), AST_FLAG_END_DTMF_ONLY); + ast_channel_clear_flag(chan, AST_FLAG_END_DTMF_ONLY); if (k == 3) { int recheck = 0; diff --git a/apps/app_dumpchan.c b/apps/app_dumpchan.c index 0789ce06e..bec778817 100644 --- a/apps/app_dumpchan.c +++ b/apps/app_dumpchan.c @@ -88,8 +88,6 @@ static int serialize_showchan(struct ast_channel *c, char *buf, size_t size) ast_channel_lock(c); bridge = ast_channel_get_bridge(c); - ast_channel_unlock(c); - snprintf(buf,size, "Name= %s\n" "Type= %s\n" @@ -166,7 +164,7 @@ static int serialize_showchan(struct ast_channel *c, char *buf, size_t size) ast_channel_appl(c) ? ast_channel_appl(c) : "(N/A)", ast_channel_data(c) ? S_OR(ast_channel_data(c), "(Empty)") : "(None)", (ast_test_flag(ast_channel_flags(c), AST_FLAG_BLOCKING) ? ast_channel_blockproc(c) : "(Not Blocking)")); - + ast_channel_unlock(c); ao2_cleanup(bridge); return 0; } diff --git a/apps/app_externalivr.c b/apps/app_externalivr.c index c2224b44b..84267a5ea 100644 --- a/apps/app_externalivr.c +++ b/apps/app_externalivr.c @@ -642,9 +642,9 @@ static int eivr_comm(struct ast_channel *chan, struct ivr_localuser *u, waitfds[0] = ast_iostream_get_fd(eivr_commands); waitfds[1] = eivr_errors ? ast_iostream_get_fd(eivr_errors) : -1; - while (1) { - if (ast_test_flag(ast_channel_flags(chan), AST_FLAG_ZOMBIE)) { - ast_chan_log(LOG_ERROR, chan, "Is a zombie\n"); + while (1) { + if (ast_test_flag(ast_channel_flags(chan), AST_FLAG_ZOMBIE)) { + ast_chan_log(LOG_ERROR, chan, "Is a zombie\n"); break; } if (!hangup_info_sent && !(ast_test_flag(&flags, run_dead)) && ast_check_hangup(chan)) { diff --git a/apps/app_meetme.c b/apps/app_meetme.c index 36f5c7727..71ca9dc9f 100644 --- a/apps/app_meetme.c +++ b/apps/app_meetme.c @@ -4661,7 +4661,7 @@ static struct ast_conference *find_conf_realtime(struct ast_channel *chan, char if (cnf) { if (confflags->flags && !cnf->chan && !ast_test_flag64(confflags, CONFFLAG_QUIET) && - ast_test_flag64(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW) | CONFFLAG_INTROUSER_VMREC) { + ast_test_flag64(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW | CONFFLAG_INTROUSER_VMREC)) { ast_log(LOG_WARNING, "No DAHDI channel available for conference, user introduction disabled (is chan_dahdi loaded?)\n"); ast_clear_flag64(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW | CONFFLAG_INTROUSER_VMREC); } diff --git a/apps/app_queue.c b/apps/app_queue.c index 0f46d280c..f158a4caa 100644 --- a/apps/app_queue.c +++ b/apps/app_queue.c @@ -1545,6 +1545,7 @@ struct queue_ent { int pending; /*!< Non-zero if we are attempting to call a member */ int max_penalty; /*!< Limit the members that can take this call to this penalty or lower */ int min_penalty; /*!< Limit the members that can take this call to this penalty or higher */ + int raise_penalty; /*!< Float lower penalty mambers to a minimum penalty */ int linpos; /*!< If using linear strategy, what position are we at? */ int linwrapped; /*!< Is the linpos wrapped? */ time_t start; /*!< When we started holding */ @@ -1570,10 +1571,11 @@ struct member { int paused; /*!< Are we paused (not accepting calls)? */ char reason_paused[80]; /*!< Reason of paused if member is paused */ int queuepos; /*!< In what order (pertains to certain strategies) should this member be called? */ + int callcompletedinsl; /*!< Whether the current call was completed within service level */ + time_t starttime; /*!< The time at which the member answered the current caller. */ time_t lastcall; /*!< When last successful call was hungup */ time_t lastpause; /*!< When started the last pause */ - unsigned int in_call:1; /*!< True if member is still in call. (so lastcall is not actual) */ - struct call_queue *lastqueue; /*!< Last queue we received a call */ + struct call_queue *lastqueue; /*!< Last queue we received a call */ unsigned int dead:1; /*!< Used to detect members deleted in realtime */ unsigned int delme:1; /*!< Flag to delete entry on reload */ char rt_uniqueid[80]; /*!< Unique id of realtime member entry */ @@ -1605,8 +1607,10 @@ struct penalty_rule { int time; /*!< Number of seconds that need to pass before applying this rule */ int max_value; /*!< The amount specified in the penalty rule for max penalty */ int min_value; /*!< The amount specified in the penalty rule for min penalty */ + int raise_value; /*!< The amount specified in the penalty rule for min penalty */ int max_relative; /*!< Is the max adjustment relative? 1 for relative, 0 for absolute */ int min_relative; /*!< Is the min adjustment relative? 1 for relative, 0 for absolute */ + int raise_relative; /*!< Is the min adjustment relative? 1 for relative, 0 for absolute */ AST_LIST_ENTRY(penalty_rule) list; /*!< Next penalty_rule */ }; @@ -1729,6 +1733,7 @@ static struct ao2_container *queues; static void update_realtime_members(struct call_queue *q); static struct member *interface_exists(struct call_queue *q, const char *interface); static int set_member_paused(const char *queuename, const char *interface, const char *reason, int paused); +static int update_queue(struct call_queue *q, struct member *member, int callcompletedinsl, time_t starttime); static struct member *find_member_by_queuename_and_interface(const char *queuename, const char *interface); /*! \brief sets the QUEUESTATUS channel variable */ @@ -2220,7 +2225,7 @@ static struct ast_json *queue_member_blob_create(struct call_queue *q, struct me "CallsTaken", mem->calls, "LastCall", (int)mem->lastcall, "LastPause", (int)mem->lastpause, - "InCall", mem->in_call, + "InCall", mem->starttime ? 1 : 0, "Status", mem->status, "Paused", mem->paused, "PausedReason", mem->reason_paused, @@ -2233,7 +2238,7 @@ static struct ast_json *queue_member_blob_create(struct call_queue *q, struct me * is available, the function immediately returns 0. If no members are available, * then -1 is returned. */ -static int get_member_status(struct call_queue *q, int max_penalty, int min_penalty, enum empty_conditions conditions, int devstate) +static int get_member_status(struct call_queue *q, int max_penalty, int min_penalty, int raise_penalty, enum empty_conditions conditions, int devstate) { struct member *member; struct ao2_iterator mem_iter; @@ -2241,7 +2246,12 @@ static int get_member_status(struct call_queue *q, int max_penalty, int min_pena ao2_lock(q); mem_iter = ao2_iterator_init(q->members, 0); for (; (member = ao2_iterator_next(&mem_iter)); ao2_ref(member, -1)) { - if ((max_penalty != INT_MAX && member->penalty > max_penalty) || (min_penalty != INT_MAX && member->penalty < min_penalty)) { + int penalty = member->penalty; + if (raise_penalty != INT_MAX && penalty < raise_penalty) { + ast_debug(4, "%s is having his penalty raised up from %d to %d\n", member->membername, penalty, raise_penalty); + penalty = raise_penalty; + } + if ((max_penalty != INT_MAX && penalty > max_penalty) || (min_penalty != INT_MAX && penalty < min_penalty)) { if (conditions & QUEUE_EMPTY_PENALTY) { ast_debug(4, "%s is unavailable because his penalty is not between %d and %d\n", member->membername, min_penalty, max_penalty); continue; @@ -2284,10 +2294,6 @@ static int get_member_status(struct call_queue *q, int max_penalty, int min_pena if (member->paused && (conditions & QUEUE_EMPTY_PAUSED)) { ast_debug(4, "%s is unavailable because he is paused'\n", member->membername); break; - } else if ((conditions & QUEUE_EMPTY_WRAPUP) && member->in_call && q->wrapuptime) { - ast_debug(4, "%s is unavailable because still in call, so we can`t check " - "wrapuptime (%d)\n", member->membername, q->wrapuptime); - break; } else if ((conditions & QUEUE_EMPTY_WRAPUP) && member->lastcall && q->wrapuptime && (time(NULL) - q->wrapuptime < member->lastcall)) { ast_debug(4, "%s is unavailable because it has only been %d seconds since his last call (wrapup time is %d)\n", member->membername, (int) (time(NULL) - member->lastcall), q->wrapuptime); break; @@ -2306,7 +2312,7 @@ static int get_member_status(struct call_queue *q, int max_penalty, int min_pena if (!devstate && (conditions & QUEUE_EMPTY_RINGING)) { /* member state still may be RINGING due to lag in event message - check again with device state */ - return get_member_status(q, max_penalty, min_penalty, conditions, 1); + return get_member_status(q, max_penalty, min_penalty, raise_penalty, conditions, 1); } return -1; } @@ -2384,6 +2390,14 @@ static void pending_members_remove(struct member *mem) static void update_status(struct call_queue *q, struct member *m, const int status) { if (m->status != status) { + /* If this member has transitioned to being available then update their queue + * information. If they are currently in a call then the leg to the agent will be + * considered done and the call finished. + */ + if (status == AST_DEVICE_NOT_INUSE) { + update_queue(q, m, m->callcompletedinsl, m->starttime); + } + m->status = status; /* Remove the member from the pending members pool only when the status changes. @@ -2430,9 +2444,6 @@ static int is_member_available(struct call_queue *q, struct member *mem) } /* Let wrapuptimes override device state availability */ - if (q->wrapuptime && mem->in_call) { - available = 0; /* member is still in call, cant check wrapuptime to lastcall time */ - } if (mem->lastcall && q->wrapuptime && (time(NULL) - q->wrapuptime < mem->lastcall)) { available = 0; } @@ -2791,8 +2802,9 @@ static void clear_queue(struct call_queue *q) struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0); while ((mem = ao2_iterator_next(&mem_iter))) { mem->calls = 0; + mem->callcompletedinsl = 0; mem->lastcall = 0; - mem->in_call = 0; + mem->starttime = 0; ao2_ref(mem, -1); } ao2_iterator_destroy(&mem_iter); @@ -2810,7 +2822,7 @@ static void clear_queue(struct call_queue *q) */ static int insert_penaltychange(const char *list_name, const char *content, const int linenum) { - char *timestr, *maxstr, *minstr, *contentdup; + char *timestr, *maxstr, *minstr, *raisestr, *contentdup; struct penalty_rule *rule = NULL, *rule_iter; struct rule_list *rl_iter; int penaltychangetime, inserted = 0; @@ -2828,8 +2840,16 @@ static int insert_penaltychange(const char *list_name, const char *content, cons } *maxstr++ = '\0'; - timestr = contentdup; + if ((minstr = strchr(maxstr,','))) { + *minstr++ = '\0'; + if ((raisestr = strchr(minstr,','))) { + *raisestr++ = '\0'; + } + } else { + raisestr = NULL; + } + timestr = contentdup; if ((penaltychangetime = atoi(timestr)) < 0) { ast_log(LOG_WARNING, "Improper time parameter specified for penaltychange rule at line %d. Ignoring.\n", linenum); ast_free(rule); @@ -2838,10 +2858,6 @@ static int insert_penaltychange(const char *list_name, const char *content, cons rule->time = penaltychangetime; - if ((minstr = strchr(maxstr,','))) { - *minstr++ = '\0'; - } - /* The last check will evaluate true if either no penalty change is indicated for a given rule * OR if a min penalty change is indicated but no max penalty change is */ if (*maxstr == '+' || *maxstr == '-' || *maxstr == '\0') { @@ -2859,6 +2875,15 @@ static int insert_penaltychange(const char *list_name, const char *content, cons rule->min_relative = 1; } + if (!ast_strlen_zero(raisestr)) { + if (*raisestr == '+' || *raisestr == '-') { + rule->raise_relative = 1; + } + rule->raise_value = atoi(raisestr); + } else { /*there was no raise specified, so assume this means no change*/ + rule->raise_relative = 1; + } + /*We have the rule made, now we need to insert it where it belongs*/ AST_LIST_TRAVERSE(&rule_lists, rl_iter, list){ if (strcasecmp(rl_iter->name, list_name)) { @@ -2915,9 +2940,10 @@ static int load_realtime_rules(void) return 0; } while ((rulecat = ast_category_browse(cfg, rulecat))) { - const char *timestr, *maxstr, *minstr, *rule_name; + const char *timestr, *maxstr, *minstr, *raisestr, *rule_name; int penaltychangetime, rule_exists = 0, inserted = 0; - int max_penalty = 0, min_penalty = 0, min_relative = 0, max_relative = 0; + int max_penalty = 0, min_penalty = 0, raise_penalty = 0; + int min_relative = 0, max_relative = 0, raise_relative = 0; struct penalty_rule *new_penalty_rule = NULL; rule_name = ast_variable_retrieve(cfg, rulecat, "rule_name"); @@ -2968,11 +2994,22 @@ static int load_realtime_rules(void) min_relative = 1; } } + if (!(raisestr = ast_variable_retrieve(cfg, rulecat, "raise_penalty")) || + ast_strlen_zero(raisestr) || sscanf(raisestr, "%30d", &raise_penalty) != 1) { + raise_penalty = 0; + raise_relative = 1; + } else { + if (*raisestr == '+' || *raisestr == '-') { + raise_relative = 1; + } + } new_penalty_rule->time = penaltychangetime; new_penalty_rule->max_relative = max_relative; new_penalty_rule->max_value = max_penalty; new_penalty_rule->min_relative = min_relative; new_penalty_rule->min_value = min_penalty; + new_penalty_rule->raise_relative = raise_relative; + new_penalty_rule->raise_value = raise_penalty; AST_LIST_TRAVERSE_SAFE_BEGIN(&new_rl->rules, pr_iter, list) { if (new_penalty_rule->time < pr_iter->time) { AST_LIST_INSERT_BEFORE_CURRENT(new_penalty_rule, list); @@ -3714,7 +3751,7 @@ static int join_queue(char *queuename, struct queue_ent *qe, enum queue_result * /* This is our one */ if (q->joinempty) { int status = 0; - if ((status = get_member_status(q, qe->max_penalty, qe->min_penalty, q->joinempty, 0))) { + if ((status = get_member_status(q, qe->max_penalty, qe->min_penalty, qe->raise_penalty, q->joinempty, 0))) { *reason = QUEUE_JOINEMPTY; ao2_unlock(q); queue_t_unref(q, "Done with realtime queue"); @@ -4275,12 +4312,6 @@ static int can_ring_entry(struct queue_ent *qe, struct callattempt *call) return 0; } - if (call->member->in_call && call->lastqueue && call->lastqueue->wrapuptime) { - ast_debug(1, "%s is in call, so not available (wrapuptime %d)\n", - call->interface, call->lastqueue->wrapuptime); - return 0; - } - if ((call->lastqueue && call->lastqueue->wrapuptime && (time(NULL) - call->lastcall < call->lastqueue->wrapuptime)) || (!call->lastqueue && qe->parent->wrapuptime && (time(NULL) - call->lastcall < qe->parent->wrapuptime))) { ast_debug(1, "Wrapuptime not yet expired on queue %s for %s\n", @@ -5429,6 +5460,32 @@ static void update_qe_rule(struct queue_ent *qe) qe->min_penalty, ast_channel_name(qe->chan), qe->pr->time); } + if (qe->raise_penalty != INT_MAX) { + char raise_penalty_str[20]; + int raise_penalty; + + if (qe->pr->raise_relative) { + raise_penalty = qe->raise_penalty + qe->pr->raise_value; + } else { + raise_penalty = qe->pr->raise_value; + } + + /* a relative change to the penalty could put it below 0 */ + if (raise_penalty < 0) { + raise_penalty = 0; + } + + if (max_penalty != INT_MAX && raise_penalty > max_penalty) { + raise_penalty = max_penalty; + } + + snprintf(raise_penalty_str, sizeof(raise_penalty_str), "%d", raise_penalty); + pbx_builtin_setvar_helper(qe->chan, "QUEUE_RAISE_PENALTY", raise_penalty_str); + qe->raise_penalty = raise_penalty; + ast_debug(3, "Setting raised penalty to %d for caller %s since %d seconds have elapsed\n", + qe->raise_penalty, ast_channel_name(qe->chan), qe->pr->time); + } + qe->pr = AST_LIST_NEXT(qe->pr, list); } @@ -5462,7 +5519,7 @@ static int wait_our_turn(struct queue_ent *qe, int ringing, enum queue_result *r if (qe->parent->leavewhenempty) { int status = 0; - if ((status = get_member_status(qe->parent, qe->max_penalty, qe->min_penalty, qe->parent->leavewhenempty, 0))) { + if ((status = get_member_status(qe->parent, qe->max_penalty, qe->min_penalty, qe->raise_penalty, qe->parent->leavewhenempty, 0))) { *reason = QUEUE_LEAVEEMPTY; ast_queue_log(qe->parent->name, ast_channel_uniqueid(qe->chan), "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long) (time(NULL) - qe->start)); res = -1; @@ -5522,14 +5579,22 @@ static int wait_our_turn(struct queue_ent *qe, int ringing, enum queue_result *r * \brief update the queue status * \retval Always 0 */ -static int update_queue(struct call_queue *q, struct member *member, int callcompletedinsl, int newtalktime) +static int update_queue(struct call_queue *q, struct member *member, int callcompletedinsl, time_t starttime) { int oldtalktime; - + int newtalktime = time(NULL) - starttime; struct member *mem; struct call_queue *qtmp; struct ao2_iterator queue_iter; + /* It is possible for us to be called when a call has already been considered terminated + * and data updated, so to ensure we only act on the call that the agent is currently in + * we check when the call was bridged. + */ + if (!starttime || (member->starttime != starttime)) { + return 0; + } + if (shared_lastcall) { queue_iter = ao2_iterator_init(queues, 0); while ((qtmp = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) { @@ -5537,10 +5602,9 @@ static int update_queue(struct call_queue *q, struct member *member, int callcom if ((mem = ao2_find(qtmp->members, member, OBJ_POINTER))) { time(&mem->lastcall); mem->calls++; + mem->callcompletedinsl = 0; + mem->starttime = 0; mem->lastqueue = q; - mem->in_call = 0; - ast_debug(4, "Marked member %s as NOT in_call. Lastcall time: %ld \n", - mem->membername, (long)mem->lastcall); ao2_ref(mem, -1); } ao2_unlock(qtmp); @@ -5550,11 +5614,10 @@ static int update_queue(struct call_queue *q, struct member *member, int callcom } else { ao2_lock(q); time(&member->lastcall); + member->callcompletedinsl = 0; member->calls++; + member->starttime = 0; member->lastqueue = q; - member->in_call = 0; - ast_debug(4, "Marked member %s as NOT in_call. Lastcall time: %ld \n", - member->membername, (long)member->lastcall); ao2_unlock(q); } /* Member might never experience any direct status change (local @@ -5593,10 +5656,15 @@ static int calc_metric(struct call_queue *q, struct member *mem, int pos, struct /* disregarding penalty on too few members? */ int membercount = ao2_container_count(q->members); unsigned char usepenalty = (membercount <= q->penaltymemberslimit) ? 0 : 1; + int penalty = mem->penalty; if (usepenalty) { - if ((qe->max_penalty != INT_MAX && mem->penalty > qe->max_penalty) || - (qe->min_penalty != INT_MAX && mem->penalty < qe->min_penalty)) { + if (qe->raise_penalty != INT_MAX && penalty < qe->raise_penalty) { + /* Low penalty is raised up to the current minimum */ + penalty = qe->raise_penalty; + } + if ((qe->max_penalty != INT_MAX && penalty > qe->max_penalty) || + (qe->min_penalty != INT_MAX && penalty < qe->min_penalty)) { return -1; } } else { @@ -5607,7 +5675,7 @@ static int calc_metric(struct call_queue *q, struct member *mem, int pos, struct switch (q->strategy) { case QUEUE_STRATEGY_RINGALL: /* Everyone equal, except for penalty */ - tmp->metric = mem->penalty * 1000000 * usepenalty; + tmp->metric = penalty * 1000000 * usepenalty; break; case QUEUE_STRATEGY_LINEAR: if (pos < qe->linpos) { @@ -5619,7 +5687,7 @@ static int calc_metric(struct call_queue *q, struct member *mem, int pos, struct } tmp->metric = pos; } - tmp->metric += mem->penalty * 1000000 * usepenalty; + tmp->metric += penalty * 1000000 * usepenalty; break; case QUEUE_STRATEGY_RRORDERED: case QUEUE_STRATEGY_RRMEMORY: @@ -5633,18 +5701,18 @@ static int calc_metric(struct call_queue *q, struct member *mem, int pos, struct } tmp->metric = pos; } - tmp->metric += mem->penalty * 1000000 * usepenalty; + tmp->metric += penalty * 1000000 * usepenalty; break; case QUEUE_STRATEGY_RANDOM: tmp->metric = ast_random() % 1000; - tmp->metric += mem->penalty * 1000000 * usepenalty; + tmp->metric += penalty * 1000000 * usepenalty; break; case QUEUE_STRATEGY_WRANDOM: - tmp->metric = ast_random() % ((1 + mem->penalty) * 1000); + tmp->metric = ast_random() % ((1 + penalty) * 1000); break; case QUEUE_STRATEGY_FEWESTCALLS: tmp->metric = mem->calls; - tmp->metric += mem->penalty * 1000000 * usepenalty; + tmp->metric += penalty * 1000000 * usepenalty; break; case QUEUE_STRATEGY_LEASTRECENT: if (!mem->lastcall) { @@ -5652,7 +5720,7 @@ static int calc_metric(struct call_queue *q, struct member *mem, int pos, struct } else { tmp->metric = 1000000 - (time(NULL) - mem->lastcall); } - tmp->metric += mem->penalty * 1000000 * usepenalty; + tmp->metric += penalty * 1000000 * usepenalty; break; default: ast_log(LOG_WARNING, "Can't calculate metric for unknown strategy %d\n", q->strategy); @@ -6012,7 +6080,7 @@ static void handle_blind_transfer(void *userdata, struct stasis_subscription *su send_agent_complete(queue_data->queue->name, caller_snapshot, member_snapshot, queue_data->member, queue_data->holdstart, queue_data->starttime, TRANSFER); update_queue(queue_data->queue, queue_data->member, queue_data->callcompletedinsl, - time(NULL) - queue_data->starttime); + queue_data->starttime); remove_stasis_subscriptions(queue_data); } @@ -6072,7 +6140,7 @@ static void handle_attended_transfer(void *userdata, struct stasis_subscription send_agent_complete(queue_data->queue->name, caller_snapshot, member_snapshot, queue_data->member, queue_data->holdstart, queue_data->starttime, TRANSFER); update_queue(queue_data->queue, queue_data->member, queue_data->callcompletedinsl, - time(NULL) - queue_data->starttime); + queue_data->starttime); remove_stasis_subscriptions(queue_data); } @@ -6273,7 +6341,7 @@ static void handle_hangup(void *userdata, struct stasis_subscription *sub, send_agent_complete(queue_data->queue->name, caller_snapshot, member_snapshot, queue_data->member, queue_data->holdstart, queue_data->starttime, reason); update_queue(queue_data->queue, queue_data->member, queue_data->callcompletedinsl, - time(NULL) - queue_data->starttime); + queue_data->starttime); remove_stasis_subscriptions(queue_data); } @@ -6534,7 +6602,6 @@ static int try_calling(struct queue_ent *qe, struct ast_flags opts, char **opt_a int x=0; char *announce = NULL; char digit = 0; - time_t callstart; time_t now = time(NULL); struct ast_bridge_config bridge_config; char nondataquality = 1; @@ -6545,12 +6612,10 @@ static int try_calling(struct queue_ent *qe, struct ast_flags opts, char **opt_a char tmpid[256]; int forwardsallowed = 1; int block_connected_line = 0; - int callcompletedinsl; struct ao2_iterator memi; struct queue_end_bridge *queue_end_bridge = NULL; - struct ao2_iterator queue_iter; /* to iterate through all queues (for shared_lastcall)*/ - struct member *mem; - struct call_queue *queuetmp; + int callcompletedinsl; + time_t starttime; memset(&bridge_config, 0, sizeof(bridge_config)); tmpid[0] = 0; @@ -6739,10 +6804,10 @@ static int try_calling(struct queue_ent *qe, struct ast_flags opts, char **opt_a /* Update parameters for the queue */ time(&now); recalc_holdtime(qe, (now - qe->start)); + member = lpeer->member; ao2_lock(qe->parent); - callcompletedinsl = ((now - qe->start) <= qe->parent->servicelevel); + callcompletedinsl = member->callcompletedinsl = ((now - qe->start) <= qe->parent->servicelevel); ao2_unlock(qe->parent); - member = lpeer->member; /* Increment the refcount for this member, since we're going to be using it for awhile in here. */ ao2_ref(member, 1); hangupcalls(qe, outgoing, peer, qe->cancel_answered_elsewhere); @@ -6978,27 +7043,6 @@ static int try_calling(struct queue_ent *qe, struct ast_flags opts, char **opt_a } qe->handled++; - /** mark member as "in_call" in all queues */ - if (shared_lastcall) { - queue_iter = ao2_iterator_init(queues, 0); - while ((queuetmp = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) { - ao2_lock(queuetmp); - if ((mem = ao2_find(queuetmp->members, member, OBJ_POINTER))) { - mem->in_call = 1; - ast_debug(4, "Marked member %s as in_call \n", mem->membername); - ao2_ref(mem, -1); - } - ao2_unlock(queuetmp); - queue_t_unref(queuetmp, "Done with iterator"); - } - ao2_iterator_destroy(&queue_iter); - } else { - ao2_lock(qe->parent); - member->in_call = 1; - ast_debug(4, "Marked member %s as in_call \n", member->membername); - ao2_unlock(qe->parent); - } - ast_queue_log(queuename, ast_channel_uniqueid(qe->chan), member->membername, "CONNECT", "%ld|%s|%ld", (long) (time(NULL) - qe->start), ast_channel_uniqueid(peer), (long)(orig - to > 0 ? (orig - to) / 1000 : 0)); @@ -7026,8 +7070,16 @@ static int try_calling(struct queue_ent *qe, struct ast_flags opts, char **opt_a queue_t_ref(qe->parent, "For bridge_config reference"); } - time(&callstart); - setup_stasis_subs(qe, peer, member, qe->start, callstart, callcompletedinsl); + ao2_lock(qe->parent); + time(&member->starttime); + starttime = member->starttime; + ao2_unlock(qe->parent); + /* As a queue member may end up in multiple calls at once if a transfer occurs with + * a Local channel in the mix we pass the current call information (starttime) to the + * Stasis subscriptions so when they update the queue member data it becomes a noop + * if this call is no longer between the caller and the queue member. + */ + setup_stasis_subs(qe, peer, member, qe->start, starttime, callcompletedinsl); bridge = ast_bridge_call_with_flags(qe->chan, peer, &bridge_config, AST_BRIDGE_FLAG_MERGE_INHIBIT_FROM | AST_BRIDGE_FLAG_MERGE_INHIBIT_TO | AST_BRIDGE_FLAG_SWAP_INHIBIT_FROM); @@ -7916,8 +7968,10 @@ static void copy_rules(struct queue_ent *qe, const char *rulename) new_pr->time = pr_iter->time; new_pr->max_value = pr_iter->max_value; new_pr->min_value = pr_iter->min_value; + new_pr->raise_value = pr_iter->raise_value; new_pr->max_relative = pr_iter->max_relative; new_pr->min_relative = pr_iter->min_relative; + new_pr->raise_relative = pr_iter->raise_relative; AST_LIST_INSERT_TAIL(&qe->qe_rules, new_pr, list); } } @@ -7943,9 +7997,10 @@ static int queue_exec(struct ast_channel *chan, const char *data) const char *user_priority; const char *max_penalty_str; const char *min_penalty_str; + const char *raise_penalty_str; int prio; int qcontinue = 0; - int max_penalty, min_penalty; + int max_penalty, min_penalty, raise_penalty; enum queue_result reason = QUEUE_UNKNOWN; /* whether to exit Queue application after the timeout hits */ int tries = 0; @@ -8057,6 +8112,18 @@ static int queue_exec(struct ast_channel *chan, const char *data) } else { min_penalty = INT_MAX; } + + if ((raise_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_RAISE_PENALTY"))) { + if (sscanf(raise_penalty_str, "%30d", &raise_penalty) == 1) { + ast_debug(1, "%s: Got raise penalty %d from ${QUEUE_RAISE_PENALTY}.\n", ast_channel_name(chan), raise_penalty); + } else { + ast_log(LOG_WARNING, "${QUEUE_RAISE_PENALTY}: Invalid value (%s), channel %s.\n", + raise_penalty_str, ast_channel_name(chan)); + raise_penalty = INT_MAX; + } + } else { + raise_penalty = INT_MAX; + } ast_channel_unlock(chan); if (ast_test_flag(&opts, OPT_RINGING)) { @@ -8086,6 +8153,7 @@ static int queue_exec(struct ast_channel *chan, const char *data) qe.prio = prio; qe.max_penalty = max_penalty; qe.min_penalty = min_penalty; + qe.raise_penalty = raise_penalty; qe.last_pos_said = 0; qe.last_pos = 0; qe.last_periodic_announce_time = time(NULL); @@ -8173,7 +8241,7 @@ check_turns: if (qe.parent->leavewhenempty) { int status = 0; - if ((status = get_member_status(qe.parent, qe.max_penalty, qe.min_penalty, qe.parent->leavewhenempty, 0))) { + if ((status = get_member_status(qe.parent, qe.max_penalty, qe.min_penalty, qe.raise_penalty, qe.parent->leavewhenempty, 0))) { record_abandoned(&qe); reason = QUEUE_LEAVEEMPTY; ast_queue_log(args.queuename, ast_channel_uniqueid(chan), "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start)); @@ -9469,7 +9537,7 @@ static char *__queues_show(struct mansession *s, int fd, int argc, const char * ast_str_append(&out, 0, "%s%s%s%s%s%s%s%s%s", mem->dynamic ? ast_term_color(COLOR_CYAN, COLOR_BLACK) : "", mem->dynamic ? " (dynamic)" : "", ast_term_reset(), mem->realtime ? ast_term_color(COLOR_MAGENTA, COLOR_BLACK) : "", mem->realtime ? " (realtime)" : "", ast_term_reset(), - mem->in_call ? ast_term_color(COLOR_BROWN, COLOR_BLACK) : "", mem->in_call ? " (in call)" : "", ast_term_reset()); + mem->starttime ? ast_term_color(COLOR_BROWN, COLOR_BLACK) : "", mem->starttime ? " (in call)" : "", ast_term_reset()); if (mem->paused) { if (ast_strlen_zero(mem->reason_paused)) { ast_str_append(&out, 0, " %s(paused was %ld secs ago)%s", @@ -9860,7 +9928,7 @@ static int manager_queues_status(struct mansession *s, const struct message *m) "%s" "\r\n", q->name, mem->membername, mem->interface, mem->state_interface, mem->dynamic ? "dynamic" : "static", - mem->penalty, mem->calls, (int)mem->lastcall, (int)mem->lastpause, mem->in_call, mem->status, + mem->penalty, mem->calls, (int)mem->lastcall, (int)mem->lastpause, mem->starttime ? 1 : 0, mem->status, mem->paused, mem->reason_paused, idText); ++q_items; } @@ -10622,7 +10690,7 @@ static char *handle_queue_rule_show(struct ast_cli_entry *e, int cmd, struct ast if (ast_strlen_zero(rule) || !strcasecmp(rl_iter->name, rule)) { ast_cli(a->fd, "Rule: %s\n", rl_iter->name); AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) { - ast_cli(a->fd, "\tAfter %d seconds, adjust QUEUE_MAX_PENALTY %s %d and adjust QUEUE_MIN_PENALTY %s %d\n", pr_iter->time, pr_iter->max_relative ? "by" : "to", pr_iter->max_value, pr_iter->min_relative ? "by" : "to", pr_iter->min_value); + ast_cli(a->fd, "\tAfter %d seconds, adjust QUEUE_MAX_PENALTY %s %d, adjust QUEUE_MIN_PENALTY %s %d and adjust QUEUE_RAISE_PENALTY %s %d\n", pr_iter->time, pr_iter->max_relative ? "by" : "to", pr_iter->max_value, pr_iter->min_relative ? "by" : "to", pr_iter->min_value, pr_iter->raise_relative ? "by" : "to", pr_iter->raise_value); } } } @@ -10920,6 +10988,7 @@ AST_DATA_STRUCTURE(member, DATA_EXPORT_MEMBER); MEMBER(queue_ent, pending, AST_DATA_INTEGER) \ MEMBER(queue_ent, max_penalty, AST_DATA_INTEGER) \ MEMBER(queue_ent, min_penalty, AST_DATA_INTEGER) \ + MEMBER(queue_ent, raise_penalty, AST_DATA_INTEGER) \ MEMBER(queue_ent, linpos, AST_DATA_INTEGER) \ MEMBER(queue_ent, linwrapped, AST_DATA_INTEGER) \ MEMBER(queue_ent, start, AST_DATA_INTEGER) \ |