74 #include <netinet/in.h>
1561 #define DEFAULT_RETRY 5
1562 #define DEFAULT_TIMEOUT 15
1564 #define MAX_PERIODIC_ANNOUNCEMENTS 10
1569 #define DEFAULT_MIN_ANNOUNCE_FREQUENCY 15
1571 #define MAX_QUEUE_BUCKETS 53
1574 #define RES_EXISTS (-1)
1575 #define RES_OUTOFMEMORY (-2)
1576 #define RES_NOSUCHQUEUE (-3)
1577 #define RES_NOT_DYNAMIC (-4)
1578 #define RES_NOT_CALLER (-5)
1595 static const char *
const pm_family =
"Queue/PersistentMembers";
1639 static const struct {
1773 #define ANNOUNCEHOLDTIME_ALWAYS 1
1774 #define ANNOUNCEHOLDTIME_ONCE 2
1775 #define QUEUE_EVENT_VARIABLES 3
1788 #define ANNOUNCEPOSITION_YES 1
1789 #define ANNOUNCEPOSITION_NO 2
1790 #define ANNOUNCEPOSITION_MORE_THAN 3
1791 #define ANNOUNCEPOSITION_LIMIT 4
2009 struct member *mem = obj;
2010 int *decrement_followers_after = arg;
2012 if (mem->
queuepos > *decrement_followers_after) {
2028 struct member *mem = obj;
2050 if (pos < queue->
rrpos) {
2057 #define queue_ref(q) ao2_bump(q)
2058 #define queue_unref(q) ({ ao2_cleanup(q); NULL; })
2059 #define queue_t_ref(q, tag) ao2_t_bump(q, tag)
2060 #define queue_t_unref(q, tag) ({ ao2_t_cleanup(q, tag); NULL; })
2061 #define queues_t_link(c, q, tag) ao2_t_link(c, q, tag)
2062 #define queues_t_unlink(c, q, tag) ao2_t_unlink(c, q, tag)
2067 char interfacevar[256]=
"";
2078 snprintf(interfacevar,
sizeof(interfacevar),
2079 "QUEUENAME=%s,QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUETALKTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f",
2111 new->pos = ++(*pos);
2123 if (!channel_string || !event_string) {
2165 if (!event_string) {
2235 if (!caller_event_string) {
2244 if (!agent_event_string) {
2251 if (!event_string) {
2322 if (caller_snapshot) {
2325 ast_debug(1,
"Empty caller_snapshot; sending incomplete event\n");
2328 if (agent_snapshot) {
2353 if (!caller_snapshot || !agent_snapshot) {
2358 agent_snapshot,
type, blob);
2376 if (!blob || !
type) {
2397 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: i, s: s, s: i, s: i}",
2402 "Membership", (mem->
dynamic ?
"dynamic" : (mem->
realtime ?
"realtime" :
"static")),
2404 "CallsTaken", mem->
calls,
2431 if (raise_penalty != INT_MAX && penalty < raise_penalty) {
2433 penalty = raise_penalty;
2435 if ((max_penalty != INT_MAX && penalty > max_penalty) || (min_penalty != INT_MAX && penalty < min_penalty)) {
2437 ast_debug(4,
"%s is unavailable because his penalty is not between %d and %d\n",
member->
membername, min_penalty, max_penalty);
2482 ast_debug(4,
"%s is unavailable because it has only been %d seconds since his last call (wrapup time is %d)\n",
2500 return get_member_status(q, max_penalty, min_penalty, raise_penalty, conditions, 1);
2514 #define MAX_CALL_ATTEMPT_BUCKETS 353
2518 const struct member *object;
2527 key =
object->interface;
2538 const struct member *object_left = obj;
2539 const struct member *object_right = arg;
2540 const char *right_key = arg;
2548 cmp = strcasecmp(object_left->
interface, right_key);
2647 char interface[80], *slash_pos;
2657 if (dev_state->
eid) {
2670 if (!found_member) {
2673 if ((slash_pos = strchr(interface,
'/'))) {
2674 if (!strncasecmp(interface,
"Local/", 6) && (slash_pos = strchr(slash_pos + 1,
'/'))) {
2679 if (!strcasecmp(interface, dev_state->
device)) {
2689 if (avail && found_member) {
2713 ast_debug(1,
"Device '%s' changed to state '%u' (%s)\n",
2718 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",
2793 if (!strcasecmp(child, includename)) {
2842 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",
2857 struct member *mem = obj;
2914 }
else if (
c > 96) {
2922 const struct member *mem = obj;
2924 const char *chname = strchr(interface,
'/');
2930 for (i = 0; i < 5 && chname[i]; i++) {
2938 struct member *mem1 = obj1;
2939 struct member *mem2 = obj2;
3077 char *timestr, *maxstr, *minstr, *raisestr, *contentdup;
3080 int penaltychangetime, inserted = 0;
3088 if (!(maxstr = strchr(contentdup,
','))) {
3089 ast_log(
LOG_WARNING,
"Improperly formatted penaltychange rule at line %d. Ignoring.\n", linenum);
3095 if ((minstr = strchr(maxstr,
','))) {
3097 if ((raisestr = strchr(minstr,
','))) {
3104 timestr = contentdup;
3105 if ((penaltychangetime = atoi(timestr)) < 0) {
3106 ast_log(
LOG_WARNING,
"Improper time parameter specified for penaltychange rule at line %d. Ignoring.\n", linenum);
3111 rule->time = penaltychangetime;
3115 if (*maxstr ==
'+' || *maxstr ==
'-' || *maxstr ==
'\0') {
3116 rule->max_relative = 1;
3119 rule->max_value = atoi(maxstr);
3122 if (*minstr ==
'+' || *minstr ==
'-') {
3123 rule->min_relative = 1;
3125 rule->min_value = atoi(minstr);
3127 rule->min_relative = 1;
3131 if (*raisestr ==
'+' || *raisestr ==
'-') {
3132 rule->raise_relative = 1;
3134 rule->raise_value = atoi(raisestr);
3136 rule->raise_relative = 1;
3141 if (strcasecmp(rl_iter->
name, list_name)) {
3146 if (
rule->time < rule_iter->time) {
3184 char *rulecat =
NULL;
3195 const char *timestr, *maxstr, *minstr, *raisestr, *rule_name;
3196 int penaltychangetime, rule_exists = 0, inserted = 0;
3197 int max_penalty = 0, min_penalty = 0, raise_penalty = 0;
3207 if (!(strcasecmp(rl_iter->
name, rule_name))) {
3214 if (!(new_rl =
ast_calloc(1,
sizeof(*new_rl)))) {
3222 if (!(timestr) || sscanf(timestr,
"%30d", &penaltychangetime) != 1) {
3223 ast_log(
LOG_NOTICE,
"Failed to parse time (%s) for one of the %s rules, skipping it\n",
3227 if (!(new_penalty_rule =
ast_calloc(1,
sizeof(*new_penalty_rule)))) {
3232 ast_strlen_zero(maxstr) || sscanf(maxstr,
"%30d", &max_penalty) != 1) {
3236 if (*maxstr ==
'+' || *maxstr ==
'-') {
3241 ast_strlen_zero(minstr) || sscanf(minstr,
"%30d", &min_penalty) != 1) {
3245 if (*minstr ==
'+' || *minstr ==
'-') {
3250 ast_strlen_zero(raisestr) || sscanf(raisestr,
"%30d", &raise_penalty) != 1) {
3254 if (*raisestr ==
'+' || *raisestr ==
'-') {
3258 new_penalty_rule->
time = penaltychangetime;
3260 new_penalty_rule->
max_value = max_penalty;
3262 new_penalty_rule->
min_value = min_penalty;
3266 if (new_penalty_rule->
time < pr_iter->
time) {
3284 char *option =
NULL;
3285 while ((option =
strsep(&value_copy,
","))) {
3286 if (!strcasecmp(option,
"paused")) {
3288 }
else if (!strcasecmp(option,
"penalty")) {
3290 }
else if (!strcasecmp(option,
"inuse")) {
3292 }
else if (!strcasecmp(option,
"ringing")) {
3294 }
else if (!strcasecmp(option,
"invalid")) {
3296 }
else if (!strcasecmp(option,
"wrapup")) {
3298 }
else if (!strcasecmp(option,
"unavailable")) {
3300 }
else if (!strcasecmp(option,
"unknown")) {
3302 }
else if (!strcasecmp(option,
"loose")) {
3304 }
else if (!strcasecmp(option,
"strict")) {
3306 }
else if ((
ast_false(option) && joinempty) || (
ast_true(option) && !joinempty)) {
3308 }
else if ((
ast_false(option) && !joinempty) || (
ast_true(option) && joinempty)) {
3311 ast_log(
LOG_WARNING,
"Unknown option %s for '%s'\n", option, joinempty ?
"joinempty" :
"leavewhenempty");
3326 if (!strcasecmp(param,
"musicclass") ||
3327 !strcasecmp(param,
"music") || !strcasecmp(param,
"musiconhold")) {
3329 }
else if (!strcasecmp(param,
"announce")) {
3331 }
else if (!strcasecmp(param,
"context")) {
3333 }
else if (!strcasecmp(param,
"timeout")) {
3338 }
else if (!strcasecmp(param,
"ringinuse")) {
3340 }
else if (!strcasecmp(param,
"setinterfacevar")) {
3342 }
else if (!strcasecmp(param,
"setqueuevar")) {
3344 }
else if (!strcasecmp(param,
"setqueueentryvar")) {
3346 }
else if (!strcasecmp(param,
"monitor-format")) {
3348 }
else if (!strcasecmp(param,
"membermacro")) {
3350 }
else if (!strcasecmp(param,
"membergosub")) {
3352 }
else if (!strcasecmp(param,
"queue-youarenext")) {
3354 }
else if (!strcasecmp(param,
"queue-thereare")) {
3356 }
else if (!strcasecmp(param,
"queue-callswaiting")) {
3358 }
else if (!strcasecmp(param,
"queue-quantity1")) {
3360 }
else if (!strcasecmp(param,
"queue-quantity2")) {
3362 }
else if (!strcasecmp(param,
"queue-holdtime")) {
3364 }
else if (!strcasecmp(param,
"queue-minutes")) {
3366 }
else if (!strcasecmp(param,
"queue-minute")) {
3368 }
else if (!strcasecmp(param,
"queue-seconds")) {
3370 }
else if (!strcasecmp(param,
"queue-thankyou")) {
3372 }
else if (!strcasecmp(param,
"queue-callerannounce")) {
3374 }
else if (!strcasecmp(param,
"queue-reporthold")) {
3376 }
else if (!strcasecmp(param,
"announce-frequency")) {
3378 }
else if (!strcasecmp(param,
"announce-to-first-user")) {
3380 }
else if (!strcasecmp(param,
"min-announce-frequency")) {
3383 }
else if (!strcasecmp(param,
"announce-round-seconds")) {
3390 "using 0 instead for queue '%s' at line %d of queues.conf\n",
3391 val, param, q->
name, linenum);
3394 "using 0 instead for queue '%s'\n",
val, param, q->
name);
3398 }
else if (!strcasecmp(param,
"announce-holdtime")) {
3399 if (!strcasecmp(
val,
"once")) {
3406 }
else if (!strcasecmp(param,
"announce-position")) {
3407 if (!strcasecmp(
val,
"limit")) {
3409 }
else if (!strcasecmp(
val,
"more")) {
3416 }
else if (!strcasecmp(param,
"announce-position-only-up")) {
3418 }
else if (!strcasecmp(param,
"announce-position-limit")) {
3420 }
else if (!strcasecmp(param,
"periodic-announce")) {
3421 if (strchr(
val,
',')) {
3440 }
else if (!strcasecmp(param,
"periodic-announce-frequency")) {
3442 }
else if (!strcasecmp(param,
"relative-periodic-announce")) {
3444 }
else if (!strcasecmp(param,
"random-periodic-announce")) {
3446 }
else if (!strcasecmp(param,
"retry")) {
3448 if (q->
retry <= 0) {
3451 }
else if (!strcasecmp(param,
"wrapuptime")) {
3453 }
else if (!strcasecmp(param,
"penaltymemberslimit")) {
3457 }
else if (!strcasecmp(param,
"autofill")) {
3459 }
else if (!strcasecmp(param,
"monitor-type")) {
3460 if (!strcasecmp(
val,
"mixmonitor")) {
3463 }
else if (!strcasecmp(param,
"autopause")) {
3465 }
else if (!strcasecmp(param,
"autopausedelay")) {
3467 }
else if (!strcasecmp(param,
"autopausebusy")) {
3469 }
else if (!strcasecmp(param,
"autopauseunavail")) {
3471 }
else if (!strcasecmp(param,
"maxlen")) {
3476 }
else if (!strcasecmp(param,
"servicelevel")) {
3478 }
else if (!strcasecmp(param,
"strategy")) {
3487 ast_log(
LOG_WARNING,
"'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
3495 ast_log(
LOG_WARNING,
"Changing to the linear strategy currently requires asterisk to be restarted.\n");
3499 }
else if (!strcasecmp(param,
"joinempty")) {
3501 }
else if (!strcasecmp(param,
"leavewhenempty")) {
3503 }
else if (!strcasecmp(param,
"reportholdtime")) {
3505 }
else if (!strcasecmp(param,
"memberdelay")) {
3507 }
else if (!strcasecmp(param,
"weight")) {
3509 }
else if (!strcasecmp(param,
"timeoutrestart")) {
3511 }
else if (!strcasecmp(param,
"defaultrule")) {
3513 }
else if (!strcasecmp(param,
"timeoutpriority")) {
3514 if (!strcasecmp(
val,
"conf")) {
3519 }
else if (failunknown) {
3521 ast_log(
LOG_WARNING,
"Unknown keyword in queue '%s': %s at line %d of queues.conf\n",
3522 q->
name, param, linenum);
3530 #define QUEUE_PAUSED_DEVSTATE AST_DEVICE_INUSE
3531 #define QUEUE_UNPAUSED_DEVSTATE AST_DEVICE_NOT_INUSE
3532 #define QUEUE_UNKNOWN_PAUSED_DEVSTATE AST_DEVICE_NOT_INUSE
3582 const char *config_val;
3593 S_OR(membername,
"NULL"));
3599 S_OR(membername,
"NULL"));
3604 penalty = atoi(penalty_str);
3607 }
else if (penalty < 0) {
3613 paused = atoi(paused_str);
3619 if (wrapuptime_str) {
3620 wrapuptime = atoi(wrapuptime_str);
3621 if (wrapuptime < 0) {
3666 if ((m =
create_queue_member(interface, membername, penalty, paused, state_interface, ringinuse, wrapuptime))) {
3746 char *category =
NULL;
3747 const char *tmp_name;
3764 }
else if (!member_config) {
3775 ast_debug(1,
"Queue %s not found in realtime.\n", queuename);
3798 for (tmpvar = queue_vars; tmpvar; tmpvar = tmpvar->
next) {
3799 if (!strcasecmp(tmpvar->
name,
"strategy")) {
3802 ast_log(
LOG_WARNING,
"'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
3817 memset(tmpbuf, 0,
sizeof(tmpbuf));
3818 for (v = queue_vars; v; v = v->
next) {
3820 if (strchr(v->
name,
'_')) {
3824 while ((
tmp = strchr(
tmp,
'_'))) {
3889 int prev_weight = 0;
3907 if (!member_config) {
3908 ast_debug(1,
"No queue_members defined in config extconfig.conf\n");
3913 prev_weight = q->
weight ? 1 : 0;
3923 if (!q->
weight && prev_weight) {
3926 if (q->
weight && !prev_weight) {
3946 char *category =
NULL;
3991 char *category =
NULL;
4007 ast_debug(3,
"Queue %s has no realtime members defined. No need for update\n", q->
name);
4084 if ((!inserted) && (qe->
prio > cur->
prio)) {
4091 if (!inserted && (qe->
prio >= cur->
prio) && position && (position <=
pos + 1)) {
4095 if (position <
pos) {
4096 ast_log(
LOG_NOTICE,
"Asked to be inserted at position %d but forced into position %d due to higher priority callers\n", position,
pos);
4111 if (q->
count == 1) {
4119 "Position", qe->
pos,
4161 int digitlen = strlen(qe->
digits);
4164 if (digitlen <
sizeof(qe->
digits) - 2) {
4166 qe->
digits[digitlen + 1] =
'\0';
4196 int res = 0, say_thanks = 0;
4197 long avgholdmins, avgholdsecs;
4263 ast_verb(3,
"Hold time for %s is %ld minute(s) %ld seconds\n", qe->
parent->
name, avgholdmins, avgholdsecs);
4276 if (avgholdmins >= 1) {
4282 if (avgholdmins == 1) {
4294 if (avgholdsecs >= 1) {
4309 ast_verb(3,
"Told %s in %s their queue position (which was %d)\n",
4349 qe->
parent->
holdtime = (((oldvalue << 2) - oldvalue) + newholdtime) >> 2;
4369 queue_t_ref(q,
"Copy queue pointer from queue entry");
4384 "Position", qe->
pos,
4399 snprintf(posstr,
sizeof(posstr),
"%d", qe->
pos);
4449 if (cur->
chan && cur->
chan != exception) {
4464 if (exception || cancel_answered_elsewhere) {
4600 ast_debug(1,
"%s paused, can't receive call\n",
call->interface);
4605 ast_debug(1,
"%s not available, can't receive call\n",
call->interface);
4615 ast_debug(1,
"Wrapuptime not yet expired on queue %s for %s\n",
4622 ast_debug(1,
"Priority queue delaying call to %s:%s\n",
4639 ast_debug(1,
"%s has another call trying, can't receive call\n",
4660 ast_debug(1,
"%s actually not available, can't receive call\n",
4690 const char *macrocontext, *macroexten;
4696 tmp->stillgoing = 0;
4702 if ((location = strchr(tech,
'/'))) {
4724 tmp->stillgoing = 0;
4754 tmp->dial_callerid_absent = 1;