72 #include <netinet/in.h>
101 #include "asterisk/stasis_channels.h"
102 #include "asterisk/stasis_message_router.h"
104 #include "asterisk/stasis_bridges.h"
108 #include "asterisk/max_forwards.h"
1447 OPT_MARK_AS_ANSWERED = (1 << 0),
1448 OPT_GO_ON = (1 << 1),
1449 OPT_DATA_QUALITY = (1 << 2),
1450 OPT_CALLEE_GO_ON = (1 << 3),
1451 OPT_CALLEE_HANGUP = (1 << 4),
1452 OPT_CALLER_HANGUP = (1 << 5),
1453 OPT_IGNORE_CALL_FW = (1 << 6),
1454 OPT_IGNORE_CONNECTEDLINE = (1 << 7),
1455 OPT_CALLEE_PARK = (1 << 8),
1456 OPT_CALLER_PARK = (1 << 9),
1457 OPT_NO_RETRY = (1 << 10),
1458 OPT_RINGING = (1 << 11),
1459 OPT_RING_WHEN_RINGING = (1 << 12),
1460 OPT_CALLEE_TRANSFER = (1 << 13),
1461 OPT_CALLER_TRANSFER = (1 << 14),
1462 OPT_CALLEE_AUTOMIXMON = (1 << 15),
1463 OPT_CALLER_AUTOMIXMON = (1 << 16),
1464 OPT_CALLEE_AUTOMON = (1 << 17),
1465 OPT_CALLER_AUTOMON = (1 << 18),
1466 OPT_PREDIAL_CALLEE = (1 << 19),
1467 OPT_PREDIAL_CALLER = (1 << 20),
1468 OPT_MUSICONHOLD_CLASS = (1 << 21),
1472 OPT_ARG_CALLEE_GO_ON = 0,
1473 OPT_ARG_PREDIAL_CALLEE,
1474 OPT_ARG_PREDIAL_CALLER,
1475 OPT_ARG_MUSICONHOLD_CLASS,
1506 QUEUE_STRATEGY_RINGALL = 0,
1507 QUEUE_STRATEGY_LEASTRECENT,
1508 QUEUE_STRATEGY_FEWESTCALLS,
1509 QUEUE_STRATEGY_RANDOM,
1510 QUEUE_STRATEGY_RRMEMORY,
1511 QUEUE_STRATEGY_LINEAR,
1512 QUEUE_STRATEGY_WRANDOM,
1513 QUEUE_STRATEGY_RRORDERED,
1517 QUEUE_AUTOPAUSE_OFF = 0,
1522 enum queue_reload_mask {
1523 QUEUE_RELOAD_PARAMETERS = (1 << 0),
1524 QUEUE_RELOAD_MEMBER = (1 << 1),
1525 QUEUE_RELOAD_RULES = (1 << 2),
1526 QUEUE_RESET_STATS = (1 << 3),
1533 { QUEUE_STRATEGY_RINGALL,
"ringall" },
1534 { QUEUE_STRATEGY_LEASTRECENT,
"leastrecent" },
1535 { QUEUE_STRATEGY_FEWESTCALLS,
"fewestcalls" },
1536 { QUEUE_STRATEGY_RANDOM,
"random" },
1537 { QUEUE_STRATEGY_RRMEMORY,
"rrmemory" },
1538 { QUEUE_STRATEGY_RRMEMORY,
"roundrobin" },
1539 { QUEUE_STRATEGY_LINEAR,
"linear" },
1540 { QUEUE_STRATEGY_WRANDOM,
"wrandom"},
1541 { QUEUE_STRATEGY_RRORDERED,
"rrordered"},
1547 } autopausesmodes [] = {
1548 { QUEUE_AUTOPAUSE_OFF,
"no" },
1549 { QUEUE_AUTOPAUSE_ON,
"yes" },
1550 { QUEUE_AUTOPAUSE_ALL,
"all" },
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)
1572 static char *app =
"Queue";
1574 static char *app_aqm =
"AddQueueMember" ;
1576 static char *app_rqm =
"RemoveQueueMember" ;
1578 static char *app_pqm =
"PauseQueueMember" ;
1580 static char *app_upqm =
"UnpauseQueueMember" ;
1582 static char *app_ql =
"QueueLog" ;
1584 static char *app_qupd =
"QueueUpdate";
1587 static const char *
const pm_family =
"Queue/PersistentMembers";
1628 QUEUE_JOINEMPTY = 2,
1629 QUEUE_LEAVEEMPTY = 3,
1630 QUEUE_JOINUNAVAIL = 4,
1631 QUEUE_LEAVEUNAVAIL = 5,
1637 static const struct {
1638 enum queue_result id;
1640 } queue_results[] = {
1641 { QUEUE_UNKNOWN,
"UNKNOWN" },
1642 { QUEUE_TIMEOUT,
"TIMEOUT" },
1643 { QUEUE_JOINEMPTY,
"JOINEMPTY" },
1644 { QUEUE_LEAVEEMPTY,
"LEAVEEMPTY" },
1645 { QUEUE_JOINUNAVAIL,
"JOINUNAVAIL" },
1646 { QUEUE_LEAVEUNAVAIL,
"LEAVEUNAVAIL" },
1647 { QUEUE_FULL,
"FULL" },
1648 { QUEUE_CONTINUE,
"CONTINUE" },
1649 { QUEUE_WITHDRAW,
"WITHDRAW" },
1652 enum queue_timeout_priority {
1653 TIMEOUT_PRIORITY_APP,
1654 TIMEOUT_PRIORITY_CONF,
1732 char membername[80];
1739 char reason_paused[80];
1748 unsigned int dead:1;
1749 unsigned int delme:1;
1750 char rt_uniqueid[80];
1751 unsigned int ringinuse:1;
1754 enum empty_conditions {
1755 QUEUE_EMPTY_PENALTY = (1 << 0),
1756 QUEUE_EMPTY_PAUSED = (1 << 1),
1757 QUEUE_EMPTY_INUSE = (1 << 2),
1758 QUEUE_EMPTY_RINGING = (1 << 3),
1759 QUEUE_EMPTY_UNAVAILABLE = (1 << 4),
1760 QUEUE_EMPTY_INVALID = (1 << 5),
1761 QUEUE_EMPTY_UNKNOWN = (1 << 6),
1762 QUEUE_EMPTY_WRAPUP = (1 << 7),
1765 enum member_properties {
1767 MEMBER_RINGINUSE = 1,
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
1832 unsigned int dead:1;
1833 unsigned int ringinuse:1;
1834 unsigned int announce_to_first_user:1;
1835 unsigned int setinterfacevar:1;
1836 unsigned int setqueuevar:1;
1837 unsigned int setqueueentryvar:1;
1838 unsigned int reportholdtime:1;
1839 unsigned int wrapped:1;
1840 unsigned int timeoutrestart:1;
1841 unsigned int announceholdtime:2;
1842 unsigned int announceposition:3;
1843 unsigned int announceposition_only_up:1;
1845 unsigned int realtime:1;
1846 unsigned int found:1;
1847 unsigned int relativeperiodicannounce:1;
1848 unsigned int autopausebusy:1;
1849 unsigned int autopauseunavail:1;
1850 enum empty_conditions joinempty;
1851 enum empty_conditions leavewhenempty;
1885 int log_restricted_caller_id:1;
1903 static void update_realtime_members(
struct call_queue *q);
1905 static int set_member_paused(
const char *queuename,
const char *
interface,
const char *reason,
int paused);
1914 for (i = 0; i < ARRAY_LEN(queue_results); i++) {
1915 if (queue_results[i].
id == res) {
1922 static const char *int2strat(
int strategy)
1926 for (x = 0; x < ARRAY_LEN(strategies); x++) {
1927 if (strategy == strategies[x].strategy) {
1928 return strategies[x].name;
1935 static int strat2int(
const char *strategy)
1939 for (x = 0; x < ARRAY_LEN(strategies); x++) {
1940 if (!strcasecmp(strategy, strategies[x].name)) {
1941 return strategies[x].strategy;
1948 static int autopause2int(
const char *autopause)
1952 if (ast_strlen_zero(autopause)) {
1953 return QUEUE_AUTOPAUSE_OFF;
1958 return QUEUE_AUTOPAUSE_ON;
1961 for (x = 0; x < ARRAY_LEN(autopausesmodes); x++) {
1962 if (!strcasecmp(autopause, autopausesmodes[x].name)) {
1963 return autopausesmodes[x].autopause;
1968 return QUEUE_AUTOPAUSE_OFF;
1971 static int queue_hash_cb(
const void *obj,
const int flags)
1978 static int queue_cmp_cb(
void *obj,
void *arg,
int flags)
2005 static int queue_member_decrement_followers(
void *obj,
void *arg,
int flag)
2007 struct member *mem = obj;
2008 int *decrement_followers_after = arg;
2010 if (mem->
queuepos > *decrement_followers_after) {
2024 static int queue_delme_members_decrement_followers(
void *obj,
void *arg,
int flag)
2026 struct member *mem = obj;
2042 static void queue_member_follower_removal(
struct call_queue *queue,
struct member *mem)
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]=
"";
2070 if (q->setqueuevar) {
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) {
2136 return queue_channel_to_ami(
"QueueCallerJoin", message);
2141 return queue_channel_to_ami(
"QueueCallerLeave", message);
2146 return queue_channel_to_ami(
"QueueCallerAbandon", message);
2150 .to_ami = queue_caller_join_to_ami,
2153 .to_ami = queue_caller_leave_to_ami,
2156 .to_ami = queue_caller_abandon_to_ami,
2165 if (!event_string) {
2176 return queue_member_to_ami(
"QueueMemberStatus", message);
2181 return queue_member_to_ami(
"QueueMemberAdded", message);
2186 return queue_member_to_ami(
"QueueMemberRemoved", message);
2191 return queue_member_to_ami(
"QueueMemberPause", message);
2196 return queue_member_to_ami(
"QueueMemberPenalty", message);
2201 return queue_member_to_ami(
"QueueMemberRinginuse", message);
2205 .to_ami = queue_member_status_to_ami,
2208 .to_ami = queue_member_added_to_ami,
2211 .to_ami = queue_member_removed_to_ami,
2214 .to_ami = queue_member_pause_to_ami,
2217 .to_ami = queue_member_penalty_to_ami,
2220 .to_ami = queue_member_ringinuse_to_ami,
2235 if (!caller_event_string) {
2236 ast_log(LOG_NOTICE,
"No caller event string, bailing\n");
2244 if (!agent_event_string) {
2245 ast_log(LOG_NOTICE,
"No agent event string, bailing\n");
2251 if (!event_string) {
2266 return queue_multi_channel_to_ami(
"AgentCalled", message);
2271 return queue_multi_channel_to_ami(
"AgentConnect", message);
2276 return queue_multi_channel_to_ami(
"AgentComplete", message);
2281 return queue_multi_channel_to_ami(
"AgentDump", message);
2286 return queue_multi_channel_to_ami(
"AgentRingNoAnswer", message);
2290 .to_ami = queue_agent_called_to_ami,
2293 .to_ami = queue_agent_connect_to_ami,
2296 .to_ami = queue_agent_complete_to_ami,
2299 .to_ami = queue_agent_dump_to_ami,
2302 .to_ami = queue_agent_ringnoanswer_to_ami,
2305 static void queue_publish_multi_channel_snapshot_blob(
struct stasis_topic *topic,
2322 if (caller_snapshot) {
2325 ast_debug(1,
"Empty caller_snapshot; sending incomplete event\n");
2328 if (agent_snapshot) {
2346 ast_channel_lock(caller);
2348 ast_channel_unlock(caller);
2349 ast_channel_lock(agent);
2351 ast_channel_unlock(agent);
2353 if (!caller_snapshot || !agent_snapshot) {
2357 queue_publish_multi_channel_snapshot_blob(
ast_channel_topic(caller), caller_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,
2422 static int get_member_status(
struct call_queue *q,
int max_penalty,
int min_penalty,
int raise_penalty,
enum empty_conditions conditions,
int devstate)
2429 for (; (member = ao2_iterator_next(&mem_iter));
ao2_ref(member, -1)) {
2430 int penalty = member->
penalty;
2431 if (raise_penalty != INT_MAX && penalty < raise_penalty) {
2432 ast_debug(4,
"%s is having his penalty raised up from %d to %d\n", member->
membername, penalty, raise_penalty);
2435 if ((max_penalty != INT_MAX && penalty > max_penalty) || (min_penalty != INT_MAX && penalty < min_penalty)) {
2436 if (conditions & QUEUE_EMPTY_PENALTY) {
2437 ast_debug(4,
"%s is unavailable because his penalty is not between %d and %d\n", member->
membername, min_penalty, max_penalty);
2444 if (conditions & QUEUE_EMPTY_INVALID) {
2445 ast_debug(4,
"%s is unavailable because his device state is 'invalid'\n", member->
membername);
2450 if (conditions & QUEUE_EMPTY_UNAVAILABLE) {
2451 ast_debug(4,
"%s is unavailable because his device state is 'unavailable'\n", member->
membername);
2456 if (conditions & QUEUE_EMPTY_INUSE) {
2457 ast_debug(4,
"%s is unavailable because his device state is 'inuse'\n", member->
membername);
2462 if (conditions & QUEUE_EMPTY_RINGING) {
2463 ast_debug(4,
"%s is unavailable because his device state is 'ringing'\n", member->
membername);
2468 if (conditions & QUEUE_EMPTY_UNKNOWN) {
2469 ast_debug(4,
"%s is unavailable because his device state is 'unknown'\n", member->
membername);
2475 if (member->
paused && (conditions & QUEUE_EMPTY_PAUSED)) {
2478 }
else if ((conditions & QUEUE_EMPTY_WRAPUP)
2482 ast_debug(4,
"%s is unavailable because it has only been %d seconds since his last call (wrapup time is %d)\n",
2498 if (!devstate && (conditions & QUEUE_EMPTY_RINGING)) {
2500 return get_member_status(q, max_penalty, min_penalty, raise_penalty, conditions, 1);
2514 #define MAX_CALL_ATTEMPT_BUCKETS 353
2516 static int pending_members_hash(
const void *obj,
const int flags)
2518 const struct member *object;
2527 key =
object->interface;
2536 static int pending_members_cmp(
void *obj,
void *arg,
int flags)
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);
2564 static void pending_members_remove(
struct member *mem)
2577 if (m->
status != status) {
2594 pending_members_remove(m);
2596 queue_publish_member_blob(queue_member_status_type(), queue_member_blob_create(q, m));
2634 if (mem->
lastcall && wrapuptime && (time(NULL) - wrapuptime < mem->
lastcall)) {
2647 char interface[80], *slash_pos;
2657 if (dev_state->
eid) {
2663 while ((q = ao2_t_iterator_next(&qiter,
"Iterate over queues"))) {
2669 for (; (m = ao2_iterator_next(&miter));
ao2_ref(m, -1)) {
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)) {
2687 avail = is_member_available(q, m);
2689 if (avail && found_member) {
2708 queue_t_unref(q,
"Done with iterator");
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",
2786 if (!strcmp(ast_get_context_name(c), parent)) {
2791 while ((inc = (
struct ast_include*) ast_walk_context_includes(c, inc))) {
2792 const char *includename = ast_get_include_name(inc);
2793 if (!strcasecmp(child, includename)) {
2806 static int extension_state_cb(
const char *context,
const char *exten,
struct ast_state_cb_info *info,
void *data)
2811 int state = info->exten_state;
2820 while ((q = ao2_t_iterator_next(&qiter,
"Iterate through queues"))) {
2824 for (; (m = ao2_iterator_next(&miter));
ao2_ref(m, -1)) {
2835 queue_t_unref(q,
"Done with iterator");
2840 ast_debug(1,
"Extension '%s@%s' changed to state '%d' (%s)\n", exten, context, device_state,
ast_devstate2str(device_state));
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",
2855 static void destroy_queue_member_cb(
void *obj)
2857 struct member *mem = obj;
2869 if ((cur = ao2_alloc(
sizeof(*cur), destroy_queue_member_cb))) {
2879 if (!ast_strlen_zero(state_interface)) {
2884 if (!ast_strlen_zero(membername)) {
2890 ast_log(LOG_WARNING,
"No location at interface '%s'\n", interface);
2894 char *exten = strsep(&context,
"@") + 5;
2910 static int compress_char(
const char c)
2914 }
else if (c > 96) {
2920 static int member_hash_fn(
const void *obj,
const int flags)
2922 const struct member *mem = obj;
2924 const char *chname = strchr(interface,
'/');
2930 for (i = 0; i < 5 && chname[i]; i++) {
2931 ret += compress_char(chname[i]) << (i * 6);
2936 static int member_cmp_fn(
void *obj1,
void *obj2,
int flags)
2938 struct member *mem1 = obj1;
2939 struct member *mem2 = obj2;
2955 q->
retry = DEFAULT_RETRY;
2966 q->announceholdtime = 1;
2974 q->setinterfacevar = 0;
2976 q->setqueueentryvar = 0;
2979 q->reportholdtime = 0;
2983 q->leavewhenempty = 0;
2986 q->timeoutrestart = 0;
2991 q->relativeperiodicannounce = 0;
2993 q->autopausebusy = 0;
2994 q->autopauseunavail = 0;
2999 if (q->strategy == QUEUE_STRATEGY_LINEAR || q->strategy == QUEUE_STRATEGY_RRORDERED) {
3004 member_hash_fn, NULL, member_cmp_fn);
3051 static void clear_queue(
struct call_queue *q)
3063 while ((mem = ao2_iterator_next(&mem_iter))) {
3085 char *timestr, *maxstr, *minstr, *raisestr, *contentdup;
3088 int penaltychangetime, inserted = 0;
3090 if (!(rule =
ast_calloc(1,
sizeof(*rule)))) {
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') {
3129 if (!ast_strlen_zero(minstr)) {
3130 if (*minstr ==
'+' || *minstr ==
'-') {
3138 if (!ast_strlen_zero(raisestr)) {
3139 if (*raisestr ==
'+' || *raisestr ==
'-') {
3149 if (strcasecmp(rl_iter->name, list_name)) {
3154 if (rule->
time < rule_iter->time) {
3171 ast_log(LOG_WARNING,
"Unknown rule list name %s; ignoring.\n", list_name);
3192 char *rulecat = NULL;
3195 ast_log(LOG_WARNING,
"Missing \"queue_rules\" in extconfig.conf\n");
3199 ast_log(LOG_WARNING,
"Failed to load queue rules from realtime\n");
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;
3206 int min_relative = 0, max_relative = 0, raise_relative = 0;
3209 rule_name = ast_variable_retrieve(cfg, rulecat,
"rule_name");
3210 if (ast_strlen_zero(rule_name)) {
3215 if (!(strcasecmp(rl_iter->name, rule_name))) {
3222 if (!(new_rl =
ast_calloc(1,
sizeof(*new_rl)))) {
3229 timestr = ast_variable_retrieve(cfg, rulecat,
"time");
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",
3232 (ast_strlen_zero(timestr) ?
"invalid value" : timestr), rule_name);
3235 if (!(new_penalty_rule =
ast_calloc(1,
sizeof(*new_penalty_rule)))) {
3239 if (!(maxstr = ast_variable_retrieve(cfg, rulecat,
"max_penalty")) ||
3240 ast_strlen_zero(maxstr) || sscanf(maxstr,
"%30d", &max_penalty) != 1) {
3244 if (*maxstr ==
'+' || *maxstr ==
'-') {
3248 if (!(minstr = ast_variable_retrieve(cfg, rulecat,
"min_penalty")) ||
3249 ast_strlen_zero(minstr) || sscanf(minstr,
"%30d", &min_penalty) != 1) {
3253 if (*minstr ==
'+' || *minstr ==
'-') {
3257 if (!(raisestr = ast_variable_retrieve(cfg, rulecat,
"raise_penalty")) ||
3258 ast_strlen_zero(raisestr) || sscanf(raisestr,
"%30d", &raise_penalty) != 1) {
3262 if (*raisestr ==
'+' || *raisestr ==
'-') {
3266 new_penalty_rule->
time = penaltychangetime;
3274 if (new_penalty_rule->
time < pr_iter->
time) {
3289 static void parse_empty_options(
const char *value,
enum empty_conditions *empty,
int joinempty)
3292 char *option = NULL;
3293 while ((option = strsep(&value_copy,
","))) {
3294 if (!strcasecmp(option,
"paused")) {
3295 *empty |= QUEUE_EMPTY_PAUSED;
3296 }
else if (!strcasecmp(option,
"penalty")) {
3297 *empty |= QUEUE_EMPTY_PENALTY;
3298 }
else if (!strcasecmp(option,
"inuse")) {
3299 *empty |= QUEUE_EMPTY_INUSE;
3300 }
else if (!strcasecmp(option,
"ringing")) {
3301 *empty |= QUEUE_EMPTY_RINGING;
3302 }
else if (!strcasecmp(option,
"invalid")) {
3303 *empty |= QUEUE_EMPTY_INVALID;
3304 }
else if (!strcasecmp(option,
"wrapup")) {
3305 *empty |= QUEUE_EMPTY_WRAPUP;
3306 }
else if (!strcasecmp(option,
"unavailable")) {
3307 *empty |= QUEUE_EMPTY_UNAVAILABLE;
3308 }
else if (!strcasecmp(option,
"unknown")) {
3309 *empty |= QUEUE_EMPTY_UNKNOWN;
3310 }
else if (!strcasecmp(option,
"loose")) {
3311 *empty = (QUEUE_EMPTY_PENALTY | QUEUE_EMPTY_INVALID);
3312 }
else if (!strcasecmp(option,
"strict")) {
3313 *empty = (QUEUE_EMPTY_PENALTY | QUEUE_EMPTY_INVALID | QUEUE_EMPTY_PAUSED | QUEUE_EMPTY_UNAVAILABLE);
3314 }
else if ((
ast_false(option) && joinempty) || (
ast_true(option) && !joinempty)) {
3315 *empty = (QUEUE_EMPTY_PENALTY | QUEUE_EMPTY_INVALID | QUEUE_EMPTY_PAUSED);
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")) {
3349 q->setinterfacevar =
ast_true(val);
3350 }
else if (!strcasecmp(param,
"setqueuevar")) {
3352 }
else if (!strcasecmp(param,
"setqueueentryvar")) {
3353 q->setqueueentryvar =
ast_true(val);
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")) {
3388 ast_debug(1,
"%s=%s for queue '%s'\n", param, val, q->
name);
3389 }
else if (!strcasecmp(param,
"announce-round-seconds")) {
3395 ast_log(LOG_WARNING,
"'%s' isn't a valid value for %s "
3396 "using 0 instead for queue '%s' at line %d of queues.conf\n",
3397 val, param, q->
name, linenum);
3399 ast_log(LOG_WARNING,
"'%s' isn't a valid value for %s "
3400 "using 0 instead for queue '%s'\n", val, param, q->
name);
3404 }
else if (!strcasecmp(param,
"announce-holdtime")) {
3405 if (!strcasecmp(val,
"once")) {
3406 q->announceholdtime = ANNOUNCEHOLDTIME_ONCE;
3408 q->announceholdtime = ANNOUNCEHOLDTIME_ALWAYS;
3410 q->announceholdtime = 0;
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,
',')) {
3431 while ((s = strsep(&buf,
",|"))) {
3446 }
else if (!strcasecmp(param,
"periodic-announce-startdelay")) {
3448 }
else if (!strcasecmp(param,
"periodic-announce-frequency")) {
3450 }
else if (!strcasecmp(param,
"relative-periodic-announce")) {
3451 q->relativeperiodicannounce =
ast_true(val);
3452 }
else if (!strcasecmp(param,
"random-periodic-announce")) {
3454 }
else if (!strcasecmp(param,
"retry")) {
3455 q->
retry = atoi(val);
3456 if (q->
retry <= 0) {
3457 q->
retry = DEFAULT_RETRY;
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")) {
3474 q->autopauseunavail =
ast_true(val);
3475 }
else if (!strcasecmp(param,
"maxlen")) {
3480 }
else if (!strcasecmp(param,
"servicelevel")) {
3482 }
else if (!strcasecmp(param,
"strategy")) {
3489 strategy = strat2int(val);
3491 ast_log(LOG_WARNING,
"'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
3493 strategy = QUEUE_STRATEGY_RINGALL;
3495 if (strategy == q->strategy) {
3498 if (strategy == QUEUE_STRATEGY_LINEAR) {
3499 ast_log(LOG_WARNING,
"Changing to the linear strategy currently requires asterisk to be restarted.\n");
3502 q->strategy = strategy;
3503 }
else if (!strcasecmp(param,
"joinempty")) {
3504 parse_empty_options(val, &q->joinempty, 1);
3505 }
else if (!strcasecmp(param,
"leavewhenempty")) {
3506 parse_empty_options(val, &q->leavewhenempty, 0);
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);
3530 ast_log(LOG_WARNING,
"Unknown keyword in queue '%s': %s\n", q->
name, param);
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
3546 static void member_add_to_queue(
struct call_queue *queue,
struct member *mem)
3562 static void member_remove_from_queue(
struct call_queue *queue,
struct member *mem)
3564 pending_members_remove(mem);
3567 queue_member_follower_removal(queue, mem);
3586 int ringinuse = q->ringinuse;
3588 const char *config_val;
3589 const char *
interface = ast_variable_retrieve(member_config, category, "interface");
3590 const char *rt_uniqueid = ast_variable_retrieve(member_config, category,
"uniqueid");
3591 const char *membername =
S_OR(ast_variable_retrieve(member_config, category,
"membername"), interface);
3592 const char *state_interface =
S_OR(ast_variable_retrieve(member_config, category,
"state_interface"), interface);
3593 const char *penalty_str = ast_variable_retrieve(member_config, category,
"penalty");
3594 const char *paused_str = ast_variable_retrieve(member_config, category,
"paused");
3595 const char *wrapuptime_str = ast_variable_retrieve(member_config, category,
"wrapuptime");
3596 const char *reason_paused = ast_variable_retrieve(member_config, category,
"reason_paused");
3598 if (ast_strlen_zero(rt_uniqueid)) {
3599 ast_log(LOG_WARNING,
"Realtime field 'uniqueid' is empty for member %s\n",
3600 S_OR(membername,
"NULL"));
3604 if (ast_strlen_zero(interface)) {
3605 ast_log(LOG_WARNING,
"Realtime field 'interface' is empty for member %s\n",
3606 S_OR(membername,
"NULL"));
3611 penalty = atoi(penalty_str);
3612 if ((penalty < 0) && negative_penalty_invalid) {
3614 }
else if (penalty < 0) {
3620 paused = atoi(paused_str);
3626 if (wrapuptime_str) {
3627 wrapuptime = atoi(wrapuptime_str);
3628 if (wrapuptime < 0) {
3633 if ((config_val = ast_variable_retrieve(member_config, category, realtime_ringinuse_field))) {
3639 ast_log(LOG_WARNING,
"Invalid value of '%s' field for %s in queue '%s'\n", realtime_ringinuse_field, interface, q->
name);
3645 while ((m = ao2_iterator_next(&mem_iter))) {
3663 if (realtime_reason_paused) {
3676 if ((m =
create_queue_member(interface, membername, penalty, paused, state_interface, ringinuse, wrapuptime))) {
3680 if (!ast_strlen_zero(reason_paused)) {
3683 if (!log_membername_as_agent) {
3684 ast_queue_log(q->
name,
"REALTIME", m->
interface,
"ADDMEMBER",
"%s", paused ?
"PAUSED" :
"");
3686 ast_queue_log(q->
name,
"REALTIME", m->
membername,
"ADDMEMBER",
"%s", paused ?
"PAUSED" :
"");
3688 member_add_to_queue(q, m);
3702 while ((cur = ao2_iterator_next(&mem_iter))) {
3704 member_remove_from_queue(q, cur);
3727 static struct call_queue *alloc_queue(
const char *queuename)
3731 if ((q = ao2_t_alloc(
sizeof(*q),
destroy_queue,
"Allocate queue"))) {
3733 queue_t_unref(q,
"String field allocation failed");
3759 char *category = NULL;
3760 const char *tmp_name;
3770 queue_t_unref(q,
"Queue is dead; can't return it");
3773 ast_log(LOG_WARNING,
"Static queue '%s' already exists. Not loading from realtime\n", q->
name);
3777 }
else if (!member_config) {
3788 ast_debug(1,
"Queue %s not found in realtime.\n", queuename);
3792 queues_t_unlink(queues, q,
"Unused; removing from container");
3794 queue_t_unref(q,
"Queue is dead; can't return it");
3802 if (!(q = alloc_queue(queuename))) {
3811 for (tmpvar = queue_vars; tmpvar; tmpvar = tmpvar->
next) {
3812 if (!strcasecmp(tmpvar->
name,
"strategy")) {
3813 q->strategy = strat2int(tmpvar->
value);
3814 if (q->strategy < 0) {
3815 ast_log(LOG_WARNING,
"'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
3817 q->strategy = QUEUE_STRATEGY_RINGALL;
3824 q->strategy = QUEUE_STRATEGY_RINGALL;
3826 queues_t_link(queues, q,
"Add queue to container");
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,
'_'))) {
3852 while ((m = ao2_iterator_next(&mem_iter))) {
3866 while ((m = ao2_iterator_next(&mem_iter))) {
3868 if (ast_strlen_zero(m->
membername) || !log_membername_as_agent) {
3869 ast_queue_log(q->
name,
"REALTIME", m->
interface,
"REMOVEMEMBER",
"%s",
"");
3871 ast_queue_log(q->
name,
"REALTIME", m->
membername,
"REMOVEMEMBER",
"%s",
"");
3873 member_remove_from_queue(q, m);
3902 int prev_weight = 0;
3907 if (!q || q->realtime) {
3917 queue_vars = ast_load_realtime(
"queues",
"name", queuename, SENTINEL);
3920 if (!member_config) {
3921 ast_debug(1,
"No queue_members defined in config extconfig.conf\n");
3926 prev_weight = q->
weight ? 1 : 0;
3927 queue_t_unref(q,
"Need to find realtime queue");
3936 if (!q->
weight && prev_weight) {
3939 if (q->
weight && !prev_weight) {
3945 update_realtime_members(q);
3956 static void load_realtime_queues(
const char *queuename)
3959 char *category = NULL;
3960 const char *name = NULL;
3967 if (ast_strlen_zero(queuename)) {
3970 name = ast_variable_retrieve(cfg, category,
"name");
3984 static int update_realtime_member_field(
struct member *mem,
const char *queue_name,
const char *field,
const char *value)
4000 static void update_realtime_members(
struct call_queue *q)
4004 char *category = NULL;
4013 while ((m = ao2_iterator_next(&mem_iter))) {
4015 member_remove_from_queue(q, m);
4020 ast_debug(3,
"Queue %s has no realtime members defined. No need for update\n", q->
name);
4029 while ((m = ao2_iterator_next(&mem_iter))) {
4043 while ((m = ao2_iterator_next(&mem_iter))) {
4045 if (ast_strlen_zero(m->
membername) || !log_membername_as_agent) {
4046 ast_queue_log(q->
name,
"REALTIME", m->
interface,
"REMOVEMEMBER",
"%s",
"");
4048 ast_queue_log(q->
name,
"REALTIME", m->
membername,
"REMOVEMEMBER",
"%s",
"");
4050 member_remove_from_queue(q, m);
4059 static int join_queue(
char *queuename,
struct queue_ent *qe,
enum queue_result *reason,
int position)
4076 *reason = QUEUE_JOINEMPTY;
4078 queue_t_unref(q,
"Done with realtime queue");
4083 *reason = QUEUE_FULL;
4084 }
else if (*reason == QUEUE_UNKNOWN) {
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,
4135 ast_debug(1,
"Queue '%s' Join, Channel '%s', Position '%d'\n", q->
name, ast_channel_name(qe->
chan), qe->
pos );
4138 queue_t_unref(q,
"Done with realtime queue");
4147 if (ast_strlen_zero(filename)) {
4151 if (!
ast_fileexists(filename, NULL, ast_channel_language(chan))) {
4157 res =
ast_streamfile(chan, filename, ast_channel_language(chan));
4174 int digitlen = strlen(qe->
digits);
4177 if (digitlen <
sizeof(qe->
digits) - 2) {
4178 qe->
digits[digitlen] = digit;
4179 qe->
digits[digitlen + 1] =
'\0';
4186 if (ast_strlen_zero(qe->
context)) {
4209 int res = 0, say_thanks = 0;
4210 long avgholdmins, avgholdsecs;
4251 ast_channel_language(qe->
chan), NULL) ||
4258 ast_channel_language(qe->
chan),
"n") ||
4276 ast_verb(3,
"Hold time for %s is %ld minute(s) %ld seconds\n", qe->
parent->
name, avgholdmins, avgholdsecs);
4280 if ((avgholdmins+avgholdsecs) > 0 && qe->
parent->announceholdtime &&
4281 ((qe->
parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE && !qe->
last_pos) ||
4282 !(qe->
parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE))) {
4289 if (avgholdmins >= 1) {
4295 if (avgholdmins == 1) {
4307 if (avgholdsecs >= 1) {
4321 if (qe->
parent->announceposition) {
4322 ast_verb(3,
"Told %s in %s their queue position (which was %d)\n",
4349 static void recalc_holdtime(
struct queue_ent *qe,
int newholdtime)
4362 qe->
parent->
holdtime = (((oldvalue << 2) - oldvalue) + newholdtime) >> 2;
4375 struct queue_ent *current, *prev = NULL;
4382 queue_t_ref(q,
"Copy queue pointer from queue entry");
4386 for (current = q->
head; current; current = current->
next) {
4387 if (current == qe) {
4397 "Position", qe->
pos,
4400 ast_debug(1,
"Queue '%s' Leave, Channel '%s'\n", q->
name, ast_channel_name(qe->
chan));
4412 snprintf(posstr,
sizeof(posstr),
"%d", qe->
pos);
4425 if (!(var = ast_load_realtime(
"queues",
"name", q->
name, SENTINEL))) {
4434 queues_t_unlink(queues, q,
"Queue is now dead; remove it from the container");
4437 queue_t_unref(q,
"Expire copied reference");
4447 static void callattempt_free(
struct callattempt *doomed)
4449 if (doomed->member) {
4461 for (cur = outgoing; cur; cur = cur->q_next) {
4462 if (cur->chan && cur->chan != exception) {
4476 if (outgoing->chan && (outgoing->chan != exception)) {
4477 if (exception || cancel_answered_elsewhere) {
4478 ast_channel_hangupcause_set(outgoing->chan, AST_CAUSE_ANSWERED_ELSEWHERE);
4489 pending_members_remove(outgoing->member);
4495 outgoing = outgoing->q_next;
4497 callattempt_free(oo);
4516 while ((mem = ao2_iterator_next(&mem_iter))) {
4518 avl += is_member_available(q, mem);
4531 if ((!q->
autofill || q->strategy == QUEUE_STRATEGY_RINGALL) && avl) {
4550 while ((q = ao2_t_iterator_next(&queue_iter,
"Iterate through queues"))) {
4552 queue_t_unref(q,
"Done with iterator");
4567 queue_t_unref(q,
"Done with iterator");
4576 static int is_longest_waiting_caller(
struct queue_ent *caller,
struct member *member)
4580 int is_longest_waiting = 1;
4585 while ((q = ao2_t_iterator_next(&queue_iter,
"Iterate through queues"))) {
4586 if (q == caller->
parent) {
4587 queue_t_unref(q,
"Done with iterator");
4607 ast_debug(1,
"Queue %s has a call at position %i that's been waiting longer (%li vs %li)\n",
4609 is_longest_waiting = 0;
4617 queue_t_unref(q,
"Done with iterator");
4618 if (!is_longest_waiting) {
4623 return is_longest_waiting;
4631 pending_members_remove(o->member);
4643 static int member_status_available(
int status)
4659 struct member *memberp = call->member;
4667 if (!memberp->
ringinuse && !member_status_available(memberp->
status)) {
4677 if (wrapuptime && (time(NULL) - memberp->
lastcall) < wrapuptime) {
4678 ast_debug(1,
"Wrapuptime not yet expired on queue %s for %s\n",
4684 if (use_weight && compare_weight(qe->
parent, memberp)) {
4685 ast_debug(1,
"Priority queue delaying call to %s:%s\n",
4690 if (force_longest_waiting_caller && !is_longest_waiting_caller(qe, memberp)) {
4691 ast_debug(1,
"Another caller was waiting longer; delaying call to %s:%s\n",
4699 ao2_lock(pending_members);
4701 mem = ao2_find(pending_members, memberp,
4708 ast_debug(1,
"%s has another call trying, can't receive call\n",
4711 ao2_unlock(pending_members);
4720 ao2_link(pending_members, memberp);
4721 ao2_unlock(pending_members);
4729 ast_debug(1,
"%s actually not available, can't receive call\n",
4731 pending_members_remove(memberp);
4763 if (!can_ring_entry(qe, tmp)) {
4770 if ((location = strchr(tech,
'/'))) {
4776 ast_channel_lock(qe->
chan);
4777 nativeformats =
ao2_bump(ast_channel_nativeformats(qe->
chan));
4778 ast_channel_unlock(qe->
chan);
4781 tmp->chan =
ast_request(tech, nativeformats, NULL, qe->
chan, location, &status);
4782 ao2_cleanup(nativeformats);
4789 pending_members_remove(tmp->member);
4791 publish_dial_end_event(qe->
chan, tmp, NULL,
"BUSY");
4802 ast_channel_hangupcause_set(tmp->chan, AST_CAUSE_ANSWERED_ELSEWHERE);
4804 ast_channel_appl_set(tmp->chan,
"AppQueue");
4805 ast_channel_data_set(tmp->chan,
"(Outgoing Line)");
4806 memset(ast_channel_whentohangup(tmp->chan), 0,
sizeof(*ast_channel_whentohangup(tmp->chan)));
4814 caller.
id = ast_channel_connected(qe->
chan)->
id;
4815 caller.
ani = ast_channel_connected(qe->
chan)->
ani;
4817 }
else if (!ast_strlen_zero(ast_channel_dialed(qe->
chan)->
number.
str)) {
4819 }
else if (!ast_strlen_zero(ast_channel_exten(qe->
chan))) {
4834 ast_max_forwards_decrement(tmp->chan);
4837 ast_channel_adsicpe_set(tmp->chan, ast_channel_adsicpe(qe->
chan));
4840 ast_channel_dialcontext_set(tmp->chan, ast_channel_context(qe->
chan));
4841 ast_channel_exten_set(tmp->chan, ast_channel_exten(qe->
chan));
4846 ast_channel_unlock(tmp->chan);
4847 ast_channel_unlock(qe->
chan);
4858 if ((res =
ast_call(tmp->chan, location, 0))) {
4860 ast_verb(3,
"Couldn't call %s\n", tmp->
interface);
4872 queue_publish_multi_channel_blob(qe->
chan, tmp->chan, queue_agent_called_type(), blob);
4876 ast_channel_unlock(tmp->chan);
4877 ast_channel_unlock(qe->
chan);
4879 ast_verb(3,
"Called %s\n", tmp->
interface);
4889 for (cur = outgoing; cur; cur = cur->q_next) {
4890 if (cur->stillgoing &&
4892 (!best || cur->metric < best->metric)) {
4917 for (cur = outgoing; cur; cur = cur->q_next) {
4927 ast_debug(1,
"Nobody left to try ringing in queue\n");
4930 if (qe->
parent->strategy == QUEUE_STRATEGY_RINGALL) {
4932 for (cur = outgoing; cur; cur = cur->q_next) {
4933 if (cur->
stillgoing && !cur->chan && cur->metric <= best->metric) {
4934 ast_debug(1,
"(Parallel) Trying '%s' with metric %d\n", cur->
interface, cur->metric);
4952 ast_debug(1,
"Queue timed out while ringing members.\n");
4958 for (cur = outgoing; cur; cur = cur->q_next) {
4980 if (qe->
parent->wrapped) {
5001 qe->
linpos = best->metric % 1000;
5038 ast_verb(3,
"Playing periodic announcement\n");
5064 if (qe->
parent->relativeperiodicannounce) {
5081 int callabandonedinsl = 0;
5092 "Position", qe->
pos,
5093 "OriginalPosition", qe->
opos,
5094 "HoldTime", (
int)(time(NULL) - qe->
start));
5099 if (callabandonedinsl) {
5114 ast_verb(3,
"Nobody picked up in %d ms\n", rnatime);
5124 "Interface", interface,
5125 "MemberName", membername,
5126 "RingTime", rnatime);
5127 queue_publish_multi_channel_blob(qe->
chan, peer, queue_agent_ringnoanswer_type(), blob);
5129 ast_queue_log(qe->
parent->
name, ast_channel_uniqueid(qe->
chan), membername,
"RINGNOANSWER",
"%d", rnatime);
5134 if ((mem = interface_exists(qe->
parent, interface))) {
5135 time_t idletime = time(&idletime)-mem->
lastcall;
5146 if (!set_member_paused(qe->
parent->
name, interface,
"Auto-Pause", 1)) {
5147 ast_verb(3,
"Auto-Pausing Queue Member %s in queue %s since they failed to answer.\n",
5150 ast_verb(3,
"Failed to pause Queue Member %s in queue %s!\n", interface, qe->
parent->
name);
5155 if (!set_member_paused(
"", interface,
"Auto-Pause", 1)) {
5156 ast_verb(3,
"Auto-Pausing Queue Member %s in all queues since they failed to answer on queue %s.\n",
5159 ast_verb(3,
"Failed to pause Queue Member %s in all queues!\n", interface);
5175 static void update_connected_line_from_peer(
struct ast_channel *chan,
struct ast_channel *peer,
int is_caller)
5181 ast_channel_lock(peer);
5183 ast_channel_unlock(peer);
5191 #define AST_MAX_WATCHERS 256
5208 struct callattempt *o, *start = NULL, *prev = NULL;
5210 int numbusies = prebusies;
5219 char membername[80] =
"";
5223 struct timeval start_time_tv =
ast_tvnow();
5224 int canceled_by_caller = 0;
5226 ast_channel_lock(qe->
chan);
5228 ast_channel_unlock(qe->
chan);
5230 starttime = (long) time(NULL);
5233 int numlines, retry, pos = 1;
5238 for (retry = 0; retry < 2; retry++) {
5240 for (o = outgoing; o; o = o->q_next) {
5245 watchers[pos++] = o->chan;
5250 prev->call_next = o;
5255 prev->call_next = NULL;
5259 if (pos > 1 || !stillgoing ||
5260 (qe->
parent->strategy != QUEUE_STRATEGY_RINGALL) ) {
5265 ring_one(qe, outgoing, &numbusies);
5269 if (numlines == (numbusies + numnochan)) {
5270 ast_debug(1,
"Everyone is busy at this time\n");
5272 ast_debug(3,
"No one is answering queue '%s' (%d numlines / %d busies / %d failed channels)\n", queue, numlines, numbusies, numnochan);
5282 for (o = start; o; o = o->call_next) {
5289 ast_channel_lock(o->chan);
5290 ast_copy_string(ochan_name, ast_channel_name(o->chan),
sizeof(ochan_name));
5291 ast_channel_unlock(o->chan);
5295 ast_verb(3,
"%s answered %s\n", ochan_name, inchan_name);
5302 update_connected_line_from_peer(in, o->chan, 1);
5309 update_connected_line_from_peer(in, o->chan, 1);
5312 if (o->aoc_s_rate_list) {
5313 size_t encoded_size;
5315 if ((encoded =
ast_aoc_encode(o->aoc_s_rate_list, &encoded_size, o->chan))) {
5322 }
else if (o->chan && (o->chan == winner)) {
5328 if (!ast_strlen_zero(ast_channel_call_forward(o->chan)) && !forwardsallowed) {
5329 ast_verb(3,
"Forwarding %s to '%s' prevented.\n", inchan_name, ast_channel_call_forward(o->chan));
5331 "CANCEL", ast_channel_call_forward(o->chan));
5336 }
else if (!ast_strlen_zero(ast_channel_call_forward(o->chan))) {
5344 ast_copy_string(tmpchan, ast_channel_call_forward(o->chan),
sizeof(tmpchan));
5345 ast_copy_string(forwarder, ast_channel_name(o->chan),
sizeof(forwarder));
5346 if ((stuff = strchr(tmpchan,
'/'))) {
5350 const char *forward_context;
5351 ast_channel_lock(o->chan);
5353 snprintf(tmpchan,
sizeof(tmpchan),
"%s@%s", ast_channel_call_forward(o->chan), forward_context ? forward_context : ast_channel_context(o->chan));
5354 ast_channel_unlock(o->chan);
5358 if (!strcasecmp(tech,
"Local")) {
5367 ast_verb(3,
"Now forwarding %s to '%s/%s' (thanks to %s)\n", inchan_name, tech, stuff, ochan_name);
5369 o->chan =
ast_request(tech, ast_channel_nativeformats(in), NULL, in, stuff, &status);
5372 "Forwarding failed to create channel to dial '%s/%s'\n",
5379 ast_channel_redirecting(original));
5380 ast_channel_unlock(o->chan);
5381 ast_channel_unlock(original);
5387 ast_max_forwards_decrement(o->chan);
5406 || ast_strlen_zero(ast_channel_redirecting(o->chan)->
from.
number.
str)) {
5421 || ast_strlen_zero(ast_channel_caller(o->chan)->
id.
number.
str);
5423 ast_channel_caller(in));
5425 ast_channel_unlock(in);
5426 if (qe->
parent->strategy != QUEUE_STRATEGY_RINGALL
5439 ast_channel_unlock(o->chan);
5445 ast_channel_unlock(o->chan);
5449 ast_log(LOG_NOTICE,
"Forwarding failed to dial '%s/%s'\n",
5456 "CANCEL", ast_channel_call_forward(original));
5477 ast_verb(3,
"%s answered %s\n", ochan_name, inchan_name);
5479 publish_dial_end_event(qe->
chan, outgoing, o->chan,
"CANCEL");
5486 update_connected_line_from_peer(in, o->chan, 1);
5493 update_connected_line_from_peer(in, o->chan, 1);
5496 if (o->aoc_s_rate_list) {
5497 size_t encoded_size;
5499 if ((encoded =
ast_aoc_encode(o->aoc_s_rate_list, &encoded_size, o->chan))) {
5508 ast_verb(3,
"%s is busy\n", ochan_name);
5510 endtime = (long) time(NULL);
5511 endtime -= starttime;
5512 rna(endtime * 1000, qe, o->chan, on, membername, qe->
parent->autopausebusy);
5514 if (qe->
parent->strategy != QUEUE_STRATEGY_RINGALL) {
5515 if (qe->
parent->timeoutrestart) {
5520 ring_one(qe, outgoing, &numbusies);
5521 starttime = (long) time(NULL);
5527 ast_verb(3,
"%s is circuit-busy\n", ochan_name);
5529 endtime = (long) time(NULL);
5530 endtime -= starttime;
5531 rna(endtime * 1000, qe, o->chan, on, membername, qe->
parent->autopauseunavail);
5533 if (qe->
parent->strategy != QUEUE_STRATEGY_RINGALL) {
5534 if (qe->
parent->timeoutrestart) {
5538 ring_one(qe, outgoing, &numbusies);
5539 starttime = (long) time(NULL);
5545 ast_verb(3,
"%s is ringing\n", ochan_name);
5560 ast_verb(3,
"Connected line update to %s prevented.\n", inchan_name);
5563 if (qe->
parent->strategy == QUEUE_STRATEGY_RINGALL) {
5566 ast_verb(3,
"%s connected line has changed. Saving it until answer for %s\n", ochan_name, inchan_name);
5590 o->aoc_s_rate_list = decoded;
5597 if (qe->
parent->strategy == QUEUE_STRATEGY_RINGALL) {
5605 ast_verb(3,
"Redirecting update to %s prevented\n",
5609 ast_verb(3,
"%s redirecting info has changed, passing it to %s\n",
5610 ochan_name, inchan_name);
5625 endtime = (long) time(NULL) - starttime;
5627 rna(endtime * 1000, qe, o->chan, on, membername, 1);
5629 if (qe->
parent->strategy != QUEUE_STRATEGY_RINGALL) {
5630 if (qe->
parent->timeoutrestart) {
5634 ring_one(qe, outgoing, &numbusies);
5635 starttime = (long) time(NULL);
5649 if (f->
data.uint32) {
5650 ast_channel_hangupcause_set(in, f->
data.uint32);
5654 canceled_by_caller = 1;
5656 ast_verb(3,
"User hit %c to disconnect call.\n", f->
subclass.
integer);
5659 canceled_by_caller = 1;
5665 canceled_by_caller = 1;
5668 if (canceled_by_caller) {
5669 publish_dial_end_event(in, outgoing, NULL,
"CANCEL");
5670 for (o = start; o; o = o->call_next) {
5679 for (o = start; o; o = o->call_next) {
5689 ast_verb(3,
"Connected line update to %s prevented.\n", ast_channel_name(o->chan));
5698 ast_verb(3,
"Redirecting update to %s prevented.\n", ast_channel_name(o->chan));
5722 for (o = start; o; o = o->call_next) {
5728 publish_dial_end_event(qe->
chan, outgoing, NULL,
"NOANSWER");
5758 ast_debug(1,
"There %s %d available %s.\n", avl != 1 ?
"are" :
"is", avl, avl != 1 ?
"members" :
"member");
5760 while ((idx < avl) && (ch) && (ch != qe)) {
5773 ast_debug(1,
"It's our turn (%s).\n", ast_channel_name(qe->
chan));
5776 ast_debug(1,
"It's not our turn (%s).\n", ast_channel_name(qe->
chan));
5781 if (avl == 0 && qe->
pos == 1) {
5782 update_realtime_members(qe->
parent);
5796 int max_penalty = INT_MAX;
5799 char max_penalty_str[20];
5808 if (max_penalty < 0) {
5812 snprintf(max_penalty_str,
sizeof(max_penalty_str),
"%d", max_penalty);
5815 ast_debug(3,
"Setting max penalty to %d for caller %s since %d seconds have elapsed\n",
5820 char min_penalty_str[20];
5830 if (min_penalty < 0) {
5834 if (max_penalty != INT_MAX && min_penalty > max_penalty) {
5838 snprintf(min_penalty_str,
sizeof(min_penalty_str),
"%d", min_penalty);
5841 ast_debug(3,
"Setting min penalty to %d for caller %s since %d seconds have elapsed\n",
5846 char raise_penalty_str[20];
5856 if (raise_penalty < 0) {
5860 if (max_penalty != INT_MAX && raise_penalty > max_penalty) {
5864 snprintf(raise_penalty_str,
sizeof(raise_penalty_str),
"%d", raise_penalty);
5867 ast_debug(3,
"Setting raised penalty to %d for caller %s since %d seconds have elapsed\n",
5893 *reason = QUEUE_WITHDRAW;
5904 *reason = QUEUE_TIMEOUT;
5908 if (qe->
parent->leavewhenempty) {
5913 *reason = QUEUE_LEAVEEMPTY;
5914 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));
5923 (res = say_position(qe,ringing))) {
5929 *reason = QUEUE_TIMEOUT;
5939 while (qe->
pr && ((time(NULL) - qe->
start) >= qe->
pr->
time)) {
5945 *reason = QUEUE_TIMEOUT;
5960 *reason = QUEUE_TIMEOUT;
5975 int newtalktime = time(NULL) - starttime;
5984 if (!starttime || (member->
starttime != starttime)) {
5988 if (shared_lastcall) {
5990 while ((qtmp = ao2_t_iterator_next(&queue_iter,
"Iterate through queues"))) {
6001 queue_t_unref(qtmp,
"Done with iterator");
6018 pending_members_remove(member);
6022 if (callcompletedinsl) {
6030 q->
talktime = (((oldtalktime << 2) - oldtalktime) + newtalktime) >> 2;
6052 if (qe->
raise_penalty != INT_MAX && penalty < qe->raise_penalty) {
6061 ast_debug(1,
"Disregarding penalty, %d members and %d in penaltymemberslimit.\n",
6065 switch (q->strategy) {
6066 case QUEUE_STRATEGY_RINGALL:
6068 tmp->metric = penalty * 1000000 * usepenalty;
6070 case QUEUE_STRATEGY_LINEAR:
6071 if (pos < qe->linpos) {
6072 tmp->metric = 1000 +
pos;
6080 tmp->metric += penalty * 1000000 * usepenalty;
6082 case QUEUE_STRATEGY_RRORDERED:
6083 case QUEUE_STRATEGY_RRMEMORY:
6085 if (pos < q->rrpos) {
6086 tmp->metric = 1000 +
pos;
6088 if (pos > q->
rrpos) {
6094 tmp->metric += penalty * 1000000 * usepenalty;
6096 case QUEUE_STRATEGY_RANDOM:
6097 tmp->metric = ast_random() % 1000;
6098 tmp->metric += penalty * 1000000 * usepenalty;
6100 case QUEUE_STRATEGY_WRANDOM:
6101 tmp->metric = ast_random() % ((1 + penalty) * 1000);
6103 case QUEUE_STRATEGY_FEWESTCALLS:
6104 tmp->metric = mem->
calls;
6105 tmp->metric += penalty * 1000000 * usepenalty;
6107 case QUEUE_STRATEGY_LEASTRECENT:
6111 tmp->metric = 1000000 - (time(NULL) - mem->
lastcall);
6113 tmp->metric += penalty * 1000000 * usepenalty;
6116 ast_log(LOG_WARNING,
"Can't calculate metric for unknown strategy %d\n", q->strategy);
6122 enum agent_complete_reason {
6131 time_t callstart,
enum agent_complete_reason rsn)
6133 const char *reason = NULL;
6144 reason =
"transfer";
6148 blob =
ast_json_pack(
"{s: s, s: s, s: s, s: I, s: I, s: s}",
6154 "Reason", reason ?:
"");
6156 queue_publish_multi_channel_snapshot_blob(
ast_queue_topic(queuename), caller, peer,
6157 queue_agent_complete_type(), blob);
6259 static void queue_stasis_data_destructor(
void *obj)
6267 ao2_cleanup(queue_data->
member);
6268 queue_unref(queue_data->
queue);
6280 queue_data->
dying = 1;
6297 queue_data = ao2_alloc(
sizeof(*queue_data), queue_stasis_data_destructor);
6303 ao2_cleanup(queue_data);
6315 queue_data->
member = mem;
6335 if (!transfer_str) {
6336 ast_log(LOG_WARNING,
"Unable to log attended transfer to queue log\n");
6341 case AST_ATTENDED_TRANSFER_DEST_BRIDGE_MERGE:
6344 case AST_ATTENDED_TRANSFER_DEST_APP:
6345 case AST_ATTENDED_TRANSFER_DEST_LOCAL_APP:
6348 case AST_ATTENDED_TRANSFER_DEST_LINK:
6352 case AST_ATTENDED_TRANSFER_DEST_THREEWAY:
6353 case AST_ATTENDED_TRANSFER_DEST_FAIL:
6383 if (queue_data->
dying) {
6394 ast_debug(3,
"Detected entry of caller channel %s into bridge %s\n",
6423 ao2_lock(queue_data);
6425 if (queue_data->
dying) {
6426 ao2_unlock(queue_data);
6432 ao2_unlock(queue_data);
6439 ao2_unlock(queue_data);
6441 exten = transfer_msg->
exten;
6442 context = transfer_msg->
context;
6446 "BLINDTRANSFER",
"%s|%s|%ld|%ld|%d",
6455 remove_stasis_subscriptions(queue_data);
6477 atxfer_msg->
dest_type == AST_ATTENDED_TRANSFER_DEST_THREEWAY) {
6481 ao2_lock(queue_data);
6483 if (queue_data->
dying) {
6484 ao2_unlock(queue_data);
6489 ao2_unlock(queue_data);
6497 ao2_unlock(queue_data);
6504 ao2_unlock(queue_data);
6507 log_attended_transfer(queue_data, atxfer_msg);
6513 remove_stasis_subscriptions(queue_data);
6527 ao2_cleanup(userdata);
6542 static void handle_local_optimization_begin(
void *userdata,
struct stasis_subscription *sub,
6554 if (queue_data->
dying) {
6567 ast_assert(source != NULL);
6571 ast_log(LOG_ERROR,
"Unable to track local channel optimization for channel %s. Expect further errors\n", local_one->
base->
name);
6576 optimization->
id =
id;
6593 static void handle_local_optimization_end(
void *userdata,
struct stasis_subscription *sub,
6605 if (queue_data->
dying) {
6622 ast_log(LOG_WARNING,
"Told of a local optimization end when we had no previous begin\n");
6626 if (
id != optimization->
id) {
6627 ast_log(LOG_WARNING,
"Local optimization end event ID does not match begin (%u != %u)\n",
6628 id, optimization->
id);
6633 ast_debug(3,
"Local optimization: Changing queue caller uniqueid from %s to %s\n",
6637 ast_debug(3,
"Local optimization: Changing queue member uniqueid from %s to %s\n",
6665 enum agent_complete_reason reason;
6667 ao2_lock(queue_data);
6669 if (queue_data->
dying) {
6670 ao2_unlock(queue_data);
6679 ao2_unlock(queue_data);
6690 ao2_unlock(queue_data);
6697 ao2_unlock(queue_data);
6699 ast_debug(3,
"Detected hangup of queue %s channel %s\n", reason == CALLER ?
"caller" :
"member",
6703 reason == CALLER ?
"COMPLETECALLER" :
"COMPLETEAGENT",
"%ld|%ld|%d",
6711 remove_stasis_subscriptions(queue_data);
6719 const char *new_channel_id;
6723 ao2_lock(queue_data);
6725 if (queue_data->
dying) {
6726 ao2_unlock(queue_data);
6731 ast_debug(1,
"Replacing caller channel %s with %s due to masquerade\n", queue_data->
caller_uniqueid, new_channel_id);
6734 ast_debug(1,
"Replacing member channel %s with %s due to masquerade\n", queue_data->
member_uniqueid, new_channel_id);
6738 ao2_unlock(queue_data);
6752 ao2_cleanup(userdata);
6773 static int setup_stasis_subs(
struct queue_ent *qe,
struct ast_channel *peer,
struct member *mem,
6774 time_t holdstart, time_t starttime,
int callcompletedinsl)
6776 struct queue_stasis_data *queue_data = queue_stasis_data_alloc(qe, peer, mem, holdstart, starttime, callcompletedinsl);
6789 handle_bridge_enter, queue_data);
6795 queue_bridge_cb, queue_data);
6809 handle_local_optimization_begin, queue_data);
6811 handle_local_optimization_end, queue_data);
6813 handle_hangup, queue_data);
6815 handle_masquerade, queue_data);
6817 queue_channel_cb, queue_data);
6831 qeb->chan = originator;
6834 static void end_bridge_callback(
void *data)
6843 queue_t_unref(q,
"Expire bridge_config reference");
6863 if (ast_test_flag(opts, OPT_CALLEE_GO_ON)) {
6864 ast_channel_lock(chan);
6867 priority = ast_channel_priority(chan);
6868 ast_channel_unlock(chan);
6870 opt_args[OPT_ARG_CALLEE_GO_ON]);
6874 static void escape_and_substitute(
struct ast_channel *chan,
const char *input,
6875 char *output,
size_t size)
6877 const char *m = input;
6881 for (p = escaped; p < escaped + size - 1; p++, m++) {
6884 if (*(m + 1) ==
'{') {
6898 if (p == escaped + size) {
6899 escaped[size - 1] =
'\0';
6902 pbx_substitute_variables_helper(chan, escaped, output, size - 1);
6905 static void setup_mixmonitor(
struct queue_ent *qe,
const char *filename)
6907 char escaped_filename[256];
6908 char file_with_ext[
sizeof(escaped_filename) +
sizeof(qe->
parent->
monfmt)];
6909 char mixmonargs[1512];
6910 char escaped_monitor_exec[1024];
6911 const char *monitor_options;
6912 const char *monitor_exec;
6914 escaped_monitor_exec[0] =
'\0';
6917 escape_and_substitute(qe->
chan, filename, escaped_filename,
sizeof(escaped_filename));
6919 ast_copy_string(escaped_filename, ast_channel_uniqueid(qe->
chan),
sizeof(escaped_filename));
6922 ast_channel_lock(qe->
chan);
6929 monitor_options =
"";
6931 ast_channel_unlock(qe->
chan);
6934 escape_and_substitute(qe->
chan, monitor_exec, escaped_monitor_exec,
sizeof(escaped_monitor_exec));
6937 snprintf(file_with_ext,
sizeof(file_with_ext),
"%s.%s", escaped_filename, qe->
parent->
monfmt);
6939 if (!ast_strlen_zero(escaped_monitor_exec)) {
6940 snprintf(mixmonargs,
sizeof(mixmonargs),
"b%s,%s", monitor_options, escaped_monitor_exec);
6942 snprintf(mixmonargs,
sizeof(mixmonargs),
"b%s", monitor_options);
6945 ast_debug(1,
"Arguments being passed to MixMonitor: %s,%s\n", file_with_ext, mixmonargs);
6948 ast_log(LOG_WARNING,
"Unable to start mixmonitor. Is the MixMonitor app loaded?\n");
6978 static int try_calling(
struct queue_ent *qe,
struct ast_flags opts,
char **opt_args,
char *announceoverride,
const char *url,
int *tries,
int *noption,
const char *agi,
const char *gosub,
int ringing)
6985 char queuename[256]=
"";
6988 struct member *member;
6990 int res = 0, bridge = 0;
6993 char *announce = NULL;
6995 time_t now = time(NULL);
6997 char nondataquality = 1;
6998 char *agiexec = NULL;
6999 char *gosubexec = NULL;
7000 const char *monitorfilename;
7001 int forwardsallowed = 1;
7002 int block_connected_line = 0;
7005 int callcompletedinsl;
7008 memset(&bridge_config, 0,
sizeof(bridge_config));
7020 if (ast_test_flag(&opts, OPT_CALLEE_TRANSFER)) {
7021 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_REDIRECT);
7023 if (ast_test_flag(&opts, OPT_CALLER_TRANSFER)) {
7024 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_REDIRECT);
7026 if (ast_test_flag(&opts, OPT_CALLEE_AUTOMON)) {
7027 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMON);
7029 if (ast_test_flag(&opts, OPT_CALLER_AUTOMON)) {
7030 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMON);
7032 if (ast_test_flag(&opts, OPT_DATA_QUALITY)) {
7035 if (ast_test_flag(&opts, OPT_CALLEE_HANGUP)) {
7036 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_DISCONNECT);
7038 if (ast_test_flag(&opts, OPT_CALLER_HANGUP)) {
7039 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT);
7041 if (ast_test_flag(&opts, OPT_CALLEE_PARK)) {
7042 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_PARKCALL);
7044 if (ast_test_flag(&opts, OPT_CALLER_PARK)) {
7045 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_PARKCALL);
7047 if (ast_test_flag(&opts, OPT_NO_RETRY)) {
7048 if (qe->
parent->strategy == QUEUE_STRATEGY_RRMEMORY || qe->
parent->strategy == QUEUE_STRATEGY_LINEAR
7049 || qe->
parent->strategy == QUEUE_STRATEGY_RRORDERED) {
7056 if (ast_test_flag(&opts, OPT_IGNORE_CALL_FW)) {
7057 forwardsallowed = 0;
7059 if (ast_test_flag(&opts, OPT_IGNORE_CONNECTEDLINE)) {
7060 block_connected_line = 1;
7062 if (ast_test_flag(&opts, OPT_CALLEE_AUTOMIXMON)) {
7063 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMIXMON);
7065 if (ast_test_flag(&opts, OPT_CALLER_AUTOMIXMON)) {
7066 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMIXMON);
7068 if (ast_test_flag(&opts, OPT_MARK_AS_ANSWERED)) {
7075 if (ast_channel_hangupcause(qe->
chan) == AST_CAUSE_ANSWERED_ELSEWHERE) {
7080 ast_debug(1,
"%s is trying to call a queue member.\n",
7081 ast_channel_name(qe->
chan));
7083 if (!ast_strlen_zero(qe->
announce)) {
7086 if (!ast_strlen_zero(announceoverride)) {
7087 announce = announceoverride;
7091 while ((cur = ao2_iterator_next(&memi))) {
7106 ast_channel_lock(qe->
chan);
7108 ast_channel_unlock(qe->
chan);
7119 tmp->q_next = outgoing;
7122 callattempt_free(tmp);
7130 to = (qe->
expire - now) * 1000;
7148 ring_one(qe, outgoing, &numbusies);
7150 ast_test_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT),
7154 if (qe->
parent->strategy == QUEUE_STRATEGY_RRMEMORY || qe->
parent->strategy == QUEUE_STRATEGY_RRORDERED) {
7158 if (qe->
parent->strategy == QUEUE_STRATEGY_LINEAR) {
7162 peer = lpeer ? lpeer->chan : NULL;
7173 ast_debug(1,
"%s: Nobody answered.\n", ast_channel_name(qe->
chan));
7189 recalc_holdtime(qe, (now - qe->
start));
7190 member = lpeer->member;
7204 ast_log(LOG_NOTICE,
"Delaying member connect for %d seconds\n", qe->
parent->
memberdelay);
7207 if (!res2 && announce) {
7212 ast_log(LOG_ERROR,
"play_file failed for '%s' on %s\n", front, ast_channel_name(peer));
7216 if (!res2 && qe->
parent->reportholdtime) {
7218 long holdtime, holdtimesecs;
7221 holdtime = labs((now - qe->
start) / 60);
7222 holdtimesecs = labs((now - qe->
start) % 60);
7224 ast_say_number(peer, holdtime, AST_DIGIT_ANY, ast_channel_language(peer),
"n");
7226 ast_log(LOG_ERROR,
"play_file failed for '%s' on %s\n", qe->
parent->
sound_minutes, ast_channel_name(peer));
7229 if (holdtimesecs > 1) {
7230 ast_say_number(peer, holdtimesecs, AST_DIGIT_ANY, ast_channel_language(peer),
"n");
7232 ast_log(LOG_ERROR,
"play_file failed for '%s' on %s\n", qe->
parent->
sound_seconds, ast_channel_name(peer));
7241 ast_log(LOG_WARNING,
"Agent on %s hungup on the customer.\n", ast_channel_name(peer));
7242 ast_queue_log(queuename, ast_channel_uniqueid(qe->
chan), member->
membername,
"AGENTDUMP",
"%s",
"");
7248 queue_publish_multi_channel_blob(qe->
chan, peer, queue_agent_dump_type(), blob);
7252 pending_members_remove(member);
7257 ast_log(LOG_NOTICE,
"Caller was about to talk to agent on %s but the caller hungup.\n", ast_channel_name(peer));
7258 ast_queue_log(queuename, ast_channel_uniqueid(qe->
chan), member->
membername,
"ABANDON",
"%d|%d|%ld", qe->
pos, qe->
opos, (
long) (time(NULL) - qe->
start));
7263 pending_members_remove(member);
7278 ast_queue_log(queuename, ast_channel_uniqueid(qe->
chan), member->
membername,
"SYSCOMPAT",
"%s",
"");
7279 ast_log(LOG_WARNING,
"Had to drop call because I couldn't make %s compatible with %s\n", ast_channel_name(qe->
chan), ast_channel_name(peer));
7283 pending_members_remove(member);
7298 if (qe->
parent->setinterfacevar && interfacevar) {
7299 ast_str_set(&interfacevar, 0,
"MEMBERINTERFACE=%s,MEMBERNAME=%s,MEMBERCALLS=%d,MEMBERLASTCALL=%ld,MEMBERPENALTY=%d,MEMBERDYNAMIC=%d,MEMBERREALTIME=%d",
7307 if (qe->
parent->setqueueentryvar && interfacevar) {
7308 ast_str_set(&interfacevar, 0,
"QEHOLDTIME=%ld,QEORIGINALPOS=%d",
7309 (
long) (time(NULL) - qe->
start), qe->
opos);
7320 setup_peer_after_bridge_goto(qe->
chan, peer, &opts, opt_args);
7321 ast_channel_lock(qe->
chan);
7325 ast_channel_unlock(qe->
chan);
7329 setup_mixmonitor(qe, monitorfilename);
7334 ast_debug(1,
"app_queue: sendurl=%s.\n", url);
7340 if (!ast_strlen_zero(gosub)) {
7348 if (!ast_strlen_zero(gosubexec)) {
7349 char *gosub_args = NULL;
7350 char *gosub_argstart;
7352 ast_debug(1,
"app_queue: gosub=%s.\n", gosubexec);
7354 gosub_argstart = strchr(gosubexec,
',');
7355 if (gosub_argstart) {
7356 const char *what_is_s =
"s";
7357 *gosub_argstart = 0;
7360 what_is_s =
"~~s~~";
7362 if (
ast_asprintf(&gosub_args,
"%s,%s,1(%s)", gosubexec, what_is_s, gosub_argstart + 1) < 0) {
7365 *gosub_argstart =
',';
7367 const char *what_is_s =
"s";
7370 what_is_s =
"~~s~~";
7372 if (
ast_asprintf(&gosub_args,
"%s,%s,1", gosubexec, what_is_s) < 0) {
7378 ast_free(gosub_args);
7380 ast_log(LOG_ERROR,
"Could not Allocate string for Gosub arguments -- Gosub Call Aborted!\n");
7384 if (!ast_strlen_zero(agi)) {
7385 ast_debug(1,
"app_queue: agi=%s.\n", agi);
7391 ast_log(LOG_WARNING,
"Asked to execute an AGI on this channel, but could not find application (agi)!\n");
7396 ast_queue_log(queuename, ast_channel_uniqueid(qe->
chan), member->
membername,
"CONNECT",
"%ld|%s|%ld", (
long) (time(NULL) - qe->
start), ast_channel_uniqueid(peer),
7397 (
long)(orig - to > 0 ? (orig - to) / 1000 : 0));
7404 "RingTime", (
ast_json_int_t)(orig - to > 0 ? (orig - to) / 1000 : 0));
7405 queue_publish_multi_channel_blob(qe->
chan, peer, queue_agent_connect_type(), blob);
7410 if ((queue_end_bridge = ao2_alloc(
sizeof(*queue_end_bridge), NULL))) {
7411 queue_end_bridge->q = qe->
parent;
7412 queue_end_bridge->chan = qe->
chan;
7413 bridge_config.end_bridge_callback = end_bridge_callback;
7414 bridge_config.end_bridge_callback_data = queue_end_bridge;
7415 bridge_config.end_bridge_callback_data_fixup = end_bridge_callback_data_fixup;
7420 queue_t_ref(qe->
parent,
"For bridge_config reference");
7432 setup_stasis_subs(qe, peer, member, qe->
start, starttime, callcompletedinsl);
7436 res = bridge ? bridge : 1;
7445 static int wait_a_bit(
struct queue_ent *qe)
7467 while ((mem = ao2_iterator_next(&mem_iter))) {
7468 if (!strcasecmp(interface, mem->
interface)) {
7487 struct member *cur_member;
7502 while ((cur_member = ao2_iterator_next(&mem_iter))) {
7524 ast_log(LOG_WARNING,
"failed to create persistent dynamic entry!\n");
7545 struct member *mem, tmpmem;
7555 update_realtime_member_field(mem, q->
name,
"penalty",
"-1");
7559 queue_t_unref(q,
"Interface wasn't dynamic, expiring temporary reference");
7562 queue_publish_member_blob(queue_member_removed_type(), queue_member_blob_create(q, mem));
7564 member_remove_from_queue(q, mem);
7567 if (queue_persistent_members) {
7580 queue_t_unref(q,
"Expiring temporary reference");
7596 struct member *new_member, *old_member;
7606 if ((old_member = interface_exists(q, interface)) == NULL) {
7607 if ((new_member =
create_queue_member(interface, membername, penalty, paused, state_interface, q->ringinuse, wrapuptime))) {
7609 if (reason_paused) {
7612 member_add_to_queue(q, new_member);
7613 queue_publish_member_blob(queue_member_added_type(), queue_member_blob_create(q, new_member));
7615 if (is_member_available(q, new_member)) {
7635 queue_t_unref(q,
"Expiring temporary reference");
7649 struct queue_ent *current, *prev = NULL, *caller_qe = NULL;
7660 for (current = q->
head; current; current = current->
next) {
7661 if (strcmp(ast_channel_name(current->
chan), caller) == 0) {
7662 ast_debug(1,
"%s Caller new priority %d in queue %s\n",
7663 caller, priority, queuename);
7664 current->
prio = priority;
7672 caller_qe = current;
7679 }
else if (immediate) {
7685 int inserted = 0, pos = 0;
7693 if (!inserted && (caller_qe->prio > current->
prio)) {
7703 current = current->
next;
7736 for (qe = q->
head; qe; qe = qe->
next) {
7737 if (!strcmp(ast_channel_name(qe->
chan), caller)) {
7739 ast_debug(1,
"Ignoring duplicate withdraw request of caller %s from queue %s\n", caller, queuename);
7742 ast_debug(1,
"Requested withdraw of caller %s from queue %s\n", caller, queuename);
7745 if (withdraw_info) {
7761 static int publish_queue_member_pause(
struct call_queue *q,
struct member *member)
7763 struct ast_json *json_blob = queue_member_blob_create(q, member);
7769 queue_publish_member_blob(queue_member_pause_type(), json_blob);
7785 static void set_queue_member_pause(
struct call_queue *q,
struct member *mem,
const char *reason,
int paused)
7787 if (mem->
paused == paused) {
7788 ast_debug(1,
"%spausing already-%spaused queue member %s:%s\n",
7789 (paused ?
"" :
"un"), (paused ?
"" :
"un"), q->
name, mem->
interface);
7793 if (realtime_reason_paused) {
7795 ast_log(LOG_WARNING,
"Failed update of realtime queue member %s:%s %spause and reason '%s'\n",
7800 ast_log(LOG_WARNING,
"Failed %spause update of realtime queue member %s:%s\n",
7810 if (paused && !ast_strlen_zero(reason)) {
7822 if (queue_persistent_members) {
7826 if (is_member_available(q, mem)) {
7828 "Queue:%s_avail", q->
name);
7831 "Queue:%s_avail", q->
name);
7834 if (!paused && !ast_strlen_zero(reason)) {
7842 ast_queue_log(q->
name,
"NONE", mem->
membername, paused ?
"PAUSE" :
"UNPAUSE",
7845 publish_queue_member_pause(q, mem);
7852 static int set_member_paused(
const char *queuename,
const char *interface,
const char *reason,
int paused)
7859 load_realtime_queues(queuename);
7863 while ((q = ao2_t_iterator_next(&queue_iter,
"Iterate over queues"))) {
7865 if (ast_strlen_zero(queuename) || !strcasecmp(q->
name, queuename)) {
7868 if ((mem = interface_exists(q, interface))) {
7875 && ast_strlen_zero(queuename)) {
7880 ast_queue_log(
"NONE",
"NONE", mem->
membername,
7881 (paused ?
"PAUSEALL" :
"UNPAUSEALL"),
"%s",
S_OR(reason,
""));
7884 set_queue_member_pause(q, mem, reason, paused);
7888 if (!ast_strlen_zero(queuename)) {
7890 queue_t_unref(q,
"Done with iterator");
7896 queue_t_unref(q,
"Done with iterator");
7900 return found ? RESULT_SUCCESS : RESULT_FAILURE;
7912 static int set_member_penalty_help_members(
struct call_queue *q,
const char *interface,
int penalty)
7915 int foundinterface = 0;
7918 if ((mem = interface_exists(q, interface))) {
7923 sprintf(rtpenalty,
"%i", penalty);
7924 update_realtime_member_field(mem, q->
name,
"penalty", rtpenalty);
7929 ast_queue_log(q->
name,
"NONE", interface,
"PENALTY",
"%d", penalty);
7930 queue_publish_member_blob(queue_member_penalty_type(), queue_member_blob_create(q, mem));
7935 return foundinterface;
7948 static void set_queue_member_ringinuse(
struct call_queue *q,
struct member *mem,
int ringinuse)
7951 update_realtime_member_field(mem, q->
name, realtime_ringinuse_field,
7952 ringinuse ?
"1" :
"0");
7957 ast_queue_log(q->
name,
"NONE", mem->
interface,
"RINGINUSE",
"%d", ringinuse);
7958 queue_publish_member_blob(queue_member_ringinuse_type(), queue_member_blob_create(q, mem));
7961 static int set_member_ringinuse_help_members(
struct call_queue *q,
const char *interface,
int ringinuse)
7964 int foundinterface = 0;
7967 if ((mem = interface_exists(q, interface))) {
7969 set_queue_member_ringinuse(q, mem, ringinuse);
7974 return foundinterface;
7977 static int set_member_value_help_members(
struct call_queue *q,
const char *interface,
int property,
int value)
7980 case MEMBER_PENALTY:
7981 return set_member_penalty_help_members(q, interface, value);
7983 case MEMBER_RINGINUSE:
7984 return set_member_ringinuse_help_members(q, interface, value);
7987 ast_log(LOG_ERROR,
"Attempted to set invalid property\n");
8000 static int set_member_value(
const char *queuename,
const char *interface,
int property,
int value)
8002 int foundinterface = 0, foundqueue = 0;
8009 case MEMBER_PENALTY:
8010 if (value < 0 && !negative_penalty_invalid) {
8011 ast_log(LOG_ERROR,
"Invalid penalty (%d)\n", value);
8012 return RESULT_FAILURE;
8016 if (ast_strlen_zero(queuename)) {
8020 char *category = NULL;
8022 const char *name = ast_variable_retrieve(queue_config, category,
"name");
8023 if (ast_strlen_zero(name)) {
8024 ast_log(LOG_WARNING,
"Ignoring realtime queue with a NULL or empty 'name.'\n");
8029 foundinterface += set_member_value_help_members(q, interface, property, value);
8040 while ((q = ao2_t_iterator_next(&queue_iter,
"Iterate through queues"))) {
8042 foundinterface += set_member_value_help_members(q, interface, property, value);
8049 foundinterface += set_member_value_help_members(q, interface, property, value);
8054 if (foundinterface) {
8055 return RESULT_SUCCESS;
8056 }
else if (!foundqueue) {
8057 ast_log (LOG_ERROR,
"Invalid queuename\n");
8059 ast_log (LOG_ERROR,
"Invalid interface\n");
8062 return RESULT_FAILURE;
8071 int foundqueue = 0, penalty;
8078 if ((mem = interface_exists(q, interface))) {
8082 queue_t_unref(q,
"Search complete");
8086 queue_t_unref(q,
"Search complete");
8091 ast_log (LOG_ERROR,
"Invalid queuename\n");
8093 ast_log (LOG_ERROR,
"Invalid interface\n");
8096 return RESULT_FAILURE;
8103 const char *queue_name;
8112 char *wrapuptime_tok;
8122 for (entry = db_tree; entry; entry = entry->next) {
8124 queue_name = entry->key + strlen(pm_family) + 2;
8140 ast_log(LOG_WARNING,
"Error loading persistent queue: '%s': it does not exist\n", queue_name);
8146 queue_t_unref(cur_queue,
"Expire reload reference");
8150 cur_ptr = queue_data;
8151 while ((member = strsep(&cur_ptr,
",|"))) {
8152 if (ast_strlen_zero(member)) {
8156 interface = strsep(&member,
";");
8157 penalty_tok = strsep(&member,
";");
8158 paused_tok = strsep(&member,
";");
8159 membername = strsep(&member,
";");
8160 state_interface = strsep(&member,
";");
8161 reason_paused = strsep(&member,
";");
8162 wrapuptime_tok = strsep(&member,
";");
8165 ast_log(LOG_WARNING,
"Error parsing persistent member string for '%s' (penalty)\n", queue_name);
8168 penalty = strtol(penalty_tok, NULL, 10);
8169 if (errno == ERANGE) {
8170 ast_log(LOG_WARNING,
"Error converting penalty: %s: Out of range.\n", penalty_tok);
8175 ast_log(LOG_WARNING,
"Error parsing persistent member string for '%s' (paused)\n", queue_name);
8178 paused = strtol(paused_tok, NULL, 10);
8179 if ((errno == ERANGE) || paused < 0 || paused > 1) {
8180 ast_log(LOG_WARNING,
"Error converting paused: %s: Expected 0 or 1.\n", paused_tok);
8184 if (!ast_strlen_zero(wrapuptime_tok)) {
8185 wrapuptime = strtol(wrapuptime_tok, NULL, 10);
8186 if (errno == ERANGE) {
8187 ast_log(LOG_WARNING,
"Error converting wrapuptime: %s: Out of range.\n", wrapuptime_tok);
8192 ast_debug(1,
"Reload Members: Queue: %s Member: %s Name: %s Penalty: %d Paused: %d ReasonPause: %s Wrapuptime: %d\n",
8193 queue_name, interface, membername, penalty, paused, reason_paused, wrapuptime);
8195 if (
add_to_queue(queue_name, interface, membername, penalty, paused, 0, state_interface, reason_paused, wrapuptime) ==
RES_OUTOFMEMORY) {
8196 ast_log(LOG_ERROR,
"Out of Memory when reloading persistent queue member\n");
8200 queue_t_unref(cur_queue,
"Expire reload reference");
8201 ast_free(queue_data);
8205 ast_log(LOG_NOTICE,
"Queue members successfully reloaded from database.\n");
8221 if (ast_strlen_zero(data)) {
8222 ast_log(LOG_WARNING,
"PauseQueueMember requires an argument ([queuename],interface[,options][,reason])\n");
8230 if (ast_strlen_zero(args.interface)) {
8231 ast_log(LOG_WARNING,
"Missing interface argument to PauseQueueMember ([queuename],interface[,options[,reason]])\n");
8235 if (set_member_paused(args.queuename, args.interface, args.reason, 1)) {
8236 ast_log(LOG_WARNING,
"Attempt to pause interface %s, not found\n", args.interface);
8257 if (ast_strlen_zero(data)) {
8258 ast_log(LOG_WARNING,
"UnpauseQueueMember requires an argument ([queuename],interface[,options[,reason]])\n");
8266 if (ast_strlen_zero(args.interface)) {
8267 ast_log(LOG_WARNING,
"Missing interface argument to UnpauseQueueMember ([queuename],interface[,options[,reason]])\n");
8271 if (set_member_paused(args.queuename, args.interface, args.reason, 0)) {
8272 ast_log(LOG_WARNING,
"Attempt to unpause interface %s, not found\n", args.interface);
8286 char *parse, *temppos = NULL;
8287 struct member *mem = NULL;
8295 if (ast_strlen_zero(data)) {
8296 ast_log(LOG_WARNING,
"RemoveQueueMember requires an argument (queuename[,interface])\n");
8304 if (ast_strlen_zero(args.interface)) {
8305 args.interface =
ast_strdupa(ast_channel_name(chan));
8306 temppos = strrchr(args.interface,
'-');
8312 ast_debug(1,
"queue: %s, member: %s\n", args.queuename, args.interface);
8314 if (log_membername_as_agent) {
8320 if (!mem || ast_strlen_zero(mem->
membername)) {
8321 ast_queue_log(args.queuename, ast_channel_uniqueid(chan), args.interface,
"REMOVEMEMBER",
"%s",
"");
8323 ast_queue_log(args.queuename, ast_channel_uniqueid(chan), mem->
membername,
"REMOVEMEMBER",
"%s",
"");
8325 ast_log(LOG_NOTICE,
"Removed interface '%s' from queue '%s'\n", args.interface, args.queuename);
8330 ast_debug(1,
"Unable to remove interface '%s' from queue '%s': Not there\n", args.interface, args.queuename);
8335 ast_log(LOG_WARNING,
"Unable to remove interface from queue '%s': No such queue\n", args.queuename);
8340 ast_log(LOG_WARNING,
"Unable to remove interface from queue '%s': '%s' is not a dynamic member\n", args.queuename, args.interface);
8357 char *parse, *tmp, *temppos = NULL;
8370 if (ast_strlen_zero(data)) {
8371 ast_log(LOG_WARNING,
"AddQueueMember requires an argument (queuename[,interface[,penalty[,options[,membername[,stateinterface][,wrapuptime]]]]])\n");
8379 if (ast_strlen_zero(args.interface)) {
8380 args.interface =
ast_strdupa(ast_channel_name(chan));
8381 temppos = strrchr(args.interface,
'-');
8387 if (!ast_strlen_zero(args.penalty)) {
8388 if ((sscanf(args.penalty,
"%30d", &penalty) != 1) || penalty < 0) {
8389 ast_log(LOG_WARNING,
"Penalty '%s' is invalid, must be an integer >= 0\n", args.penalty);
8394 if (!ast_strlen_zero(args.wrapuptime)) {
8395 tmp = args.wrapuptime;
8397 wrapuptime = atoi(tmp);
8398 if (wrapuptime < 0) {
8405 switch (
add_to_queue(args.queuename, args.interface, args.membername, penalty, 0, queue_persistent_members, args.state_interface, NULL, wrapuptime)) {
8407 if (ast_strlen_zero(args.membername) || !log_membername_as_agent) {
8408 ast_queue_log(args.queuename, ast_channel_uniqueid(chan), args.interface,
"ADDMEMBER",
"%s",
"");
8410 ast_queue_log(args.queuename, ast_channel_uniqueid(chan), args.membername,
"ADDMEMBER",
"%s",
"");
8412 ast_log(LOG_NOTICE,
"Added interface '%s' to queue '%s'\n", args.interface, args.queuename);
8417 ast_log(LOG_WARNING,
"Unable to add interface '%s' to queue '%s': Already there\n", args.interface, args.queuename);
8422 ast_log(LOG_WARNING,
"Unable to add interface to queue '%s': No such queue\n", args.queuename);
8427 ast_log(LOG_ERROR,
"Out of memory adding interface %s to queue %s\n", args.interface, args.queuename);
8447 if (ast_strlen_zero(data)) {
8448 ast_log(LOG_WARNING,
"QueueLog requires arguments (queuename,uniqueid,membername,event[,additionalinfo]\n");
8456 if (ast_strlen_zero(args.queuename) || ast_strlen_zero(args.uniqueid)
8457 || ast_strlen_zero(args.membername) || ast_strlen_zero(args.event)) {
8458 ast_log(LOG_WARNING,
"QueueLog requires arguments (queuename,uniqueid,membername,event[,additionalinfo])\n");
8462 ast_queue_log(args.queuename, args.uniqueid, args.membername, args.event,
8463 "%s", args.params ? args.params :
"");
8473 const char *tmp = ast_strlen_zero(rulename) ? qe->
parent->
defaultrule : rulename;
8476 if (!strcasecmp(rl_iter->name, tmp)) {
8484 ast_log(LOG_ERROR,
"Memory allocation error when copying penalty rules! Aborting!\n");
8516 const char *user_priority;
8517 const char *max_penalty_str;
8518 const char *min_penalty_str;
8519 const char *raise_penalty_str;
8523 enum queue_result reason = QUEUE_UNKNOWN;
8528 int makeannouncement = 0;
8544 char *opt_args[OPT_ARG_ARRAY_SIZE];
8548 if (ast_strlen_zero(data)) {
8549 ast_log(LOG_WARNING,
"Queue requires an argument: queuename[,options[,URL[,announceoverride[,timeout[,agi[,gosub[,rule[,position]]]]]]]]\n");
8553 ast_channel_lock(chan);
8554 max_forwards = ast_max_forwards_get(chan);
8555 ast_channel_unlock(chan);
8557 if (max_forwards <= 0) {
8558 ast_log(LOG_WARNING,
"Channel '%s' cannot enter queue. Max forwards exceeded\n", ast_channel_name(chan));
8565 ast_debug(1,
"queue: %s, options: %s, url: %s, announce: %s, timeout: %s, agi: %s, gosub: %s, rule: %s, position: %s\n",
8567 S_OR(args.options,
""),
8569 S_OR(args.announceoverride,
""),
8570 S_OR(args.queuetimeoutstr,
""),
8572 S_OR(args.gosub,
""),
8573 S_OR(args.rule,
""),
8574 S_OR(args.position,
""));
8576 if (!ast_strlen_zero(args.options)) {
8581 qe.
start = time(NULL);
8586 if (!ast_strlen_zero(args.queuetimeoutstr)) {
8587 qe.
expire = qe.
start + atoi(args.queuetimeoutstr);
8593 ast_channel_lock(chan);
8595 if (user_priority) {
8596 if (sscanf(user_priority,
"%30d", &prio) == 1) {
8597 ast_debug(1,
"%s: Got priority %d from ${QUEUE_PRIO}.\n", ast_channel_name(chan), prio);
8599 ast_log(LOG_WARNING,
"${QUEUE_PRIO}: Invalid value (%s), channel %s.\n",
8600 user_priority, ast_channel_name(chan));
8604 ast_debug(3,
"NO QUEUE_PRIO variable found. Using default.\n");
8611 if (sscanf(max_penalty_str,
"%30d", &max_penalty) == 1) {
8612 ast_debug(1,
"%s: Got max penalty %d from ${QUEUE_MAX_PENALTY}.\n", ast_channel_name(chan), max_penalty);
8614 ast_log(LOG_WARNING,
"${QUEUE_MAX_PENALTY}: Invalid value (%s), channel %s.\n",
8615 max_penalty_str, ast_channel_name(chan));
8616 max_penalty = INT_MAX;
8619 max_penalty = INT_MAX;
8623 if (sscanf(min_penalty_str,
"%30d", &min_penalty) == 1) {
8624 ast_debug(1,
"%s: Got min penalty %d from ${QUEUE_MIN_PENALTY}.\n", ast_channel_name(chan), min_penalty);
8626 ast_log(LOG_WARNING,
"${QUEUE_MIN_PENALTY}: Invalid value (%s), channel %s.\n",
8627 min_penalty_str, ast_channel_name(chan));
8628 min_penalty = INT_MAX;
8631 min_penalty = INT_MAX;
8635 if (sscanf(raise_penalty_str,
"%30d", &raise_penalty) == 1) {
8636 ast_debug(1,
"%s: Got raise penalty %d from ${QUEUE_RAISE_PENALTY}.\n", ast_channel_name(chan), raise_penalty);
8638 ast_log(LOG_WARNING,
"${QUEUE_RAISE_PENALTY}: Invalid value (%s), channel %s.\n",
8639 raise_penalty_str, ast_channel_name(chan));
8640 raise_penalty = INT_MAX;
8643 raise_penalty = INT_MAX;
8645 ast_channel_unlock(chan);
8647 if (ast_test_flag(&opts, OPT_RINGING)) {
8651 if (ringing != 1 && ast_test_flag(&opts, OPT_RING_WHEN_RINGING)) {
8655 if (ast_test_flag(&opts, OPT_GO_ON)) {
8659 if (args.position) {
8660 position = atoi(args.position);
8662 ast_log(LOG_WARNING,
"Invalid position '%s' given for call to queue '%s'. Assuming no preference for position\n", args.position, args.queuename);
8667 ast_debug(1,
"queue: %s, expires: %ld, priority: %d\n",
8668 args.queuename, (
long)qe.
expire, prio);
8680 if (join_queue(args.queuename, &qe, &reason, position)) {
8681 ast_log(LOG_WARNING,
"Unable to join queue '%s'\n", args.queuename);
8685 ast_assert(qe.
parent != NULL);
8694 ast_queue_log(args.queuename, ast_channel_uniqueid(chan),
"NONE",
"ENTERQUEUE",
"%s|%s|%d",
8696 S_COR(cid_allow && ast_channel_caller(chan)->
id.
number.valid, ast_channel_caller(chan)->
id.
number.str,
""),
8700 if (ast_test_flag(&opts, OPT_PREDIAL_CALLEE)
8701 && !ast_strlen_zero(opt_args[OPT_ARG_PREDIAL_CALLEE])) {
8707 if (ast_test_flag(&opts, OPT_PREDIAL_CALLER)
8708 && !ast_strlen_zero(opt_args[OPT_ARG_PREDIAL_CALLER])) {
8714 if (ast_test_flag(&opts, OPT_MUSICONHOLD_CLASS)
8715 && !ast_strlen_zero(opt_args[OPT_ARG_MUSICONHOLD_CLASS])) {
8744 reason = QUEUE_WITHDRAW;
8752 reason = QUEUE_TIMEOUT;
8754 ast_queue_log(args.queuename, ast_channel_uniqueid(chan),
"NONE",
"EXITWITHTIMEOUT",
"%d|%d|%ld",
8759 if (makeannouncement) {
8762 if ((res = say_position(&qe, ringing))) {
8767 makeannouncement = 1;
8778 reason = QUEUE_WITHDRAW;
8786 reason = QUEUE_TIMEOUT;
8788 ast_queue_log(args.queuename, ast_channel_uniqueid(chan),
"NONE",
"EXITWITHTIMEOUT",
8789 "%d|%d|%ld", qe.
pos, qe.
opos, (
long) (time(NULL) - qe.
start));
8799 res = try_calling(&qe, opts, opt_args, args.announceoverride, args.url, &tries, &noption, args.agi, args.gosub, ringing);
8804 if (qe.
parent->leavewhenempty) {
8808 reason = QUEUE_LEAVEEMPTY;
8809 ast_queue_log(args.queuename, ast_channel_uniqueid(chan),
"NONE",
"EXITEMPTY",
"%d|%d|%ld", qe.
pos, qe.
opos, (
long)(time(NULL) - qe.
start));
8817 ast_verb(3,
"Exiting on time-out cycle\n");
8818 ast_queue_log(args.queuename, ast_channel_uniqueid(chan),
"NONE",
"EXITWITHTIMEOUT",
8819 "%d|%d|%ld", qe.
pos, qe.
opos, (
long) (time(NULL) - qe.
start));
8821 reason = QUEUE_TIMEOUT;
8830 reason = QUEUE_TIMEOUT;
8832 ast_queue_log(qe.
parent->
name, ast_channel_uniqueid(qe.
chan),
"NONE",
"EXITWITHTIMEOUT",
"%d|%d|%ld", qe.
pos, qe.
opos, (
long) (time(NULL) - qe.
start));
8837 res = wait_a_bit(&qe);
8843 update_realtime_members(qe.
parent);
8850 ast_debug(1,
"Darn priorities, going back in queue (%s)!\n", ast_channel_name(qe.
chan));
8857 if (reason == QUEUE_WITHDRAW) {
8864 }
else if (res < 0) {
8867 ast_queue_log(args.queuename, ast_channel_uniqueid(chan),
"NONE",
"ABANDON",
8869 (
long) (time(NULL) - qe.
start));
8871 }
else if (reason == QUEUE_LEAVEEMPTY) {
8874 }
else if (qcontinue) {
8875 reason = QUEUE_CONTINUE;
8879 ast_queue_log(args.queuename, ast_channel_uniqueid(chan),
"NONE",
"EXITWITHKEY",
8905 if (reason != QUEUE_UNKNOWN)
8928 char interfacevar[256] =
"";
8931 if (ast_strlen_zero(data)) {
8932 ast_log(LOG_ERROR,
"%s requires an argument: queuename\n", cmd);
8938 if (q->setqueuevar) {
8946 snprintf(interfacevar,
sizeof(interfacevar),
8947 "QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUETALKTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f",
8954 queue_t_unref(q,
"Done with QUEUE() function");
8956 ast_log(LOG_WARNING,
"queue %s was not found\n", data);
8959 snprintf(buf, len,
"%d", res);
8974 if (ast_strlen_zero(data)) {
8975 ast_log(LOG_ERROR,
"%s requires an argument: queuename\n", cmd);
8979 snprintf(buf, len,
"%d", q != NULL? 1 : 0);
8981 queue_t_unref(q,
"Done with temporary reference in QUEUE_EXISTS()");
8987 static struct member *get_interface_helper(
struct call_queue *q,
const char *interface)
8991 if (ast_strlen_zero(interface)) {
8992 ast_log(LOG_ERROR,
"QUEUE_MEMBER: Missing required interface argument.\n");
8996 m = interface_exists(q, interface);
8998 ast_log(LOG_ERROR,
"Queue member interface '%s' not in queue '%s'.\n",
8999 interface, q->
name);
9025 if (ast_strlen_zero(data)) {
9027 "Missing required argument. %s(<queuename>,<option>[,<interface>])\n",
9034 if (ast_strlen_zero(args.queuename) || ast_strlen_zero(args.option)) {
9036 "Missing required argument. %s(<queuename>,<option>[,<interface>])\n",
9043 if (!strcasecmp(args.option,
"logged")) {
9045 while ((m = ao2_iterator_next(&mem_iter))) {
9053 }
else if (!strcasecmp(args.option,
"free")) {
9055 while ((m = ao2_iterator_next(&mem_iter))) {
9063 }
else if (!strcasecmp(args.option,
"ready")) {
9067 while ((m = ao2_iterator_next(&mem_iter))) {
9076 }
else if (!strcasecmp(args.option,
"count")) {
9078 }
else if (!strcasecmp(args.option,
"penalty")) {
9079 m = get_interface_helper(q, args.interface);
9084 }
else if (!strcasecmp(args.option,
"paused")) {
9085 m = get_interface_helper(q, args.interface);
9090 }
else if ((!strcasecmp(args.option,
"ignorebusy")
9091 || !strcasecmp(args.option,
"ringinuse"))) {
9092 m = get_interface_helper(q, args.interface);
9098 ast_log(LOG_ERROR,
"%s: Invalid option '%s' provided.\n", cmd, args.option);
9101 queue_t_unref(q,
"Done with temporary reference in QUEUE_MEMBER()");
9103 ast_log(LOG_WARNING,
"queue %s was not found\n", args.queuename);
9106 snprintf(buf, len,
"%d", count);
9122 if (ast_strlen_zero(data)) {
9124 "Missing required argument. %s([<queuename>],<option>,<interface>)\n",
9131 if (ast_strlen_zero(args.option)
9132 || ast_strlen_zero(args.interface)) {
9134 "Missing required argument. %s([<queuename>],<option>,<interface>)\n",
9144 memvalue = atoi(value);
9145 if (!strcasecmp(args.option,
"penalty")) {
9146 if (set_member_value(args.queuename, args.interface, MEMBER_PENALTY, memvalue)) {
9147 ast_log(LOG_ERROR,
"Invalid interface, queue, or penalty\n");
9150 }
else if (!strcasecmp(args.option,
"paused")) {
9151 memvalue = (memvalue <= 0) ? 0 : 1;
9152 if (set_member_paused(args.queuename, args.interface, NULL, memvalue)) {
9153 ast_log(LOG_ERROR,
"Invalid interface or queue\n");
9156 }
else if (!strcasecmp(args.option,
"ignorebusy")
9157 || !strcasecmp(args.option,
"ringinuse")) {
9158 memvalue = (memvalue <= 0) ? 0 : 1;
9159 if (set_member_value(args.queuename, args.interface, MEMBER_RINGINUSE, memvalue)) {
9160 ast_log(LOG_ERROR,
"Invalid interface or queue\n");
9164 ast_log(LOG_ERROR,
"%s: Invalid option '%s' provided.\n", cmd, args.option);
9181 static int depflag = 1;
9185 ast_log(LOG_NOTICE,
"The function QUEUE_MEMBER_COUNT has been deprecated in favor of the QUEUE_MEMBER function and will not be in further releases.\n");
9188 if (ast_strlen_zero(data)) {
9189 ast_log(LOG_ERROR,
"%s requires an argument: queuename\n", cmd);
9196 while ((m = ao2_iterator_next(&mem_iter))) {
9205 queue_t_unref(q,
"Done with temporary reference in QUEUE_MEMBER_COUNT");
9207 ast_log(LOG_WARNING,
"queue %s was not found\n", data);
9210 snprintf(buf, len,
"%d", count);
9230 if (ast_strlen_zero(data)) {
9231 ast_log(LOG_ERROR,
"Missing argument. QUEUE_GET_CHANNEL(<queuename>,<position>)\n");
9238 if (ast_strlen_zero(args.queuename)) {
9239 ast_log (LOG_ERROR,
"The <queuename> parameter is required.\n");
9243 if (ast_strlen_zero(args.position)) {
9246 if (sscanf(args.position,
"%30d", &position) != 1) {
9247 ast_log (LOG_ERROR,
"<position> parameter must be an integer.\n");
9251 ast_log (LOG_ERROR,
"<position> parameter must be an integer greater than zero.\n");
9258 .
name = args.queuename,
9265 if (q->
count >= position) {
9268 for (qe = q->
head; qe; qe = qe->
next) {
9269 if (qe->
pos == position) {
9276 queue_t_unref(q,
"Done with reference in QUEUE_GET_CHANNEL()");
9280 var = ast_load_realtime(
"queues",
"name", args.queuename, SENTINEL);
9290 ast_log(LOG_WARNING,
"queue %s was not found\n", args.queuename);
9305 if (ast_strlen_zero(data)) {
9306 ast_log(LOG_ERROR,
"QUEUE_WAITING_COUNT requires an argument: queuename\n");
9314 queue_t_unref(q,
"Done with reference in QUEUE_WAITING_COUNT()");
9315 }
else if ((var = ast_load_realtime(
"queues",
"name", data, SENTINEL))) {
9323 ast_log(LOG_WARNING,
"queue %s was not found\n", data);
9326 snprintf(buf, len,
"%d", count);
9340 if (ast_strlen_zero(data)) {
9341 ast_log(LOG_ERROR,
"QUEUE_MEMBER_LIST requires an argument: queuename\n");
9346 int buflen = 0, count = 0;
9351 while ((m = ao2_iterator_next(&mem_iter))) {
9354 strncat(buf + buflen,
",", len - buflen - 1);
9357 strncat(buf + buflen, m->
interface, len - buflen - 1);
9360 if (buflen >= len - 2) {
9362 ast_log(LOG_WARNING,
"Truncating list\n");
9369 queue_t_unref(q,
"Done with QUEUE_MEMBER_LIST()");
9371 ast_log(LOG_WARNING,
"queue %s was not found\n", data);
9374 buf[len - 1] =
'\0';
9390 if (ast_strlen_zero(data)) {
9391 ast_log(LOG_ERROR,
"Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
9397 if (args.argc < 2) {
9398 ast_log(LOG_ERROR,
"Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
9405 snprintf (buf, len,
"%d", penalty);
9420 if (ast_strlen_zero(data)) {
9421 ast_log(LOG_ERROR,
"Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
9427 if (args.argc < 2) {
9428 ast_log(LOG_ERROR,
"Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
9432 penalty = atoi(value);
9434 if (ast_strlen_zero(args.interface)) {
9435 ast_log (LOG_ERROR,
"<interface> parameter can't be null\n");
9440 if (set_member_value(args.queuename, args.interface, MEMBER_PENALTY, penalty)) {
9441 ast_log(LOG_ERROR,
"Invalid interface, queue or penalty\n");
9449 .
name =
"QUEUE_EXISTS",
9454 .
name =
"QUEUE_VARIABLES",
9459 .
name =
"QUEUE_MEMBER",
9465 .
name =
"QUEUE_MEMBER_COUNT",
9470 .
name =
"QUEUE_GET_CHANNEL",
9475 .
name =
"QUEUE_WAITING_COUNT",
9480 .
name =
"QUEUE_MEMBER_LIST",
9485 .
name =
"QUEUE_MEMBER_PENALTY",
9499 const char *general_val = NULL;
9500 if ((general_val = ast_variable_retrieve(cfg,
"general",
"realtime_rules"))) {
9501 realtime_rules =
ast_true(general_val);
9516 char *rulecat = NULL;
9521 ast_log(LOG_NOTICE,
"No queuerules.conf file found, queues will not follow penalty rules\n");
9523 }
else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
9524 ast_log(LOG_NOTICE,
"queuerules.conf has not changed since it was last loaded. Not taking any action.\n");
9526 }
else if (cfg == CONFIG_STATUS_FILEINVALID) {
9527 ast_log(LOG_ERROR,
"Config file queuerules.conf is in an invalid format. Aborting.\n");
9539 if (!strcasecmp(rulecat,
"general")) {
9543 if (!(new_rl =
ast_calloc(1,
sizeof(*new_rl)))) {
9550 for (rulevar = ast_variable_browse(cfg, rulecat); rulevar; rulevar = rulevar->
next)
9551 if(!strcasecmp(rulevar->
name,
"penaltychange"))
9554 ast_log(LOG_WARNING,
"Don't know how to handle rule type '%s' on line %d\n", rulevar->
name, rulevar->lineno);
9572 queue_persistent_members = 0;
9573 autofill_default = 0;
9574 montype_default = 0;
9575 shared_lastcall = 0;
9576 negative_penalty_invalid = 0;
9577 log_membername_as_agent = 0;
9578 force_longest_waiting_caller = 0;
9584 const char *general_val = NULL;
9585 if ((general_val = ast_variable_retrieve(cfg,
"general",
"persistentmembers"))) {
9586 queue_persistent_members =
ast_true(general_val);
9588 if ((general_val = ast_variable_retrieve(cfg,
"general",
"autofill"))) {
9589 autofill_default =
ast_true(general_val);
9591 if ((general_val = ast_variable_retrieve(cfg,
"general",
"monitor-type"))) {
9592 if (!strcasecmp(general_val,
"mixmonitor"))
9593 montype_default = 1;
9595 if ((general_val = ast_variable_retrieve(cfg,
"general",
"shared_lastcall"))) {
9596 shared_lastcall =
ast_true(general_val);
9598 if ((general_val = ast_variable_retrieve(cfg,
"general",
"negative_penalty_invalid"))) {
9599 negative_penalty_invalid =
ast_true(general_val);
9601 if ((general_val = ast_variable_retrieve(cfg,
"general",
"log_membername_as_agent"))) {
9602 log_membername_as_agent =
ast_true(general_val);
9604 if ((general_val = ast_variable_retrieve(cfg,
"general",
"force_longest_waiting_caller"))) {
9605 force_longest_waiting_caller =
ast_true(general_val);
9619 char *membername, *interface, *state_interface, *tmp;
9621 struct member *cur, *newm;
9622 struct member tmpmem;
9635 if (ast_strlen_zero(memberdata)) {
9636 ast_log(LOG_WARNING,
"Empty queue member definition. Moving on!\n");
9645 interface = args.interface;
9646 if (!ast_strlen_zero(args.penalty)) {
9649 penalty = atoi(tmp);
9657 if (!ast_strlen_zero(args.membername)) {
9658 membername = args.membername;
9661 membername = interface;
9664 if (!ast_strlen_zero(args.state_interface)) {
9665 state_interface = args.state_interface;
9668 state_interface = interface;
9671 if (!ast_strlen_zero(args.ringinuse)) {
9672 tmp = args.ringinuse;
9679 ast_log(LOG_ERROR,
"Member %s has an invalid ringinuse value. Using %s ringinuse value.\n",
9680 membername, q->
name);
9681 ringinuse = q->ringinuse;
9684 ringinuse = q->ringinuse;
9687 if (!ast_strlen_zero(args.wrapuptime)) {
9688 tmp = args.wrapuptime;
9690 wrapuptime = atoi(tmp);
9691 if (wrapuptime < 0) {
9702 if ((newm =
create_queue_member(interface, membername, penalty, cur ? cur->
paused : 0, state_interface, ringinuse, wrapuptime))) {
9717 member_add_to_queue(q, newm);
9728 static int mark_member_dead(
void *obj,
void *arg,
int flags)
9730 struct member *member = obj;
9737 static int kill_dead_members(
void *obj,
void *arg,
int flags)
9739 struct member *member = obj;
9741 if (!member->
delme) {
9763 struct member *member;
9769 const int queue_reload = ast_test_flag(mask, QUEUE_RELOAD_PARAMETERS);
9770 const int member_reload = ast_test_flag(mask, QUEUE_RELOAD_MEMBER);
9771 int prev_weight = 0;
9778 if (!(q = alloc_queue(queuename))) {
9794 prev_weight = q->
weight ? 1 : 0;
9798 ast_log(LOG_WARNING,
"Queue '%s' already defined! Skipping!\n", queuename);
9803 queue_t_unref(q,
"We exist! Expiring temporary pointer");
9812 if ((tmpvar = ast_variable_retrieve(cfg, queuename,
"strategy"))) {
9813 q->strategy = strat2int(tmpvar);
9814 if (q->strategy < 0) {
9815 ast_log(LOG_WARNING,
"'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
9817 q->strategy = QUEUE_STRATEGY_RINGALL;
9820 q->strategy = QUEUE_STRATEGY_RINGALL;
9824 if (member_reload) {
9830 for (var = ast_variable_browse(cfg, queuename); var; var = var->
next) {
9831 if (queue_reload && strcasecmp(var->
name,
"member")) {
9837 for (var = ast_variable_browse(cfg, queuename); var; var = var->
next) {
9838 if (member_reload && !strcasecmp(var->
name,
"member")) {
9844 if (member_reload) {
9847 while ((member = ao2_iterator_next(&mem_iter))) {
9860 if (!q->
weight && prev_weight) {
9862 }
else if (q->
weight && !prev_weight) {
9867 if (member_reload) {
9875 queues_t_link(queues, q,
"Add queue to container");
9879 queue_t_unref(q,
"Expiring creation reference");
9882 static int mark_unfound(
void *obj,
void *arg,
int flags)
9885 char *queuename = arg;
9886 if (!q->realtime && (ast_strlen_zero(queuename) || !strcasecmp(queuename, q->
name))) {
9892 static int kill_if_unfound(
void *obj,
void *arg,
int flags)
9895 char *queuename = arg;
9896 if (!q->realtime && !q->found && (ast_strlen_zero(queuename) || !strcasecmp(queuename, q->
name))) {
9921 const int queue_reload = ast_test_flag(mask, QUEUE_RELOAD_PARAMETERS);
9924 ast_log(LOG_NOTICE,
"No call queueing config file (queues.conf), so no call queues\n");
9926 }
else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
9928 }
else if (cfg == CONFIG_STATUS_FILEINVALID) {
9929 ast_log(LOG_ERROR,
"Config file queues.conf is in an invalid format. Aborting.\n");
9943 if (!strcasecmp(cat,
"general") && queue_reload) {
9947 if (ast_strlen_zero(queuename) || !strcasecmp(cat, queuename))
9979 while ((q = ao2_t_iterator_next(&queue_iter,
"Iterate through queues"))) {
9981 if (ast_strlen_zero(queuename) || !strcasecmp(q->
name, queuename))
9984 queue_t_unref(q,
"Done with iterator");
10007 if (ast_test_flag(mask, QUEUE_RELOAD_RULES)) {
10010 if (ast_test_flag(mask, QUEUE_RESET_STATS)) {
10013 if (ast_test_flag(mask, (QUEUE_RELOAD_PARAMETERS | QUEUE_RELOAD_MEMBER))) {
10025 ast_cli(fd,
"%s\n", str);
10035 struct ast_str *out = ast_str_alloca(512);
10036 time_t now = time(NULL);
10053 ast_str_append(&out, 0,
") in '%s' strategy (%ds holdtime, %ds talktime), W:%d, C:%d, A:%d, SL:%2.1f%%, SL2:%2.1f%% within %ds",
10059 struct member *mem;
10063 while ((mem = ao2_iterator_next(&mem_iter))) {
10096 COLOR_RED : COLOR_GREEN, COLOR_BLACK),
10099 ast_str_append(&out, 0,
" has taken %d calls (last was %ld secs ago)",
10120 for (qe = q->
head; qe; qe = qe->
next) {
10121 ast_str_set(&out, 0,
" %d. %s (wait: %ld:%2.2ld, prio: %d)",
10122 pos++, ast_channel_name(qe->
chan), (
long) (now - qe->
start) / 60,
10123 (
long) (now - qe->
start) % 60, qe->
prio);
10141 struct ast_str *out = ast_str_alloca(512);
10147 if (argc != 2 && argc != 3) {
10148 return CLI_SHOWUSAGE;
10158 ast_str_set(&out, 0,
"No such queue: %s.", argv[2]);
10161 return CLI_SUCCESS;
10170 char *category = NULL;
10172 const char *queuename = ast_variable_retrieve(cfg, category,
"name");
10173 if (ast_strlen_zero(queuename)) {
10174 ast_log(LOG_WARNING,
"Ignoring realtime queue with a NULL or empty 'name.'\n");
10178 queue_t_unref(q,
"Done with temporary pointer");
10190 if (!sorted_queues) {
10191 return CLI_SUCCESS;
10195 return CLI_SUCCESS;
10204 while ((q = ao2_t_iterator_next(&queue_iter,
"Iterate through queues"))) {
10214 if (!realtime_queue) {
10216 queue_t_unref(q,
"Done with iterator");
10219 queue_t_unref(realtime_queue,
"Queue is already in memory");
10226 queue_t_unref(q,
"Done with iterator");
10234 return CLI_SUCCESS;
10251 int list_len, word_len = strlen(word);
10252 const char *find, *end_find, *end_list;
10255 while(isspace(*list)) {
10259 while((find = strstr(list, word))) {
10261 if (find != list && *(find - 1) !=
' ') {
10264 while(!isspace(*list) && *list !=
'\0') {
10268 while(isspace(*list)) {
10275 list_len = strlen(list);
10276 end_find = find + word_len;
10277 end_list = list + list_len;
10278 if (end_find == end_list || *end_find !=
' ') {
10281 while(!isspace(*list) && *list !=
'\0') {
10285 while(isspace(*list)) {
10309 static char *
complete_queue(
const char *line,
const char *word,
int pos,
int state, ptrdiff_t word_list_offset)
10314 int wordlen = strlen(word);
10316 const char *word_list = NULL;
10320 if (word_list_offset && strlen(line) >= word_list_offset) {
10321 word_list = line + word_list_offset;
10325 while ((q = ao2_t_iterator_next(&queue_iter,
"Iterate through queues"))) {
10326 if (!strncasecmp(word, q->
name, wordlen) && ++which > state
10329 queue_t_unref(q,
"Done with iterator");
10332 queue_t_unref(q,
"Done with iterator");
10339 if (!ret && which == state && !wordlen && !strncmp(
"queue show", line, 10)) {
10346 static char *complete_queue_show(
const char *line,
const char *word,
int pos,
int state)
10360 "Usage: queue show\n"
10361 " Provides summary information on a specified queue.\n";
10364 return complete_queue_show(a->line, a->word, a->pos, a->n);
10370 static int manager_queue_rule_show(
struct mansession *s,
const struct message *m)
10378 if (!ast_strlen_zero(
id)) {
10384 if (ast_strlen_zero(rule) || !strcasecmp(rule, rl_iter->name)) {
10389 if (!ast_strlen_zero(rule)) {
10402 return RESULT_SUCCESS;
10411 int qchancount = 0;
10412 int qlongestholdtime = 0;
10413 int qsummaries = 0;
10419 struct member *mem;
10424 load_realtime_queues(queuefilter);
10430 if (!ast_strlen_zero(
id)) {
10431 snprintf(idText,
sizeof(idText),
"ActionID: %s\r\n",
id);
10434 while ((q = ao2_t_iterator_next(&queue_iter,
"Iterate through queues"))) {
10438 if (ast_strlen_zero(queuefilter) || !strcasecmp(q->
name, queuefilter)) {
10443 qlongestholdtime = 0;
10447 while ((mem = ao2_iterator_next(&mem_iter))) {
10450 if (member_status_available(mem->
status) && !mem->
paused) {
10457 for (qe = q->
head; qe; qe = qe->
next) {
10458 if ((now - qe->
start) > qlongestholdtime) {
10459 qlongestholdtime = now - qe->
start;
10466 "Available: %d\r\n"
10470 "LongestHoldTime: %d\r\n"
10473 q->
name, qmemcount, qmemavail, qchancount, q->
holdtime, q->
talktime, qlongestholdtime, idText);
10477 queue_t_unref(q,
"Done with iterator");
10484 return RESULT_SUCCESS;
10501 struct member *mem;
10506 load_realtime_queues(queuefilter);
10512 if (!ast_strlen_zero(
id)) {
10513 snprintf(idText,
sizeof(idText),
"ActionID: %s\r\n",
id);
10517 while ((q = ao2_t_iterator_next(&queue_iter,
"Iterate through queues"))) {
10521 if (ast_strlen_zero(queuefilter) || !strcasecmp(q->
name, queuefilter)) {
10532 "Completed: %d\r\n"
10533 "Abandoned: %d\r\n"
10534 "ServiceLevel: %d\r\n"
10535 "ServicelevelPerf: %2.1f\r\n"
10536 "ServicelevelPerf2: %2.1f\r\n"
10546 while ((mem = ao2_iterator_next(&mem_iter))) {
10547 if (ast_strlen_zero(memberfilter) || !strcmp(mem->
interface, memberfilter) || !strcmp(mem->
membername, memberfilter)) {
10552 "StateInterface: %s\r\n"
10553 "Membership: %s\r\n"
10555 "CallsTaken: %d\r\n"
10557 "LastPause: %d\r\n"
10558 "LoginTime: %d\r\n"
10562 "PausedReason: %s\r\n"
10563 "Wrapuptime: %d\r\n"
10577 for (qe = q->
head; qe; qe = qe->
next) {
10583 "CallerIDNum: %s\r\n"
10584 "CallerIDName: %s\r\n"
10585 "ConnectedLineNum: %s\r\n"
10586 "ConnectedLineName: %s\r\n"
10591 q->
name, pos++, ast_channel_name(qe->
chan), ast_channel_uniqueid(qe->
chan),
10596 (
long) (now - qe->
start), qe->
prio, idText);
10601 queue_t_unref(q,
"Done with iterator");
10608 return RESULT_SUCCESS;
10611 static int manager_add_queue_member(
struct mansession *s,
const struct message *m)
10613 const char *queuename, *interface, *penalty_s, *paused_s, *membername, *state_interface, *wrapuptime_s;
10614 int paused, penalty, wrapuptime = 0;
10624 if (ast_strlen_zero(queuename)) {
10629 if (ast_strlen_zero(interface)) {
10634 if (ast_strlen_zero(penalty_s)) {
10636 }
else if (sscanf(penalty_s,
"%30d", &penalty) != 1 || penalty < 0) {
10640 if (ast_strlen_zero(wrapuptime_s)) {
10642 }
else if (sscanf(wrapuptime_s,
"%30d", &wrapuptime) != 1 || wrapuptime < 0) {
10646 if (ast_strlen_zero(paused_s)) {
10652 switch (
add_to_queue(queuename, interface, membername, penalty, paused, queue_persistent_members, state_interface, NULL, wrapuptime)) {
10654 if (ast_strlen_zero(membername) || !log_membername_as_agent) {
10655 ast_queue_log(queuename,
"MANAGER", interface,
"ADDMEMBER",
"%s", paused ?
"PAUSED" :
"");
10657 ast_queue_log(queuename,
"MANAGER", membername,
"ADDMEMBER",
"%s", paused ?
"PAUSED" :
"");
10675 static int manager_remove_queue_member(
struct mansession *s,
const struct message *m)
10677 const char *queuename, *interface;
10678 struct member *mem = NULL;
10683 if (ast_strlen_zero(queuename) || ast_strlen_zero(interface)) {
10688 if (log_membername_as_agent) {
10694 if (!mem || ast_strlen_zero(mem->
membername)) {
10695 ast_queue_log(queuename,
"MANAGER", interface,
"REMOVEMEMBER",
"%s",
"");
10697 ast_queue_log(queuename,
"MANAGER", mem->
membername,
"REMOVEMEMBER",
"%s",
"");
10705 astman_send_error(s, m,
"Unable to remove interface from queue: No such queue");
10722 static int manager_pause_queue_member(
struct mansession *s,
const struct message *m)
10724 const char *queuename, *interface, *paused_s, *reason;
10732 if (ast_strlen_zero(interface) || ast_strlen_zero(paused_s)) {
10739 if (set_member_paused(queuename, interface, reason, paused)) {
10742 astman_send_ack(s, m, paused ?
"Interface paused successfully" :
"Interface unpaused successfully");
10747 static int manager_queue_log_custom(
struct mansession *s,
const struct message *m)
10749 const char *queuename, *
event, *message, *interface, *uniqueid;
10757 if (ast_strlen_zero(queuename) || ast_strlen_zero(event)) {
10762 ast_queue_log(queuename,
S_OR(uniqueid,
"NONE"), interface, event,
"%s", message);
10768 static int manager_queue_reload(
struct mansession *s,
const struct message *m)
10771 const char *queuename = NULL;
10772 int header_found = 0;
10776 ast_set_flag(&mask, QUEUE_RELOAD_MEMBER);
10780 ast_set_flag(&mask, QUEUE_RELOAD_RULES);
10784 ast_set_flag(&mask, QUEUE_RELOAD_PARAMETERS);
10788 if (!header_found) {
10789 ast_set_flag(&mask, AST_FLAGS_ALL & ~QUEUE_RESET_STATS);
10800 static int manager_queue_reset(
struct mansession *s,
const struct message *m)
10802 const char *queuename = NULL;
10803 struct ast_flags mask = {QUEUE_RESET_STATS,};
10815 static char *complete_queue_add_member(
const char *line,
const char *word,
int pos,
int state)
10822 return state == 0 ?
ast_strdup(
"to") : NULL;
10826 return state == 0 ?
ast_strdup(
"penalty") : NULL;
10828 if (0 <= state && state < 100) {
10831 sprintf(num,
"%d", state);
10838 return state == 0 ?
ast_strdup(
"as") : NULL;
10846 static int manager_queue_member_ringinuse(
struct mansession *s,
const struct message *m)
10848 const char *queuename, *interface, *ringinuse_s;
10857 if (ast_strlen_zero(interface) || ast_strlen_zero(ringinuse_s)) {
10867 astman_send_error(s, m,
"'RingInUse' parameter must be a truth value (yes/no, on/off, 0/1, etc)");
10871 if (set_member_value(queuename, interface, MEMBER_RINGINUSE, ringinuse)) {
10880 static int manager_queue_member_penalty(
struct mansession *s,
const struct message *m)
10882 const char *queuename, *interface, *penalty_s;
10890 if (ast_strlen_zero(interface) || ast_strlen_zero(penalty_s)) {
10895 penalty = atoi(penalty_s);
10897 if (set_member_value((
char *)queuename, (
char *)interface, MEMBER_PENALTY, penalty)) {
10906 static int manager_change_priority_caller_on_queue(
struct mansession *s,
const struct message *m)
10908 const char *queuename, *caller, *priority_s, *immediate_s;
10909 int priority = 0, immediate = 0;
10916 if (ast_strlen_zero(queuename)) {
10921 if (ast_strlen_zero(caller)) {
10926 if (ast_strlen_zero(priority_s)) {
10929 }
else if (sscanf(priority_s,
"%30d", &priority) != 1) {
10934 if (!ast_strlen_zero(immediate_s)) {
10935 immediate =
ast_true(immediate_s);
10943 astman_send_error(s, m,
"Unable to change priority caller on queue: No such queue");
10946 astman_send_error(s, m,
"Unable to change priority caller on queue: No such caller");
10953 static int manager_request_withdraw_caller_from_queue(
struct mansession *s,
const struct message *m)
10961 if (ast_strlen_zero(queuename)) {
10966 if (ast_strlen_zero(caller)) {
10976 astman_send_error(s, m,
"Unable to request withdraw from queue: No such queue");
10979 astman_send_error(s, m,
"Unable to request withdraw from queue: No such caller");
10982 astman_send_error(s, m,
"Unable to request withdraw from queue: Already requested");
10992 const char *queuename, *interface, *membername = NULL, *state_interface = NULL;
10997 e->
command =
"queue add member";
10999 "Usage: queue add member <dial string> to <queue> [penalty <penalty> [as <membername> [state_interface <interface>]]]\n"
11000 " Add a dial string (Such as a channel,e.g. SIP/6001) to a queue with optionally: a penalty, membername and a state_interface\n";
11003 return complete_queue_add_member(a->line, a->word, a->pos, a->n);
11006 if ((a->argc != 6) && (a->argc != 8) && (a->argc != 10) && (a->argc != 12)) {
11007 return CLI_SHOWUSAGE;
11008 }
else if (strcmp(a->argv[4],
"to")) {
11009 return CLI_SHOWUSAGE;
11010 }
else if ((a->argc >= 8) && strcmp(a->argv[6],
"penalty")) {
11011 return CLI_SHOWUSAGE;
11012 }
else if ((a->argc >= 10) && strcmp(a->argv[8],
"as")) {
11013 return CLI_SHOWUSAGE;
11014 }
else if ((a->argc == 12) && strcmp(a->argv[10],
"state_interface")) {
11015 return CLI_SHOWUSAGE;
11018 queuename = a->argv[5];
11019 interface = a->argv[3];
11020 if (a->argc >= 8) {
11021 if (sscanf(a->argv[7],
"%30d", &penalty) == 1) {
11023 ast_cli(a->fd,
"Penalty must be >= 0\n");
11027 ast_cli(a->fd,
"Penalty must be an integer >= 0\n");
11034 if (a->argc >= 10) {
11035 membername = a->argv[9];
11038 if (a->argc >= 12) {
11039 state_interface = a->argv[11];
11042 switch (
add_to_queue(queuename, interface, membername, penalty, 0, queue_persistent_members, state_interface, NULL, 0)) {
11044 if (ast_strlen_zero(membername) || !log_membername_as_agent) {
11045 ast_queue_log(queuename,
"CLI", interface,
"ADDMEMBER",
"%s",
"");
11047 ast_queue_log(queuename,
"CLI", membername,
"ADDMEMBER",
"%s",
"");
11049 ast_cli(a->fd,
"Added interface '%s' to queue '%s'\n", interface, queuename);
11050 return CLI_SUCCESS;
11052 ast_cli(a->fd,
"Unable to add interface '%s' to queue '%s': Already there\n", interface, queuename);
11053 return CLI_FAILURE;
11055 ast_cli(a->fd,
"Unable to add interface to queue '%s': No such queue\n", queuename);
11056 return CLI_FAILURE;
11058 ast_cli(a->fd,
"Out of memory\n");
11059 return CLI_FAILURE;
11061 ast_cli(a->fd,
"Member not dynamic\n");
11062 return CLI_FAILURE;
11064 return CLI_FAILURE;
11068 static char *complete_queue_remove_member(
const char *line,
const char *word,
int pos,
int state)
11075 int wordlen = strlen(word);
11078 if (pos > 5 || pos < 3) {
11082 return (state == 0 ?
ast_strdup(
"from") : NULL);
11091 while ((q = ao2_t_iterator_next(&queue_iter,
"Iterate through queues"))) {
11094 while ((m = ao2_iterator_next(&mem_iter))) {
11095 if (!strncasecmp(word, m->
membername, wordlen) && ++which > state) {
11101 queue_t_unref(q,
"Done with iterator, returning interface name");
11109 queue_t_unref(q,
"Done with iterator");
11118 const char *queuename, *interface;
11119 struct member *mem = NULL;
11120 char *res = CLI_FAILURE;
11124 e->
command =
"queue remove member";
11126 "Usage: queue remove member <channel> from <queue>\n"
11127 " Remove a specific channel from a queue.\n";
11130 return complete_queue_remove_member(a->line, a->word, a->pos, a->n);
11133 if (a->argc != 6) {
11134 return CLI_SHOWUSAGE;
11135 }
else if (strcmp(a->argv[4],
"from")) {
11136 return CLI_SHOWUSAGE;
11139 queuename = a->argv[5];
11140 interface = a->argv[3];
11142 if (log_membername_as_agent) {
11148 if (!mem || ast_strlen_zero(mem->
membername)) {
11149 ast_queue_log(queuename,
"CLI", interface,
"REMOVEMEMBER",
"%s",
"");
11151 ast_queue_log(queuename,
"CLI", mem->
membername,
"REMOVEMEMBER",
"%s",
"");
11153 ast_cli(a->fd,
"Removed interface %s from queue '%s'\n", interface, queuename);
11157 ast_cli(a->fd,
"Unable to remove interface '%s' from queue '%s': Not there\n", interface, queuename);
11160 ast_cli(a->fd,
"Unable to remove interface from queue '%s': No such queue\n", queuename);
11163 ast_cli(a->fd,
"Out of memory\n");
11166 ast_cli(a->fd,
"Unable to remove interface '%s' from queue '%s': Member is not dynamic\n", interface, queuename);
11181 const char *queuename, *caller;
11182 int priority, immediate = 0;
11183 char *res = CLI_FAILURE;
11187 e->
command =
"queue priority caller";
11189 "Usage: queue priority caller <channel> on <queue> to <priority> [immediate]\n"
11190 " Change the priority of a channel on a queue, optionally applying the change in relation to existing callers.\n";
11197 return CLI_SHOWUSAGE;
11198 }
else if (strcmp(a->argv[4],
"on")) {
11199 return CLI_SHOWUSAGE;
11200 }
else if (strcmp(a->argv[6],
"to")) {
11201 return CLI_SHOWUSAGE;
11202 }
else if (sscanf(a->argv[7],
"%30d", &priority) != 1) {
11203 ast_log (LOG_ERROR,
"<priority> parameter must be an integer.\n");
11204 return CLI_SHOWUSAGE;
11205 }
else if (a->argc == 9) {
11206 if (strcmp(a->argv[8],
"immediate")) {
11207 return CLI_SHOWUSAGE;
11212 caller = a->argv[3];
11213 queuename = a->argv[5];
11220 ast_cli(a->fd,
"Unable change priority caller %s on queue '%s': No such queue\n", caller, queuename);
11223 ast_cli(a->fd,
"Unable to change priority caller '%s' on queue '%s': Not there\n", caller, queuename);
11233 static char *complete_queue_pause_member(
const char *line,
const char *word,
int pos,
int state)
11240 return state == 0 ?
ast_strdup(
"queue") : NULL;
11244 return state == 0 ?
ast_strdup(
"reason") : NULL;
11254 const char *queuename, *interface, *reason;
11259 e->
command =
"queue {pause|unpause} member";
11261 "Usage: queue {pause|unpause} member <member> [queue <queue> [reason <reason>]]\n"
11262 " Pause or unpause a queue member. Not specifying a particular queue\n"
11263 " will pause or unpause a member across all queues to which the member\n"
11267 return complete_queue_pause_member(a->line, a-> word, a->pos, a->n);
11270 if (a->argc < 4 || a->argc == 5 || a->argc == 7 || a->argc > 8) {
11271 return CLI_SHOWUSAGE;
11272 }
else if (a->argc >= 5 && strcmp(a->argv[4],
"queue")) {
11273 return CLI_SHOWUSAGE;
11274 }
else if (a->argc == 8 && strcmp(a->argv[6],
"reason")) {
11275 return CLI_SHOWUSAGE;
11279 interface = a->argv[3];
11280 queuename = a->argc >= 6 ? a->argv[5] : NULL;
11281 reason = a->argc == 8 ? a->argv[7] : NULL;
11282 paused = !strcasecmp(a->argv[1],
"pause");
11284 if (set_member_paused(queuename, interface, reason, paused) == RESULT_SUCCESS) {
11285 ast_cli(a->fd,
"%spaused interface '%s'", paused ?
"" :
"un", interface);
11286 if (!ast_strlen_zero(queuename)) {
11287 ast_cli(a->fd,
" in queue '%s'", queuename);
11289 if (!ast_strlen_zero(reason)) {
11290 ast_cli(a->fd,
" for reason '%s'", reason);
11292 ast_cli(a->fd,
"\n");
11293 return CLI_SUCCESS;
11295 ast_cli(a->fd,
"Unable to %spause interface '%s'", paused ?
"" :
"un", interface);
11296 if (!ast_strlen_zero(queuename)) {
11297 ast_cli(a->fd,
" in queue '%s'", queuename);
11299 if (!ast_strlen_zero(reason)) {
11300 ast_cli(a->fd,
" for reason '%s'", reason);
11302 ast_cli(a->fd,
"\n");
11303 return CLI_FAILURE;
11307 static char *complete_queue_set_member_value(
const char *line,
const char *word,
int pos,
int state)
11332 const char *queuename = NULL, *interface;
11337 e->
command =
"queue set ringinuse";
11339 "Usage: queue set ringinuse <yes/no> on <interface> [in <queue>]\n"
11340 " Set a member's ringinuse in the queue specified. If no queue is specified\n"
11341 " then that interface's penalty is set in all queues to which that interface is a member.\n";
11345 return complete_queue_set_member_value(a->line, a->word, a->pos, a->n);
11349 if (a->argc != 6 && a->argc != 8) {
11350 return CLI_SHOWUSAGE;
11354 if (strcmp(a->argv[4],
"on") || (a->argc > 6 && strcmp(a->argv[6],
"in"))) {
11355 return CLI_SHOWUSAGE;
11359 if (a->argc == 8) {
11360 queuename = a->argv[7];
11364 interface = a->argv[5];
11372 return CLI_SHOWUSAGE;
11375 switch (set_member_value(queuename, interface, MEMBER_RINGINUSE, ringinuse)) {
11376 case RESULT_SUCCESS:
11377 ast_cli(a->fd,
"Set ringinuse on interface '%s' from queue '%s'\n", interface, queuename);
11378 return CLI_SUCCESS;
11379 case RESULT_FAILURE:
11380 ast_cli(a->fd,
"Failed to set ringinuse on interface '%s' from queue '%s'\n", interface, queuename);
11381 return CLI_FAILURE;
11383 return CLI_FAILURE;
11389 const char *queuename = NULL, *interface;
11394 e->
command =
"queue set penalty";
11396 "Usage: queue set penalty <penalty> on <interface> [in <queue>]\n"
11397 " Set a member's penalty in the queue specified. If no queue is specified\n"
11398 " then that interface's penalty is set in all queues to which that interface is a member\n";
11401 return complete_queue_set_member_value(a->line, a->word, a->pos, a->n);
11404 if (a->argc != 6 && a->argc != 8) {
11405 return CLI_SHOWUSAGE;
11406 }
else if (strcmp(a->argv[4],
"on") || (a->argc > 6 && strcmp(a->argv[6],
"in"))) {
11407 return CLI_SHOWUSAGE;
11410 if (a->argc == 8) {
11411 queuename = a->argv[7];
11413 interface = a->argv[5];
11414 penalty = atoi(a->argv[3]);
11416 switch (set_member_value(queuename, interface, MEMBER_PENALTY, penalty)) {
11417 case RESULT_SUCCESS:
11418 ast_cli(a->fd,
"Set penalty on interface '%s' from queue '%s'\n", interface, queuename);
11419 return CLI_SUCCESS;
11420 case RESULT_FAILURE:
11421 ast_cli(a->fd,
"Failed to set penalty on interface '%s' from queue '%s'\n", interface, queuename);
11422 return CLI_FAILURE;
11424 return CLI_FAILURE;
11428 static char *complete_queue_rule_show(
const char *line,
const char *word,
int pos,
int state)
11432 int wordlen = strlen(word);
11440 if (!strncasecmp(word, rl_iter->name, wordlen) && ++which > state) {
11457 e->
command =
"queue show rules";
11459 "Usage: queue show rules [rulename]\n"
11460 " Show the list of rules associated with rulename. If no\n"
11461 " rulename is specified, list all rules defined in queuerules.conf\n";
11464 return complete_queue_rule_show(a->line, a->word, a->pos, a->n);
11467 if (a->argc != 3 && a->argc != 4) {
11468 return CLI_SHOWUSAGE;
11471 rule = a->argc == 4 ? a->argv[3] :
"";
11474 if (ast_strlen_zero(rule) || !strcasecmp(rl_iter->name, rule)) {
11475 ast_cli(a->fd,
"Rule: %s\n", rl_iter->name);
11477 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);
11482 return CLI_SUCCESS;
11487 struct ast_flags mask = {QUEUE_RESET_STATS,};
11492 e->
command =
"queue reset stats";
11494 "Usage: queue reset stats [<queuenames>]\n"
11496 "Issuing this command will reset statistics for\n"
11497 "<queuenames>, or for all queues if no queue is\n"
11509 return CLI_SHOWUSAGE;
11512 if (a->argc == 3) {
11514 return CLI_SUCCESS;
11517 for (i = 3; i < a->argc; ++i) {
11521 return CLI_SUCCESS;
11531 e->
command =
"queue reload {parameters|members|rules|all}";
11533 "Usage: queue reload {parameters|members|rules|all} [<queuenames>]\n"
11534 "Reload queues. If <queuenames> are specified, only reload information pertaining\n"
11535 "to <queuenames>. One of 'parameters,' 'members,' 'rules,' or 'all' must be\n"
11536 "specified in order to know what information to reload. Below is an explanation\n"
11537 "of each of these qualifiers.\n"
11539 "\t'members' - reload queue members from queues.conf\n"
11540 "\t'parameters' - reload all queue options except for queue members\n"
11541 "\t'rules' - reload the queuerules.conf file\n"
11542 "\t'all' - reload queue rules, parameters, and members\n"
11544 "Note: the 'rules' qualifier here cannot actually be applied to a specific queue.\n"
11545 "Use of the 'rules' qualifier causes queuerules.conf to be reloaded. Even if only\n"
11546 "one queue is specified when using this command, reloading queue rules may cause\n"
11547 "other queues to be affected\n";
11552 const char *command_end = a->line + strlen(
"queue reload ");
11553 command_end = strchr(command_end,
' ');
11554 if (!command_end) {
11555 command_end = a->line + strlen(a->line);
11557 return complete_queue(a->line, a->word, a->pos, a->n, command_end - a->line);
11564 return CLI_SHOWUSAGE;
11566 if (!strcasecmp(a->argv[2],
"rules")) {
11567 ast_set_flag(&mask, QUEUE_RELOAD_RULES);
11568 }
else if (!strcasecmp(a->argv[2],
"members")) {
11569 ast_set_flag(&mask, QUEUE_RELOAD_MEMBER);
11570 }
else if (!strcasecmp(a->argv[2],
"parameters")) {
11571 ast_set_flag(&mask, QUEUE_RELOAD_PARAMETERS);
11572 }
else if (!strcasecmp(a->argv[2],
"all")) {
11573 ast_set_flag(&mask, AST_FLAGS_ALL & ~QUEUE_RESET_STATS);
11576 if (a->argc == 3) {
11578 return CLI_SUCCESS;
11581 for (i = 3; i < a->argc; ++i) {
11585 return CLI_SUCCESS;
11596 struct member *mem;
11597 int newtalktime = 0;
11607 if (ast_strlen_zero(data)) {
11608 ast_log(LOG_WARNING,
"QueueUpdate requires arguments (queuename,uniqueid,agent,status,talktime,params[totaltime,callednumber])\n");
11616 if (ast_strlen_zero(args.queuename) || ast_strlen_zero(args.uniqueid) || ast_strlen_zero(args.agent) || ast_strlen_zero(args.status)) {
11617 ast_log(LOG_WARNING,
"Missing argument to QueueUpdate (queuename,uniqueid,agent,status,talktime,params[totaltime|callednumber])\n");
11621 if (!ast_strlen_zero(args.talktime)) {
11622 newtalktime = atoi(args.talktime);
11627 ast_log(LOG_WARNING,
"QueueUpdate could not find requested queue '%s'\n", args.queuename);
11634 while ((mem = ao2_iterator_next(&mem_iter))) {
11635 if (!strcasecmp(mem->
membername, args.agent)) {
11636 if (!strcasecmp(args.status,
"ANSWER")) {
11638 q->
talktime = (((oldtalktime << 2) - oldtalktime) + newtalktime) >> 2;
11644 if (newtalktime <= q->servicelevel) {
11653 ast_queue_log(args.queuename, args.uniqueid, args.agent,
"OUTCALL",
"%s|%s|%s", args.status, args.talktime, args.params);
11663 queue_t_unref(q,
"Done with temporary pointer");
11669 AST_CLI_DEFINE(queue_show,
"Show status of a specified queue"),
11670 AST_CLI_DEFINE(handle_queue_rule_show,
"Show the rules defined in queuerules.conf"),
11671 AST_CLI_DEFINE(handle_queue_add_member,
"Add a channel to a specified queue"),
11672 AST_CLI_DEFINE(handle_queue_remove_member,
"Removes a channel from a specified queue"),
11673 AST_CLI_DEFINE(handle_queue_pause_member,
"Pause or unpause a queue member"),
11674 AST_CLI_DEFINE(handle_queue_set_member_penalty,
"Set penalty for a channel of a specified queue"),
11675 AST_CLI_DEFINE(handle_queue_set_member_ringinuse,
"Set ringinuse for a channel of a specified queue"),
11676 AST_CLI_DEFINE(handle_queue_reload,
"Reload queues, members, queue rules, or parameters"),
11677 AST_CLI_DEFINE(handle_queue_reset,
"Reset statistics for a queue"),
11678 AST_CLI_DEFINE(handle_queue_change_priority_caller,
"Change priority caller on queue"),
11684 static int unload_module(
void)
11687 agent_router = NULL;
11689 topic_forwarder = stasis_forward_cancel(topic_forwarder);
11742 ao2_cleanup(queues);
11743 ao2_cleanup(pending_members);
11762 struct ast_flags mask = {AST_FLAGS_ALL, };
11768 queue_hash_cb, NULL, queue_cmp_cb);
11774 MAX_CALL_ATTEMPT_BUCKETS, pending_members_hash, NULL, pending_members_cmp);
11775 if (!pending_members) {
11787 ast_realtime_require_field(
"queue_members",
"paused", RQ_INTEGER1, 1,
"uniqueid", RQ_UINTEGER2, 5,
"reason_paused", RQ_CHAR, 80, SENTINEL);
11796 if (!member_config) {
11797 realtime_ringinuse_field =
"ringinuse";
11799 const char *config_val;
11801 if ((config_val = ast_variable_retrieve(member_config, NULL,
"ringinuse"))) {
11802 ast_log(LOG_NOTICE,
"ringinuse field entries found in queue_members table. Using 'ringinuse'\n");
11803 realtime_ringinuse_field =
"ringinuse";
11804 }
else if ((config_val = ast_variable_retrieve(member_config, NULL,
"ignorebusy"))) {
11805 ast_log(LOG_NOTICE,
"ignorebusy field found in queue_members table with no ringinuse field. Using 'ignorebusy'\n");
11806 realtime_ringinuse_field =
"ignorebusy";
11808 ast_log(LOG_NOTICE,
"No entries were found for ringinuse/ignorebusy in queue_members table. Using 'ringinuse'\n");
11809 realtime_ringinuse_field =
"ringinuse";
11812 if (ast_variable_retrieve(member_config, NULL,
"reason_paused")) {
11813 realtime_reason_paused = 1;
11818 if (queue_persistent_members) {
11854 if (!device_state_sub) {
11862 if (!manager_topic || !queue_topic) {
11867 if (!topic_forwarder) {
11878 if (!agent_router) {
11915 static int reload(
void)
11917 struct ast_flags mask = {AST_FLAGS_ALL & ~QUEUE_RESET_STATS,};
11929 struct member *mem = NULL;
11936 queue_t_unref(q,
"Expiring temporary reference.");
11941 AST_MODULE_INFO(
ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER,
"True Call Queueing",
11942 .support_level = AST_MODULE_SUPPORT_CORE,
11944 .unload = unload_module,
static int get_member_penalty(char *queuename, char *interface)
Gets members penalty.
struct ast_str * sound_periodicannounce[MAX_PERIODIC_ANNOUNCEMENTS]
ast_include: include= support in extensions.conf
static int queue_function_qac_dep(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
Get the total number of members in a specific queue (Deprecated)
struct ast_variable * next
int ast_safe_sleep(struct ast_channel *chan, int ms)
Wait for a specified amount of time, looking for hangups.
struct ast_channel * ast_waitfor_n(struct ast_channel **chan, int n, int *ms)
Waits for input on a group of channels Wait for input on an array of channels for a given # of millis...
Struct containing info for an AMI event to send out.
void ast_party_connected_line_init(struct ast_party_connected_line *init)
Initialize the given connected line structure.
static void do_print(struct mansession *s, int fd, const char *str)
direct output to manager or cli with proper terminator
const char * ast_devstate2str(enum ast_device_state devstate) attribute_pure
Convert device state to text string for output.
void ast_set_callerid(struct ast_channel *chan, const char *cid_num, const char *cid_name, const char *cid_ani)
Set caller ID number, name and ANI and generate AMI event.
struct call_queue * parent
void ast_channel_req_accountcodes_precious(struct ast_channel *chan, const struct ast_channel *requestor, enum ast_channel_requestor_relationship relationship)
Setup new channel accountcodes from the requestor channel after ast_request().
Main Channel structure associated with a channel.
Local proxy channel special access.
ast_device_state
Device States.
char * str
Subscriber phone number (Malloced)
static struct member * create_queue_member(const char *interface, const char *membername, int penalty, int paused, const char *state_interface, int ringinuse, int wrapuptime)
allocate space for new queue member and set fields based on parameters passed
static int qupd_exec(struct ast_channel *chan, const char *data)
Update Queue with data of an outgoing call.
static void handle_blind_transfer(void *userdata, struct stasis_subscription *sub, struct stasis_message *msg)
Handle a blind transfer event.
int ast_streamfile(struct ast_channel *c, const char *filename, const char *preflang)
Streams a file.
#define MAX_PERIODIC_ANNOUNCEMENTS
#define AST_LIST_LOCK(head)
Locks a list.
static int store_next_rr(struct queue_ent *qe, struct callattempt *outgoing)
Search for best metric and add to Round Robbin queue.
struct ast_channel_snapshot_base * base
Asterisk locking-related definitions:
void astman_append(struct mansession *s, const char *fmt,...)
Asterisk main include file. File version handling, generic pbx functions.
#define AST_LIST_FIRST(head)
Returns the first entry contained in a list.
static int rqm_exec(struct ast_channel *chan, const char *data)
RemoveQueueMember application.
int ao2_container_count(struct ao2_container *c)
Returns the number of elements in a container.
char digits[AST_MAX_EXTENSION]
char * str
Subscriber phone number (Malloced)
const ast_string_field sound_holdtime
void ast_channel_set_caller_event(struct ast_channel *chan, const struct ast_party_caller *caller, const struct ast_set_party_caller *update)
Set the caller id information in the Asterisk channel and generate an AMI event if the caller id name...
int ast_autoservice_start(struct ast_channel *chan)
Automatically service a channel for us...
static struct call_queue * find_load_queue_rt_friendly(const char *queuename)
struct ast_channel * chan
struct ast_json * ast_json_pack(char const *format,...)
Helper for creating complex JSON values.
int pbx_exec(struct ast_channel *c, struct ast_app *app, const char *data)
Execute an application.
const ast_string_field membergosub
String manipulation functions.
static int load_module(void)
Load the module.
int stasis_message_router_add(struct stasis_message_router *router, struct stasis_message_type *message_type, stasis_subscription_cb callback, void *data)
Add a route to a message router.
CallerID (and other GR30) management and generation Includes code and algorithms from the Zapata libr...
Message representing attended transfer.
void ast_variables_destroy(struct ast_variable *var)
Free variable list.
struct stasis_message_router * bridge_router
struct ast_channel_snapshot * channel
struct ast_party_id id
Connected party ID.
int last_periodic_announce_sound
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
The arg parameter is a search key, but is not an object.
static int force_longest_waiting_caller
queues.conf [general] option
struct ast_json_payload * ast_json_payload_create(struct ast_json *json)
Create an ao2 object to pass json blobs as data payloads for stasis.
void ast_party_connected_line_set_init(struct ast_party_connected_line *init, const struct ast_party_connected_line *guide)
Initialize the given connected line structure using the given guide for a set update operation...
char exten[AST_MAX_EXTENSION]
Support for translation of data formats. translate.c.
static int upqm_exec(struct ast_channel *chan, const char *data)
UnpauseQueueMember application.
Message published during a blind transfer.
int ast_indicate(struct ast_channel *chan, int condition)
Indicates condition of channel.
void ast_json_unref(struct ast_json *value)
Decrease refcount on value. If refcount reaches zero, value is freed.
struct ast_party_name name
Subscriber name.
void ast_channel_publish_cached_blob(struct ast_channel *chan, struct stasis_message_type *type, struct ast_json *blob)
Publish a channel blob message using the latest snapshot from the cache.
#define ANNOUNCEPOSITION_NO
struct ast_party_id from
Who is redirecting the call (Sent to the party the call is redirected toward)
void ast_channel_publish_dial(struct ast_channel *caller, struct ast_channel *peer, const char *dialstring, const char *dialstatus)
Publish in the ast_channel_topic or ast_channel_topic_all topics a stasis message for the channels in...
int ast_extension_state_del(int id, ast_state_cb_type change_cb)
Deletes a state change watcher by ID.
char state_interface[AST_CHANNEL_NAME]
void ast_channel_update_redirecting(struct ast_channel *chan, const struct ast_party_redirecting *redirecting, const struct ast_set_party_redirecting *update)
Indicate that the redirecting id has changed.
#define DEFAULT_MIN_ANNOUNCE_FREQUENCY
The minimum number of seconds between position announcements.
unsigned int announceposition_only_up
#define STASIS_MESSAGE_TYPE_INIT(name)
Boiler-plate messaging macro for initializing message types.
struct ast_channel_snapshot * snapshot
static void queue_rules_set_global_params(struct ast_config *cfg)
int ast_channel_supports_html(struct ast_channel *channel)
Checks for HTML support on a channel.
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the 'standard' argument separation process for an application.
void astman_send_list_complete_start(struct mansession *s, const struct message *m, const char *event_name, int count)
Start the list complete event.
descriptor for a cli entry.
#define AST_LIST_UNLOCK(head)
Attempts to unlock a list.
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
AST_JSON_INT_T ast_json_int_t
Primarily used to cast when packing to an "I" type.
static int negative_penalty_invalid
queues.conf [general] option
void ast_db_freetree(struct ast_db_entry *entry)
Free structure created by ast_db_gettree()
int ast_party_id_presentation(const struct ast_party_id *id)
Determine the overall presentation value for the given party.
#define ao2_callback(c, flags, cb_fn, arg)
ao2_callback() is a generic function that applies cb_fn() to all objects in a container, as described below.
void ast_channel_update_connected_line(struct ast_channel *chan, const struct ast_party_connected_line *connected, const struct ast_set_party_connected_line *update)
Indicate that the connected line information has changed.
int ast_unload_realtime(const char *family)
Release any resources cached for a realtime family.
enum ast_device_state state
#define ao2_container_alloc_list(ao2_options, container_options, sort_fn, cmp_fn)
Allocate and initialize a list container.
int ast_check_realtime(const char *family)
Check if realtime engine is configured for family.
int ast_call(struct ast_channel *chan, const char *addr, int timeout)
Make a call.
Structure for variables, used for configurations and for channel variables.
struct stasis_topic * ast_queue_topic_all(void)
Get the Stasis Message Bus API topic for queue messages.
static void queue_reset_global_params(void)
Structure representing a snapshot of channel state.
#define AST_LIST_NEXT(elm, field)
Returns the next entry in the list after the given entry.
static int log_membername_as_agent
queues.conf [general] option
struct ast_frame * ast_read(struct ast_channel *chan)
Reads a frame.
struct ast_str * ast_manager_build_channel_state_string_prefix(const struct ast_channel_snapshot *snapshot, const char *prefix)
Generate the AMI message body from a channel snapshot.
static int queue_function_exists(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
Check if a given queue exists.
#define STASIS_MESSAGE_TYPE_CLEANUP(name)
Boiler-plate messaging macro for cleaning up message types.
static int calc_metric(struct call_queue *q, struct member *mem, int pos, struct queue_ent *qe, struct callattempt *tmp)
Calculate the metric of each member in the outgoing callattempts.
void * ast_aoc_destroy_decoded(struct ast_aoc_decoded *decoded)
free an ast_aoc_decoded object
Assume that the ao2_container is already locked.
struct stasis_message_type * stasis_message_type(const struct stasis_message *msg)
Get the message type for a stasis_message.
int stasis_subscription_set_filter(struct stasis_subscription *subscription, enum stasis_subscription_message_filter filter)
Set the message type filtering level on a subscription.
ast_channel_state
ast_channel states
static int reload_queue_rules(int reload)
Reload the rules defined in queuerules.conf.
char * str
Subscriber name (Malloced)
int ast_indicate_data(struct ast_channel *chan, int condition, const void *data, size_t datalen)
Indicates condition of channel, with payload.
int ast_db_get_allocated(const char *family, const char *key, char **out)
Get key value specified by family/key as a heap allocated string.
const ast_string_field uniqueid
struct stasis_topic * ast_bridge_topic_all(void)
A topic which publishes the events for all bridges.
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
#define ast_cli_register_multiple(e, len)
Register multiple commands.
static char * __queues_show(struct mansession *s, int fd, int argc, const char *const *argv)
Show queue(s) status and statistics.
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
const char * ast_hangup_cause_to_dial_status(int hangup_cause)
Convert a hangup cause to a publishable dial status.
int periodicannouncestartdelay
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
#define AST_DECLARE_STRING_FIELDS(field_list)
Declare the fields needed in a structure.
int log_restricted_caller_id
static void reload_single_member(const char *memberdata, struct call_queue *q)
reload information pertaining to a single member
void astman_send_ack(struct mansession *s, const struct message *m, char *msg)
Send ack in manager transaction.
int ast_start_mixmonitor(struct ast_channel *chan, const char *filename, const char *options)
Start a mixmonitor on a channel with the given parameters.
int64_t ast_tvdiff_ms(struct timeval end, struct timeval start)
Computes the difference (in milliseconds) between two struct timeval instances.
#define ast_strdup(str)
A wrapper for strdup()
void ast_party_connected_line_free(struct ast_party_connected_line *doomed)
Destroy the connected line information contents.
static void set_queue_result(struct ast_channel *chan, enum queue_result res)
sets the QUEUESTATUS channel variable
static int queue_function_memberpenalty_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
Dialplan function QUEUE_MEMBER_PENALTY() Gets the members penalty.
char bridge[AST_UUID_STR_LEN]
Generic File Format Support. Should be included by clients of the file handling routines. File service providers should instead include mod_format.h.
char * ast_category_browse(struct ast_config *config, const char *prev_name)
Browse categories.
static int say_periodic_announcement(struct queue_ent *qe, int ringing)
Playback announcement to queued members if period has elapsed.
static struct stasis_topic * manager_topic
A stasis_topic that all topics AMI cares about will be forwarded to.
const ast_string_field sound_thanks
int ast_channel_setoption(struct ast_channel *channel, int option, void *data, int datalen, int block)
Sets an option on a channel.
int ast_unregister_application(const char *app)
Unregister an application.
unsigned int announce_to_first_user
void ast_moh_stop(struct ast_channel *chan)
Turn off music on hold on a given channel.
#define AST_LIST_TRAVERSE_SAFE_END
Closes a safe loop traversal block.
struct ao2_container * members
#define AST_MAX_WATCHERS
Maximum number of channels we can watch at a time.
int ast_update_realtime(const char *family, const char *keyfield, const char *lookup,...) attribute_sentinel
Update realtime configuration.
struct ast_bridge_channel_snapshot_pair to_transferee
struct ast_manager_event_blob * ast_manager_event_blob_create(int event_flags, const char *manager_event, const char *extra_fields_fmt,...)
Construct a ast_manager_event_blob.
void stasis_message_router_unsubscribe_and_join(struct stasis_message_router *router)
Unsubscribe the router from the upstream topic, blocking until the final message has been processed...
int ast_atomic_fetchadd_int(volatile int *p, int v)
Atomically add v to *p and return the previous value of *p.
int announcepositionlimit
static int queue_function_mem_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
Dialplan function QUEUE_MEMBER() Sets the members penalty / paused / ringinuse.
static int insert_penaltychange(const char *list_name, const char *content, const int linenum)
Change queue penalty by adding rule.
static void rt_handle_member_record(struct call_queue *q, char *category, struct ast_config *member_config)
Find rt member record to update otherwise create one.
struct ast_channel_snapshot * ast_channel_snapshot_create(struct ast_channel *chan)
Generate a snapshot of the channel state. This is an ao2 object, so ao2_cleanup() to deallocate...
int ast_custom_function_unregister(struct ast_custom_function *acf)
Unregister a custom function.
char interface[AST_CHANNEL_NAME]
int ast_canmatch_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
Looks for a valid matching extension.
struct ast_json * ast_multi_channel_blob_get_json(struct ast_multi_channel_blob *obj)
Retrieve the JSON blob from a ast_multi_channel_blob. Returned ast_json is still owned by obj...
const char * pbx_builtin_getvar_helper(struct ast_channel *chan, const char *name)
Return a pointer to the value of the corresponding channel variable.
struct ast_frame_subclass subclass
#define ast_asprintf(ret, fmt,...)
A wrapper for asprintf()
Blob of data associated with a channel.
const char * astman_get_header(const struct message *m, char *var)
Get header from manager transaction.
struct ast_channel * ast_request(const char *type, struct ast_format_cap *request_cap, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *addr, int *cause)
Requests a channel.
const char * source_chan_uniqueid
struct ast_str * ast_manager_str_from_json_object(struct ast_json *blob, key_exclusion_cb exclusion_cb)
Convert a JSON object into an AMI compatible string.
#define AST_APP_OPTIONS(holder, options...)
Declares an array of options for an application.
const struct ast_channel_tech * tech
#define ao2_bump(obj)
Bump refcount on an AO2 object by one, returning the object.
static struct callattempt * wait_for_answer(struct queue_ent *qe, struct callattempt *outgoing, int *to, char *digit, int prebusies, int caller_disconnect, int forwardsallowed)
Wait for a member to answer the call.
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
struct ast_party_id id
Caller party ID.
static int queue_function_var(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
create interface var with all queue details.
static int queue_function_memberpenalty_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
Dialplan function QUEUE_MEMBER_PENALTY() Sets the members penalty.
struct ast_bridge_channel_snapshot_pair to_transfer_target
Configuration File Parser.
int ast_channel_datastore_inherit(struct ast_channel *from, struct ast_channel *to)
Inherit datastores from a parent to a child.
struct stasis_message_type * ast_device_state_message_type(void)
Get the Stasis message type for device state messages.
struct ast_bridge_snapshot * bridge
static char * realtime_ringinuse_field
name of the ringinuse field in the realtime database
struct stasis_topic * ast_channel_topic_all(void)
A topic which publishes the events for all channels.
enum ast_transfer_result result
void ast_replace_subargument_delimiter(char *s)
Replace '^' in a string with ','.
const char * predial_callee
struct ast_context * ast_context_find(const char *name)
Find a context.
The arg parameter is a partial search key similar to OBJ_SEARCH_KEY.
Generic Advice of Charge encode and decode routines.
#define ast_config_load(filename, flags)
Load a config file.
const ast_string_field sound_minute
struct ast_party_connected_line connected
int ast_channel_make_compatible(struct ast_channel *chan, struct ast_channel *peer)
Make the frame formats of two channels compatible.
struct queue_ent::@51 qe_rules
struct ast_aoc_decoded * ast_aoc_decode(struct ast_aoc_encoded *encoded, size_t size, struct ast_channel *chan)
decodes an encoded aoc payload.
int ast_devstate_changed(enum ast_device_state state, enum ast_devstate_cache cachable, const char *fmt,...)
Tells Asterisk the State for Device is changed.
static void queue_set_param(struct call_queue *q, const char *param, const char *val, int linenum, int failunknown)
Configure a queue parameter.
struct ast_party_id ani
Automatic Number Identification (ANI)
int periodicannouncefrequency
General Asterisk PBX channel definitions.
struct call_queue * queue
int ast_realtime_require_field(const char *family,...) attribute_sentinel
Inform realtime what fields that may be stored.
void astman_send_list_complete_end(struct mansession *s)
End the list complete event.
struct ast_channel_snapshot * links[2]
static int pqm_exec(struct ast_channel *chan, const char *data)
PauseQueueMember application.
const struct ast_eid * eid
The EID of the server where this message originated.
#define ast_string_field_init(x, size)
Initialize a field pool and fields.
static char * complete_queue(const char *line, const char *word, int pos, int state, ptrdiff_t word_list_offset)
Check if a given word is in a space-delimited list.
#define ast_strdupa(s)
duplicate a string in memory from the stack
struct ast_aoc_encoded * ast_aoc_encode(struct ast_aoc_decoded *decoded, size_t *out_size, struct ast_channel *chan)
encodes a decoded aoc structure so it can be passed on the wire
unsigned int block_connected_update
const ast_string_field sound_thereare
Data structure associated with a custom dialplan function.
void ast_multi_channel_blob_add_channel(struct ast_multi_channel_blob *obj, const char *role, struct ast_channel_snapshot *snapshot)
Add a ast_channel_snapshot to a ast_multi_channel_blob object.
static void free_members(struct call_queue *q, int all)
Iterate through queue's member list and delete them.
static void ringing(struct ast_channel *chan)
Helper method to send a ringing indication to a channel in a bridge.
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
#define AST_MAX_EXTENSION
char * ast_strip(char *s)
Strip leading/trailing whitespace from a string.
const ast_string_field context
static int update_queue(struct call_queue *q, struct member *member, int callcompletedinsl, time_t starttime)
update the queue status
int ast_channel_redirecting_sub(struct ast_channel *autoservice_chan, struct ast_channel *sub_chan, const void *redirecting_info, int is_frame)
Run a redirecting interception subroutine and update a channel's redirecting information.
Structure representing relevant data during a local channel optimization.
#define AST_STRING_FIELD(name)
Declare a string field.
static struct member * find_member_by_queuename_and_interface(const char *queuename, const char *interface)
Find a member by looking up queuename and interface.
void ast_party_number_init(struct ast_party_number *init)
Initialize the given number structure.
void * end_bridge_callback_data
Caller Party information.
structure to hold extensions
#define S_COR(a, b, c)
returns the equivalent of logic or for strings, with an additional boolean check: second one if not e...
In case you didn't read that giant block of text above the mansession_session struct, the mansession is named this solely to keep the API the same in Asterisk. This structure really represents data that is different from Manager action to Manager action. The mansession_session pointer contained within points to session-specific data.
static int reload_handler(int reload, struct ast_flags *mask, const char *queuename)
The command center for all reload operations.
const ast_string_field sound_next
static struct callattempt * find_best(struct callattempt *outgoing)
find the entry with the best metric, or NULL
const char * ast_json_string_get(const struct ast_json *string)
Get the value of a JSON string.
char * ast_strsep(char **s, const char sep, uint32_t flags)
Act like strsep but ignore separators inside quotes.
A set of macros to manage forward-linked lists.
#define ast_malloc(len)
A wrapper for malloc()
struct stasis_message_type * ast_channel_agent_login_type(void)
Message type for agent login on a channel.
struct ast_bridge_snapshot * bridge
#define ast_debug(level,...)
Log a DEBUG message.
static void queue_set_global_params(struct ast_config *cfg)
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
struct call_queue * lastqueue
struct stasis_message_type * ast_channel_agent_logoff_type(void)
Message type for agent logoff on a channel.
int ast_exists_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
Determine whether an extension exists.
struct ast_party_id ani
Automatic Number Identification (ANI)
Blob of data associated with a bridge.
#define ANNOUNCEPOSITION_MORE_THAN
static void rna(int rnatime, struct queue_ent *qe, struct ast_channel *peer, char *interface, char *membername, int autopause)
RNA == Ring No Answer. Common code that is executed when we try a queue member and they don't answer...
struct ast_str * ast_manager_build_channel_state_string(const struct ast_channel_snapshot *snapshot)
Generate the AMI message body from a channel snapshot.
const ast_string_field member_uniqueid
int ast_app_parse_options(const struct ast_app_option *options, struct ast_flags *flags, char **args, char *optstr)
Parses a string containing application options and sets flags/arguments.
struct ast_config * ast_config_new(void)
Create a new base configuration structure.
int ast_pre_call(struct ast_channel *chan, const char *sub_args)
Execute a Gosub call on the channel before a call is placed.
void ast_channel_req_accountcodes(struct ast_channel *chan, const struct ast_channel *requestor, enum ast_channel_requestor_relationship relationship)
Setup new channel accountcodes from the requestor channel after ast_request().
Structure to describe a channel "technology", ie a channel driver See for examples: ...
Core PBX routines and definitions.
void ast_party_number_free(struct ast_party_number *doomed)
Destroy the party number contents.
int ast_autoservice_stop(struct ast_channel *chan)
Stop servicing a channel for us...
int ast_check_hangup(struct ast_channel *chan)
Check to see if a channel is needing hang up.
#define AST_LIST_HEAD_STATIC(name, type)
Defines a structure to be used to hold a list of specified type, statically initialized.
int ast_bridge_call_with_flags(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, unsigned int flags)
Bridge a call, and add additional flags to the bridge.
struct ast_channel_snapshot_caller * caller
enum ast_attended_transfer_dest_type dest_type
static int num_available_members(struct call_queue *q)
Get the number of members available to accept a call.
static void handle_attended_transfer(void *userdata, struct stasis_subscription *sub, struct stasis_message *msg)
Handle an attended transfer event.
struct stasis_topic * ast_channel_topic(struct ast_channel *chan)
A topic which publishes the events for a particular channel.
static int montype_default
queues.conf [general] option
int ast_channel_has_role(struct ast_channel *channel, const char *role_name)
Check if a role exists on a channel.
struct stasis_message_type * ast_local_optimization_end_type(void)
Message type for when a local channel optimization completes.
static void leave_queue(struct queue_ent *qe)
Caller leaving queue.
int ast_manager_unregister(const char *action)
Unregister a registered manager command.
static int ring_entry(struct queue_ent *qe, struct callattempt *tmp, int *busies)
Part 2 of ring_one.
The AMI - Asterisk Manager Interface - is a TCP protocol created to manage Asterisk with third-party ...
#define AST_LIST_HEAD_NOLOCK(name, type)
Defines a structure to be used to hold a list of specified type (with no lock).
We define a custom "local user" structure because we use it not only for keeping track of what is in ...
static void destroy_queue(void *obj)
Free queue's member list then its string fields.
int ast_connected_line_parse_data(const unsigned char *data, size_t datalen, struct ast_party_connected_line *connected)
Parse connected line indication frame data.
struct ast_config * ast_load_realtime_multientry(const char *family,...) attribute_sentinel
Retrieve realtime configuration.
static int autofill_default
queues.conf [general] option
const ast_string_field bridge_uniqueid
static int request_withdraw_caller_from_queue(const char *queuename, const char *caller, const char *withdraw_info)
Request to withdraw a caller from a queue.
struct stasis_message_router * channel_router
static int get_wrapuptime(struct call_queue *q, struct member *member)
Return wrapuptime.
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
#define ao2_container_alloc_hash(ao2_options, container_options, n_buckets, hash_fn, sort_fn, cmp_fn)
Allocate and initialize a hash container with the desired number of buckets.
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is "true". This function checks to see whether a string passed to it is an indication of an "true" value. It checks to see if the string is "yes", "true", "y", "t", "on" or "1".
void ast_party_caller_set_init(struct ast_party_caller *init, const struct ast_party_caller *guide)
Initialize the given caller structure using the given guide for a set update operation.
Support for dynamic strings.
#define ANNOUNCEPOSITION_YES
static int word_in_list(const char *list, const char *word)
Check if a given word is in a space-delimited list.
#define AST_APP_OPTION_ARG(option, flagno, argno)
Declares an application option that accepts an argument.
#define ao2_unlink(container, obj)
Remove an object from a container.
struct ast_db_entry * ast_db_gettree(const char *family, const char *keytree)
Get a list of values within the astdb tree.
int ao2_container_dup(struct ao2_container *dest, struct ao2_container *src, enum search_flags flags)
Copy all object references in the src container into the dest container.
void * stasis_message_data(const struct stasis_message *msg)
Get the data contained in a message.
int ast_remaining_ms(struct timeval start, int max_ms)
Calculate remaining milliseconds given a starting timestamp and upper bound.
struct stasis_message * stasis_message_create(struct stasis_message_type *type, void *data)
Create a new message.
int ast_channel_connected_line_sub(struct ast_channel *autoservice_chan, struct ast_channel *sub_chan, const void *connected_info, int frame)
Run a connected line interception subroutine and update a channel's connected line information...
const ast_string_field name
Channel datastore data for max forwards.
void ast_party_connected_line_set(struct ast_party_connected_line *dest, const struct ast_party_connected_line *src, const struct ast_set_party_connected_line *update)
Set the connected line information based on another connected line source.
static int extensionstate2devicestate(int state)
Helper function which converts from extension state to device state values.
static int queue_function_mem_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
Get number either busy / free / ready or total members of a specific queue.
Connected Line/Party information.
unsigned int pending_connected_update
int pbx_builtin_setvar_multiple(struct ast_channel *chan, const char *data)
Parse and set multiple channel variables, where the pairs are separated by the ',' character...
int ast_moh_start(struct ast_channel *chan, const char *mclass, const char *interpclass)
Turn on music on hold on a given channel.
const ast_string_field caller_uniqueid
char context[AST_MAX_CONTEXT]
struct local_optimization caller_optimize
static int ql_exec(struct ast_channel *chan, const char *data)
QueueLog application.
Redirecting Line information. RDNIS (Redirecting Directory Number Information Service) Where a call d...
struct stasis_topic * ast_device_state_topic_all(void)
Get the Stasis topic for device state messages.
static void update_status(struct call_queue *q, struct member *m, const int status)
set a member's status based on device state of that member's state_interface.
int ast_goto_if_exists(struct ast_channel *chan, const char *context, const char *exten, int priority)
static void dump_queue_members(struct call_queue *pm_queue)
Dump all members in a specific queue to the database.
void ast_channel_publish_dial_forward(struct ast_channel *caller, struct ast_channel *peer, struct ast_channel *forwarded, const char *dialstring, const char *dialstatus, const char *forward)
Publish in the ast_channel_topic or ast_channel_topic_all topics a stasis message for the channels in...
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
#define AST_LIST_ENTRY(type)
Declare a forward link structure inside a list entry.
int stasis_subscription_final_message(struct stasis_subscription *sub, struct stasis_message *msg)
Determine whether a message is the final message to be received on a subscription.
static void device_state_cb(void *unused, struct stasis_subscription *sub, struct stasis_message *msg)
set a member's status based on device state of that member's interface
int ast_channel_sendurl(struct ast_channel *channel, const char *url)
Sends a URL on a given link Send URL on link.
void stasis_publish(struct stasis_topic *topic, struct stasis_message *message)
Publish a message to a topic's subscribers.
#define SCOPED_AO2LOCK(varname, obj)
scoped lock specialization for ao2 mutexes.
union ast_frame::@224 data
void ast_channel_inherit_variables(const struct ast_channel *parent, struct ast_channel *child)
Inherits channel variable from parent to child channel.
#define ast_calloc(num, len)
A wrapper for calloc()
struct local_optimization member_optimize
const char * ast_term_reset(void)
Returns the terminal reset code.
static int queue_function_queuegetchannel(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
Dialplan function QUEUE_GET_CHANNEL() Get caller channel waiting at specified position in the queue...
void ast_hangup(struct ast_channel *chan)
Hang up a channel.
const ast_string_field moh
void * ast_aoc_destroy_encoded(struct ast_aoc_encoded *encoded)
free an ast_aoc_encoded object
struct stasis_subscription * stasis_unsubscribe_and_join(struct stasis_subscription *subscription)
Cancel a subscription, blocking until the last message is processed.
Module has failed to load, may be in an inconsistent state.
static int manager_queues_status(struct mansession *s, const struct message *m)
Queue status info via AMI.
const char * ast_term_color(int fgcolor, int bgcolor)
Return a color sequence string.
const ast_string_field uniqueid
const ast_string_field defaultrule
An API for managing task processing threads that can be shared across modules.
const ast_string_field sound_seconds
Basic bridge subclass API.
void ast_autoservice_chan_hangup_peer(struct ast_channel *chan, struct ast_channel *peer)
Put chan into autoservice while hanging up peer.
Structure used to handle boolean flags.
struct stasis_topic * ast_manager_get_topic(void)
Get the Stasis Message Bus API topic for AMI.
static int wait_our_turn(struct queue_ent *qe, int ringing, enum queue_result *reason)
The waiting areas for callers who are not actively calling members.
static int manager_queues_summary(struct mansession *s, const struct message *m)
Summary of queue info via the AMI.
struct call_queue::@54 rules
int randomperiodicannounce
int pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const char *value)
Add a variable to the channel variable stack, removing the most recently set value for the same name...
static int queue_function_queuememberlist(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
Dialplan function QUEUE_MEMBER_LIST() Get list of members in a specific queue.
int ast_waitfordigit(struct ast_channel *c, int ms)
Waits for a digit.
#define ast_channel_lock_both(chan1, chan2)
Lock two channels.
static int change_priority_caller_on_queue(const char *queuename, const char *caller, int priority, int immediate)
Change priority caller into a queue.
const ast_string_field announce
const ast_string_field sound_reporthold
size_t ast_str_strlen(const struct ast_str *buf)
Returns the current length of the string stored within buf.
Assume that the ao2_container is already locked.
struct stasis_message_type * ast_local_optimization_begin_type(void)
Message type for when a local channel optimization begins.
STASIS_MESSAGE_TYPE_DEFN_LOCAL(cdr_sync_message_type)
A message type used to synchronize with the CDR topic.
The arg parameter is an object of the same type.
void stasis_message_router_unsubscribe(struct stasis_message_router *router)
Unsubscribe the router from the upstream topic.
int transit_network_select
Transit Network Select.
#define ANNOUNCEPOSITION_LIMIT
static int store_next_lin(struct queue_ent *qe, struct callattempt *outgoing)
Search for best metric and add to Linear queue.
void ast_party_redirecting_free(struct ast_party_redirecting *doomed)
Destroy the redirecting information contents.
void ast_party_connected_line_copy(struct ast_party_connected_line *dest, const struct ast_party_connected_line *src)
Copy the source connected line information to the destination connected line.
static void record_abandoned(struct queue_ent *qe)
Record that a caller gave up on waiting in queue.
static void send_agent_complete(const char *queuename, struct ast_channel_snapshot *caller, struct ast_channel_snapshot *peer, const struct member *member, time_t holdstart, time_t callstart, enum agent_complete_reason rsn)
Send out AMI message with member call completion status information.
const ast_string_field sound_calls
After Bridge Execution API.
static int realtime_rules
queuerules.conf [general] option
struct ast_channel_snapshot * ast_channel_snapshot_get_latest(const char *uniqueid)
Obtain the latest ast_channel_snapshot from the Stasis Message Bus API cache. This is an ao2 object...
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
int ast_say_number(struct ast_channel *chan, int num, const char *ints, const char *lang, const char *options)
says a number
Standard Command Line Interface.
static struct call_queue * find_queue_by_name_rt(const char *queuename, struct ast_variable *queue_vars, struct ast_config *member_config)
Reload a single queue via realtime.
time_t last_periodic_announce_time
struct ast_json * ast_json_object_get(struct ast_json *object, const char *key)
Get a field from a JSON object.
int ast_db_del(const char *family, const char *key)
Delete entry in astdb.
static void queue_rules_reset_global_params(void)
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
void ast_connected_line_copy_from_caller(struct ast_party_connected_line *dest, const struct ast_party_caller *src)
Copy the caller information to the connected line information.
int ast_extension_state(struct ast_channel *c, const char *context, const char *exten)
Uses hint and devicestate callback to get the state of an extension.
static void insert_entry(struct call_queue *q, struct queue_ent *prev, struct queue_ent *new, int *pos)
Insert the 'new' entry after the 'prev' entry of queue 'q'.
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one...
ast_app: A registered application
void ast_party_redirecting_init(struct ast_party_redirecting *init)
Initialize the given redirecting structure.
static int get_member_status(struct call_queue *q, int max_penalty, int min_penalty, int raise_penalty, enum empty_conditions conditions, int devstate)
Check if members are available.
int attribute_pure ast_false(const char *val)
Make sure something is false. Determine if a string containing a boolean value is "false"...
struct ast_channel_snapshot * ast_multi_channel_blob_get_channel(struct ast_multi_channel_blob *obj, const char *role)
Retrieve a channel snapshot associated with a specific role from a ast_multi_channel_blob.
int ast_waitstream(struct ast_channel *c, const char *breakon)
Waits for a stream to stop or digit to be pressed.
static void set_queue_variables(struct call_queue *q, struct ast_channel *chan)
Set variables of queue.
int ast_fileexists(const char *filename, const char *fmt, const char *preflang)
Checks for the existence of a given file.
static int realtime_reason_paused
does realtime backend support reason_paused
A multi channel blob data structure for multi_channel_blob stasis messages.
static int aqm_exec(struct ast_channel *chan, const char *data)
AddQueueMember application.
static void hangupcalls(struct queue_ent *qe, struct callattempt *outgoing, struct ast_channel *exception, int cancel_answered_elsewhere)
Hang up a list of outgoing calls.
struct ast_party_dialed::@206 number
Dialed/Called number.
User data for stasis subscriptions used for queue calls.
#define ao2_t_find(container, arg, flags, tag)
char state_context[AST_MAX_CONTEXT]
int stasis_subscription_accept_message_type(struct stasis_subscription *subscription, const struct stasis_message_type *type)
Indicate to a subscription that we are interested in a message type.
Data structure associated with a single frame of data.
int ast_app_exec_sub(struct ast_channel *autoservice_chan, struct ast_channel *sub_chan, const char *sub_args, int ignore_hangup)
Run a subroutine on a channel, placing an optional second channel into autoservice.
Internal Asterisk hangup causes.
int stasis_message_router_set_default(struct stasis_message_router *router, stasis_subscription_cb callback, void *data)
Sets the default route of a router.
static int use_weight
Records that one or more queues use weight.
Abstract JSON element (object, array, string, int, ...).
#define AST_OPTION_TONE_VERIFY
static void reload_single_queue(struct ast_config *cfg, struct ast_flags *mask, const char *queuename)
Reload information pertaining to a particular queue.
The structure that contains device state.
loadable MixMonitor functionality
Handy terminal functions for vt* terms.
int ast_db_put(const char *family, const char *key, const char *value)
Store value addressed by family/key.
static void print_queue(struct mansession *s, int fd, struct call_queue *q)
Print a single queue to AMI or the CLI.
#define ao2_container_alloc_rbtree(ao2_options, container_options, sort_fn, cmp_fn)
Allocate and initialize a red-black tree container.
void ast_bridge_set_after_go_on(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *parseable_goto)
Set channel to go on in the dialplan after the bridge.
static int play_file(struct ast_bridge_channel *bridge_channel, struct ast_channel *channel, const char *filename)
Playback the given filename and monitor for any dtmf interrupts.
enum ast_aoc_type ast_aoc_get_msg_type(struct ast_aoc_decoded *decoded)
get the message type, AOC-D, AOC-E, or AOC Request
static int get_queue_member_status(struct member *cur)
Return the current state of a member.
#define AST_LIST_TRAVERSE_SAFE_BEGIN(head, var, field)
Loops safely over (traverses) the entries in a list.
enum ast_frame_type frametype
static const char *const pm_family
Persistent Members astdb family.
static int queue_exec(struct ast_channel *chan, const char *data)
The starting point for all queue calls.
static void reload_queue_members(void)
Reload dynamic queue members persisted into the astdb.
const ast_string_field sound_minutes
char context[AST_MAX_CONTEXT]
AO2_STRING_FIELD_SORT_FN(transport_monitor, key)
Sort function for struct transport_monitor.
static void do_hang(struct callattempt *o)
common hangup actions
unsigned char valid
TRUE if the name information is valid/present.
const ast_string_field queue_quantity2
Search option field mask.
static void init_queue(struct call_queue *q)
Initialize Queue default values.
Call Parking and Pickup API Includes code and algorithms from the Zapata library. ...
int cancel_answered_elsewhere
#define AST_APP_OPTION(option, flagno)
Declares an application option that does not accept an argument.
#define AST_LIST_INSERT_BEFORE_CURRENT(elm, field)
Inserts a list entry before the current entry during a traversal.
static int queue_function_queuewaitingcount(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
Dialplan function QUEUE_WAITING_COUNT() Get number callers waiting in a specific queue.
struct stasis_forward * stasis_forward_all(struct stasis_topic *from_topic, struct stasis_topic *to_topic)
Create a subscription which forwards all messages from one topic to another.
int ast_extension_state_add(const char *context, const char *exten, ast_state_cb_type change_cb, void *data)
Add watcher for extension states.
void ast_config_destroy(struct ast_config *cfg)
Destroys a config.
static int remove_from_queue(const char *queuename, const char *interface)
Remove member from queue.
struct stasis_topic * ast_queue_topic(const char *queuename)
Get the Stasis Message Bus API topic for queue messages for a particular queue name.
static void copy_rules(struct queue_ent *qe, const char *rulename)
Copy rule from global list into specified queue.
struct ast_app * pbx_findapp(const char *app)
Look up an application.
struct stasis_message_type * ast_channel_hangup_request_type(void)
Message type for when a hangup is requested on a channel.
#define ast_manager_register_xml(action, authority, func)
Register a manager callback using XML documentation to describe the manager.
static void update_qe_rule(struct queue_ent *qe)
update rules for queues
Say numbers and dates (maybe words one day too)
#define ASTERISK_GPL_KEY
The text the key() function should return.
unsigned int dial_callerid_absent
struct ast_channel * ast_channel_get_by_name(const char *name)
Find a channel by name.
const ast_string_field queue_quantity1
void astman_send_error(struct mansession *s, const struct message *m, char *error)
Send error in manager transaction.
Asterisk module definitions.
static int is_our_turn(struct queue_ent *qe)
Check if we should start attempting to call queue members.
intmax_t ast_json_integer_get(const struct ast_json *integer)
Get the value from a JSON integer.
struct stasis_message_type * ast_channel_masquerade_type(void)
Message type for when a channel is being masqueraded.
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
static struct stasis_subscription * device_state_sub
Subscription to device state change messages.
static int valid_exit(struct queue_ent *qe, char digit)
Check for valid exit from queue via goto.
static int shared_lastcall
queues.conf [general] option
Persistent data storage (akin to *doze registry)
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application's arguments.
#define ast_string_field_free_memory(x)
free all memory - to be called before destroying the object
Application convenience functions, designed to give consistent look and feel to Asterisk apps...
ast_context: An extension context
const ast_string_field sound_callerannounce
unsigned char valid
TRUE if the number information is valid/present.
enum ast_transfer_result result
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.
#define ast_custom_function_register(acf)
Register a custom function.
static int context_included(const char *parent, const char *child)
Returns if one context includes another context.
static int add_to_queue(const char *queuename, const char *interface, const char *membername, int penalty, int paused, int dump, const char *state_interface, const char *reason_paused, int wrapuptime)
Add member to queue.
int ast_stopstream(struct ast_channel *c)
Stops a stream.
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
static int queue_persistent_members
queues.conf [general] option
static int reload_queues(int reload, struct ast_flags *mask, const char *queuename)
reload the queues.conf file
struct ast_multi_channel_blob * ast_multi_channel_blob_create(struct ast_json *blob)
Create a ast_multi_channel_blob suitable for a stasis_message.
void ast_party_redirecting_copy(struct ast_party_redirecting *dest, const struct ast_party_redirecting *src)
Copy the source redirecting information to the destination redirecting.
static force_inline int attribute_pure ast_str_case_hash(const char *str)
Compute a hash value on a case-insensitive string.
static int clear_stats(const char *queuename)
Facilitates resetting statistics for a queue.
static int ring_one(struct queue_ent *qe, struct callattempt *outgoing, int *busies)
Place a call to a queue member.
char state_exten[AST_MAX_EXTENSION]
const ast_string_field name
#define ast_str_create(init_len)
Create a malloc'ed dynamic length string.
#define AST_APP_ARG(name)
Define an application argument.
#define ast_string_field_set(x, field, data)
Set a field to a simple string value.
static int load_realtime_rules(void)
Load queue rules from realtime.
void astman_send_listack(struct mansession *s, const struct message *m, char *msg, char *listflag)
Send ack in manager transaction to begin a list.
struct ast_party_number number
Subscriber phone number.
#define ao2_link(container, obj)
Add an object to a container.