72#include <netinet/in.h>
1582#define DEFAULT_RETRY 5
1583#define DEFAULT_TIMEOUT 15
1585#define MAX_PERIODIC_ANNOUNCEMENTS 10
1590#define DEFAULT_MIN_ANNOUNCE_FREQUENCY 15
1592#define MAX_QUEUE_BUCKETS 53
1595#define RES_EXISTS (-1)
1596#define RES_OUTOFMEMORY (-2)
1597#define RES_NOSUCHQUEUE (-3)
1598#define RES_NOT_DYNAMIC (-4)
1599#define RES_NOT_CALLER (-5)
1616static const char *
const pm_family =
"Queue/PersistentMembers";
1666static const struct {
1800#define ANNOUNCEHOLDTIME_ALWAYS 1
1801#define ANNOUNCEHOLDTIME_ONCE 2
1802#define QUEUE_EVENT_VARIABLES 3
1815#define ANNOUNCEPOSITION_YES 1
1816#define ANNOUNCEPOSITION_NO 2
1817#define ANNOUNCEPOSITION_MORE_THAN 3
1818#define ANNOUNCEPOSITION_LIMIT 4
2036 struct member *mem = obj;
2037 int *decrement_followers_after = arg;
2039 if (mem->
queuepos > *decrement_followers_after) {
2055 struct member *mem = obj;
2077 if (pos < queue->
rrpos) {
2084#define queue_ref(q) ao2_bump(q)
2085#define queue_unref(q) ({ ao2_cleanup(q); NULL; })
2086#define queue_t_ref(q, tag) ao2_t_bump(q, tag)
2087#define queue_t_unref(q, tag) ({ ao2_t_cleanup(q, tag); NULL; })
2088#define queues_t_link(c, q, tag) ao2_t_link(c, q, tag)
2089#define queues_t_unlink(c, q, tag) ao2_t_unlink(c, q, tag)
2094 char interfacevar[256]=
"";
2105 snprintf(interfacevar,
sizeof(interfacevar),
2106 "QUEUENAME=%s,QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUETALKTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f",
2140 new->pos = ++(*pos);
2152 if (!channel_string || !event_string) {
2194 if (!event_string) {
2264 if (!caller_event_string) {
2273 if (!agent_event_string) {
2280 if (!event_string) {
2351 if (caller_snapshot) {
2354 ast_debug(1,
"Empty caller_snapshot; sending incomplete event\n");
2357 if (agent_snapshot) {
2382 if (!caller_snapshot || !agent_snapshot) {
2387 agent_snapshot,
type, blob);
2405 if (!blob || !
type) {
2426 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}",
2431 "Membership", (mem->
dynamic ?
"dynamic" : (mem->
realtime ?
"realtime" :
"static")),
2433 "CallsTaken", mem->
calls,
2460 if (raise_penalty != INT_MAX && penalty < raise_penalty) {
2462 penalty = raise_penalty;
2464 if ((max_penalty != INT_MAX && penalty > max_penalty) || (min_penalty != INT_MAX && penalty < min_penalty)) {
2466 ast_debug(4,
"%s is unavailable because his penalty is not between %d and %d\n",
member->
membername, min_penalty, max_penalty);
2511 ast_debug(4,
"%s is unavailable because it has only been %d seconds since his last call (wrapup time is %d)\n",
2529 return get_member_status(q, max_penalty, min_penalty, raise_penalty, conditions, 1);
2543#define MAX_CALL_ATTEMPT_BUCKETS 353
2547 const struct member *object;
2556 key =
object->interface;
2567 const struct member *object_left = obj;
2568 const struct member *object_right = arg;
2569 const char *right_key = arg;
2577 cmp = strcasecmp(object_left->
interface, right_key);
2676 char interface[80], *slash_pos;
2686 if (dev_state->
eid) {
2699 if (!found_member) {
2702 if ((slash_pos = strchr(interface,
'/'))) {
2703 if (!strncasecmp(interface,
"Local/", 6) && (slash_pos = strchr(slash_pos + 1,
'/'))) {
2708 if (!strcasecmp(interface, dev_state->
device)) {
2718 if (avail && found_member) {
2742 ast_debug(1,
"Device '%s' changed to state '%u' (%s)\n",
2747 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",
2822 if (!strcasecmp(child, includename)) {
2871 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",
2886 struct member *mem = obj;
2943 }
else if (
c > 96) {
2951 const struct member *mem = obj;
2953 const char *chname = strchr(interface,
'/');
2959 for (i = 0; i < 5 && chname[i]; i++) {
2967 struct member *mem1 = obj1;
2968 struct member *mem2 = obj2;
3114 char *timestr, *maxstr, *minstr, *raisestr, *contentdup;
3117 int penaltychangetime, inserted = 0;
3125 if (!(maxstr = strchr(contentdup,
','))) {
3126 ast_log(
LOG_WARNING,
"Improperly formatted penaltychange rule at line %d. Ignoring.\n", linenum);
3132 if ((minstr = strchr(maxstr,
','))) {
3134 if ((raisestr = strchr(minstr,
','))) {
3141 timestr = contentdup;
3142 if ((penaltychangetime = atoi(timestr)) < 0) {
3143 ast_log(
LOG_WARNING,
"Improper time parameter specified for penaltychange rule at line %d. Ignoring.\n", linenum);
3148 rule->time = penaltychangetime;
3152 if (*maxstr ==
'+' || *maxstr ==
'-' || *maxstr ==
'\0') {
3153 rule->max_relative = 1;
3156 rule->max_value = atoi(maxstr);
3159 if (*minstr ==
'+' || *minstr ==
'-') {
3160 rule->min_relative = 1;
3162 rule->min_value = atoi(minstr);
3164 rule->min_relative = 1;
3168 if (*raisestr ==
'+' || *raisestr ==
'-') {
3169 rule->raise_relative = 1;
3171 rule->raise_value = atoi(raisestr);
3173 rule->raise_relative = 1;
3178 if (strcasecmp(rl_iter->
name, list_name)) {
3183 if (
rule->time < rule_iter->time) {
3221 char *rulecat =
NULL;
3232 const char *timestr, *maxstr, *minstr, *raisestr, *rule_name;
3233 int penaltychangetime, rule_exists = 0, inserted = 0;
3234 int max_penalty = 0, min_penalty = 0, raise_penalty = 0;
3244 if (!(strcasecmp(rl_iter->
name, rule_name))) {
3251 if (!(new_rl =
ast_calloc(1,
sizeof(*new_rl)))) {
3259 if (!(timestr) || sscanf(timestr,
"%30d", &penaltychangetime) != 1) {
3260 ast_log(
LOG_NOTICE,
"Failed to parse time (%s) for one of the %s rules, skipping it\n",
3264 if (!(new_penalty_rule =
ast_calloc(1,
sizeof(*new_penalty_rule)))) {
3269 ast_strlen_zero(maxstr) || sscanf(maxstr,
"%30d", &max_penalty) != 1) {
3273 if (*maxstr ==
'+' || *maxstr ==
'-') {
3278 ast_strlen_zero(minstr) || sscanf(minstr,
"%30d", &min_penalty) != 1) {
3282 if (*minstr ==
'+' || *minstr ==
'-') {
3287 ast_strlen_zero(raisestr) || sscanf(raisestr,
"%30d", &raise_penalty) != 1) {
3291 if (*raisestr ==
'+' || *raisestr ==
'-') {
3295 new_penalty_rule->
time = penaltychangetime;
3297 new_penalty_rule->
max_value = max_penalty;
3299 new_penalty_rule->
min_value = min_penalty;
3303 if (new_penalty_rule->
time < pr_iter->
time) {
3321 char *option =
NULL;
3322 while ((option =
strsep(&value_copy,
","))) {
3323 if (!strcasecmp(option,
"paused")) {
3325 }
else if (!strcasecmp(option,
"penalty")) {
3327 }
else if (!strcasecmp(option,
"inuse")) {
3329 }
else if (!strcasecmp(option,
"ringing")) {
3331 }
else if (!strcasecmp(option,
"invalid")) {
3333 }
else if (!strcasecmp(option,
"wrapup")) {
3335 }
else if (!strcasecmp(option,
"unavailable")) {
3337 }
else if (!strcasecmp(option,
"unknown")) {
3339 }
else if (!strcasecmp(option,
"loose")) {
3341 }
else if (!strcasecmp(option,
"strict")) {
3343 }
else if ((
ast_false(option) && joinempty) || (
ast_true(option) && !joinempty)) {
3345 }
else if ((
ast_false(option) && !joinempty) || (
ast_true(option) && joinempty)) {
3348 ast_log(
LOG_WARNING,
"Unknown option %s for '%s'\n", option, joinempty ?
"joinempty" :
"leavewhenempty");
3363 if (!strcasecmp(param,
"musicclass") ||
3364 !strcasecmp(param,
"music") || !strcasecmp(param,
"musiconhold")) {
3366 }
else if (!strcasecmp(param,
"announce")) {
3368 }
else if (!strcasecmp(param,
"context")) {
3370 }
else if (!strcasecmp(param,
"timeout")) {
3375 }
else if (!strcasecmp(param,
"ringinuse")) {
3377 }
else if (!strcasecmp(param,
"setinterfacevar")) {
3379 }
else if (!strcasecmp(param,
"setqueuevar")) {
3381 }
else if (!strcasecmp(param,
"setqueueentryvar")) {
3383 }
else if (!strcasecmp(param,
"monitor-format")) {
3385 }
else if (!strcasecmp(param,
"membergosub")) {
3387 }
else if (!strcasecmp(param,
"queue-youarenext")) {
3389 }
else if (!strcasecmp(param,
"queue-thereare")) {
3391 }
else if (!strcasecmp(param,
"queue-callswaiting")) {
3393 }
else if (!strcasecmp(param,
"queue-quantity1")) {
3395 }
else if (!strcasecmp(param,
"queue-quantity2")) {
3397 }
else if (!strcasecmp(param,
"queue-holdtime")) {
3399 }
else if (!strcasecmp(param,
"queue-minutes")) {
3401 }
else if (!strcasecmp(param,
"queue-minute")) {
3403 }
else if (!strcasecmp(param,
"queue-seconds")) {
3405 }
else if (!strcasecmp(param,
"queue-thankyou")) {
3407 }
else if (!strcasecmp(param,
"queue-callerannounce")) {
3409 }
else if (!strcasecmp(param,
"queue-reporthold")) {
3411 }
else if (!strcasecmp(param,
"announce-frequency")) {
3413 }
else if (!strcasecmp(param,
"announce-to-first-user")) {
3415 }
else if (!strcasecmp(param,
"min-announce-frequency")) {
3418 }
else if (!strcasecmp(param,
"announce-round-seconds")) {
3425 "using 0 instead for queue '%s' at line %d of queues.conf\n",
3426 val, param, q->
name, linenum);
3429 "using 0 instead for queue '%s'\n",
val, param, q->
name);
3433 }
else if (!strcasecmp(param,
"announce-holdtime")) {
3434 if (!strcasecmp(
val,
"once")) {
3441 }
else if (!strcasecmp(param,
"announce-position")) {
3442 if (!strcasecmp(
val,
"limit")) {
3444 }
else if (!strcasecmp(
val,
"more")) {
3451 }
else if (!strcasecmp(param,
"announce-position-only-up")) {
3453 }
else if (!strcasecmp(param,
"announce-position-limit")) {
3455 }
else if (!strcasecmp(param,
"periodic-announce")) {
3456 if (strchr(
val,
',')) {
3475 }
else if (!strcasecmp(param,
"periodic-announce-startdelay")) {
3477 }
else if (!strcasecmp(param,
"periodic-announce-frequency")) {
3479 }
else if (!strcasecmp(param,
"relative-periodic-announce")) {
3481 }
else if (!strcasecmp(param,
"random-periodic-announce")) {
3483 }
else if (!strcasecmp(param,
"retry")) {
3485 if (q->
retry <= 0) {
3488 }
else if (!strcasecmp(param,
"wrapuptime")) {
3490 }
else if (!strcasecmp(param,
"penaltymemberslimit")) {
3494 }
else if (!strcasecmp(param,
"autofill")) {
3496 }
else if (!strcasecmp(param,
"autopause")) {
3498 }
else if (!strcasecmp(param,
"autopausedelay")) {
3500 }
else if (!strcasecmp(param,
"autopausebusy")) {
3502 }
else if (!strcasecmp(param,
"autopauseunavail")) {
3504 }
else if (!strcasecmp(param,
"maxlen")) {
3509 }
else if (!strcasecmp(param,
"servicelevel")) {
3511 }
else if (!strcasecmp(param,
"strategy")) {
3520 ast_log(
LOG_WARNING,
"'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
3528 ast_log(
LOG_WARNING,
"Changing to the linear strategy currently requires asterisk to be restarted.\n");
3532 }
else if (!strcasecmp(param,
"joinempty")) {
3534 }
else if (!strcasecmp(param,
"leavewhenempty")) {
3536 }
else if (!strcasecmp(param,
"reportholdtime")) {
3538 }
else if (!strcasecmp(param,
"memberdelay")) {
3540 }
else if (!strcasecmp(param,
"weight")) {
3542 }
else if (!strcasecmp(param,
"timeoutrestart")) {
3544 }
else if (!strcasecmp(param,
"defaultrule")) {
3546 }
else if (!strcasecmp(param,
"timeoutpriority")) {
3547 if (!strcasecmp(
val,
"conf")) {
3552 }
else if (!strcasecmp(param,
"log-restricted-caller-id")) {
3554 }
else if (failunknown) {
3556 ast_log(
LOG_WARNING,
"Unknown keyword in queue '%s': %s at line %d of queues.conf\n",
3557 q->
name, param, linenum);
3565#define QUEUE_PAUSED_DEVSTATE AST_DEVICE_INUSE
3566#define QUEUE_UNPAUSED_DEVSTATE AST_DEVICE_NOT_INUSE
3567#define QUEUE_UNKNOWN_PAUSED_DEVSTATE AST_DEVICE_NOT_INUSE
3617 const char *config_val;
3629 S_OR(membername,
"NULL"));
3635 S_OR(membername,
"NULL"));
3640 penalty = atoi(penalty_str);
3643 }
else if (penalty < 0) {
3649 paused = atoi(paused_str);
3655 if (wrapuptime_str) {
3656 wrapuptime = atoi(wrapuptime_str);
3657 if (wrapuptime < 0) {
3705 if ((m =
create_queue_member(interface, membername, penalty, paused, state_interface, ringinuse, wrapuptime))) {
3788 char *category =
NULL;
3789 const char *tmp_name;
3806 }
else if (!member_config) {
3817 ast_debug(1,
"Queue %s not found in realtime.\n", queuename);
3840 for (tmpvar = queue_vars; tmpvar; tmpvar = tmpvar->
next) {
3841 if (!strcasecmp(tmpvar->
name,
"strategy")) {
3844 ast_log(
LOG_WARNING,
"'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
3859 memset(tmpbuf, 0,
sizeof(tmpbuf));
3860 for (v = queue_vars; v; v = v->
next) {
3862 if (strchr(v->
name,
'_')) {
3866 while ((
tmp = strchr(
tmp,
'_'))) {
3931 int prev_weight = 0;
3949 if (!member_config) {
3950 ast_debug(1,
"No queue_members defined in config extconfig.conf\n");
3955 prev_weight = q->
weight ? 1 : 0;
3965 if (!q->
weight && prev_weight) {
3968 if (q->
weight && !prev_weight) {
3988 char *category =
NULL;
4033 char *category =
NULL;
4049 ast_debug(3,
"Queue %s has no realtime members defined. No need for update\n", q->
name);
4126 if ((!inserted) && (qe->
prio > cur->
prio)) {
4133 if (!inserted && (qe->
prio >= cur->
prio) && position && (position <=
pos + 1)) {
4137 if (position <
pos) {
4138 ast_log(
LOG_NOTICE,
"Asked to be inserted at position %d but forced into position %d due to higher priority callers\n", position,
pos);
4153 if (q->
count == 1) {
4161 "Position", qe->
pos,
4203 int digitlen = strlen(qe->
digits);
4206 if (digitlen <
sizeof(qe->
digits) - 2) {
4208 qe->
digits[digitlen + 1] =
'\0';
4238 int res = 0, say_thanks = 0;
4239 long avgholdmins, avgholdsecs;
4305 ast_verb(3,
"Hold time for %s is %ld minute(s) %ld seconds\n", qe->
parent->
name, avgholdmins, avgholdsecs);
4318 if (avgholdmins >= 1) {
4324 if (avgholdmins == 1) {
4336 if (avgholdsecs >= 1) {
4351 ast_verb(3,
"Told %s in %s their queue position (which was %d)\n",
4391 qe->
parent->
holdtime = (((oldvalue << 2) - oldvalue) + newholdtime) >> 2;
4411 queue_t_ref(q,
"Copy queue pointer from queue entry");
4426 "Position", qe->
pos,
4441 snprintf(posstr,
sizeof(posstr),
"%d", qe->
pos);
4491 if (cur->
chan && cur->
chan != exception) {
4506 if (exception || cancel_answered_elsewhere) {