Newer
Older
Tilghman Lesher
committed
if (*m == '\0')
break;
}
Tilghman Lesher
committed
if (p == meid2 + sizeof(meid2))
meid2[sizeof(meid2) - 1] = '\0';
pbx_substitute_variables_helper(qe->chan, meid2, meid, sizeof(meid) - 1);
Tilghman Lesher
committed
snprintf(tmpid2, sizeof(tmpid2), "%s.%s", tmpid, qe->parent->monfmt);
mixmonapp = pbx_findapp("MixMonitor");
BJ Weschke
committed
if (!monitor_options)
if (mixmonapp) {
if (!ast_strlen_zero(monitor_exec))
Tilghman Lesher
committed
snprintf(mixmonargs, sizeof(mixmonargs), "%s,b%s,%s", tmpid2, monitor_options, monitor_exec);
Tilghman Lesher
committed
snprintf(mixmonargs, sizeof(mixmonargs), "%s,b%s", tmpid2, monitor_options);
ast_debug(1, "Arguments being passed to MixMonitor: %s\n", mixmonargs);
/* We purposely lock the CDR so that pbx_exec does not update the application data */
if (qe->chan->cdr)
ast_set_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED);
ret = pbx_exec(qe->chan, mixmonapp, mixmonargs);
if (qe->chan->cdr)
ast_clear_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED);
} else
ast_log(LOG_WARNING, "Asked to run MixMonitor on this call, but cannot find the MixMonitor app!\n");
Mark Spencer
committed
}
/* Drop out of the queue at this point, to prepare for next caller */
leave_queue(qe);
if (!ast_strlen_zero(url) && ast_channel_supports_html(peer)) {
ast_debug(1, "app_queue: sendurl=%s.\n", url);
/* run a macro for this connection if defined. The macro simply returns, no action is taken on the result */
/* use macro from dialplan if passed as a option, otherwise use the default queue macro */
if (!ast_strlen_zero(macro)) {
macroexec = ast_strdupa(macro);
} else {
if (qe->parent->membermacro)
macroexec = ast_strdupa(qe->parent->membermacro);
}
if (!ast_strlen_zero(macroexec)) {
ast_debug(1, "app_queue: macro=%s.\n", macroexec);
res = ast_autoservice_start(qe->chan);
if (res) {
ast_log(LOG_ERROR, "Unable to start autoservice on calling channel\n");
res = -1;
}
app = pbx_findapp("Macro");
Tilghman Lesher
committed
if (app) {
res = pbx_exec(qe->chan, app, macroexec);
ast_debug(1, "Macro exited with status %d\n", res);
res = 0;
} else {
ast_log(LOG_ERROR, "Could not find application Macro\n");
res = -1;
}
Tilghman Lesher
committed
if (ast_autoservice_stop(qe->chan) < 0) {
ast_log(LOG_ERROR, "Could not stop autoservice on calling channel\n");
res = -1;
}
}
Steve Murphy
committed
3078
3079
3080
3081
3082
3083
3084
3085
3086
3087
3088
3089
3090
3091
3092
3093
3094
3095
3096
3097
3098
3099
/* run a gosub for this connection if defined. The gosub simply returns, no action is taken on the result */
/* use gosub from dialplan if passed as a option, otherwise use the default queue gosub */
if (!ast_strlen_zero(gosub)) {
gosubexec = ast_strdupa(gosub);
} else {
if (qe->parent->membergosub)
gosubexec = ast_strdupa(qe->parent->membergosub);
}
if (!ast_strlen_zero(gosubexec)) {
if (option_debug)
ast_log(LOG_DEBUG, "app_queue: gosub=%s.\n", gosubexec);
res = ast_autoservice_start(qe->chan);
if (res) {
ast_log(LOG_ERROR, "Unable to start autoservice on calling channel\n");
res = -1;
}
app = pbx_findapp("Gosub");
if (app) {
char *gosub_args, *gosub_argstart;
/* Set where we came from */
ast_copy_string(qe->chan->context, "app_dial_gosub_virtual_context", sizeof(qe->chan->context));
ast_copy_string(qe->chan->exten, "s", sizeof(qe->chan->exten));
qe->chan->priority = 0;
Tilghman Lesher
committed
gosub_argstart = strchr(gosubexec, ',');
if (gosub_argstart) {
*gosub_argstart = 0;
Tilghman Lesher
committed
asprintf(&gosub_args, "%s,s,1(%s)", gosubexec, gosub_argstart + 1);
*gosub_argstart = '|';
} else {
Tilghman Lesher
committed
asprintf(&gosub_args, "%s,s,1", gosubexec);
}
if (gosub_args) {
res = pbx_exec(qe->chan, app, gosub_args);
ast_pbx_run(qe->chan);
free(gosub_args);
if (option_debug)
ast_log(LOG_DEBUG, "Gosub exited with status %d\n", res);
} else
ast_log(LOG_ERROR, "Could not Allocate string for Gosub arguments -- Gosub Call Aborted!\n");
Steve Murphy
committed
res = 0;
} else {
ast_log(LOG_ERROR, "Could not find application Gosub\n");
res = -1;
}
if (ast_autoservice_stop(qe->chan) < 0) {
ast_log(LOG_ERROR, "Could not stop autoservice on calling channel\n");
res = -1;
}
}
if (!ast_strlen_zero(agi)) {
ast_debug(1, "app_queue: agi=%s.\n", agi);
app = pbx_findapp("agi");
if (app) {
agiexec = ast_strdupa(agi);
ret = pbx_exec(qe->chan, app, agiexec);
ast_log(LOG_WARNING, "Asked to execute an AGI on this channel, but could not find application (agi)!\n");
}
Jason Parker
committed
ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "CONNECT", "%ld|%s|%ld", (long) time(NULL) - qe->start, peer->uniqueid,
(long)(orig - to > 0 ? (orig - to) / 1000 : 0));
if (update_cdr && qe->chan->cdr)
ast_copy_string(qe->chan->cdr->dstchannel, member->membername, sizeof(qe->chan->cdr->dstchannel));
Mark Spencer
committed
if (qe->parent->eventwhencalled)
manager_event(EVENT_FLAG_AGENT, "AgentConnect",
"Queue: %s\r\n"
"Uniqueid: %s\r\n"
"Channel: %s\r\n"
"Member: %s\r\n"
"Holdtime: %ld\r\n"
"BridgedChannel: %s\r\n"
Jason Parker
committed
"Ringtime: %ld\r\n"
queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
Jason Parker
committed
(long) time(NULL) - qe->start, peer->uniqueid, (long)(orig - to > 0 ? (orig - to) / 1000 : 0),
qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
Kevin P. Fleming
committed
ast_copy_string(oldcontext, qe->chan->context, sizeof(oldcontext));
ast_copy_string(oldexten, qe->chan->exten, sizeof(oldexten));
Mark Spencer
committed
bridge = ast_bridge_call(qe->chan,peer, &bridge_config);
if (strcasecmp(oldcontext, qe->chan->context) || strcasecmp(oldexten, qe->chan->exten)) {
ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld",
qe->chan->exten, qe->chan->context, (long) (callstart - qe->start),
(long) (time(NULL) - callstart));
send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), TRANSFER);
Russell Bryant
committed
} else if (ast_check_hangup(qe->chan)) {
ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETECALLER", "%ld|%ld|%d",
(long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), CALLER);
ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETEAGENT", "%ld|%ld|%d",
(long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), AGENT);
if (bridge != AST_PBX_NO_HANGUP_PEER)
update_queue(qe->parent, member, callcompletedinsl);
Mark Spencer
committed
hangupcalls(outgoing, NULL);
return res;
}
static int wait_a_bit(struct queue_ent *qe)
{
/* Don't need to hold the lock while we setup the outgoing calls */
int retrywait = qe->parent->retry * 1000;
Mark Spencer
committed
int res = ast_waitfordigit(qe->chan, retrywait);
if (res > 0 && !valid_exit(qe, res))
res = 0;
return res;
static struct member *interface_exists(struct call_queue *q, const char *interface)
struct ao2_iterator mem_iter;
if (!q)
return NULL;
mem_iter = ao2_iterator_init(q->members, 0);
while ((mem = ao2_iterator_next(&mem_iter))) {
if (!strcasecmp(interface, mem->interface))
return mem;
/* Dump all members in a specific queue to the database
* <pm_family>/<queuename> = <interface>;<penalty>;<paused>[|...]
static void dump_queue_members(struct call_queue *pm_queue)
char value[PM_MAX_LEN];
int value_len = 0;
int res;
struct ao2_iterator mem_iter;
memset(value, 0, sizeof(value));
mem_iter = ao2_iterator_init(pm_queue->members, 0);
while ((cur_member = ao2_iterator_next(&mem_iter))) {
if (!cur_member->dynamic) {
ao2_ref(cur_member, -1);
}
res = snprintf(value + value_len, sizeof(value) - value_len, "%s%s;%d;%d;%s",
value_len ? "|" : "", cur_member->interface, cur_member->penalty, cur_member->paused, cur_member->membername);
ao2_ref(cur_member, -1);
if (res != strlen(value + value_len)) {
ast_log(LOG_WARNING, "Could not create persistent member string, out of space\n");
break;
}
value_len += res;
if (value_len && !cur_member) {
if (ast_db_put(pm_family, pm_queue->name, value))
ast_log(LOG_WARNING, "failed to create persistent dynamic entry!\n");
} else
/* Delete the entry if the queue is empty or there is an error */
ast_db_del(pm_family, pm_queue->name);
static int remove_from_queue(const char *queuename, const char *interface)
Mark Michelson
committed
struct call_queue *q, tmpq;
int res = RES_NOSUCHQUEUE;
ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
Mark Michelson
committed
ast_copy_string(tmpq.name, queuename, sizeof(tmpq.name));
if((q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
ao2_lock(q);
if ((mem = ao2_find(q->members, &tmpmem, OBJ_POINTER))) {
/* XXX future changes should beware of this assumption!! */
if(!mem->dynamic) {
ao2_ref(mem, -1);
manager_event(EVENT_FLAG_AGENT, "QueueMemberRemoved",
"Location: %s\r\n"
"MemberName: %s\r\n",
q->name, mem->interface, mem->membername);
if (queue_persistent_members)
dump_queue_members(q);
res = RES_OKAY;
} else {
res = RES_EXISTS;
Mark Michelson
committed
ao2_unlock(q);
queue_unref(q);
if (res == RES_OKAY)
BJ Weschke
committed
remove_from_interfaces(interface);
return res;
}
BJ Weschke
committed
static int add_to_queue(const char *queuename, const char *interface, const char *membername, int penalty, int paused, int dump)
struct member *new_member, *old_member;
int res = RES_NOSUCHQUEUE;
Tilghman Lesher
committed
/* \note Ensure the appropriate realtime queue is loaded. Note that this
* short-circuits if the queue is already in memory. */
if (!(q = load_realtime_queue(queuename)))
return res;
Tilghman Lesher
committed
Mark Michelson
committed
ao2_lock(queues);
Tilghman Lesher
committed
Mark Michelson
committed
ao2_lock(q);
if ((old_member = interface_exists(q, interface)) == NULL) {
add_to_interfaces(interface);
if ((new_member = create_queue_member(interface, membername, penalty, paused))) {
new_member->dynamic = 1;
ao2_link(q->members, new_member);
manager_event(EVENT_FLAG_AGENT, "QueueMemberAdded",
"Membership: %s\r\n"
"Penalty: %d\r\n"
"CallsTaken: %d\r\n"
"LastCall: %d\r\n"
"Status: %d\r\n"
"Paused: %d\r\n",
q->name, new_member->interface, new_member->membername,
new_member->penalty, new_member->calls, (int) new_member->lastcall,
new_member->status, new_member->paused);
if (dump)
dump_queue_members(q);
res = RES_OKAY;
Tilghman Lesher
committed
} else {
Mark Michelson
committed
ao2_unlock(q);
ao2_unlock(queues);
return res;
}
static int set_member_paused(const char *queuename, const char *interface, const char *reason, int paused)
Mark Michelson
committed
struct ao2_iterator queue_iter;
/* Special event for when all queues are paused - individual events still generated */
/* XXX In all other cases, we use the membername, but since this affects all queues, we cannot */
if (ast_strlen_zero(queuename))
ast_queue_log("NONE", "NONE", interface, (paused ? "PAUSEALL" : "UNPAUSEALL"), "%s", "");
Mark Michelson
committed
queue_iter = ao2_iterator_init(queues, 0);
while((q = ao2_iterator_next(&queue_iter))) {
ao2_lock(q);
if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
if ((mem = interface_exists(q, interface))) {
found++;
if (mem->paused == paused) {
ast_debug(1, "%spausing already-%spaused queue member %s:%s\n", (paused ? "" : "un"), (paused ? "" : "un"), q->name, interface);
mem->paused = paused;
if (queue_persistent_members)
update_realtime_member_field(mem, q->name, "paused", paused ? "1" : "0");
ast_queue_log(q->name, "NONE", mem->membername, (paused ? "PAUSE" : "UNPAUSE"), "%s", S_OR(reason, ""));
if (!ast_strlen_zero(reason)) {
manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
"Queue: %s\r\n"
"Location: %s\r\n"
"MemberName: %s\r\n"
"Paused: %d\r\n"
"Reason: %s\r\n",
q->name, mem->interface, mem->membername, paused, reason);
} else {
manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
"Queue: %s\r\n"
"Location: %s\r\n"
"MemberName: %s\r\n"
"Paused: %d\r\n",
q->name, mem->interface, mem->membername, paused);
}
Mark Michelson
committed
ao2_unlock(q);
queue_unref(q);
return found ? RESULT_SUCCESS : RESULT_FAILURE;
/* Reload dynamic queue members persisted into the astdb */
static void reload_queue_members(void)
{
char *cur_ptr;
char *queue_name;
char *member;
char *interface;
char *penalty_tok;
int penalty = 0;
char *paused_tok;
int paused = 0;
struct ast_db_entry *db_tree;
struct ast_db_entry *entry;
Mark Michelson
committed
struct call_queue *cur_queue, tmpq;
char queue_data[PM_MAX_LEN];
Mark Michelson
committed
ao2_lock(queues);
/* Each key in 'pm_family' is the name of a queue */
db_tree = ast_db_gettree(pm_family, NULL);
for (entry = db_tree; entry; entry = entry->next) {
queue_name = entry->key + strlen(pm_family) + 2;
Mark Michelson
committed
ast_copy_string(tmpq.name, queue_name, sizeof(tmpq.name));
cur_queue = ao2_find(queues, &tmpq, OBJ_POINTER);
if (!cur_queue)
cur_queue = load_realtime_queue(queue_name);
if (!cur_queue) {
/* If the queue no longer exists, remove it from the
* database */
ast_log(LOG_WARNING, "Error loading persistent queue: '%s': it does not exist\n", queue_name);
ast_db_del(pm_family, queue_name);
Mark Michelson
committed
}
Mark Michelson
committed
if (ast_db_get(pm_family, queue_name, queue_data, PM_MAX_LEN)) {
queue_unref(cur_queue);
Mark Michelson
committed
}
Tilghman Lesher
committed
while ((member = strsep(&cur_ptr, ",|"))) {
if (ast_strlen_zero(member))
continue;
interface = strsep(&member, ";");
penalty_tok = strsep(&member, ";");
paused_tok = strsep(&member, ";");
membername = strsep(&member, ";");
ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (penalty)\n", queue_name);
break;
}
penalty = strtol(penalty_tok, NULL, 10);
if (errno == ERANGE) {
ast_log(LOG_WARNING, "Error converting penalty: %s: Out of range.\n", penalty_tok);
break;
}
if (!paused_tok) {
ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (paused)\n", queue_name);
break;
}
paused = strtol(paused_tok, NULL, 10);
if ((errno == ERANGE) || paused < 0 || paused > 1) {
ast_log(LOG_WARNING, "Error converting paused: %s: Expected 0 or 1.\n", paused_tok);
break;
}
if (ast_strlen_zero(membername))
membername = interface;
ast_debug(1, "Reload Members: Queue: %s Member: %s Name: %s Penalty: %d Paused: %d\n", queue_name, interface, membername, penalty, paused);
if (add_to_queue(queue_name, interface, membername, penalty, paused, 0) == RES_OUTOFMEMORY) {
ast_log(LOG_ERROR, "Out of Memory when reloading persistent queue member\n");
break;
Mark Michelson
committed
queue_unref(cur_queue);
Mark Michelson
committed
ao2_unlock(queues);
ast_log(LOG_NOTICE, "Queue members successfully reloaded from database.\n");
static int pqm_exec(struct ast_channel *chan, void *data)
{
char *parse;
AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(queuename);
AST_APP_ARG(interface);
AST_APP_ARG(options);
AST_APP_ARG(reason);
if (ast_strlen_zero(data)) {
ast_log(LOG_WARNING, "PauseQueueMember requires an argument ([queuename]|interface[|options][|reason])\n");
Russell Bryant
committed
parse = ast_strdupa(data);
AST_STANDARD_APP_ARGS(args, parse);
if (ast_strlen_zero(args.interface)) {
ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename]|interface[|options[|reason]])\n");
if (set_member_paused(args.queuename, args.interface, args.reason, 1)) {
ast_log(LOG_WARNING, "Attempt to pause interface %s, not found\n", args.interface);
pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND");
pbx_builtin_setvar_helper(chan, "PQMSTATUS", "PAUSED");
return 0;
}
static int upqm_exec(struct ast_channel *chan, void *data)
{
char *parse;
AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(queuename);
AST_APP_ARG(interface);
AST_APP_ARG(options);
AST_APP_ARG(reason);
if (ast_strlen_zero(data)) {
ast_log(LOG_WARNING, "UnpauseQueueMember requires an argument ([queuename]|interface[|options[|reason]])\n");
Russell Bryant
committed
parse = ast_strdupa(data);
AST_STANDARD_APP_ARGS(args, parse);
if (ast_strlen_zero(args.interface)) {
ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename]|interface[|options[|reason]])\n");
if (set_member_paused(args.queuename, args.interface, args.reason, 0)) {
ast_log(LOG_WARNING, "Attempt to unpause interface %s, not found\n", args.interface);
pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND");
pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "UNPAUSED");
static int rqm_exec(struct ast_channel *chan, void *data)
{
int res=-1;
char *parse, *temppos = NULL;
AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(queuename);
AST_APP_ARG(interface);
AST_APP_ARG(options);
);
if (ast_strlen_zero(data)) {
ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename[|interface[|options]])\n");
Russell Bryant
committed
parse = ast_strdupa(data);
AST_STANDARD_APP_ARGS(args, parse);
if (ast_strlen_zero(args.interface)) {
temppos = strrchr(args.interface, '-');
if (temppos)
*temppos = '\0';
switch (remove_from_queue(args.queuename, args.interface)) {
case RES_OKAY:
ast_queue_log(args.queuename, chan->uniqueid, args.interface, "REMOVEMEMBER", "%s", "");
ast_log(LOG_NOTICE, "Removed interface '%s' from queue '%s'\n", args.interface, args.queuename);
pbx_builtin_setvar_helper(chan, "RQMSTATUS", "REMOVED");
res = 0;
break;
case RES_EXISTS:
ast_debug(1, "Unable to remove interface '%s' from queue '%s': Not there\n", args.interface, args.queuename);
pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTINQUEUE");
res = 0;
break;
case RES_NOSUCHQUEUE:
ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", args.queuename);
pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOSUCHQUEUE");
res = 0;
break;
case RES_NOT_DYNAMIC:
ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': '%s' is not a dynamic member\n", args.queuename, args.interface);
pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTDYNAMIC");
res = 0;
break;
return res;
}
static int aqm_exec(struct ast_channel *chan, void *data)
{
int res=-1;
char *parse, *temppos = NULL;
AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(queuename);
AST_APP_ARG(interface);
AST_APP_ARG(penalty);
AST_APP_ARG(options);
if (ast_strlen_zero(data)) {
ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename[|[interface]|[penalty][|options][|membername]])\n");
Russell Bryant
committed
parse = ast_strdupa(data);
AST_STANDARD_APP_ARGS(args, parse);
if (ast_strlen_zero(args.interface)) {
temppos = strrchr(args.interface, '-');
if (temppos)
*temppos = '\0';
}
if (!ast_strlen_zero(args.penalty)) {
if ((sscanf(args.penalty, "%d", &penalty) != 1) || penalty < 0) {
ast_log(LOG_WARNING, "Penalty '%s' is invalid, must be an integer >= 0\n", args.penalty);
penalty = 0;
switch (add_to_queue(args.queuename, args.interface, args.membername, penalty, 0, queue_persistent_members)) {
case RES_OKAY:
ast_queue_log(args.queuename, chan->uniqueid, args.interface, "ADDMEMBER", "%s", "");
ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", args.interface, args.queuename);
pbx_builtin_setvar_helper(chan, "AQMSTATUS", "ADDED");
res = 0;
break;
case RES_EXISTS:
ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': Already there\n", args.interface, args.queuename);
pbx_builtin_setvar_helper(chan, "AQMSTATUS", "MEMBERALREADY");
res = 0;
break;
case RES_NOSUCHQUEUE:
ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", args.queuename);
pbx_builtin_setvar_helper(chan, "AQMSTATUS", "NOSUCHQUEUE");
res = 0;
break;
case RES_OUTOFMEMORY:
ast_log(LOG_ERROR, "Out of memory adding member %s to queue %s\n", args.interface, args.queuename);
break;
}
static int ql_exec(struct ast_channel *chan, void *data)
{
char *parse;
AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(queuename);
AST_APP_ARG(uniqueid);
AST_APP_ARG(event);
AST_APP_ARG(params);
);
if (ast_strlen_zero(data)) {
ast_log(LOG_WARNING, "QueueLog requires arguments (queuename|uniqueid|membername|event[|additionalinfo]\n");
return -1;
}
parse = ast_strdupa(data);
AST_STANDARD_APP_ARGS(args, parse);
if (ast_strlen_zero(args.queuename) || ast_strlen_zero(args.uniqueid)
|| ast_strlen_zero(args.membername) || ast_strlen_zero(args.event)) {
ast_log(LOG_WARNING, "QueueLog requires arguments (queuename|uniqueid|membername|event[|additionalinfo])\n");
return -1;
}
ast_queue_log(args.queuename, args.uniqueid, args.membername, args.event,
"%s", args.params ? args.params : "");
return 0;
}
static int queue_exec(struct ast_channel *chan, void *data)
{
int res=-1;
const char *user_priority;
Kevin P. Fleming
committed
const char *max_penalty_str;
Jason Parker
committed
int qcontinue = 0;
Kevin P. Fleming
committed
int max_penalty;
Mark Spencer
committed
enum queue_result reason = QUEUE_UNKNOWN;
/* whether to exit Queue application after the timeout hits */
int tries = 0;
int noption = 0;
Steve Murphy
committed
int makeannouncement = 0;
AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(queuename);
AST_APP_ARG(options);
AST_APP_ARG(url);
AST_APP_ARG(announceoverride);
AST_APP_ARG(queuetimeoutstr);
AST_APP_ARG(agi);
AST_APP_ARG(macro);
Steve Murphy
committed
AST_APP_ARG(gosub);
/* Our queue entry */
struct queue_ent qe;
if (ast_strlen_zero(data)) {
ast_log(LOG_WARNING, "Queue requires an argument: queuename[|options[|URL[|announceoverride[|timeout[|agi]]]]]\n");
parse = ast_strdupa(data);
AST_STANDARD_APP_ARGS(args, parse);
Russell Bryant
committed
/* Setup our queue entry */
memset(&qe, 0, sizeof(qe));
qe.start = time(NULL);
/* set the expire time based on the supplied timeout; */
if (args.queuetimeoutstr)
qe.expire = qe.start + atoi(args.queuetimeoutstr);
else
qe.expire = 0;
/* Get the priority from the variable ${QUEUE_PRIO} */
user_priority = pbx_builtin_getvar_helper(chan, "QUEUE_PRIO");
if (user_priority) {
if (sscanf(user_priority, "%d", &prio) == 1) {
ast_debug(1, "%s: Got priority %d from ${QUEUE_PRIO}.\n", chan->name, prio);
} else {
ast_log(LOG_WARNING, "${QUEUE_PRIO}: Invalid value (%s), channel %s.\n",
Mark Spencer
committed
user_priority, chan->name);
prio = 0;
}
} else {
ast_debug(3, "NO QUEUE_PRIO variable found. Using default.\n");
Kevin P. Fleming
committed
/* Get the maximum penalty from the variable ${QUEUE_MAX_PENALTY} */
if ((max_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MAX_PENALTY"))) {
if (sscanf(max_penalty_str, "%d", &max_penalty) == 1) {
ast_debug(1, "%s: Got max penalty %d from ${QUEUE_MAX_PENALTY}.\n", chan->name, max_penalty);
Kevin P. Fleming
committed
} else {
ast_log(LOG_WARNING, "${QUEUE_MAX_PENALTY}: Invalid value (%s), channel %s.\n",
max_penalty_str, chan->name);
max_penalty = 0;
}
} else {
max_penalty = 0;
}
if (args.options && (strchr(args.options, 'r')))
Mark Spencer
committed
ringing = 1;
Jason Parker
committed
if (args.options && (strchr(args.options, 'c')))
qcontinue = 1;
ast_debug(1, "queue: %s, options: %s, url: %s, announce: %s, expires: %ld, priority: %d\n",
args.queuename, args.options, args.url, args.announceoverride, (long)qe.expire, prio);
Kevin P. Fleming
committed
qe.prio = prio;
qe.max_penalty = max_penalty;
qe.last_pos_said = 0;
qe.last_pos = 0;
qe.last_periodic_announce_time = time(NULL);
qe.last_periodic_announce_sound = 0;
if (!join_queue(args.queuename, &qe, &reason)) {
ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ENTERQUEUE", "%s|%s", S_OR(args.url, ""),
if (ringing) {
ast_indicate(chan, AST_CONTROL_RINGING);
Kevin P. Fleming
committed
ast_moh_start(chan, qe.moh, NULL);
/* This is the wait loop for callers 2 through maxlen */
res = wait_our_turn(&qe, ringing, &reason);
Mark Michelson
committed
if (res) {
Mark Michelson
committed
}
Steve Murphy
committed
makeannouncement = 0;
for (;;) {
/* This is the wait loop for the head caller*/
/* To exit, they may get their call answered; */
/* they may dial a digit from the queue context; */
/* or, they may timeout. */
enum queue_member_status stat;
Mark Spencer
committed
/* Leave if we have exceeded our queuetimeout */
if (qe.expire && (time(NULL) > qe.expire)) {
record_abandoned(&qe);
reason = QUEUE_TIMEOUT;
res = 0;
ast_queue_log(args.queuename, chan->uniqueid,"NONE", "EXITWITHTIMEOUT", "%d|%d|%ld",
qe.pos, qe.opos, (long) time(NULL) - qe.start);
break;
}
Kevin P. Fleming
committed
if (makeannouncement) {
/* Make a position announcement, if enabled */
Steve Murphy
committed
if (qe.parent->announcefrequency)
if ((res = say_position(&qe,ringing)))
/* Make a periodic announcement, if enabled */
Steve Murphy
committed
if (qe.parent->periodicannouncefrequency)
if ((res = say_periodic_announcement(&qe,ringing)))
/* Try calling all queue members for 'timeout' seconds */
res = try_calling(&qe, args.options, args.announceoverride, args.url, &tries, &noption, args.agi, args.macro, args.gosub, ringing);
Mark Michelson
committed
if (res) {
Mark Michelson
committed
}
Mark Spencer
committed
stat = get_member_status(qe.parent, qe.max_penalty);
/* exit after 'timeout' cycle if 'n' option enabled */
if (noption && tries >= qe.parent->membercount) {
ast_verb(3, "Exiting on time-out cycle\n");
ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
record_abandoned(&qe);
reason = QUEUE_TIMEOUT;
res = 0;
break;
}
Mark Spencer
committed
/* leave the queue if no agents, if enabled */
if (qe.parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) {
record_abandoned(&qe);
reason = QUEUE_LEAVEEMPTY;
ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
res = 0;
break;
}
Mark Spencer
committed
/* leave the queue if no reachable agents, if enabled */
if ((qe.parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS || stat == QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS)) {
record_abandoned(&qe);
reason = QUEUE_LEAVEUNAVAIL;
ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
res = 0;
break;
}
if ((qe.parent->leavewhenempty == QUEUE_EMPTY_LOOSE) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) {
record_abandoned(&qe);
reason = QUEUE_LEAVEUNAVAIL;
res = 0;
break;
}
/* Leave if we have exceeded our queuetimeout */
if (qe.expire && (time(NULL) > qe.expire)) {
record_abandoned(&qe);
reason = QUEUE_TIMEOUT;
res = 0;
ast_queue_log(qe.parent->name, qe.chan->uniqueid,"NONE", "EXITWITHTIMEOUT", "%d|%d|%ld", qe.pos, qe.opos, (long) time(NULL) - qe.start);
break;
}
/* If using dynamic realtime members, we should regenerate the member list for this queue */
update_realtime_members(qe.parent);
/* OK, we didn't get anybody; wait for 'retry' seconds; may get a digit to exit with */
res = wait_a_bit(&qe);
if (res)
goto stop;
/* Since this is a priority queue and
* it is not sure that we are still at the head
* of the queue, go and check for our turn again.
*/
if (!is_our_turn(&qe)) {
ast_debug(1, "Darn priorities, going back in queue (%s)!\n", qe.chan->name);
goto check_turns;
stop:
if (res) {
if (res < 0) {
if (!qe.handled) {
record_abandoned(&qe);
ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ABANDON",
"%d|%d|%ld", qe.pos, qe.opos,
(long) time(NULL) - qe.start);
}
res = -1;
} else if (qe.valid_digits) {
ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHKEY",
"%s|%d", qe.digits, qe.pos);
}
}
if (res >= 0 && res != AST_PBX_KEEPALIVE) {
if (ringing) {
ast_indicate(chan, -1);
} else {
ast_moh_stop(chan);
}
set_queue_variables(&qe);
Mark Spencer
committed
if (reason != QUEUE_UNKNOWN)
set_queue_result(chan, reason);
ast_log(LOG_WARNING, "Unable to join queue '%s'\n", args.queuename);
Mark Spencer
committed
set_queue_result(chan, reason);
res = 0;
Kevin P. Fleming
committed
static int queue_function_var(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
{
int res = -1;
Mark Michelson
committed
struct call_queue *q, tmpq;
char interfacevar[256]="";
float sl = 0;
buf[0] = '\0';
if (ast_strlen_zero(data)) {
ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
return -1;
}
Mark Michelson
committed
ast_copy_string(tmpq.name, data, sizeof(tmpq.name));