74 #include <netinet/in.h> 1429 #define DEFAULT_RETRY 5 1430 #define DEFAULT_TIMEOUT 15 1432 #define MAX_PERIODIC_ANNOUNCEMENTS 10 1437 #define DEFAULT_MIN_ANNOUNCE_FREQUENCY 15 1439 #define MAX_QUEUE_BUCKETS 53 1442 #define RES_EXISTS (-1) 1443 #define RES_OUTOFMEMORY (-2) 1444 #define RES_NOSUCHQUEUE (-3) 1445 #define RES_NOT_DYNAMIC (-4) 1446 #define RES_NOT_CALLER (-5) 1463 static const char *
const pm_family =
"Queue/PersistentMembers";
1506 static const struct {
1541 char interface[256];
1598 char membername[80];
1605 char reason_paused[80];
1615 char rt_uniqueid[80];
1636 #define ANNOUNCEHOLDTIME_ALWAYS 1 1637 #define ANNOUNCEHOLDTIME_ONCE 2 1638 #define QUEUE_EVENT_VARIABLES 3 1651 #define ANNOUNCEPOSITION_YES 1 1652 #define ANNOUNCEPOSITION_NO 2 1653 #define ANNOUNCEPOSITION_MORE_THAN 3 1654 #define ANNOUNCEPOSITION_LIMIT 4 1871 struct member *mem = obj;
1872 int *decrement_followers_after = arg;
1874 if (mem->
queuepos > *decrement_followers_after) {
1889 struct member *mem = obj;
1911 if (pos < queue->
rrpos) {
1918 #define queue_ref(q) ao2_bump(q) 1919 #define queue_unref(q) ({ ao2_cleanup(q); NULL; }) 1920 #define queue_t_ref(q, tag) ao2_t_bump(q, tag) 1921 #define queue_t_unref(q, tag) ({ ao2_t_cleanup(q, tag); NULL; }) 1922 #define queues_t_link(c, q, tag) ao2_t_link(c, q, tag) 1923 #define queues_t_unlink(c, q, tag) ao2_t_unlink(c, q, tag) 1928 char interfacevar[256]=
"";
1939 snprintf(interfacevar,
sizeof(interfacevar),
1940 "QUEUENAME=%s,QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUETALKTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f",
1972 new->pos = ++(*pos);
1984 if (!channel_string || !event_string) {
2026 if (!event_string) {
2096 if (!caller_event_string) {
2105 if (!agent_event_string) {
2112 if (!event_string) {
2183 if (caller_snapshot) {
2186 ast_debug(1,
"Empty caller_snapshot; sending incomplete event\n");
2189 if (agent_snapshot) {
2214 if (!caller_snapshot || !agent_snapshot) {
2219 agent_snapshot, type, blob);
2239 if (!blob || !type) {
2260 return ast_json_pack(
"{s: s, s: s, s: s, s: s, s: s, s: i, s: i, s: i, s: i, s: i, s: i, s: i, s: s, s: i, s: i}",
2265 "Membership", (mem->
dynamic ?
"dynamic" : (mem->
realtime ?
"realtime" :
"static")),
2267 "CallsTaken", mem->
calls,
2292 int penalty = member->
penalty;
2293 if (raise_penalty != INT_MAX && penalty < raise_penalty) {
2294 ast_debug(4,
"%s is having his penalty raised up from %d to %d\n", member->
membername, penalty, raise_penalty);
2295 penalty = raise_penalty;
2297 if ((max_penalty != INT_MAX && penalty > max_penalty) || (min_penalty != INT_MAX && penalty < min_penalty)) {
2299 ast_debug(4,
"%s is unavailable because his penalty is not between %d and %d\n", member->
membername, min_penalty, max_penalty);
2307 ast_debug(4,
"%s is unavailable because his device state is 'invalid'\n", member->
membername);
2313 ast_debug(4,
"%s is unavailable because his device state is 'unavailable'\n", member->
membername);
2319 ast_debug(4,
"%s is unavailable because his device state is 'inuse'\n", member->
membername);
2325 ast_debug(4,
"%s is unavailable because his device state is 'ringing'\n", member->
membername);
2331 ast_debug(4,
"%s is unavailable because his device state is 'unknown'\n", member->
membername);
2344 ast_debug(4,
"%s is unavailable because it has only been %d seconds since his last call (wrapup time is %d)\n",
2362 return get_member_status(q, max_penalty, min_penalty, raise_penalty, conditions, 1);
2376 #define MAX_CALL_ATTEMPT_BUCKETS 353 2380 const struct member *object;
2389 key =
object->interface;
2400 const struct member *object_left = obj;
2401 const struct member *object_right = arg;
2402 const char *right_key = arg;
2410 cmp = strcasecmp(object_left->
interface, right_key);
2439 if (m->
status != status) {
2509 char interface[80], *slash_pos;
2519 if (dev_state->
eid) {
2532 if (!found_member) {
2535 if ((slash_pos = strchr(interface,
'/'))) {
2536 if (!strncasecmp(interface,
"Local/", 6) && (slash_pos = strchr(slash_pos + 1,
'/'))) {
2541 if (!strcasecmp(interface, dev_state->
device)) {
2551 if (avail && found_member) {
2575 ast_debug(1,
"Device '%s' changed to state '%u' (%s)\n",
2580 ast_debug(3,
"Device '%s' changed to state '%u' (%s) but we don't care because they're not a member of any queue.\n",
2661 ast_debug(1,
"Extension '%s@%s' changed to state '%d' (%s)\n", exten, context, device_state,
ast_devstate2str(device_state));
2663 ast_debug(3,
"Extension '%s@%s' changed to state '%d' (%s) but we don't care because they're not a member of any queue.\n",
2678 struct member *mem = obj;
2734 }
else if (c > 96) {
2742 const struct member *mem = obj;
2744 const char *chname = strchr(interface,
'/');
2750 for (i = 0; i < 5 && chname[i]; i++) {
2758 struct member *mem1 = obj1;
2759 struct member *mem2 = obj2;
2897 char *timestr, *maxstr, *minstr, *raisestr, *contentdup;
2900 int penaltychangetime, inserted = 0;
2902 if (!(rule =
ast_calloc(1,
sizeof(*rule)))) {
2908 if (!(maxstr = strchr(contentdup,
','))) {
2909 ast_log(
LOG_WARNING,
"Improperly formatted penaltychange rule at line %d. Ignoring.\n", linenum);
2915 if ((minstr = strchr(maxstr,
','))) {
2917 if ((raisestr = strchr(minstr,
','))) {
2924 timestr = contentdup;
2925 if ((penaltychangetime = atoi(timestr)) < 0) {
2926 ast_log(
LOG_WARNING,
"Improper time parameter specified for penaltychange rule at line %d. Ignoring.\n", linenum);
2931 rule->
time = penaltychangetime;
2935 if (*maxstr ==
'+' || *maxstr ==
'-' || *maxstr ==
'\0') {
2942 if (*minstr ==
'+' || *minstr ==
'-') {
2951 if (*raisestr ==
'+' || *raisestr ==
'-') {
2961 if (strcasecmp(rl_iter->
name, list_name)) {
2966 if (rule->
time < rule_iter->time) {
3004 char *rulecat =
NULL;
3015 const char *timestr, *maxstr, *minstr, *raisestr, *rule_name;
3016 int penaltychangetime, rule_exists = 0, inserted = 0;
3017 int max_penalty = 0, min_penalty = 0, raise_penalty = 0;
3027 if (!(strcasecmp(rl_iter->
name, rule_name))) {
3034 if (!(new_rl =
ast_calloc(1,
sizeof(*new_rl)))) {
3042 if (!(timestr) || sscanf(timestr,
"%30d", &penaltychangetime) != 1) {
3043 ast_log(
LOG_NOTICE,
"Failed to parse time (%s) for one of the %s rules, skipping it\n",
3047 if (!(new_penalty_rule =
ast_calloc(1,
sizeof(*new_penalty_rule)))) {
3052 ast_strlen_zero(maxstr) || sscanf(maxstr,
"%30d", &max_penalty) != 1) {
3056 if (*maxstr ==
'+' || *maxstr ==
'-') {
3061 ast_strlen_zero(minstr) || sscanf(minstr,
"%30d", &min_penalty) != 1) {
3065 if (*minstr ==
'+' || *minstr ==
'-') {
3070 ast_strlen_zero(raisestr) || sscanf(raisestr,
"%30d", &raise_penalty) != 1) {
3074 if (*raisestr ==
'+' || *raisestr ==
'-') {
3078 new_penalty_rule->
time = penaltychangetime;
3080 new_penalty_rule->
max_value = max_penalty;
3082 new_penalty_rule->
min_value = min_penalty;
3086 if (new_penalty_rule->
time < pr_iter->
time) {
3104 char *option =
NULL;
3105 while ((option =
strsep(&value_copy,
","))) {
3106 if (!strcasecmp(option,
"paused")) {
3108 }
else if (!strcasecmp(option,
"penalty")) {
3110 }
else if (!strcasecmp(option,
"inuse")) {
3112 }
else if (!strcasecmp(option,
"ringing")) {
3114 }
else if (!strcasecmp(option,
"invalid")) {
3116 }
else if (!strcasecmp(option,
"wrapup")) {
3118 }
else if (!strcasecmp(option,
"unavailable")) {
3120 }
else if (!strcasecmp(option,
"unknown")) {
3122 }
else if (!strcasecmp(option,
"loose")) {
3124 }
else if (!strcasecmp(option,
"strict")) {
3126 }
else if ((
ast_false(option) && joinempty) || (
ast_true(option) && !joinempty)) {
3128 }
else if ((
ast_false(option) && !joinempty) || (
ast_true(option) && joinempty)) {
3131 ast_log(
LOG_WARNING,
"Unknown option %s for '%s'\n", option, joinempty ?
"joinempty" :
"leavewhenempty");
3146 if (!strcasecmp(param,
"musicclass") ||
3147 !strcasecmp(param,
"music") || !strcasecmp(param,
"musiconhold")) {
3149 }
else if (!strcasecmp(param,
"announce")) {
3151 }
else if (!strcasecmp(param,
"context")) {
3153 }
else if (!strcasecmp(param,
"timeout")) {
3158 }
else if (!strcasecmp(param,
"ringinuse")) {
3160 }
else if (!strcasecmp(param,
"setinterfacevar")) {
3162 }
else if (!strcasecmp(param,
"setqueuevar")) {
3164 }
else if (!strcasecmp(param,
"setqueueentryvar")) {
3166 }
else if (!strcasecmp(param,
"monitor-format")) {
3168 }
else if (!strcasecmp(param,
"membermacro")) {
3170 }
else if (!strcasecmp(param,
"membergosub")) {
3172 }
else if (!strcasecmp(param,
"queue-youarenext")) {
3174 }
else if (!strcasecmp(param,
"queue-thereare")) {
3176 }
else if (!strcasecmp(param,
"queue-callswaiting")) {
3178 }
else if (!strcasecmp(param,
"queue-quantity1")) {
3180 }
else if (!strcasecmp(param,
"queue-quantity2")) {
3182 }
else if (!strcasecmp(param,
"queue-holdtime")) {
3184 }
else if (!strcasecmp(param,
"queue-minutes")) {
3186 }
else if (!strcasecmp(param,
"queue-minute")) {
3188 }
else if (!strcasecmp(param,
"queue-seconds")) {
3190 }
else if (!strcasecmp(param,
"queue-thankyou")) {
3192 }
else if (!strcasecmp(param,
"queue-callerannounce")) {
3194 }
else if (!strcasecmp(param,
"queue-reporthold")) {
3196 }
else if (!strcasecmp(param,
"announce-frequency")) {
3198 }
else if (!strcasecmp(param,
"announce-to-first-user")) {
3200 }
else if (!strcasecmp(param,
"min-announce-frequency")) {
3202 ast_debug(1,
"%s=%s for queue '%s'\n", param, val, q->
name);
3203 }
else if (!strcasecmp(param,
"announce-round-seconds")) {
3210 "using 0 instead for queue '%s' at line %d of queues.conf\n",
3211 val, param, q->
name, linenum);
3214 "using 0 instead for queue '%s'\n", val, param, q->
name);
3218 }
else if (!strcasecmp(param,
"announce-holdtime")) {
3219 if (!strcasecmp(val,
"once")) {
3226 }
else if (!strcasecmp(param,
"announce-position")) {
3227 if (!strcasecmp(val,
"limit")) {
3229 }
else if (!strcasecmp(val,
"more")) {
3236 }
else if (!strcasecmp(param,
"announce-position-only-up")) {
3238 }
else if (!strcasecmp(param,
"announce-position-limit")) {
3240 }
else if (!strcasecmp(param,
"periodic-announce")) {
3241 if (strchr(val,
',')) {
3245 while ((s =
strsep(&buf,
",|"))) {
3260 }
else if (!strcasecmp(param,
"periodic-announce-frequency")) {
3262 }
else if (!strcasecmp(param,
"relative-periodic-announce")) {
3264 }
else if (!strcasecmp(param,
"random-periodic-announce")) {
3266 }
else if (!strcasecmp(param,
"retry")) {
3267 q->
retry = atoi(val);
3268 if (q->
retry <= 0) {
3271 }
else if (!strcasecmp(param,
"wrapuptime")) {
3273 }
else if (!strcasecmp(param,
"penaltymemberslimit")) {
3277 }
else if (!strcasecmp(param,
"autofill")) {
3279 }
else if (!strcasecmp(param,
"monitor-type")) {
3280 if (!strcasecmp(val,
"mixmonitor")) {
3283 }
else if (!strcasecmp(param,
"autopause")) {
3285 }
else if (!strcasecmp(param,
"autopausedelay")) {
3287 }
else if (!strcasecmp(param,
"autopausebusy")) {
3289 }
else if (!strcasecmp(param,
"autopauseunavail")) {
3291 }
else if (!strcasecmp(param,
"maxlen")) {
3296 }
else if (!strcasecmp(param,
"servicelevel")) {
3298 }
else if (!strcasecmp(param,
"strategy")) {
3307 ast_log(
LOG_WARNING,
"'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
3315 ast_log(
LOG_WARNING,
"Changing to the linear strategy currently requires asterisk to be restarted.\n");
3319 }
else if (!strcasecmp(param,
"joinempty")) {
3321 }
else if (!strcasecmp(param,
"leavewhenempty")) {
3323 }
else if (!strcasecmp(param,
"reportholdtime")) {
3325 }
else if (!strcasecmp(param,
"memberdelay")) {
3327 }
else if (!strcasecmp(param,
"weight")) {
3329 }
else if (!strcasecmp(param,
"timeoutrestart")) {
3331 }
else if (!strcasecmp(param,
"defaultrule")) {
3333 }
else if (!strcasecmp(param,
"timeoutpriority")) {
3334 if (!strcasecmp(val,
"conf")) {
3339 }
else if (failunknown) {
3341 ast_log(
LOG_WARNING,
"Unknown keyword in queue '%s': %s at line %d of queues.conf\n",
3342 q->
name, param, linenum);
3350 #define QUEUE_PAUSED_DEVSTATE AST_DEVICE_INUSE 3351 #define QUEUE_UNPAUSED_DEVSTATE AST_DEVICE_NOT_INUSE 3352 #define QUEUE_UNKNOWN_PAUSED_DEVSTATE AST_DEVICE_NOT_INUSE 3402 const char *config_val;
3413 S_OR(membername,
"NULL"));
3419 S_OR(membername,
"NULL"));
3424 penalty = atoi(penalty_str);
3425 if ((penalty < 0) && negative_penalty_invalid) {
3427 }
else if (penalty < 0) {
3433 paused = atoi(paused_str);
3439 if (wrapuptime_str) {
3440 wrapuptime = atoi(wrapuptime_str);
3441 if (wrapuptime < 0) {
3452 ast_log(
LOG_WARNING,
"Invalid value of '%s' field for %s in queue '%s'\n", realtime_ringinuse_field, interface, q->
name);
3486 if ((m =
create_queue_member(interface, membername, penalty, paused, state_interface, ringinuse, wrapuptime))) {
3490 if (!log_membername_as_agent) {
3566 char *category =
NULL;
3567 const char *tmp_name;
3584 }
else if (!member_config) {
3595 ast_debug(1,
"Queue %s not found in realtime.\n", queuename);
3618 for (tmpvar = queue_vars; tmpvar; tmpvar = tmpvar->
next) {
3619 if (!strcasecmp(tmpvar->
name,
"strategy")) {
3622 ast_log(
LOG_WARNING,
"'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
3637 memset(tmpbuf, 0,
sizeof(tmpbuf));
3638 for (v = queue_vars; v; v = v->
next) {
3640 if (strchr(v->
name,
'_')) {
3644 while ((tmp = strchr(tmp,
'_'))) {
3709 int prev_weight = 0;
3727 if (!member_config) {
3728 ast_debug(1,
"No queue_members defined in config extconfig.conf\n");
3733 prev_weight = q->
weight ? 1 : 0;
3743 if (!q->
weight && prev_weight) {
3746 if (q->
weight && !prev_weight) {
3777 char *category =
NULL;
3793 ast_debug(3,
"Queue %s has no realtime members defined. No need for update\n", q->
name);
3870 if ((!inserted) && (qe->
prio > cur->
prio)) {
3877 if (!inserted && (qe->
prio >= cur->
prio) && position && (position <= pos + 1)) {
3881 if (position < pos) {
3882 ast_log(
LOG_NOTICE,
"Asked to be inserted at position %d but forced into position %d due to higher priority callers\n", position, pos);
3897 if (q->
count == 1) {
3905 "Position", qe->
pos,
3947 int digitlen = strlen(qe->
digits);
3950 if (digitlen <
sizeof(qe->
digits) - 2) {
3952 qe->
digits[digitlen + 1] =
'\0';
3982 int res = 0, announceposition = 0;
3983 long avgholdmins, avgholdsecs;
4013 announceposition = 1;
4017 if (announceposition == 1) {
4054 ast_verb(3,
"Hold time for %s is %ld minute(s) %ld seconds\n", qe->
parent->
name, avgholdmins, avgholdsecs);
4066 if (avgholdmins >= 1) {
4072 if (avgholdmins == 1) {
4084 if (avgholdsecs >= 1) {
4101 ast_verb(3,
"Told %s in %s their queue position (which was %d)\n",
4141 qe->
parent->
holdtime = (((oldvalue << 2) - oldvalue) + newholdtime) >> 2;
4161 queue_t_ref(q,
"Copy queue pointer from queue entry");
4165 for (current = q->
head; current; current = current->
next) {
4166 if (current == qe) {
4176 "Position", qe->
pos,
4191 snprintf(posstr,
sizeof(posstr),
"%d", qe->
pos);
4195 current->
pos = ++pos;
4213 queues_t_unlink(queues, q,
"Queue is now dead; remove it from the container");
4242 for (cur = outgoing; cur; cur = cur->
q_next) {
4243 if (cur->
chan && cur->
chan != exception) {
4257 if (outgoing->
chan && (outgoing->
chan != exception)) {
4258 if (exception || cancel_answered_elsewhere) {
4276 outgoing = outgoing->
q_next;
4408 if (wrapuptime && (time(
NULL) - memberp->
lastcall) < wrapuptime) {
4409 ast_debug(1,
"Wrapuptime not yet expired on queue %s for %s\n",
4416 ast_debug(1,
"Priority queue delaying call to %s:%s\n",
4426 mem =
ao2_find(pending_members, memberp,
4433 ast_debug(1,
"%s has another call trying, can't receive call\n",
4445 ao2_link(pending_members, memberp);
4454 ast_debug(1,
"%s actually not available, can't receive call\n",
4484 const char *macrocontext, *macroexten;
4496 if ((location = strchr(tech,
'/'))) {
4618 for (cur = outgoing; cur; cur = cur->
q_next) {
4619 if (cur->stillgoing &&
4646 for (cur = outgoing; cur; cur = cur->
q_next) {
4656 ast_debug(1,
"Nobody left to try ringing in queue\n");
4661 for (cur = outgoing; cur; cur = cur->
q_next) {
4681 ast_debug(1,
"Queue timed out while ringing members.\n");
4687 for (cur = outgoing; cur; cur = cur->
q_next) {
4767 ast_verb(3,
"Playing periodic announcement\n");
4810 int callabandonedinsl = 0;
4821 "Position", qe->
pos,
4822 "OriginalPosition", qe->
opos,
4823 "HoldTime", (
int)(time(
NULL) - qe->
start));
4828 if (callabandonedinsl) {
4843 ast_verb(3,
"Nobody picked up in %d ms\n", rnatime);
4853 "Interface", interface,
4854 "MemberName", membername,
4855 "RingTime", rnatime);
4864 time_t idletime = time(&idletime)-mem->
lastcall;
4876 ast_verb(3,
"Auto-Pausing Queue Member %s in queue %s since they failed to answer.\n",
4879 ast_verb(3,
"Failed to pause Queue Member %s in queue %s!\n", interface, qe->
parent->
name);
4885 ast_verb(3,
"Auto-Pausing Queue Member %s in all queues since they failed to answer on queue %s.\n",
4888 ast_verb(3,
"Failed to pause Queue Member %s in all queues!\n", interface);
4923 #define AST_MAX_WATCHERS 256 4942 int numbusies = prebusies;
4951 char membername[80] =
"";
4955 struct timeval start_time_tv =
ast_tvnow();
4956 int canceled_by_caller = 0;
4962 starttime = (long) time(
NULL);
4965 int numlines, retry, pos = 1;
4970 for (retry = 0; retry < 2; retry++) {
4972 for (o = outgoing; o; o = o->
q_next) {
4977 watchers[pos++] = o->
chan;
4991 if (pos > 1 || !stillgoing ||
4997 ring_one(qe, outgoing, &numbusies);
5001 if (numlines == (numbusies + numnochan)) {
5002 ast_debug(1,
"Everyone is busy at this time\n");
5004 ast_debug(3,
"No one is answering queue '%s' (%d numlines / %d busies / %d failed channels)\n", queue, numlines, numbusies, numnochan);
5027 ast_verb(3,
"%s answered %s\n", ochan_name, inchan_name);