72#include <netinet/in.h>
1553#define DEFAULT_RETRY 5
1554#define DEFAULT_TIMEOUT 15
1556#define MAX_PERIODIC_ANNOUNCEMENTS 10
1561#define DEFAULT_MIN_ANNOUNCE_FREQUENCY 15
1563#define MAX_QUEUE_BUCKETS 53
1566#define RES_EXISTS (-1)
1567#define RES_OUTOFMEMORY (-2)
1568#define RES_NOSUCHQUEUE (-3)
1569#define RES_NOT_DYNAMIC (-4)
1570#define RES_NOT_CALLER (-5)
1587static const char *
const pm_family =
"Queue/PersistentMembers";
1637static const struct {
1771#define ANNOUNCEHOLDTIME_ALWAYS 1
1772#define ANNOUNCEHOLDTIME_ONCE 2
1773#define QUEUE_EVENT_VARIABLES 3
1786#define ANNOUNCEPOSITION_YES 1
1787#define ANNOUNCEPOSITION_NO 2
1788#define ANNOUNCEPOSITION_MORE_THAN 3
1789#define ANNOUNCEPOSITION_LIMIT 4
2007 struct member *mem = obj;
2008 int *decrement_followers_after = arg;
2010 if (mem->
queuepos > *decrement_followers_after) {
2026 struct member *mem = obj;
2048 if (pos < queue->
rrpos) {
2055#define queue_ref(q) ao2_bump(q)
2056#define queue_unref(q) ({ ao2_cleanup(q); NULL; })
2057#define queue_t_ref(q, tag) ao2_t_bump(q, tag)
2058#define queue_t_unref(q, tag) ({ ao2_t_cleanup(q, tag); NULL; })
2059#define queues_t_link(c, q, tag) ao2_t_link(c, q, tag)
2060#define queues_t_unlink(c, q, tag) ao2_t_unlink(c, q, tag)
2065 char interfacevar[256]=
"";
2076 snprintf(interfacevar,
sizeof(interfacevar),
2077 "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;
3085 char *timestr, *maxstr, *minstr, *raisestr, *contentdup;
3088 int penaltychangetime, inserted = 0;
3096 if (!(maxstr = strchr(contentdup,
','))) {
3097 ast_log(
LOG_WARNING,
"Improperly formatted penaltychange rule at line %d. Ignoring.\n", linenum);
3103 if ((minstr = strchr(maxstr,
','))) {
3105 if ((raisestr = strchr(minstr,
','))) {
3112 timestr = contentdup;
3113 if ((penaltychangetime = atoi(timestr)) < 0) {
3114 ast_log(
LOG_WARNING,
"Improper time parameter specified for penaltychange rule at line %d. Ignoring.\n", linenum);
3119 rule->time = penaltychangetime;
3123 if (*maxstr ==
'+' || *maxstr ==
'-' || *maxstr ==
'\0') {
3124 rule->max_relative = 1;
3127 rule->max_value = atoi(maxstr);
3130 if (*minstr ==
'+' || *minstr ==
'-') {
3131 rule->min_relative = 1;
3133 rule->min_value = atoi(minstr);
3135 rule->min_relative = 1;
3139 if (*raisestr ==
'+' || *raisestr ==
'-') {
3140 rule->raise_relative = 1;
3142 rule->raise_value = atoi(raisestr);
3144 rule->raise_relative = 1;
3149 if (strcasecmp(rl_iter->
name, list_name)) {
3154 if (
rule->time < rule_iter->time) {
3192 char *rulecat =
NULL;
3203 const char *timestr, *maxstr, *minstr, *raisestr, *rule_name;
3204 int penaltychangetime, rule_exists = 0, inserted = 0;
3205 int max_penalty = 0, min_penalty = 0, raise_penalty = 0;
3215 if (!(strcasecmp(rl_iter->
name, rule_name))) {
3222 if (!(new_rl =
ast_calloc(1,
sizeof(*new_rl)))) {
3230 if (!(timestr) || sscanf(timestr,
"%30d", &penaltychangetime) != 1) {
3231 ast_log(
LOG_NOTICE,
"Failed to parse time (%s) for one of the %s rules, skipping it\n",
3235 if (!(new_penalty_rule =
ast_calloc(1,
sizeof(*new_penalty_rule)))) {
3240 ast_strlen_zero(maxstr) || sscanf(maxstr,
"%30d", &max_penalty) != 1) {
3244 if (*maxstr ==
'+' || *maxstr ==
'-') {
3249 ast_strlen_zero(minstr) || sscanf(minstr,
"%30d", &min_penalty) != 1) {
3253 if (*minstr ==
'+' || *minstr ==
'-') {
3258 ast_strlen_zero(raisestr) || sscanf(raisestr,
"%30d", &raise_penalty) != 1) {
3262 if (*raisestr ==
'+' || *raisestr ==
'-') {
3266 new_penalty_rule->
time = penaltychangetime;
3268 new_penalty_rule->
max_value = max_penalty;
3270 new_penalty_rule->
min_value = min_penalty;
3274 if (new_penalty_rule->
time < pr_iter->
time) {
3292 char *option =
NULL;
3293 while ((option =
strsep(&value_copy,
","))) {
3294 if (!strcasecmp(option,
"paused")) {
3296 }
else if (!strcasecmp(option,
"penalty")) {
3298 }
else if (!strcasecmp(option,
"inuse")) {
3300 }
else if (!strcasecmp(option,
"ringing")) {
3302 }
else if (!strcasecmp(option,
"invalid")) {
3304 }
else if (!strcasecmp(option,
"wrapup")) {
3306 }
else if (!strcasecmp(option,
"unavailable")) {
3308 }
else if (!strcasecmp(option,
"unknown")) {
3310 }
else if (!strcasecmp(option,
"loose")) {
3312 }
else if (!strcasecmp(option,
"strict")) {
3314 }
else if ((
ast_false(option) && joinempty) || (
ast_true(option) && !joinempty)) {
3316 }
else if ((
ast_false(option) && !joinempty) || (
ast_true(option) && joinempty)) {
3319 ast_log(
LOG_WARNING,
"Unknown option %s for '%s'\n", option, joinempty ?
"joinempty" :
"leavewhenempty");
3334 if (!strcasecmp(param,
"musicclass") ||
3335 !strcasecmp(param,
"music") || !strcasecmp(param,
"musiconhold")) {
3337 }
else if (!strcasecmp(param,
"announce")) {
3339 }
else if (!strcasecmp(param,
"context")) {
3341 }
else if (!strcasecmp(param,
"timeout")) {
3346 }
else if (!strcasecmp(param,
"ringinuse")) {
3348 }
else if (!strcasecmp(param,
"setinterfacevar")) {
3350 }
else if (!strcasecmp(param,
"setqueuevar")) {
3352 }
else if (!strcasecmp(param,
"setqueueentryvar")) {
3354 }
else if (!strcasecmp(param,
"monitor-format")) {
3356 }
else if (!strcasecmp(param,
"membergosub")) {
3358 }
else if (!strcasecmp(param,
"queue-youarenext")) {
3360 }
else if (!strcasecmp(param,
"queue-thereare")) {
3362 }
else if (!strcasecmp(param,
"queue-callswaiting")) {
3364 }
else if (!strcasecmp(param,
"queue-quantity1")) {
3366 }
else if (!strcasecmp(param,
"queue-quantity2")) {
3368 }
else if (!strcasecmp(param,
"queue-holdtime")) {
3370 }
else if (!strcasecmp(param,
"queue-minutes")) {
3372 }
else if (!strcasecmp(param,
"queue-minute")) {
3374 }
else if (!strcasecmp(param,
"queue-seconds")) {
3376 }
else if (!strcasecmp(param,
"queue-thankyou")) {
3378 }
else if (!strcasecmp(param,
"queue-callerannounce")) {
3380 }
else if (!strcasecmp(param,
"queue-reporthold")) {
3382 }
else if (!strcasecmp(param,
"announce-frequency")) {
3384 }
else if (!strcasecmp(param,
"announce-to-first-user")) {
3386 }
else if (!strcasecmp(param,
"min-announce-frequency")) {
3389 }
else if (!strcasecmp(param,
"announce-round-seconds")) {
3396 "using 0 instead for queue '%s' at line %d of queues.conf\n",
3397 val, param, q->
name, linenum);
3400 "using 0 instead for queue '%s'\n",
val, param, q->
name);
3404 }
else if (!strcasecmp(param,
"announce-holdtime")) {
3405 if (!strcasecmp(
val,
"once")) {
3412 }
else if (!strcasecmp(param,
"announce-position")) {
3413 if (!strcasecmp(
val,
"limit")) {
3415 }
else if (!strcasecmp(
val,
"more")) {
3422 }
else if (!strcasecmp(param,
"announce-position-only-up")) {
3424 }
else if (!strcasecmp(param,
"announce-position-limit")) {
3426 }
else if (!strcasecmp(param,
"periodic-announce")) {
3427 if (strchr(
val,
',')) {
3446 }
else if (!strcasecmp(param,
"periodic-announce-startdelay")) {
3448 }
else if (!strcasecmp(param,
"periodic-announce-frequency")) {
3450 }
else if (!strcasecmp(param,
"relative-periodic-announce")) {
3452 }
else if (!strcasecmp(param,
"random-periodic-announce")) {
3454 }
else if (!strcasecmp(param,
"retry")) {
3456 if (q->
retry <= 0) {
3459 }
else if (!strcasecmp(param,
"wrapuptime")) {
3461 }
else if (!strcasecmp(param,
"penaltymemberslimit")) {
3465 }
else if (!strcasecmp(param,
"autofill")) {
3467 }
else if (!strcasecmp(param,
"autopause")) {
3469 }
else if (!strcasecmp(param,
"autopausedelay")) {
3471 }
else if (!strcasecmp(param,
"autopausebusy")) {
3473 }
else if (!strcasecmp(param,
"autopauseunavail")) {
3475 }
else if (!strcasecmp(param,
"maxlen")) {
3480 }
else if (!strcasecmp(param,
"servicelevel")) {
3482 }
else if (!strcasecmp(param,
"strategy")) {
3491 ast_log(
LOG_WARNING,
"'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
3499 ast_log(
LOG_WARNING,
"Changing to the linear strategy currently requires asterisk to be restarted.\n");
3503 }
else if (!strcasecmp(param,
"joinempty")) {
3505 }
else if (!strcasecmp(param,
"leavewhenempty")) {
3507 }
else if (!strcasecmp(param,
"reportholdtime")) {
3509 }
else if (!strcasecmp(param,
"memberdelay")) {
3511 }
else if (!strcasecmp(param,
"weight")) {
3513 }
else if (!strcasecmp(param,
"timeoutrestart")) {
3515 }
else if (!strcasecmp(param,
"defaultrule")) {
3517 }
else if (!strcasecmp(param,
"timeoutpriority")) {
3518 if (!strcasecmp(
val,
"conf")) {
3523 }
else if (!strcasecmp(param,
"log-restricted-caller-id")) {
3525 }
else if (failunknown) {
3527 ast_log(
LOG_WARNING,
"Unknown keyword in queue '%s': %s at line %d of queues.conf\n",
3528 q->
name, param, linenum);
3536#define QUEUE_PAUSED_DEVSTATE AST_DEVICE_INUSE
3537#define QUEUE_UNPAUSED_DEVSTATE AST_DEVICE_NOT_INUSE
3538#define QUEUE_UNKNOWN_PAUSED_DEVSTATE AST_DEVICE_NOT_INUSE
3588 const char *config_val;
3600 S_OR(membername,
"NULL"));
3606 S_OR(membername,
"NULL"));
3611 penalty = atoi(penalty_str);
3614 }
else if (penalty < 0) {
3620 paused = atoi(paused_str);
3626 if (wrapuptime_str) {
3627 wrapuptime = atoi(wrapuptime_str);
3628 if (wrapuptime < 0) {
3676 if ((m =
create_queue_member(interface, membername, penalty, paused, state_interface, ringinuse, wrapuptime))) {
3759 char *category =
NULL;
3760 const char *tmp_name;
3777 }
else if (!member_config) {
3788 ast_debug(1,
"Queue %s not found in realtime.\n", queuename);
3811 for (tmpvar = queue_vars; tmpvar; tmpvar = tmpvar->
next) {
3812 if (!strcasecmp(tmpvar->
name,
"strategy")) {
3815 ast_log(
LOG_WARNING,
"'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
3830 memset(tmpbuf, 0,
sizeof(tmpbuf));
3831 for (v = queue_vars; v; v = v->
next) {
3833 if (strchr(v->
name,
'_')) {
3837 while ((
tmp = strchr(
tmp,
'_'))) {
3902 int prev_weight = 0;
3920 if (!member_config) {
3921 ast_debug(1,
"No queue_members defined in config extconfig.conf\n");
3926 prev_weight = q->
weight ? 1 : 0;
3936 if (!q->
weight && prev_weight) {
3939 if (q->
weight && !prev_weight) {
3959 char *category =
NULL;
4004 char *category =
NULL;
4020 ast_debug(3,
"Queue %s has no realtime members defined. No need for update\n", q->
name);
4097 if ((!inserted) && (qe->
prio > cur->
prio)) {
4104 if (!inserted && (qe->
prio >= cur->
prio) && position && (position <=
pos + 1)) {
4108 if (position <
pos) {
4109 ast_log(
LOG_NOTICE,
"Asked to be inserted at position %d but forced into position %d due to higher priority callers\n", position,
pos);
4124 if (q->
count == 1) {
4132 "Position", qe->
pos,
4174 int digitlen = strlen(qe->
digits);
4177 if (digitlen <
sizeof(qe->
digits) - 2) {
4179 qe->
digits[digitlen + 1] =
'\0';
4209 int res = 0, say_thanks = 0;
4210 long avgholdmins, avgholdsecs;
4276 ast_verb(3,
"Hold time for %s is %ld minute(s) %ld seconds\n", qe->
parent->
name, avgholdmins, avgholdsecs);
4289 if (avgholdmins >= 1) {
4295 if (avgholdmins == 1) {
4307 if (avgholdsecs >= 1) {
4322 ast_verb(3,
"Told %s in %s their queue position (which was %d)\n",
4362 qe->
parent->
holdtime = (((oldvalue << 2) - oldvalue) + newholdtime) >> 2;
4382 queue_t_ref(q,
"Copy queue pointer from queue entry");
4397 "Position", qe->
pos,
4412 snprintf(posstr,
sizeof(posstr),
"%d", qe->
pos);
4462 if (cur->
chan && cur->
chan != exception) {
4477 if (exception || cancel_answered_elsewhere) {