Mon May 14 04:44:13 2007

Asterisk developer's documentation


app_queue.c File Reference

True call queues with optional send URL on answer. More...

#include "asterisk.h"
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <sys/time.h>
#include <sys/signal.h>
#include <netinet/in.h>
#include "asterisk/lock.h"
#include "asterisk/file.h"
#include "asterisk/logger.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/options.h"
#include "asterisk/app.h"
#include "asterisk/linkedlists.h"
#include "asterisk/module.h"
#include "asterisk/translate.h"
#include "asterisk/say.h"
#include "asterisk/features.h"
#include "asterisk/musiconhold.h"
#include "asterisk/cli.h"
#include "asterisk/manager.h"
#include "asterisk/config.h"
#include "asterisk/monitor.h"
#include "asterisk/utils.h"
#include "asterisk/causes.h"
#include "asterisk/astdb.h"
#include "asterisk/devicestate.h"
#include "asterisk/stringfields.h"

Include dependency graph for app_queue.c:

Go to the source code of this file.

Data Structures

struct  call_queue
struct  callattempt
 We define a custom "local user" structure because we use it not only for keeping track of what is in use but also for keeping track of who we're dialing. More...
struct  member
struct  member_interface
struct  queue_ent
struct  statechange
struct  strategy

Defines

#define ANNOUNCEHOLDTIME_ALWAYS   1
#define ANNOUNCEHOLDTIME_ONCE   2
#define AST_MAX_WATCHERS   256
#define DEFAULT_RETRY   5
#define DEFAULT_TIMEOUT   15
#define MAX_PERIODIC_ANNOUNCEMENTS   10
#define PM_MAX_LEN   8192
#define QUEUE_EMPTY_NORMAL   1
#define QUEUE_EMPTY_STRICT   2
#define QUEUE_EVENT_VARIABLES   3
#define RECHECK   1
#define RES_EXISTS   (-1)
#define RES_NOSUCHQUEUE   (-3)
#define RES_OKAY   0
#define RES_OUTOFMEMORY   (-2)

Enumerations

enum  {
  QUEUE_STRATEGY_RINGALL = 0, QUEUE_STRATEGY_ROUNDROBIN, QUEUE_STRATEGY_LEASTRECENT, QUEUE_STRATEGY_FEWESTCALLS,
  QUEUE_STRATEGY_RANDOM, QUEUE_STRATEGY_RRMEMORY
}
enum  queue_member_status { QUEUE_NO_MEMBERS, QUEUE_NO_REACHABLE_MEMBERS, QUEUE_NORMAL }
enum  queue_result {
  QUEUE_UNKNOWN = 0, QUEUE_TIMEOUT = 1, QUEUE_JOINEMPTY = 2, QUEUE_LEAVEEMPTY = 3,
  QUEUE_JOINUNAVAIL = 4, QUEUE_LEAVEUNAVAIL = 5, QUEUE_FULL = 6
}

Functions

static int __queues_show (struct mansession *s, int manager, int fd, int argc, char **argv)
static int add_to_interfaces (const char *interface)
static int add_to_queue (const char *queuename, const char *interface, const char *membername, int penalty, int paused, int dump)
static struct call_queuealloc_queue (const char *queuename)
static int aqm_exec (struct ast_channel *chan, void *data)
static AST_LIST_HEAD_STATIC (queues, call_queue)
static AST_LIST_HEAD_STATIC (interfaces, member_interface)
 AST_MODULE_INFO (ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT,"True Call Queueing",.load=load_module,.unload=unload_module,.reload=reload,)
static int background_file (struct queue_ent *qe, struct ast_channel *chan, char *filename)
static int calc_metric (struct call_queue *q, struct member *mem, int pos, struct queue_ent *qe, struct callattempt *tmp)
static void * changethread (void *data)
static void clear_and_free_interfaces (void)
static void clear_queue (struct call_queue *q)
static int compare_weight (struct call_queue *rq, struct member *member)
static char * complete_queue (const char *line, const char *word, int pos, int state)
static char * complete_queue_add_member (const char *line, const char *word, int pos, int state)
static char * complete_queue_remove_member (const char *line, const char *word, int pos, int state)
static struct membercreate_queue_member (const char *interface, const char *membername, int penalty, int paused)
static void destroy_queue (struct call_queue *q)
static void do_hang (struct callattempt *o)
 common hangup actions
static void dump_queue_members (struct call_queue *pm_queue)
static struct callattemptfind_best (struct callattempt *outgoing)
 find the entry with the best metric, or NULL
static struct call_queuefind_queue_by_name_rt (const char *queuename, struct ast_variable *queue_vars, struct ast_config *member_config)
 Reload a single queue via realtime.
static void free_members (struct call_queue *q, int all)
static enum queue_member_status get_member_status (struct call_queue *q, int max_penalty)
static int handle_queue_add_member (int fd, int argc, char *argv[])
static int handle_queue_remove_member (int fd, int argc, char *argv[])
static void hangupcalls (struct callattempt *outgoing, struct ast_channel *exception)
static void init_queue (struct call_queue *q)
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'.
static char * int2strat (int strategy)
static struct memberinterface_exists (struct call_queue *q, const char *interface)
static int interface_exists_global (const char *interface)
static int is_our_turn (struct queue_ent *qe)
static int join_queue (char *queuename, struct queue_ent *qe, enum queue_result *reason)
static void leave_queue (struct queue_ent *qe)
static int load_module (void)
static struct call_queueload_realtime_queue (const char *queuename)
static int manager_add_queue_member (struct mansession *s, const struct message *m)
static int manager_pause_queue_member (struct mansession *s, const struct message *m)
static int manager_queues_show (struct mansession *s, const struct message *m)
static int manager_queues_status (struct mansession *s, const struct message *m)
static int manager_remove_queue_member (struct mansession *s, const struct message *m)
static int play_file (struct ast_channel *chan, char *filename)
static int pqm_exec (struct ast_channel *chan, void *data)
static int ql_exec (struct ast_channel *chan, void *data)
static int queue_exec (struct ast_channel *chan, void *data)
static int queue_function_qac (struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
static int queue_function_queuememberlist (struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
static int queue_function_queuewaitingcount (struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
static void queue_set_param (struct call_queue *q, const char *param, const char *val, int linenum, int failunknown)
 Configure a queue parameter.
static int queue_show (int fd, int argc, char **argv)
static void recalc_holdtime (struct queue_ent *qe)
static void record_abandoned (struct queue_ent *qe)
static int reload (void)
static void reload_queue_members (void)
static int reload_queues (void)
static int remove_from_interfaces (const char *interface)
static int remove_from_queue (const char *queuename, const char *interface)
static int ring_entry (struct queue_ent *qe, struct callattempt *tmp, int *busies)
static int ring_one (struct queue_ent *qe, struct callattempt *outgoing, int *busies)
static void rna (int rnatime, struct queue_ent *qe, char *interface, char *membername)
 RNA == Ring No Answer. Common code that is executed when we try a queue member and they don't answer.
static int rqm_exec (struct ast_channel *chan, void *data)
static void rr_dep_warning (void)
static void rt_handle_member_record (struct call_queue *q, char *interface, const char *membername, const char *penalty_str, const char *paused_str)
static int say_periodic_announcement (struct queue_ent *qe)
static int say_position (struct queue_ent *qe)
static int set_member_paused (const char *queuename, const char *interface, int paused)
static void set_queue_result (struct ast_channel *chan, enum queue_result res)
static int statechange_queue (const char *dev, int state, void *ign)
static int store_next (struct queue_ent *qe, struct callattempt *outgoing)
static int strat2int (const char *strategy)
static int try_calling (struct queue_ent *qe, const char *options, char *announceoverride, const char *url, int *go_on, const char *agi)
static int unload_module (void)
static int update_dial_status (struct call_queue *q, struct member *member, int status)
static int update_queue (struct call_queue *q, struct member *member)
static int update_status (struct call_queue *q, struct member *member, int status)
static int upqm_exec (struct ast_channel *chan, void *data)
static int valid_exit (struct queue_ent *qe, char digit)
static char * vars2manager (struct ast_channel *chan, char *vars, size_t len)
static int wait_a_bit (struct queue_ent *qe)
static struct callattemptwait_for_answer (struct queue_ent *qe, struct callattempt *outgoing, int *to, char *digit, int prebusies, int caller_disconnect, int forwardsallowed)
static int wait_our_turn (struct queue_ent *qe, int ringing, enum queue_result *reason)

Variables

static char * app = "Queue"
static char * app_aqm = "AddQueueMember"
static char * app_aqm_descrip
static char * app_aqm_synopsis = "Dynamically adds queue members"
static char * app_pqm = "PauseQueueMember"
static char * app_pqm_descrip
static char * app_pqm_synopsis = "Pauses a queue member"
static char * app_ql = "QueueLog"
static char * app_ql_descrip
static char * app_ql_synopsis = "Writes to the queue_log"
static char * app_rqm = "RemoveQueueMember"
static char * app_rqm_descrip
static char * app_rqm_synopsis = "Dynamically removes queue members"
static char * app_upqm = "UnpauseQueueMember"
static char * app_upqm_descrip
static char * app_upqm_synopsis = "Unpauses a queue member"
static int autofill_default = 0
 queues.conf [general] option
static struct ast_cli_entry cli_add_queue_member_deprecated
static struct ast_cli_entry cli_queue []
static struct ast_cli_entry cli_remove_queue_member_deprecated
static struct ast_cli_entry cli_show_queue_deprecated
static char * descrip
static int montype_default = 0
 queues.conf [general] option
static const char * pm_family = "/Queue/PersistentMembers"
 Persistent Members astdb family.
static char qam_cmd_usage []
static char qrm_cmd_usage []
static int queue_persistent_members = 0
 queues.conf [general] option
struct {
   enum queue_result   id
   char *   text
queue_results []
static char queue_show_usage []
static struct ast_custom_function queueagentcount_function
static struct ast_custom_function queuemembercount_function
static struct ast_custom_function queuememberlist_function
static struct ast_custom_function queuewaitingcount_function
static struct strategy strategies []
static char * synopsis = "Queue a call for a call queue"
static int use_weight = 0
 queues.conf per-queue weight option


Detailed Description

True call queues with optional send URL on answer.

Author:
Mark Spencer <markster@digium.com>
Development notes
Note:
2004-11-25: Persistent Dynamic Members added by: NetNation Communications (www.netnation.com) Kevin Lindsay <kevinl@netnation.com>
Each dynamic agent in each queue is now stored in the astdb. When asterisk is restarted, each agent will be automatically readded into their recorded queues. This feature can be configured with the 'persistent_members=<1|0>' setting in the '[general]' category in queues.conf. The default is on.

Note:
2004-06-04: Priorities in queues added by inAccess Networks (work funded by Hellas On Line (HOL) www.hol.gr).

These features added by David C. Troy <dave@toad.net>:

Patch Version 1.07 2003-12-24 01

Added servicelevel statistic by Michiel Betel <michiel@betel.nl> Added Priority jumping code for adding and removing queue members by Jonathan Stanton <asterisk@doilooklikeicare.com>

Fixed to work with CVS as of 2004-02-25 and released as 1.07a by Matthew Enger <m.enger@xi.com.au>

Definition in file app_queue.c.


Define Documentation

#define ANNOUNCEHOLDTIME_ALWAYS   1

Definition at line 338 of file app_queue.c.

Referenced by queue_set_param().

#define ANNOUNCEHOLDTIME_ONCE   2

Definition at line 339 of file app_queue.c.

Referenced by queue_set_param(), and say_position().

#define AST_MAX_WATCHERS   256

Definition at line 1861 of file app_queue.c.

#define DEFAULT_RETRY   5

Definition at line 117 of file app_queue.c.

Referenced by init_queue(), and queue_set_param().

#define DEFAULT_TIMEOUT   15

Definition at line 118 of file app_queue.c.

Referenced by queue_set_param().

#define MAX_PERIODIC_ANNOUNCEMENTS   10

Definition at line 120 of file app_queue.c.

Referenced by init_queue(), queue_set_param(), and say_periodic_announcement().

#define PM_MAX_LEN   8192

Definition at line 240 of file app_queue.c.

Referenced by dump_queue_members(), and reload_queue_members().

#define QUEUE_EMPTY_NORMAL   1

Definition at line 336 of file app_queue.c.

Referenced by queue_set_param().

#define QUEUE_EMPTY_STRICT   2

Definition at line 337 of file app_queue.c.

Referenced by join_queue(), queue_exec(), queue_set_param(), and wait_our_turn().

#define QUEUE_EVENT_VARIABLES   3

Definition at line 340 of file app_queue.c.

Referenced by queue_set_param(), ring_entry(), and try_calling().

#define RECHECK   1

Definition at line 119 of file app_queue.c.

Referenced by wait_our_turn().

#define RES_EXISTS   (-1)

Definition at line 123 of file app_queue.c.

Referenced by add_to_queue(), aqm_exec(), handle_queue_add_member(), handle_queue_remove_member(), manager_add_queue_member(), manager_remove_queue_member(), remove_from_queue(), and rqm_exec().

#define RES_NOSUCHQUEUE   (-3)

Definition at line 125 of file app_queue.c.

Referenced by add_to_queue(), aqm_exec(), handle_queue_add_member(), handle_queue_remove_member(), manager_add_queue_member(), manager_remove_queue_member(), remove_from_queue(), and rqm_exec().

#define RES_OKAY   0

Definition at line 122 of file app_queue.c.

Referenced by add_to_queue(), aqm_exec(), handle_queue_add_member(), handle_queue_remove_member(), manager_add_queue_member(), manager_remove_queue_member(), remove_from_queue(), and rqm_exec().

#define RES_OUTOFMEMORY   (-2)

Definition at line 124 of file app_queue.c.

Referenced by add_to_queue(), aqm_exec(), handle_queue_add_member(), handle_queue_remove_member(), manager_add_queue_member(), manager_remove_queue_member(), and reload_queue_members().


Enumeration Type Documentation

anonymous enum

Enumerator:
QUEUE_STRATEGY_RINGALL 
QUEUE_STRATEGY_ROUNDROBIN 
QUEUE_STRATEGY_LEASTRECENT 
QUEUE_STRATEGY_FEWESTCALLS 
QUEUE_STRATEGY_RANDOM 
QUEUE_STRATEGY_RRMEMORY 

Definition at line 96 of file app_queue.c.

enum queue_member_status

Enumerator:
QUEUE_NO_MEMBERS 
QUEUE_NO_REACHABLE_MEMBERS 
QUEUE_NORMAL 

Definition at line 472 of file app_queue.c.

00472                          {
00473    QUEUE_NO_MEMBERS,
00474    QUEUE_NO_REACHABLE_MEMBERS,
00475    QUEUE_NORMAL
00476 };

enum queue_result

Enumerator:
QUEUE_UNKNOWN 
QUEUE_TIMEOUT 
QUEUE_JOINEMPTY 
QUEUE_LEAVEEMPTY 
QUEUE_JOINUNAVAIL 
QUEUE_LEAVEUNAVAIL 
QUEUE_FULL 

Definition at line 254 of file app_queue.c.

00254                   {
00255    QUEUE_UNKNOWN = 0,
00256    QUEUE_TIMEOUT = 1,
00257    QUEUE_JOINEMPTY = 2,
00258    QUEUE_LEAVEEMPTY = 3,
00259    QUEUE_JOINUNAVAIL = 4,
00260    QUEUE_LEAVEUNAVAIL = 5,
00261    QUEUE_FULL = 6,
00262 };


Function Documentation

static int __queues_show ( struct mansession s,
int  manager,
int  fd,
int  argc,
char **  argv 
) [static]

Definition at line 3889 of file app_queue.c.

References ast_build_string(), ast_cli(), AST_LIST_EMPTY, AST_LIST_LOCK, AST_LIST_NEXT, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_mutex_lock(), ast_mutex_unlock(), astman_append(), member::calls, call_queue::callsabandoned, call_queue::callscompleted, call_queue::callscompletedinsl, queue_ent::chan, call_queue::count, devstate2str(), member::dynamic, call_queue::head, call_queue::holdtime, int2strat(), member::interface, member::lastcall, load_realtime_queue(), call_queue::lock, call_queue::maxlen, call_queue::members, call_queue::name, queue_ent::next, member::next, member::paused, member::penalty, queue_ent::prio, queue_show(), RESULT_SHOWUSAGE, RESULT_SUCCESS, s, call_queue::servicelevel, queue_ent::start, member::status, call_queue::strategy, and call_queue::weight.

Referenced by manager_queues_show(), and queue_show().

03890 {
03891    struct call_queue *q;
03892    struct queue_ent *qe;
03893    struct member *mem;
03894    int pos, queue_show;
03895    time_t now;
03896    char max_buf[80];
03897    char *max;
03898    size_t max_left;
03899    float sl = 0;
03900    char *term = manager ? "\r\n" : "\n";
03901 
03902    time(&now);
03903    if (argc == 2)
03904       queue_show = 0;
03905    else if (argc == 3)
03906       queue_show = 1;
03907    else
03908       return RESULT_SHOWUSAGE;
03909 
03910    /* We only want to load realtime queues when a specific queue is asked for. */
03911    if (queue_show)
03912       load_realtime_queue(argv[2]);
03913 
03914    AST_LIST_LOCK(&queues);
03915    if (AST_LIST_EMPTY(&queues)) {
03916       AST_LIST_UNLOCK(&queues);
03917       if (queue_show) {
03918          if (s)
03919             astman_append(s, "No such queue: %s.%s",argv[2], term);
03920          else
03921             ast_cli(fd, "No such queue: %s.%s",argv[2], term);
03922       } else {
03923          if (s)
03924             astman_append(s, "No queues.%s", term);
03925          else
03926             ast_cli(fd, "No queues.%s", term);
03927       }
03928       return RESULT_SUCCESS;
03929    }
03930    AST_LIST_TRAVERSE(&queues, q, list) {
03931       ast_mutex_lock(&q->lock);
03932       if (queue_show) {
03933          if (strcasecmp(q->name, argv[2]) != 0) {
03934             ast_mutex_unlock(&q->lock);
03935             if (!AST_LIST_NEXT(q, list)) {
03936                ast_cli(fd, "No such queue: %s.%s",argv[2], term);
03937                break;
03938             }
03939             continue;
03940          }
03941       }
03942       max_buf[0] = '\0';
03943       max = max_buf;
03944       max_left = sizeof(max_buf);
03945       if (q->maxlen)
03946          ast_build_string(&max, &max_left, "%d", q->maxlen);
03947       else
03948          ast_build_string(&max, &max_left, "unlimited");
03949       sl = 0;
03950       if (q->callscompleted > 0)
03951          sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
03952       if (s)
03953          astman_append(s, "%-12.12s has %d calls (max %s) in '%s' strategy (%ds holdtime), W:%d, C:%d, A:%d, SL:%2.1f%% within %ds%s",
03954             q->name, q->count, max_buf, int2strat(q->strategy), q->holdtime, q->weight,
03955             q->callscompleted, q->callsabandoned,sl,q->servicelevel, term);
03956       else
03957          ast_cli(fd, "%-12.12s has %d calls (max %s) in '%s' strategy (%ds holdtime), W:%d, C:%d, A:%d, SL:%2.1f%% within %ds%s",
03958             q->name, q->count, max_buf, int2strat(q->strategy), q->holdtime, q->weight, q->callscompleted, q->callsabandoned,sl,q->servicelevel, term);
03959       if (q->members) {
03960          if (s)
03961             astman_append(s, "   Members: %s", term);
03962          else
03963             ast_cli(fd, "   Members: %s", term);
03964          for (mem = q->members; mem; mem = mem->next) {
03965             max_buf[0] = '\0';
03966             max = max_buf;
03967             max_left = sizeof(max_buf);
03968             if (mem->penalty)
03969                ast_build_string(&max, &max_left, " with penalty %d", mem->penalty);
03970             if (mem->dynamic)
03971                ast_build_string(&max, &max_left, " (dynamic)");
03972             if (mem->paused)
03973                ast_build_string(&max, &max_left, " (paused)");
03974             ast_build_string(&max, &max_left, " (%s)", devstate2str(mem->status));
03975             if (mem->calls) {
03976                ast_build_string(&max, &max_left, " has taken %d calls (last was %ld secs ago)",
03977                   mem->calls, (long) (time(NULL) - mem->lastcall));
03978             } else
03979                ast_build_string(&max, &max_left, " has taken no calls yet");
03980             if (s)
03981                astman_append(s, "      %s%s%s", mem->interface, max_buf, term);
03982             else
03983                ast_cli(fd, "      %s%s%s", mem->interface, max_buf, term);
03984          }
03985       } else if (s)
03986          astman_append(s, "   No Members%s", term);
03987       else  
03988          ast_cli(fd, "   No Members%s", term);
03989       if (q->head) {
03990          pos = 1;
03991          if (s)
03992             astman_append(s, "   Callers: %s", term);
03993          else
03994             ast_cli(fd, "   Callers: %s", term);
03995          for (qe = q->head; qe; qe = qe->next) {
03996             if (s)
03997                astman_append(s, "      %d. %s (wait: %ld:%2.2ld, prio: %d)%s",
03998                   pos++, qe->chan->name, (long) (now - qe->start) / 60,
03999                   (long) (now - qe->start) % 60, qe->prio, term);
04000             else
04001                ast_cli(fd, "      %d. %s (wait: %ld:%2.2ld, prio: %d)%s", pos++,
04002                   qe->chan->name, (long) (now - qe->start) / 60,
04003                   (long) (now - qe->start) % 60, qe->prio, term);
04004          }
04005       } else if (s)
04006          astman_append(s, "   No Callers%s", term);
04007       else
04008          ast_cli(fd, "   No Callers%s", term);
04009       if (s)
04010          astman_append(s, "%s", term);
04011       else
04012          ast_cli(fd, "%s", term);
04013       ast_mutex_unlock(&q->lock);
04014       if (queue_show)
04015          break;
04016    }
04017    AST_LIST_UNLOCK(&queues);
04018    return RESULT_SUCCESS;
04019 }

static int add_to_interfaces ( const char *  interface  )  [static]

Definition at line 690 of file app_queue.c.

References ast_calloc, AST_LIST_INSERT_HEAD, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), member_interface::interface, LOG_DEBUG, and option_debug.

Referenced by add_to_queue(), reload_queues(), and rt_handle_member_record().

00691 {
00692    struct member_interface *curint;
00693 
00694    AST_LIST_LOCK(&interfaces);
00695    AST_LIST_TRAVERSE(&interfaces, curint, list) {
00696       if (!strcasecmp(curint->interface, interface))
00697          break;
00698    }
00699 
00700    if (curint) {
00701       AST_LIST_UNLOCK(&interfaces);
00702       return 0;
00703    }
00704 
00705    if (option_debug)
00706       ast_log(LOG_DEBUG, "Adding %s to the list of interfaces that make up all of our queue members.\n", interface);
00707    
00708    if ((curint = ast_calloc(1, sizeof(*curint)))) {
00709       ast_copy_string(curint->interface, interface, sizeof(curint->interface));
00710       AST_LIST_INSERT_HEAD(&interfaces, curint, list);
00711    }
00712    AST_LIST_UNLOCK(&interfaces);
00713 
00714    return 0;
00715 }

static int add_to_queue ( const char *  queuename,
const char *  interface,
const char *  membername,
int  penalty,
int  paused,
int  dump 
) [static]

Definition at line 2816 of file app_queue.c.

References add_to_interfaces(), AST_LIST_LOCK, AST_LIST_UNLOCK, ast_mutex_lock(), ast_mutex_unlock(), member::calls, create_queue_member(), dump_queue_members(), member::dynamic, EVENT_FLAG_AGENT, member::interface, interface_exists(), member::lastcall, load_realtime_queue(), call_queue::lock, manager_event(), member::membername, call_queue::members, call_queue::name, member::next, member::paused, member::penalty, RES_EXISTS, RES_NOSUCHQUEUE, RES_OKAY, RES_OUTOFMEMORY, and member::status.

Referenced by aqm_exec(), handle_queue_add_member(), manager_add_queue_member(), and reload_queue_members().

02817 {
02818    struct call_queue *q;
02819    struct member *new_member;
02820    int res = RES_NOSUCHQUEUE;
02821 
02822    /* \note Ensure the appropriate realtime queue is loaded.  Note that this
02823     * short-circuits if the queue is already in memory. */
02824    if (!(q = load_realtime_queue(queuename)))
02825       return res;
02826 
02827    AST_LIST_LOCK(&queues);
02828 
02829    ast_mutex_lock(&q->lock);
02830    if (interface_exists(q, interface) == NULL) {
02831       add_to_interfaces(interface);
02832       if ((new_member = create_queue_member(interface, membername, penalty, paused))) {
02833          new_member->dynamic = 1;
02834          new_member->next = q->members;
02835          q->members = new_member;
02836          manager_event(EVENT_FLAG_AGENT, "QueueMemberAdded",
02837             "Queue: %s\r\n"
02838             "Location: %s\r\n"
02839             "MemberName: %s\r\n"
02840             "Membership: %s\r\n"
02841             "Penalty: %d\r\n"
02842             "CallsTaken: %d\r\n"
02843             "LastCall: %d\r\n"
02844             "Status: %d\r\n"
02845             "Paused: %d\r\n",
02846             q->name, new_member->interface, new_member->membername,
02847             new_member->dynamic ? "dynamic" : "static",
02848             new_member->penalty, new_member->calls, (int) new_member->lastcall,
02849             new_member->status, new_member->paused);
02850          
02851          if (dump)
02852             dump_queue_members(q);
02853          
02854          res = RES_OKAY;
02855       } else {
02856          res = RES_OUTOFMEMORY;
02857       }
02858    } else {
02859       res = RES_EXISTS;
02860    }
02861    ast_mutex_unlock(&q->lock);
02862    AST_LIST_UNLOCK(&queues);
02863 
02864    return res;
02865 }

static struct call_queue* alloc_queue ( const char *  queuename  )  [static]

Definition at line 634 of file app_queue.c.

References ast_calloc, and ast_mutex_init().

Referenced by find_queue_by_name_rt(), and reload_queues().

00635 {
00636    struct call_queue *q;
00637 
00638    if ((q = ast_calloc(1, sizeof(*q)))) {
00639       ast_mutex_init(&q->lock);
00640       ast_copy_string(q->name, queuename, sizeof(q->name));
00641    }
00642    return q;
00643 }

static int aqm_exec ( struct ast_channel chan,
void *  data 
) [static]

Definition at line 3168 of file app_queue.c.

References add_to_queue(), AST_APP_ARG, AST_DECLARE_APP_ARGS, ast_goto_if_exists(), ast_log(), ast_module_user_add, ast_module_user_remove, ast_opt_priority_jumping, ast_queue_log(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), ast_module_user::chan, ast_channel::context, LOG_ERROR, LOG_NOTICE, LOG_WARNING, parse(), pbx_builtin_setvar_helper(), RES_EXISTS, RES_NOSUCHQUEUE, RES_OKAY, and RES_OUTOFMEMORY.

Referenced by load_module().

03169 {
03170    int res=-1;
03171    struct ast_module_user *lu;
03172    char *parse, *temppos = NULL;
03173    int priority_jump = 0;
03174    AST_DECLARE_APP_ARGS(args,
03175       AST_APP_ARG(queuename);
03176       AST_APP_ARG(interface);
03177       AST_APP_ARG(penalty);
03178       AST_APP_ARG(options);
03179       AST_APP_ARG(membername);
03180    );
03181    int penalty = 0;
03182 
03183    if (ast_strlen_zero(data)) {
03184       ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename[|[interface]|[penalty][|options][|membername]])\n");
03185       return -1;
03186    }
03187 
03188    parse = ast_strdupa(data);
03189 
03190    AST_STANDARD_APP_ARGS(args, parse);
03191 
03192    lu = ast_module_user_add(chan);
03193 
03194    if (ast_strlen_zero(args.interface)) {
03195       args.interface = ast_strdupa(chan->name);
03196       temppos = strrchr(args.interface, '-');
03197       if (temppos)
03198          *temppos = '\0';
03199    }
03200 
03201    if (!ast_strlen_zero(args.penalty)) {
03202       if ((sscanf(args.penalty, "%d", &penalty) != 1) || penalty < 0) {
03203          ast_log(LOG_WARNING, "Penalty '%s' is invalid, must be an integer >= 0\n", args.penalty);
03204          penalty = 0;
03205       }
03206    }
03207    
03208    if (args.options) {
03209       if (strchr(args.options, 'j'))
03210          priority_jump = 1;
03211    }
03212 
03213    if (ast_strlen_zero(args.membername))
03214       args.membername = args.interface;
03215 
03216 
03217    switch (add_to_queue(args.queuename, args.interface, args.membername, penalty, 0, queue_persistent_members)) {
03218    case RES_OKAY:
03219       ast_queue_log(args.queuename, chan->uniqueid, args.interface, "ADDMEMBER", "%s", "");
03220       ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", args.interface, args.queuename);
03221       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "ADDED");
03222       res = 0;
03223       break;
03224    case RES_EXISTS:
03225       ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': Already there\n", args.interface, args.queuename);
03226       if (priority_jump || ast_opt_priority_jumping)
03227          ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
03228       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "MEMBERALREADY");
03229       res = 0;
03230       break;
03231    case RES_NOSUCHQUEUE:
03232       ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", args.queuename);
03233       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "NOSUCHQUEUE");
03234       res = 0;
03235       break;
03236    case RES_OUTOFMEMORY:
03237       ast_log(LOG_ERROR, "Out of memory adding member %s to queue %s\n", args.interface, args.queuename);
03238       break;
03239    }
03240 
03241    ast_module_user_remove(lu);
03242 
03243    return res;
03244 }

static AST_LIST_HEAD_STATIC ( queues  ,
call_queue   
) [static]

static AST_LIST_HEAD_STATIC ( interfaces  ,
member_interface   
) [static]

AST_MODULE_INFO ( ASTERISK_GPL_KEY  ,
AST_MODFLAG_DEFAULT  ,
"True Call Queueing"  ,
load = load_module,
unload = unload_module,
reload = reload 
)

static int background_file ( struct queue_ent qe,
struct ast_channel chan,
char *  filename 
) [static]

Definition at line 1769 of file app_queue.c.

References AST_DIGIT_ANY, ast_stopstream(), ast_streamfile(), ast_waitstream(), callattempt::chan, and valid_exit().

Referenced by say_periodic_announcement().

01770 {
01771    int res;
01772 
01773    ast_stopstream(chan);
01774    res = ast_streamfile(chan, filename, chan->language);
01775 
01776    if (!res) {
01777       /* Wait for a keypress */
01778       res = ast_waitstream(chan, AST_DIGIT_ANY);
01779       if (res < 0 || !valid_exit(qe, res))
01780          res = 0;
01781 
01782       /* Stop playback */
01783       ast_stopstream(chan);
01784    }
01785    
01786    return res;
01787 }

static int calc_metric ( struct call_queue q,
struct member mem,
int  pos,
struct queue_ent qe,
struct callattempt tmp 
) [static]

Definition at line 2241 of file app_queue.c.

References ast_log(), ast_random(), member::calls, member::lastcall, LOG_WARNING, queue_ent::max_penalty, callattempt::metric, member::penalty, QUEUE_STRATEGY_FEWESTCALLS, QUEUE_STRATEGY_LEASTRECENT, QUEUE_STRATEGY_RANDOM, QUEUE_STRATEGY_RINGALL, QUEUE_STRATEGY_ROUNDROBIN, call_queue::rrpos, call_queue::strategy, and call_queue::wrapped.

Referenced by try_calling().

02242 {
02243    if (qe->max_penalty && (mem->penalty > qe->max_penalty))
02244       return -1;
02245 
02246    switch (q->strategy) {
02247    case QUEUE_STRATEGY_RINGALL:
02248       /* Everyone equal, except for penalty */
02249       tmp->metric = mem->penalty * 1000000;
02250       break;
02251    case QUEUE_STRATEGY_ROUNDROBIN:
02252       if (!pos) {
02253          if (!q->wrapped) {
02254             /* No more channels, start over */
02255             q->rrpos = 0;
02256          } else {
02257             /* Prioritize next entry */
02258             q->rrpos++;
02259          }
02260          q->wrapped = 0;
02261       }
02262       /* Fall through */
02263    case QUEUE_STRATEGY_RRMEMORY:
02264       if (pos < q->rrpos) {
02265          tmp->metric = 1000 + pos;
02266       } else {
02267          if (pos > q->rrpos)
02268             /* Indicate there is another priority */
02269             q->wrapped = 1;
02270          tmp->metric = pos;
02271       }
02272       tmp->metric += mem->penalty * 1000000;
02273       break;
02274    case QUEUE_STRATEGY_RANDOM:
02275       tmp->metric = ast_random() % 1000;
02276       tmp->metric += mem->penalty * 1000000;
02277       break;
02278    case QUEUE_STRATEGY_FEWESTCALLS:
02279       tmp->metric = mem->calls;
02280       tmp->metric += mem->penalty * 1000000;
02281       break;
02282    case QUEUE_STRATEGY_LEASTRECENT:
02283       if (!mem->lastcall)
02284          tmp->metric = 0;
02285       else
02286          tmp->metric = 1000000 - (time(NULL) - mem->lastcall);
02287       tmp->metric += mem->penalty * 1000000;
02288       break;
02289    default:
02290       ast_log(LOG_WARNING, "Can't calculate metric for unknown strategy %d\n", q->strategy);
02291       break;
02292    }
02293    return 0;
02294 }

static void* changethread ( void *  data  )  [static]

Definition at line 512 of file app_queue.c.

References AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), ast_mutex_lock(), ast_mutex_unlock(), ast_strdupa, member::calls, statechange::dev, devstate2str(), member::dynamic, EVENT_FLAG_AGENT, free, member::interface, member_interface::interface, member::lastcall, call_queue::lock, LOG_DEBUG, manager_event(), call_queue::maskmemberstatus, member::membername, call_queue::members, call_queue::name, member::next, option_debug, member::paused, member::penalty, statechange::state, and member::status.

Referenced by statechange_queue().

00513 {
00514    struct call_queue *q;
00515    struct statechange *sc = data;
00516    struct member *cur;
00517    struct member_interface *curint;
00518    char *loc;
00519    char *technology;
00520 
00521    technology = ast_strdupa(sc->dev);
00522    loc = strchr(technology, '/');
00523    if (loc) {
00524       *loc++ = '\0';
00525    } else {
00526       free(sc);
00527       return NULL;
00528    }
00529 
00530    AST_LIST_LOCK(&interfaces);
00531    AST_LIST_TRAVERSE(&interfaces, curint, list) {
00532       char *interface;
00533       char *slash_pos;
00534       interface = ast_strdupa(curint->interface);
00535       if ((slash_pos = strchr(interface, '/')))
00536          if ((slash_pos = strchr(slash_pos + 1, '/')))
00537             *slash_pos = '\0';
00538 
00539       if (!strcasecmp(interface, sc->dev))
00540          break;
00541    }
00542    AST_LIST_UNLOCK(&interfaces);
00543 
00544    if (!curint) {
00545       if (option_debug > 2)
00546          ast_log(LOG_DEBUG, "Device '%s/%s' changed to state '%d' (%s) but we don't care because they're not a member of any queue.\n", technology, loc, sc->state, devstate2str(sc->state));
00547       free(sc);
00548       return NULL;
00549    }
00550 
00551    if (option_debug)
00552       ast_log(LOG_DEBUG, "Device '%s/%s' changed to state '%d' (%s)\n", technology, loc, sc->state, devstate2str(sc->state));
00553    AST_LIST_LOCK(&queues);
00554    AST_LIST_TRAVERSE(&queues, q, list) {
00555       ast_mutex_lock(&q->lock);
00556       for (cur = q->members; cur; cur = cur->next) {
00557          char *interface;
00558          char *slash_pos;
00559          interface = ast_strdupa(cur->interface);
00560          if ((slash_pos = strchr(interface, '/')))
00561             if ((slash_pos = strchr(slash_pos + 1, '/')))
00562                *slash_pos = '\0';
00563 
00564          if (strcasecmp(sc->dev, interface))
00565             continue;
00566 
00567          if (cur->status != sc->state) {
00568             cur->status = sc->state;
00569             if (q->maskmemberstatus)
00570                continue;
00571 
00572             manager_event(EVENT_FLAG_AGENT, "QueueMemberStatus",
00573                "Queue: %s\r\n"
00574                "Location: %s\r\n"
00575                "MemberName: %s\r\n"
00576                "Membership: %s\r\n"
00577                "Penalty: %d\r\n"
00578                "CallsTaken: %d\r\n"
00579                "LastCall: %d\r\n"
00580                "Status: %d\r\n"
00581                "Paused: %d\r\n",
00582                q->name, cur->interface, cur->membername, cur->dynamic ? "dynamic" : "static",
00583                cur->penalty, cur->calls, (int)cur->lastcall, cur->status, cur->paused);
00584          }
00585       }
00586       ast_mutex_unlock(&q->lock);
00587    }
00588    AST_LIST_UNLOCK(&queues);
00589 
00590    return NULL;
00591 }

static void clear_and_free_interfaces ( void   )  [static]

Definition at line 761 of file app_queue.c.

References AST_LIST_LOCK, AST_LIST_REMOVE_HEAD, AST_LIST_UNLOCK, and free.

Referenced by unload_module().

00762 {
00763    struct member_interface *curint;
00764 
00765    AST_LIST_LOCK(&interfaces);
00766    while ((curint = AST_LIST_REMOVE_HEAD(&interfaces, list)))
00767       free(curint);
00768    AST_LIST_UNLOCK(&interfaces);
00769 }

static void clear_queue ( struct call_queue q  )  [static]

Definition at line 681 of file app_queue.c.

References call_queue::callsabandoned, call_queue::callscompleted, call_queue::callscompletedinsl, call_queue::holdtime, and call_queue::wrapuptime.

Referenced by find_queue_by_name_rt(), and reload_queues().

00682 {
00683    q->holdtime = 0;
00684    q->callscompleted = 0;
00685    q->callsabandoned = 0;
00686    q->callscompletedinsl = 0;
00687    q->wrapuptime = 0;
00688 }

static int compare_weight ( struct call_queue rq,
struct member member 
) [static]

Definition at line 1500 of file app_queue.c.

References AST_LIST_TRAVERSE, ast_log(), ast_mutex_lock(), ast_mutex_unlock(), call_queue::count, member::interface, call_queue::lock, LOG_DEBUG, call_queue::members, call_queue::name, member::next, and call_queue::weight.

Referenced by ring_entry().

01501 {
01502    struct call_queue *q;
01503    struct member *mem;
01504    int found = 0;
01505    
01506    /* &qlock and &rq->lock already set by try_calling()
01507     * to solve deadlock */
01508    AST_LIST_TRAVERSE(&queues, q, list) {
01509       if (q == rq) /* don't check myself, could deadlock */
01510          continue;
01511       ast_mutex_lock(&q->lock);
01512       if (q->count && q->members) {
01513          for (mem = q->members; mem; mem = mem->next) {
01514             if (strcmp(mem->interface, member->interface))
01515                continue;
01516 
01517             ast_log(LOG_DEBUG, "Found matching member %s in queue '%s'\n", mem->interface, q->name);
01518             if (q->weight > rq->weight) {
01519                ast_log(LOG_DEBUG, "Queue '%s' (weight %d, calls %d) is preferred over '%s' (weight %d, calls %d)\n", q->name, q->weight, q->count, rq->name, rq->weight, rq->count);
01520                found = 1;
01521                break;
01522             }
01523          }
01524       }
01525       ast_mutex_unlock(&q->lock);
01526       if (found)
01527          break;
01528    }
01529    return found;
01530 }

static char* complete_queue ( const char *  line,
const char *  word,
int  pos,
int  state 
) [static]

Definition at line 4026 of file app_queue.c.

References AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_strdup, and call_queue::name.

Referenced by complete_queue_add_member(), and complete_queue_remove_member().

04027 {
04028    struct call_queue *q;
04029    char *ret = NULL;
04030    int which = 0;
04031    int wordlen = strlen(word);
04032    
04033    AST_LIST_LOCK(&queues);
04034    AST_LIST_TRAVERSE(&queues, q, list) {
04035       if (!strncasecmp(word, q->name, wordlen) && ++which > state) {
04036          ret = ast_strdup(q->name); 
04037          break;
04038       }
04039    }
04040    AST_LIST_UNLOCK(&queues);
04041 
04042    return ret;
04043 }

static char* complete_queue_add_member ( const char *  line,
const char *  word,
int  pos,
int  state 
) [static]

Definition at line 4312 of file app_queue.c.

References ast_malloc, ast_strdup, and complete_queue().

04313 {
04314    /* 0 - queue; 1 - add; 2 - member; 3 - <interface>; 4 - to; 5 - <queue>; 6 - penalty; 7 - <penalty>; 8 - as; 9 - <membername> */
04315    switch (pos) {
04316    case 3:  /* Don't attempt to complete name of interface (infinite possibilities) */
04317       return NULL;
04318    case 4:  /* only one possible match, "to" */
04319       return state == 0 ? ast_strdup("to") : NULL;
04320    case 5:  /* <queue> */
04321       return complete_queue(line, word, pos, state);
04322    case 6: /* only one possible match, "penalty" */
04323       return state == 0 ? ast_strdup("penalty") : NULL;
04324    case 7:
04325       if (state < 100) {   /* 0-99 */
04326          char *num;
04327          if ((num = ast_malloc(3))) {
04328             sprintf(num, "%d", state);
04329          }
04330          return num;
04331       } else {
04332          return NULL;
04333       }
04334    case 8: /* only one possible match, "as" */
04335       return state == 0 ? ast_strdup("as") : NULL;
04336    case 9:  /* Don't attempt to complete name of member (infinite possibilities) */
04337       return NULL;
04338    default:
04339       return NULL;
04340    }
04341 }

static char* complete_queue_remove_member ( const char *  line,
const char *  word,
int  pos,
int  state 
) [static]

Definition at line 4375 of file app_queue.c.

References AST_LIST_EMPTY, AST_LIST_TRAVERSE, ast_mutex_lock(), ast_mutex_unlock(), ast_strdup, complete_queue(), member::interface, call_queue::lock, call_queue::members, and member::next.

04376 {
04377    int which = 0;
04378    struct call_queue *q;
04379    struct member *m;
04380 
04381    /* 0 - queue; 1 - remove; 2 - member; 3 - <member>; 4 - from; 5 - <queue> */
04382    if (pos > 5 || pos < 3)
04383       return NULL;
04384    if (pos == 4)  /* only one possible match, 'from' */
04385       return state == 0 ? ast_strdup("from") : NULL;
04386 
04387    if (pos == 5)  /* No need to duplicate code */
04388       return complete_queue(line, word, pos, state);
04389 
04390    /* here is the case for 3, <member> */
04391    if (!AST_LIST_EMPTY(&queues)) { /* XXX unnecessary ? the traverse does that for us */
04392       AST_LIST_TRAVERSE(&queues, q, list) {
04393          ast_mutex_lock(&q->lock);
04394          for (m = q->members ; m ; m = m->next) {
04395             if (++which > state) {
04396                ast_mutex_unlock(&q->lock);
04397                return ast_strdup(m->interface);
04398             }
04399          }
04400          ast_mutex_unlock(&q->lock);
04401       }
04402    }
04403 
04404    return NULL;
04405 }

static struct member* create_queue_member ( const char *  interface,
const char *  membername,
int  penalty,
int  paused 
) [static]

Definition at line 617 of file app_queue.c.

References ast_calloc, ast_device_state(), ast_log(), LOG_WARNING, and member::penalty.

Referenced by add_to_queue(), reload_queues(), and rt_handle_member_record().

00618 {
00619    struct member *cur;
00620    
00621    if ((cur = ast_calloc(1, sizeof(*cur)))) {
00622       cur->penalty = penalty;
00623       cur->paused = paused;
00624       ast_copy_string(cur->interface, interface, sizeof(cur->interface));
00625       ast_copy_string(cur->membername, membername, sizeof(cur->membername));
00626       if (!strchr(cur->interface, '/'))
00627          ast_log(LOG_WARNING, "No location at interface '%s'\n", interface);
00628       cur->status = ast_device_state(interface);
00629    }
00630 
00631    return cur;
00632 }

static void destroy_queue ( struct call_queue q  )  [static]

Definition at line 986 of file app_queue.c.

References ast_mutex_destroy(), free, free_members(), and call_queue::lock.

Referenced by find_queue_by_name_rt(), and leave_queue().

00987 {
00988    free_members(q, 1);
00989    ast_mutex_destroy(&q->lock);
00990    free(q);
00991 }

static void do_hang ( struct callattempt o  )  [static]

common hangup actions

Definition at line 1533 of file app_queue.c.

References ast_hangup(), callattempt::chan, and callattempt::stillgoing.

Referenced by ring_entry(), and wait_for_answer().

01534 {
01535    o->stillgoing = 0;
01536    ast_hangup(o->chan);
01537    o->chan = NULL;
01538 }

static void dump_queue_members ( struct call_queue pm_queue  )  [static]

Definition at line 2728 of file app_queue.c.

References ast_db_del(), ast_db_put(), ast_log(), member::dynamic, member::interface, LOG_WARNING, member::membername, call_queue::members, call_queue::name, member::next, member::paused, member::penalty, and PM_MAX_LEN.

Referenced by add_to_queue(), remove_from_queue(), and set_member_paused().

02729 {
02730    struct member *cur_member;
02731    char value[PM_MAX_LEN];
02732    int value_len = 0;
02733    int res;
02734 
02735    memset(value, 0, sizeof(value));
02736 
02737    if (!pm_queue)
02738       return;
02739 
02740    for (cur_member = pm_queue->members; cur_member; cur_member = cur_member->next) {
02741       if (!cur_member->dynamic)
02742          continue;
02743 
02744       res = snprintf(value + value_len, sizeof(value) - value_len, "%s;%d;%d;%s%s",
02745          cur_member->interface, cur_member->penalty, cur_member->paused, cur_member->membername,
02746          cur_member->next ? "|" : "");
02747       if (res != strlen(value + value_len)) {
02748          ast_log(LOG_WARNING, "Could not create persistent member string, out of space\n");
02749          break;
02750       }
02751       value_len += res;
02752    }
02753    
02754    if (value_len && !cur_member) {
02755       if (ast_db_put(pm_family, pm_queue->name, value))
02756          ast_log(LOG_WARNING, "failed to create persistent dynamic entry!\n");
02757    } else
02758       /* Delete the entry if the queue is empty or there is an error */
02759       ast_db_del(pm_family, pm_queue->name);
02760 }

static struct callattempt* find_best ( struct callattempt outgoing  )  [static]

find the entry with the best metric, or NULL

Definition at line 1696 of file app_queue.c.

References callattempt::metric, and callattempt::q_next.

01697 {
01698    struct callattempt *best = NULL, *cur;
01699 
01700    for (cur = outgoing; cur; cur = cur->q_next) {
01701       if (cur->stillgoing &&              /* Not already done */
01702          !cur->chan &&              /* Isn't already going */
01703          (!best || cur->metric < best->metric)) {     /* We haven't found one yet, or it's better */
01704          best = cur;
01705       }
01706    }
01707 
01708    return best;
01709 }

static struct call_queue* find_queue_by_name_rt ( const char *  queuename,
struct ast_variable queue_vars,
struct ast_config member_config 
) [static]

Reload a single queue via realtime.

Returns:
Return the queue, or NULL if it doesn't exist.
Note:
Should be called with the global qlock locked.

Note:
Hmm, can't seem to distinguish a DB failure from a not found condition... So we might delete an in-core queue in case of DB failure.

Definition at line 996 of file app_queue.c.

References alloc_queue(), AST_LIST_INSERT_HEAD, AST_LIST_REMOVE, AST_LIST_TRAVERSE, ast_log(), ast_mutex_lock(), ast_mutex_unlock(), clear_queue(), call_queue::dead, destroy_queue(), init_queue(), member_interface::interface, call_queue::lock, LOG_DEBUG, ast_variable::name, call_queue::name, ast_variable::next, queue_set_param(), call_queue::realtime, and ast_variable::value.

Referenced by load_realtime_queue().

00997 {
00998    struct ast_variable *v;
00999    struct call_queue *q;
01000    struct member *m, *prev_m, *next_m;
01001    char *interface = NULL;
01002    char *tmp, *tmp_name;
01003    char tmpbuf[64];  /* Must be longer than the longest queue param name. */
01004 
01005    /* Find the queue in the in-core list (we will create a new one if not found). */
01006    AST_LIST_TRAVERSE(&queues, q, list) {
01007       if (!strcasecmp(q->name, queuename))
01008          break;
01009    }
01010 
01011    /* Static queues override realtime. */
01012    if (q) {
01013       ast_mutex_lock(&q->lock);
01014       if (!q->realtime) {
01015          if (q->dead) {
01016             ast_mutex_unlock(&q->lock);
01017             return NULL;
01018          } else {
01019             ast_mutex_unlock(&q->lock);
01020             return q;
01021          }
01022       }
01023    } else if (!member_config)
01024       /* Not found in the list, and it's not realtime ... */
01025       return NULL;
01026 
01027    /* Check if queue is defined in realtime. */
01028    if (!queue_vars) {
01029       /* Delete queue from in-core list if it has been deleted in realtime. */
01030       if (q) {
01031          /*! \note Hmm, can't seem to distinguish a DB failure from a not
01032             found condition... So we might delete an in-core queue
01033             in case of DB failure. */
01034          ast_log(LOG_DEBUG, "Queue %s not found in realtime.\n", queuename);
01035 
01036          q->dead = 1;
01037          /* Delete if unused (else will be deleted when last caller leaves). */
01038          if (!q->count) {
01039             /* Delete. */
01040             AST_LIST_REMOVE(&queues, q, list);
01041             ast_mutex_unlock(&q->lock);
01042             destroy_queue(q);
01043          } else
01044             ast_mutex_unlock(&q->lock);
01045       }
01046       return NULL;
01047    }
01048 
01049    /* Create a new queue if an in-core entry does not exist yet. */
01050    if (!q) {
01051       if (!(q = alloc_queue(queuename)))
01052          return NULL;
01053       ast_mutex_lock(&q->lock);
01054       clear_queue(q);
01055       q->realtime = 1;
01056       AST_LIST_INSERT_HEAD(&queues, q, list);
01057    }
01058    init_queue(q);    /* Ensure defaults for all parameters not set explicitly. */
01059 
01060    memset(tmpbuf, 0, sizeof(tmpbuf));
01061    for (v = queue_vars; v; v = v->next) {
01062       /* Convert to dashes `-' from underscores `_' as the latter are more SQL friendly. */
01063       if ((tmp = strchr(v->name, '_'))) {
01064          ast_copy_string(tmpbuf, v->name, sizeof(tmpbuf));
01065          tmp_name = tmpbuf;
01066          tmp = tmp_name;
01067          while ((tmp = strchr(tmp, '_')))
01068             *tmp++ = '-';
01069       } else
01070          tmp_name = v->name;
01071       queue_set_param(q, tmp_name, v->value, -1, 0);
01072    }
01073 
01074    if (q->strategy == QUEUE_STRATEGY_ROUNDROBIN)
01075       rr_dep_warning();
01076 
01077    /* Temporarily set non-dynamic members dead so we can detect deleted ones. */
01078    for (m = q->members; m; m = m->next) {
01079       if (!m->dynamic)
01080          m->dead = 1;
01081    }
01082 
01083    while ((interface = ast_category_browse(member_config, interface))) {
01084       rt_handle_member_record(q, interface,
01085          S_OR(ast_variable_retrieve(member_config, interface, "membername"), interface),
01086          ast_variable_retrieve(member_config, interface, "penalty"),
01087          ast_variable_retrieve(member_config, interface, "paused"));
01088    }
01089 
01090    /* Delete all realtime members that have been deleted in DB. */
01091    m = q->members;
01092    prev_m = NULL;
01093    while (m) {
01094       next_m = m->next;
01095       if (m->dead) {
01096          if (prev_m) {
01097             prev_m->next = next_m;
01098          } else {
01099             q->members = next_m;
01100          }
01101          remove_from_interfaces(m->interface);
01102          free(m);
01103       } else {
01104          prev_m = m;
01105       }
01106       m = next_m;
01107    }
01108 
01109    ast_mutex_unlock(&q->lock);
01110 
01111    return q;
01112 }

static void free_members ( struct call_queue q,
int  all 
) [static]

Definition at line 967 of file app_queue.c.

References member::dynamic, free, member::interface, call_queue::members, member::next, and remove_from_interfaces().

Referenced by destroy_queue().

00968 {
00969    /* Free non-dynamic members */
00970    struct member *curm, *next, *prev = NULL;
00971 
00972    for (curm = q->members; curm; curm = next) {
00973       next = curm->next;
00974       if (all || !curm->dynamic) {
00975          if (prev)
00976             prev->next = next;
00977          else
00978             q->members = next;
00979          remove_from_interfaces(curm->interface);
00980          free(curm);
00981       } else
00982          prev = curm;
00983    }
00984 }

static enum queue_member_status get_member_status ( struct call_queue q,
int  max_penalty 
) [static]

Definition at line 478 of file app_queue.c.

References AST_DEVICE_INVALID, AST_DEVICE_UNAVAILABLE, ast_mutex_lock(), ast_mutex_unlock(), call_queue::lock, call_queue::members, member::next, member::paused, member::penalty, QUEUE_NO_MEMBERS, QUEUE_NO_REACHABLE_MEMBERS, QUEUE_NORMAL, result, and member::status.

Referenced by join_queue(), queue_exec(), and wait_our_turn().

00479 {
00480    struct member *member;
00481    enum queue_member_status result = QUEUE_NO_MEMBERS;
00482 
00483    ast_mutex_lock(&q->lock);
00484    for (member = q->members; member; member = member->next) {
00485       if (max_penalty && (member->penalty > max_penalty))
00486          continue;
00487 
00488       if (member->paused) continue;
00489 
00490       switch (member->status) {
00491       case AST_DEVICE_INVALID:
00492          /* nothing to do */
00493          break;
00494       case AST_DEVICE_UNAVAILABLE:
00495          result = QUEUE_NO_REACHABLE_MEMBERS;
00496          break;
00497       default:
00498          ast_mutex_unlock(&q->lock);
00499          return QUEUE_NORMAL;
00500       }
00501    }
00502    
00503    ast_mutex_unlock(&q->lock);
00504    return result;
00505 }

static int handle_queue_add_member ( int  fd,
int  argc,
char *  argv[] 
) [static]

Definition at line 4256 of file app_queue.c.

References add_to_queue(), ast_cli(), ast_queue_log(), member::interface, member::membername, member::penalty, RES_EXISTS, RES_NOSUCHQUEUE, RES_OKAY, RES_OUTOFMEMORY, RESULT_FAILURE, RESULT_SHOWUSAGE, and RESULT_SUCCESS.

04257 {
04258    char *queuename, *interface, *membername;
04259    int penalty;
04260 
04261    if ((argc != 6) && (argc != 8) && (argc != 10)) {
04262       return RESULT_SHOWUSAGE;
04263    } else if (strcmp(argv[4], "to")) {
04264       return RESULT_SHOWUSAGE;
04265    } else if ((argc == 8) && strcmp(argv[6], "penalty")) {
04266       return RESULT_SHOWUSAGE;
04267    } else if ((argc == 10) && strcmp(argv[8], "as")) {
04268       return RESULT_SHOWUSAGE;
04269    }
04270 
04271    queuename = argv[5];
04272    interface = argv[3];
04273    if (argc >= 8) {
04274       if (sscanf(argv[7], "%d", &penalty) == 1) {
04275          if (penalty < 0) {
04276             ast_cli(fd, "Penalty must be >= 0\n");
04277             penalty = 0;
04278          }
04279       } else {
04280          ast_cli(fd, "Penalty must be an integer >= 0\n");
04281          penalty = 0;
04282       }
04283    } else {
04284       penalty = 0;
04285    }
04286 
04287    if (argc >= 10) {
04288       membername = argv[9];
04289    } else {
04290       membername = interface;
04291    }
04292 
04293    switch (add_to_queue(queuename, interface, membername, penalty, 0, queue_persistent_members)) {
04294    case RES_OKAY:
04295       ast_queue_log(queuename, "CLI", interface, "ADDMEMBER", "%s", "");
04296       ast_cli(fd, "Added interface '%s' to queue '%s'\n", interface, queuename);
04297       return RESULT_SUCCESS;
04298    case RES_EXISTS:
04299       ast_cli(fd, "Unable to add interface '%s' to queue '%s': Already there\n", interface, queuename);
04300       return RESULT_FAILURE;
04301    case RES_NOSUCHQUEUE:
04302       ast_cli(fd, "Unable to add interface to queue '%s': No such queue\n", queuename);
04303       return RESULT_FAILURE;
04304    case RES_OUTOFMEMORY:
04305       ast_cli(fd, "Out of memory\n");
04306       return RESULT_FAILURE;
04307    default:
04308       return RESULT_FAILURE;
04309    }
04310 }

static int handle_queue_remove_member ( int  fd,
int  argc,
char *  argv[] 
) [static]

Definition at line 4343 of file app_queue.c.

References ast_cli(), ast_queue_log(), member::interface, remove_from_queue(), RES_EXISTS, RES_NOSUCHQUEUE, RES_OKAY, RES_OUTOFMEMORY, RESULT_FAILURE, RESULT_SHOWUSAGE, and RESULT_SUCCESS.

04344 {
04345    char *queuename, *interface;
04346 
04347    if (argc != 6) {
04348       return RESULT_SHOWUSAGE;
04349    } else if (strcmp(argv[4], "from")) {
04350       return RESULT_SHOWUSAGE;
04351    }
04352 
04353    queuename = argv[5];
04354    interface = argv[3];
04355 
04356    switch (remove_from_queue(queuename, interface)) {
04357    case RES_OKAY:
04358       ast_queue_log(queuename, "CLI", interface, "REMOVEMEMBER", "%s", "");
04359       ast_cli(fd, "Removed interface '%s' from queue '%s'\n", interface, queuename);
04360       return RESULT_SUCCESS;
04361    case RES_EXISTS:
04362       ast_cli(fd, "Unable to remove interface '%s' from queue '%s': Not there\n", interface, queuename);
04363       return RESULT_FAILURE;
04364    case RES_NOSUCHQUEUE:
04365       ast_cli(fd, "Unable to remove interface from queue '%s': No such queue\n", queuename);
04366       return RESULT_FAILURE;
04367    case RES_OUTOFMEMORY:
04368       ast_cli(fd, "Out of memory\n");
04369       return RESULT_FAILURE;
04370    default:
04371       return RESULT_FAILURE;
04372    }
04373 }

static void hangupcalls ( struct callattempt outgoing,
struct ast_channel exception 
) [static]

Definition at line 1440 of file app_queue.c.

References ast_hangup(), callattempt::chan, free, and callattempt::q_next.

Referenced by try_calling().

01441 {
01442    struct callattempt *oo;
01443 
01444    while (outgoing) {
01445       /* Hangup any existing lines we have open */
01446       if (outgoing->chan && (outgoing->chan != exception))
01447          ast_hangup(outgoing->chan);
01448       oo = outgoing;
01449       outgoing = outgoing->q_next;
01450       free(oo);
01451    }
01452 }

static void init_queue ( struct call_queue q  )  [static]

Definition at line 645 of file app_queue.c.

References call_queue::announce, call_queue::announcefrequency, call_queue::announceholdtime, call_queue::autofill, call_queue::context, call_queue::dead, DEFAULT_RETRY, MAX_PERIODIC_ANNOUNCEMENTS, call_queue::maxlen, call_queue::moh, call_queue::monfmt, call_queue::montype, call_queue::periodicannouncefrequency, call_queue::retry, call_queue::ringinuse, call_queue::roundingseconds, call_queue::servicelevel, call_queue::setinterfacevar, call_queue::sound_calls, call_queue::sound_holdtime, call_queue::sound_lessthan, call_queue::sound_minutes, call_queue::sound_next, call_queue::sound_periodicannounce, call_queue::sound_reporthold, call_queue::sound_seconds, call_queue::sound_thanks, call_queue::sound_thereare, and call_queue::timeout.

Referenced by find_queue_by_name_rt(), and reload_queues().

00646 {
00647    int i;
00648 
00649    q->dead = 0;
00650    q->retry = DEFAULT_RETRY;
00651    q->timeout = -1;
00652    q->maxlen = 0;
00653    q->announcefrequency = 0;
00654    q->announceholdtime = 0;
00655    q->roundingseconds = 0; /* Default - don't announce seconds */
00656    q->servicelevel = 0;
00657    q->ringinuse = 1;
00658    q->setinterfacevar = 0;
00659    q->autofill = autofill_default;
00660    q->montype = montype_default;
00661    q->moh[0] = '\0';
00662    q->announce[0] = '\0';
00663    q->context[0] = '\0';
00664    q->monfmt[0] = '\0';
00665    q->periodicannouncefrequency = 0;
00666    ast_copy_string(q->sound_next, "queue-youarenext", sizeof(q->sound_next));
00667    ast_copy_string(q->sound_thereare, "queue-thereare", sizeof(q->sound_thereare));
00668    ast_copy_string(q->sound_calls, "queue-callswaiting", sizeof(q->sound_calls));
00669    ast_copy_string(q->sound_holdtime, "queue-holdtime", sizeof(q->sound_holdtime));
00670    ast_copy_string(q->sound_minutes, "queue-minutes", sizeof(q->sound_minutes));
00671    ast_copy_string(q->sound_seconds, "queue-seconds", sizeof(q->sound_seconds));
00672    ast_copy_string(q->sound_thanks, "queue-thankyou", sizeof(q->sound_thanks));
00673    ast_copy_string(q->sound_lessthan, "queue-less-than", sizeof(q->sound_lessthan));
00674    ast_copy_string(q->sound_reporthold, "queue-reporthold", sizeof(q->sound_reporthold));
00675    ast_copy_string(q->sound_periodicannounce[0], "queue-periodic-announce", sizeof(q->sound_periodicannounce[0]));
00676    for (i = 1; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) {
00677       q->sound_periodicannounce[i][0]='\0';
00678    }
00679 }

static void insert_entry ( struct call_queue q,
struct queue_ent prev,
struct queue_ent new,
int *  pos 
) [inline, static]

Insert the 'new' entry after the 'prev' entry of queue 'q'.

Definition at line 453 of file app_queue.c.

References call_queue::head, queue_ent::next, and queue_ent::parent.

Referenced by join_queue().

00454 {
00455    struct queue_ent *cur;
00456 
00457    if (!q || !new)
00458       return;
00459    if (prev) {
00460       cur = prev->next;
00461       prev->next = new;
00462    } else {
00463       cur = q->head;
00464       q->head = new;
00465    }
00466    new->next = cur;
00467    new->parent = q;
00468    new->pos = ++(*pos);
00469    new->opos = *pos;
00470 }

static char* int2strat ( int  strategy  )  [static]

Definition at line 428 of file app_queue.c.

References name, and strategies.

Referenced by __queues_show().

00429 {
00430    int x;
00431 
00432    for (x = 0; x < sizeof(strategies) / sizeof(strategies[0]); x++) {
00433       if (strategy == strategies[x].strategy)
00434          return strategies[x].name;
00435    }
00436 
00437    return "<unknown>";
00438 }

static struct member* interface_exists ( struct call_queue q,
const char *  interface 
) [static]

Definition at line 2707 of file app_queue.c.

References member::interface, call_queue::members, and member::next.

Referenced by add_to_queue(), remove_from_queue(), and set_member_paused().

02708 {
02709    struct member *mem;
02710 
02711    if (!q)
02712       return NULL;
02713 
02714    for (mem = q->members; mem; mem = mem->next) {
02715       if (!strcasecmp(interface, mem->interface))
02716          return mem;
02717    }
02718 
02719    return NULL;
02720 }

static int interface_exists_global ( const char *  interface  )  [static]

Definition at line 717 of file app_queue.c.

References AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_mutex_lock(), ast_mutex_unlock(), member::interface, call_queue::lock, call_queue::members, and member::next.

Referenced by remove_from_interfaces().

00718 {
00719    struct call_queue *q;
00720    struct member *mem;
00721    int ret = 0;
00722 
00723    AST_LIST_LOCK(&queues);
00724    AST_LIST_TRAVERSE(&queues, q, list) {
00725       ast_mutex_lock(&q->lock);
00726       for (mem = q->members; mem && !ret; mem = mem->next) {
00727          if (!strcasecmp(interface, mem->interface))
00728             ret = 1;
00729       }
00730       ast_mutex_unlock(&q->lock);
00731       if (ret)
00732          break;
00733    }
00734    AST_LIST_UNLOCK(&queues);
00735 
00736    return ret;
00737 }

static int is_our_turn ( struct queue_ent qe  )  [static]

Definition at line 2099 of file app_queue.c.

References AST_DEVICE_NOT_INUSE, AST_DEVICE_UNKNOWN, ast_log(), ast_mutex_lock(), ast_mutex_unlock(), call_queue::autofill, queue_ent::chan, call_queue::head, call_queue::lock, LOG_DEBUG, call_queue::members, queue_ent::next, member::next, option_debug, queue_ent::parent, QUEUE_STRATEGY_RINGALL, member::status, and call_queue::strategy.

Referenced by queue_exec(), and wait_our_turn().

02100 {
02101    struct queue_ent *ch;
02102    struct member *cur;
02103    int avl = 0;
02104    int idx = 0;
02105    int res;
02106 
02107    if (!qe->parent->autofill) {
02108       /* Atomically read the parent head -- does not need a lock */
02109       ch = qe->parent->head;
02110       /* If we are now at the top of the head, break out */
02111       if (ch == qe) {
02112          if (option_debug)
02113             ast_log(LOG_DEBUG, "It's our turn (%s).\n", qe->chan->name);
02114          res = 1;
02115       } else {
02116          if (option_debug)
02117             ast_log(LOG_DEBUG, "It's not our turn (%s).\n", qe->chan->name);
02118          res = 0;
02119       }  
02120 
02121    } else {
02122       /* This needs a lock. How many members are available to be served? */
02123       ast_mutex_lock(&qe->parent->lock);
02124          
02125       ch = qe->parent->head;
02126    
02127       if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
02128          if (option_debug)
02129             ast_log(LOG_DEBUG, "Even though there are %d available members, the strategy is ringall so only the head call is allowed in\n", avl);
02130          avl = 1;
02131       } else {
02132          for (cur = qe->parent->members; cur; cur = cur->next) {
02133             switch (cur->status) {
02134             case AST_DEVICE_NOT_INUSE:
02135             case AST_DEVICE_UNKNOWN:
02136                avl++;
02137                break;
02138             }
02139          }
02140       }
02141 
02142       if (option_debug)
02143          ast_log(LOG_DEBUG, "There are %d available members.\n", avl);
02144    
02145       while ((idx < avl) && (ch) && (ch != qe)) {
02146          idx++;
02147          ch = ch->next;       
02148       }
02149    
02150       /* If the queue entry is within avl [the number of available members] calls from the top ... */
02151       if (ch && idx < avl) {
02152          if (option_debug)
02153             ast_log(LOG_DEBUG, "It's our turn (%s).\n", qe->chan->name);
02154          res = 1;
02155       } else {
02156          if (option_debug)
02157             ast_log(LOG_DEBUG, "It's not our turn (%s).\n", qe->chan->name);
02158          res = 0;
02159       }
02160       
02161       ast_mutex_unlock(&qe->parent->lock);
02162    }
02163 
02164    return res;
02165 }

static int join_queue ( char *  queuename,
struct queue_ent qe,
enum queue_result reason 
) [static]

Definition at line 1161 of file app_queue.c.

References queue_ent::announce, AST_LIST_LOCK, AST_LIST_UNLOCK, ast_log(), ast_mutex_lock(), ast_mutex_unlock(), queue_ent::chan, ast_channel::cid, ast_callerid::cid_name, ast_callerid::cid_num, queue_ent::context, call_queue::count, EVENT_FLAG_CALL, get_member_status(), call_queue::head, insert_entry(), call_queue::joinempty, load_realtime_queue(), call_queue::lock, LOG_DEBUG, manager_event(), queue_ent::max_penalty, call_queue::maxlen, queue_ent::moh, queue_ent::next, option_debug, queue_ent::pos, queue_ent::prio, QUEUE_EMPTY_STRICT, QUEUE_FULL, QUEUE_JOINEMPTY, QUEUE_JOINUNAVAIL, QUEUE_NO_MEMBERS, QUEUE_NO_REACHABLE_MEMBERS, and S_OR.

Referenced by queue_exec().

01162 {
01163    struct call_queue *q;
01164    struct queue_ent *cur, *prev = NULL;
01165    int res = -1;
01166    int pos = 0;
01167    int inserted = 0;
01168    enum queue_member_status stat;
01169 
01170    if (!(q = load_realtime_queue(queuename)))
01171       return res;
01172 
01173    AST_LIST_LOCK(&queues);
01174    ast_mutex_lock(&q->lock);
01175 
01176    /* This is our one */
01177    stat = get_member_status(q, qe->max_penalty);
01178    if (!q->joinempty && (stat == QUEUE_NO_MEMBERS))
01179       *reason = QUEUE_JOINEMPTY;
01180    else if ((q->joinempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS))
01181       *reason = QUEUE_JOINUNAVAIL;
01182    else if (q->maxlen && (q->count >= q->maxlen))
01183       *reason = QUEUE_FULL;
01184    else {
01185       /* There's space for us, put us at the right position inside
01186        * the queue.
01187        * Take into account the priority of the calling user */
01188       inserted = 0;
01189       prev = NULL;
01190       cur = q->head;
01191       while (cur) {
01192          /* We have higher priority than the current user, enter
01193           * before him, after all the other users with priority
01194           * higher or equal to our priority. */
01195          if ((!inserted) && (qe->prio > cur->prio)) {
01196             insert_entry(q, prev, qe, &pos);
01197             inserted = 1;
01198          }
01199          cur->pos = ++pos;
01200          prev = cur;
01201          cur = cur->next;
01202       }
01203       /* No luck, join at the end of the queue */
01204       if (!inserted)
01205          insert_entry(q, prev, qe, &pos);
01206       ast_copy_string(qe->moh, q->moh, sizeof(qe->moh));
01207       ast_copy_string(qe->announce, q->announce, sizeof(qe->announce));
01208       ast_copy_string(qe->context, q->context, sizeof(qe->context));
01209       q->count++;
01210       res = 0;
01211       manager_event(EVENT_FLAG_CALL, "Join",
01212          "Channel: %s\r\nCallerID: %s\r\nCallerIDName: %s\r\nQueue: %s\r\nPosition: %d\r\nCount: %d\r\nUniqueid: %s\r\n",
01213          qe->chan->name,
01214          S_OR(qe->chan->cid.cid_num, "unknown"), /* XXX somewhere else it is <unknown> */
01215          S_OR(qe->chan->cid.cid_name, "unknown"),
01216          q->name, qe->pos, q->count, qe->chan->uniqueid );
01217       if (option_debug)
01218          ast_log(LOG_DEBUG, "Queue '%s' Join, Channel '%s', Position '%d'\n", q->name, qe->chan->name, qe->pos );
01219    }
01220    ast_mutex_unlock(&q->lock);
01221    AST_LIST_UNLOCK(&queues);
01222 
01223    return res;
01224 }

static void leave_queue ( struct queue_ent qe  )  [static]

Definition at line 1396 of file app_queue.c.

References AST_LIST_LOCK, AST_LIST_REMOVE, AST_LIST_UNLOCK, ast_log(), ast_mutex_lock(), ast_mutex_unlock(), queue_ent::chan, call_queue::count, call_queue::dead, destroy_queue(), EVENT_FLAG_CALL, call_queue::head, call_queue::lock, LOG_DEBUG, manager_event(), call_queue::name, queue_ent::next, option_debug, queue_ent::parent, and queue_ent::pos.

Referenced by queue_exec(), try_calling(), and wait_our_turn().

01397 {
01398    struct call_queue *q;
01399    struct queue_ent *cur, *prev = NULL;
01400    int pos = 0;
01401 
01402    if (!(q = qe->parent))
01403       return;
01404    ast_mutex_lock(&q->lock);
01405 
01406    prev = NULL;
01407    for (cur = q->head; cur; cur = cur->next) {
01408       if (cur == qe) {
01409          q->count--;
01410 
01411          /* Take us out of the queue */
01412          manager_event(EVENT_FLAG_CALL, "Leave",
01413             "Channel: %s\r\nQueue: %s\r\nCount: %d\r\nUniqueid: %s\r\n",
01414             qe->chan->name, q->name,  q->count, qe->chan->uniqueid);
01415          if (option_debug)
01416             ast_log(LOG_DEBUG, "Queue '%s' Leave, Channel '%s'\n", q->name, qe->chan->name );
01417          /* Take us out of the queue */
01418          if (prev)
01419             prev->next = cur->next;
01420          else
01421             q->head = cur->next;
01422       } else {
01423          /* Renumber the people after us in the queue based on a new count */
01424          cur->pos = ++pos;
01425          prev = cur;
01426       }
01427    }
01428    ast_mutex_unlock(&q->lock);
01429 
01430    if (q->dead && !q->count) {   
01431       /* It's dead and nobody is in it, so kill it */
01432       AST_LIST_LOCK(&queues);
01433       AST_LIST_REMOVE(&queues, q, list);
01434       AST_LIST_UNLOCK(&queues);
01435       destroy_queue(q);
01436    }
01437 }

static int load_module ( void   )  [static]

Definition at line 4480 of file app_queue.c.

References aqm_exec(), ast_cli_register_multiple(), ast_custom_function_register(), ast_devstate_add(), ast_manager_register, AST_MODULE_LOAD_DECLINE, ast_register_application(), cli_queue, EVENT_FLAG_AGENT, manager_add_queue_member(), manager_pause_queue_member(), manager_queues_show(), manager_queues_status(), manager_remove_queue_member(), pqm_exec(), ql_exec(), queue_exec(), queueagentcount_function, queuemembercount_function, queuememberlist_function, queuewaitingcount_function, reload_queue_members(), reload_queues(), rqm_exec(), statechange_queue(), and upqm_exec().

04481 {
04482    int res;
04483    if(!reload_queues())
04484       return AST_MODULE_LOAD_DECLINE;
04485    if (queue_persistent_members)
04486       reload_queue_members();
04487    ast_cli_register_multiple(cli_queue, sizeof(cli_queue) / sizeof(struct ast_cli_entry));
04488    res = ast_register_application(app, queue_exec, synopsis, descrip);
04489    res |= ast_register_application(app_aqm, aqm_exec, app_aqm_synopsis, app_aqm_descrip);
04490    res |= ast_register_application(app_rqm, rqm_exec, app_rqm_synopsis, app_rqm_descrip);
04491    res |= ast_register_application(app_pqm, pqm_exec, app_pqm_synopsis, app_pqm_descrip);
04492    res |= ast_register_application(app_upqm, upqm_exec, app_upqm_synopsis, app_upqm_descrip);
04493    res |= ast_register_application(app_ql, ql_exec, app_ql_synopsis, app_ql_descrip);
04494    res |= ast_manager_register("Queues", 0, manager_queues_show, "Queues");
04495    res |= ast_manager_register("QueueStatus", 0, manager_queues_status, "Queue Status");
04496    res |= ast_manager_register("QueueAdd", EVENT_FLAG_AGENT, manager_add_queue_member, "Add interface to queue.");
04497    res |= ast_manager_register("QueueRemove", EVENT_FLAG_AGENT, manager_remove_queue_member, "Remove interface from queue.");
04498    res |= ast_manager_register("QueuePause", EVENT_FLAG_AGENT, manager_pause_queue_member, "Makes a queue member temporarily unavailable");
04499    res |= ast_custom_function_register(&queueagentcount_function);
04500    res |= ast_custom_function_register(&queuemembercount_function);
04501    res |= ast_custom_function_register(&queuememberlist_function);
04502    res |= ast_custom_function_register(&queuewaitingcount_function);
04503    res |= ast_devstate_add(statechange_queue, NULL);
04504 
04505    return res;
04506 }

static struct call_queue* load_realtime_queue ( const char *  queuename  )  [static]

Definition at line 1114 of file app_queue.c.

References ast_config_destroy(), AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_load_realtime(), ast_load_realtime_multientry(), ast_log(), ast_variables_destroy(), find_queue_by_name_rt(), LOG_ERROR, call_queue::name, and call_queue::realtime.

Referenced by __queues_show(), add_to_queue(), and join_queue().

01115 {
01116    struct ast_variable *queue_vars;
01117    struct ast_config *member_config = NULL;
01118    struct call_queue *q;
01119 
01120    /* Find the queue in the in-core list first. */
01121    AST_LIST_LOCK(&queues);
01122    AST_LIST_TRAVERSE(&queues, q, list) {
01123       if (!strcasecmp(q->name, queuename)) {
01124          break;
01125       }
01126    }
01127    AST_LIST_UNLOCK(&queues);
01128 
01129    if (!q || q->realtime) {
01130       /*! \note Load from realtime before taking the global qlock, to avoid blocking all
01131          queue operations while waiting for the DB.
01132 
01133          This will be two separate database transactions, so we might
01134          see queue parameters as they were before another process
01135          changed the queue and member list as it was after the change.
01136          Thus we might see an empty member list when a queue is
01137          deleted. In practise, this is unlikely to cause a problem. */
01138 
01139       queue_vars = ast_load_realtime("queues", "name", queuename, NULL);
01140       if (queue_vars) {
01141          member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", queuename, NULL);
01142          if (!member_config) {
01143             ast_log(LOG_ERROR, "no queue_members defined in your config (extconfig.conf).\n");
01144             return NULL;
01145          }
01146       }
01147 
01148       AST_LIST_LOCK(&queues);
01149 
01150       q = find_queue_by_name_rt(queuename, queue_vars, member_config);
01151       if (member_config)
01152          ast_config_destroy(member_config);
01153       if (queue_vars)
01154          ast_variables_destroy(queue_vars);
01155 
01156       AST_LIST_UNLOCK(&queues);
01157    }
01158    return q;
01159 }

static int manager_add_queue_member ( struct mansession s,
const struct message m 
) [static]

Definition at line 4149 of file app_queue.c.

References add_to_queue(), ast_queue_log(), ast_strlen_zero(), ast_true(), astman_get_header(), astman_send_ack(), astman_send_error(), member::interface, member::membername, member::paused, member::penalty, RES_EXISTS, RES_NOSUCHQUEUE, RES_OKAY, RES_OUTOFMEMORY, and s.

Referenced by load_module().

04150 {
04151    const char *queuename, *interface, *penalty_s, *paused_s, *membername;
04152    int paused, penalty = 0;
04153 
04154    queuename = astman_get_header(m, "Queue");
04155    interface = astman_get_header(m, "Interface");
04156    penalty_s = astman_get_header(m, "Penalty");
04157    paused_s = astman_get_header(m, "Paused");
04158    membername = astman_get_header(m, "MemberName");
04159 
04160    if (ast_strlen_zero(queuename)) {
04161       astman_send_error(s, m, "'Queue' not specified.");
04162       return 0;
04163    }
04164 
04165    if (ast_strlen_zero(interface)) {
04166       astman_send_error(s, m, "'Interface' not specified.");
04167       return 0;
04168    }
04169 
04170    if (ast_strlen_zero(penalty_s))
04171       penalty = 0;
04172    else if (sscanf(penalty_s, "%d", &penalty) != 1)
04173       penalty = 0;
04174 
04175    if (ast_strlen_zero(paused_s))
04176       paused = 0;
04177    else
04178       paused = abs(ast_true(paused_s));
04179 
04180    if (ast_strlen_zero(membername))
04181       membername = interface;
04182 
04183    switch (add_to_queue(queuename, interface, membername, penalty, paused, queue_persistent_members)) {
04184    case RES_OKAY:
04185       ast_queue_log(queuename, "MANAGER", interface, "ADDMEMBER", "%s", "");
04186       astman_send_ack(s, m, "Added interface to queue");
04187       break;
04188    case RES_EXISTS:
04189       astman_send_error(s, m, "Unable to add interface: Already there");
04190       break;
04191    case RES_NOSUCHQUEUE:
04192       astman_send_error(s, m, "Unable to add interface to queue: No such queue");
04193       break;
04194    case RES_OUTOFMEMORY:
04195       astman_send_error(s, m, "Out of memory");
04196       break;
04197    }
04198 
04199    return 0;
04200 }

static int manager_pause_queue_member ( struct mansession s,
const struct message m 
) [static]

Definition at line 4233 of file app_queue.c.

References ast_strlen_zero(), ast_true(), astman_get_header(), astman_send_ack(), astman_send_error(), member::interface, member::paused, s, and set_member_paused().

Referenced by load_module().

04234 {
04235    const char *queuename, *interface, *paused_s;
04236    int paused;
04237 
04238    interface = astman_get_header(m, "Interface");
04239    paused_s = astman_get_header(m, "Paused");
04240    queuename = astman_get_header(m, "Queue");   /* Optional - if not supplied, pause the given Interface in all queues */
04241 
04242    if (ast_strlen_zero(interface) || ast_strlen_zero(paused_s)) {
04243       astman_send_error(s, m, "Need 'Interface' and 'Paused' parameters.");
04244       return 0;
04245    }
04246 
04247    paused = abs(ast_true(paused_s));
04248 
04249    if (set_member_paused(queuename, interface, paused))
04250       astman_send_error(s, m, "Interface not found");
04251    else
04252       astman_send_ack(s, m, paused ? "Interface paused successfully" : "Interface unpaused successfully");
04253    return 0;
04254 }

static int manager_queues_show ( struct mansession s,
const struct message m 
) [static]

Definition at line 4048 of file app_queue.c.

References __queues_show(), astman_append(), RESULT_SUCCESS, and s.

Referenced by load_module().

04049 {
04050    char *a[] = { "queue", "show" };
04051 
04052    __queues_show(s, 1, -1, 2, a);
04053    astman_append(s, "\r\n\r\n"); /* Properly terminate Manager output */
04054 
04055    return RESULT_SUCCESS;
04056 }

static int manager_queues_status ( struct mansession s,
const struct message m 
) [static]

Definition at line 4059 of file app_queue.c.

References AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_mutex_lock(), ast_mutex_unlock(), ast_strlen_zero(), astman_append(), astman_get_header(), astman_send_ack(), member::calls, call_queue::callsabandoned, call_queue::callscompleted, call_queue::callscompletedinsl, queue_ent::chan, ast_channel::cid, ast_callerid::cid_name, ast_callerid::cid_num, call_queue::count, member::dynamic, call_queue::head, call_queue::holdtime, member::interface, member::lastcall, call_queue::lock, call_queue::maxlen, call_queue::members, call_queue::name, queue_ent::next, member::next, member::paused, member::penalty, RESULT_SUCCESS, s, S_OR, call_queue::servicelevel, queue_ent::start, member::status, and call_queue::weight.

Referenced by load_module().

04060 {
04061    time_t now;
04062    int pos;
04063    const char *id = astman_get_header(m,"ActionID");
04064    const char *queuefilter = astman_get_header(m,"Queue");
04065    const char *memberfilter = astman_get_header(m,"Member");
04066    char idText[256] = "";
04067    struct call_queue *q;
04068    struct queue_ent *qe;
04069    float sl = 0;
04070    struct member *mem;
04071 
04072    astman_send_ack(s, m, "Queue status will follow");
04073    time(&now);
04074    AST_LIST_LOCK(&queues);
04075    if (!ast_strlen_zero(id))
04076       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
04077 
04078    AST_LIST_TRAVERSE(&queues, q, list) {
04079       ast_mutex_lock(&q->lock);
04080 
04081       /* List queue properties */
04082       if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) {
04083          if (q->callscompleted > 0)
04084             sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
04085          astman_append(s, "Event: QueueParams\r\n"
04086             "Queue: %s\r\n"
04087             "Max: %d\r\n"
04088             "Calls: %d\r\n"
04089             "Holdtime: %d\r\n"
04090             "Completed: %d\r\n"
04091             "Abandoned: %d\r\n"
04092             "ServiceLevel: %d\r\n"
04093             "ServicelevelPerf: %2.1f\r\n"
04094             "Weight: %d\r\n"
04095             "%s"
04096             "\r\n",
04097             q->name, q->maxlen, q->count, q->holdtime, q->callscompleted,
04098             q->callsabandoned, q->servicelevel, sl, q->weight, idText);
04099          /* List Queue Members */
04100          for (mem = q->members; mem; mem = mem->next) {
04101             if (ast_strlen_zero(memberfilter) || !strcmp(mem->interface, memberfilter)) {
04102                astman_append(s, "Event: QueueMember\r\n"
04103                   "Queue: %s\r\n"
04104                   "Location: %s\r\n"
04105                   "Membership: %s\r\n"
04106                   "Penalty: %d\r\n"
04107                   "CallsTaken: %d\r\n"
04108                   "LastCall: %d\r\n"
04109                   "Status: %d\r\n"
04110                   "Paused: %d\r\n"
04111                   "%s"
04112                   "\r\n",
04113                   q->name, mem->interface, mem->dynamic ? "dynamic" : "static",
04114                   mem->penalty, mem->calls, (int)mem->lastcall, mem->status, mem->paused, idText);
04115             }
04116          }
04117          /* List Queue Entries */
04118          pos = 1;
04119          for (qe = q->head; qe; qe = qe->next) {
04120             astman_append(s, "Event: QueueEntry\r\n"
04121                "Queue: %s\r\n"
04122                "Position: %d\r\n"
04123                "Channel: %s\r\n"
04124                "CallerID: %s\r\n"
04125                "CallerIDName: %s\r\n"
04126                "Wait: %ld\r\n"
04127                "%s"
04128                "\r\n",
04129                q->name, pos++, qe->chan->name,
04130                S_OR(qe->chan->cid.cid_num, "unknown"),
04131                S_OR(qe->chan->cid.cid_name, "unknown"),
04132                (long) (now - qe->start), idText);
04133          }
04134       }
04135       ast_mutex_unlock(&q->lock);
04136    }
04137 
04138    astman_append(s,
04139       "Event: QueueStatusComplete\r\n"
04140       "%s"
04141       "\r\n",idText);
04142 
04143    AST_LIST_UNLOCK(&queues);
04144 
04145 
04146    return RESULT_SUCCESS;
04147 }

static int manager_remove_queue_member ( struct mansession s,
const struct message m 
) [static]

Definition at line 4202 of file app_queue.c.

References ast_queue_log(), ast_strlen_zero(), astman_get_header(), astman_send_ack(), astman_send_error(), member::interface, remove_from_queue(), RES_EXISTS, RES_NOSUCHQUEUE, RES_OKAY, RES_OUTOFMEMORY, and s.

Referenced by load_module().

04203 {
04204    const char *queuename, *interface;
04205 
04206    queuename = astman_get_header(m, "Queue");
04207    interface = astman_get_header(m, "Interface");
04208 
04209    if (ast_strlen_zero(queuename) || ast_strlen_zero(interface)) {
04210       astman_send_error(s, m, "Need 'Queue' and 'Interface' parameters.");
04211       return 0;
04212    }
04213 
04214    switch (remove_from_queue(queuename, interface)) {
04215    case RES_OKAY:
04216       ast_queue_log(queuename, "MANAGER", interface, "REMOVEMEMBER", "%s", "");
04217       astman_send_ack(s, m, "Removed interface from queue");
04218       break;
04219    case RES_EXISTS:
04220       astman_send_error(s, m, "Unable to remove interface: Not there");
04221       break;
04222    case RES_NOSUCHQUEUE:
04223       astman_send_error(s, m, "Unable to remove interface from queue: No such queue");
04224       break;
04225    case RES_OUTOFMEMORY:
04226       astman_send_error(s, m, "Out of memory");
04227       break;
04228    }
04229 
04230    return 0;
04231 }

static int play_file ( struct ast_channel chan,
char *  filename 
) [static]

Definition at line 1226 of file app_queue.c.

References AST_DIGIT_ANY, ast_stopstream(), ast_streamfile(), ast_waitstream(), and queue_ent::chan.

Referenced by say_position(), and try_calling().

01227 {
01228    int res;
01229 
01230    ast_stopstream(chan);
01231    res = ast_streamfile(chan, filename, chan->language);
01232    if (!res)
01233       res = ast_waitstream(chan, AST_DIGIT_ANY);
01234    ast_stopstream(chan);
01235 
01236    return res;
01237 }

static int pqm_exec ( struct ast_channel chan,
void *  data 
) [static]

Definition at line 3000 of file app_queue.c.

References AST_APP_ARG, AST_DECLARE_APP_ARGS, ast_goto_if_exists(), ast_log(), ast_module_user_add, ast_module_user_remove, ast_opt_priority_jumping, AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), ast_module_user::chan, ast_channel::context, LOG_WARNING, parse(), pbx_builtin_setvar_helper(), and set_member_paused().

Referenced by load_module().

03001 {
03002    struct ast_module_user *lu;
03003    char *parse;
03004    int priority_jump = 0;
03005    AST_DECLARE_APP_ARGS(args,
03006       AST_APP_ARG(queuename);
03007       AST_APP_ARG(interface);
03008       AST_APP_ARG(options);
03009    );
03010 
03011    if (ast_strlen_zero(data)) {
03012       ast_log(LOG_WARNING, "PauseQueueMember requires an argument ([queuename]|interface[|options])\n");
03013       return -1;
03014    }
03015 
03016    parse = ast_strdupa(data);
03017 
03018    AST_STANDARD_APP_ARGS(args, parse);
03019 
03020    lu = ast_module_user_add(chan);
03021 
03022    if (args.options) {
03023       if (strchr(args.options, 'j'))
03024          priority_jump = 1;
03025    }
03026 
03027    if (ast_strlen_zero(args.interface)) {
03028       ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename]|interface[|options])\n");
03029       ast_module_user_remove(lu);
03030       return -1;
03031    }
03032 
03033    if (set_member_paused(args.queuename, args.interface, 1)) {
03034       ast_log(LOG_WARNING, "Attempt to pause interface %s, not found\n", args.interface);
03035       if (priority_jump || ast_opt_priority_jumping) {
03036          if (ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101)) {
03037             pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND");
03038             ast_module_user_remove(lu);
03039             return 0;
03040          }
03041       }
03042       ast_module_user_remove(lu);
03043       pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND");
03044       return -1;
03045    }
03046 
03047    ast_module_user_remove(lu);
03048    pbx_builtin_setvar_helper(chan, "PQMSTATUS", "PAUSED");
03049 
03050    return 0;
03051 }

static int ql_exec ( struct ast_channel chan,
void *  data 
) [static]

Definition at line 3246 of file app_queue.c.

References AST_APP_ARG, AST_DECLARE_APP_ARGS, ast_log(), ast_module_user_add, ast_module_user_remove, ast_queue_log(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), ast_module_user::chan, event, LOG_WARNING, and parse().

Referenced by load_module().

03247 {
03248    struct ast_module_user *u;
03249    char *parse;
03250 
03251    AST_DECLARE_APP_ARGS(args,
03252       AST_APP_ARG(queuename);
03253       AST_APP_ARG(uniqueid);
03254       AST_APP_ARG(membername);
03255       AST_APP_ARG(event);
03256       AST_APP_ARG(params);
03257    );
03258 
03259    if (ast_strlen_zero(data)) {
03260       ast_log(LOG_WARNING, "QueueLog requires arguments (queuename|uniqueid|membername|event[|additionalinfo]\n");
03261       return -1;
03262    }
03263 
03264    u = ast_module_user_add(chan);
03265 
03266    parse = ast_strdupa(data);
03267 
03268    AST_STANDARD_APP_ARGS(args, parse);
03269 
03270    if (ast_strlen_zero(args.queuename) || ast_strlen_zero(args.uniqueid)
03271        || ast_strlen_zero(args.membername) || ast_strlen_zero(args.event)) {
03272       ast_log(LOG_WARNING, "QueueLog requires arguments (queuename|uniqueid|membername|event[|additionalinfo])\n");
03273       ast_module_user_remove(u);
03274       return -1;
03275    }
03276 
03277    ast_queue_log(args.queuename, args.uniqueid, args.membername, args.event, 
03278       "%s", args.params ? args.params : "");
03279 
03280    ast_module_user_remove(u);
03281 
03282    return 0;
03283 }

static int queue_exec ( struct ast_channel chan,
void *  data 
) [static]

Definition at line 3285 of file app_queue.c.

References AST_APP_ARG, AST_CONTROL_RINGING, AST_DECLARE_APP_ARGS, ast_indicate(), ast_log(), ast_module_user_add, ast_moh_start(), ast_moh_stop(), AST_PBX_KEEPALIVE, ast_queue_log(), AST_STANDARD_APP_ARGS, ast_stopstream(), ast_strdupa, ast_strlen_zero(), ast_verbose(), queue_ent::chan, ast_channel::cid, ast_callerid::cid_num, get_member_status(), is_our_turn(), join_queue(), leave_queue(), LOG_DEBUG, LOG_WARNING, option_debug, option_verbose, parse(), pbx_builtin_getvar_helper(), QUEUE_EMPTY_STRICT, QUEUE_LEAVEEMPTY, QUEUE_LEAVEUNAVAIL, QUEUE_NO_MEMBERS, QUEUE_NO_REACHABLE_MEMBERS, QUEUE_TIMEOUT, QUEUE_UNKNOWN, record_abandoned(), S_OR, say_periodic_announcement(), say_position(), set_queue_result(), try_calling(), valid_exit(), VERBOSE_PREFIX_3, wait_a_bit(), and wait_our_turn().

Referenced by load_module().

03286 {
03287    int res=-1;
03288    int ringing=0;
03289    struct ast_module_user *lu;
03290    const char *user_priority;
03291    const char *max_penalty_str;
03292    int prio;
03293    int max_penalty;
03294    enum queue_result reason = QUEUE_UNKNOWN;
03295    /* whether to exit Queue application after the timeout hits */
03296    int go_on = 0;
03297    char *parse;
03298    AST_DECLARE_APP_ARGS(args,
03299       AST_APP_ARG(queuename);
03300       AST_APP_ARG(options);
03301       AST_APP_ARG(url);
03302       AST_APP_ARG(announceoverride);
03303       AST_APP_ARG(queuetimeoutstr);
03304       AST_APP_ARG(agi);
03305    );
03306    /* Our queue entry */
03307    struct queue_ent qe;
03308    
03309    if (ast_strlen_zero(data)) {
03310       ast_log(LOG_WARNING, "Queue requires an argument: queuename[|options[|URL[|announceoverride[|timeout[|agi]]]]]\n");
03311       return -1;
03312    }
03313    
03314    parse = ast_strdupa(data);
03315    AST_STANDARD_APP_ARGS(args, parse);
03316 
03317    lu = ast_module_user_add(chan);
03318 
03319    /* Setup our queue entry */
03320    memset(&qe, 0, sizeof(qe));
03321    qe.start = time(NULL);
03322 
03323    /* set the expire time based on the supplied timeout; */
03324    if (args.queuetimeoutstr)
03325       qe.expire = qe.start + atoi(args.queuetimeoutstr);
03326    else
03327       qe.expire = 0;
03328 
03329    /* Get the priority from the variable ${QUEUE_PRIO} */
03330    user_priority = pbx_builtin_getvar_helper(chan, "QUEUE_PRIO");
03331    if (user_priority) {
03332       if (sscanf(user_priority, "%d", &prio) == 1) {
03333          if (option_debug)
03334             ast_log(LOG_DEBUG, "%s: Got priority %d from ${QUEUE_PRIO}.\n",
03335                chan->name, prio);
03336       } else {
03337          ast_log(LOG_WARNING, "${QUEUE_PRIO}: Invalid value (%s), channel %s.\n",
03338             user_priority, chan->name);
03339          prio = 0;
03340       }
03341    } else {
03342       if (option_debug > 2)
03343          ast_log(LOG_DEBUG, "NO QUEUE_PRIO variable found. Using default.\n");
03344       prio = 0;
03345    }
03346 
03347    /* Get the maximum penalty from the variable ${QUEUE_MAX_PENALTY} */
03348    if ((max_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MAX_PENALTY"))) {
03349       if (sscanf(max_penalty_str, "%d", &max_penalty) == 1) {
03350          if (option_debug)
03351             ast_log(LOG_DEBUG, "%s: Got max penalty %d from ${QUEUE_MAX_PENALTY}.\n",
03352                chan->name, max_penalty);
03353       } else {
03354          ast_log(LOG_WARNING, "${QUEUE_MAX_PENALTY}: Invalid value (%s), channel %s.\n",
03355             max_penalty_str, chan->name);
03356          max_penalty = 0;
03357       }
03358    } else {
03359       max_penalty = 0;
03360    }
03361 
03362    if (args.options && (strchr(args.options, 'r')))
03363       ringing = 1;
03364 
03365    if (option_debug)
03366       ast_log(LOG_DEBUG, "queue: %s, options: %s, url: %s, announce: %s, expires: %ld, priority: %d\n",
03367          args.queuename, args.options, args.url, args.announceoverride, (long)qe.expire, prio);
03368 
03369    qe.chan = chan;
03370    qe.prio = prio;
03371    qe.max_penalty = max_penalty;
03372    qe.last_pos_said = 0;
03373    qe.last_pos = 0;
03374    qe.last_periodic_announce_time = time(NULL);
03375    qe.last_periodic_announce_sound = 0;
03376    if (!join_queue(args.queuename, &qe, &reason)) {
03377       ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ENTERQUEUE", "%s|%s", S_OR(args.url, ""),
03378          S_OR(chan->cid.cid_num, ""));
03379 check_turns:
03380       if (ringing) {
03381          ast_indicate(chan, AST_CONTROL_RINGING);
03382       } else {
03383          ast_moh_start(chan, qe.moh, NULL);
03384       }
03385       for (;;) {
03386          /* This is the wait loop for callers 2 through maxlen */
03387 
03388          res = wait_our_turn(&qe, ringing, &reason);
03389          /* If they hungup, return immediately */
03390          if (res < 0) {
03391             /* Record this abandoned call */
03392             record_abandoned(&qe);
03393             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ABANDON", "%d|%d|%ld",
03394                qe.pos, qe.opos, (long) time(NULL) - qe.start);
03395             if (option_verbose > 2) {
03396                ast_verbose(VERBOSE_PREFIX_3 "User disconnected from queue %s while waiting their turn\n", args.queuename);
03397             }
03398             res = -1;
03399             break;
03400          }
03401          if (!res)
03402             break;
03403          if (valid_exit(&qe, res)) {
03404             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%s|%d", qe.digits, qe.pos);
03405             break;
03406          }
03407       }
03408       if (!res) {
03409          int makeannouncement = 0;
03410 
03411          for (;;) {
03412             /* This is the wait loop for the head caller*/
03413             /* To exit, they may get their call answered; */
03414             /* they may dial a digit from the queue context; */
03415             /* or, they may timeout. */
03416 
03417             enum queue_member_status stat;
03418 
03419             /* Leave if we have exceeded our queuetimeout */
03420             if (qe.expire && (time(NULL) > qe.expire)) {
03421                record_abandoned(&qe);
03422                reason = QUEUE_TIMEOUT;
03423                res = 0;
03424                ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
03425                break;
03426             }
03427 
03428             if (makeannouncement) {
03429                /* Make a position announcement, if enabled */
03430                if (qe.parent->announcefrequency && !ringing &&
03431                   (res = say_position(&qe))) {
03432                   ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%s|%d", qe.digits, qe.pos);
03433                   break;
03434                }
03435 
03436             }
03437             makeannouncement = 1;
03438 
03439             /* Make a periodic announcement, if enabled */
03440             if (qe.parent->periodicannouncefrequency && !ringing &&
03441                (res = say_periodic_announcement(&qe))) {
03442                ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%c|%d", res, qe.pos);
03443                break;
03444             }
03445 
03446             /* Try calling all queue members for 'timeout' seconds */
03447             res = try_calling(&qe, args.options, args.announceoverride, args.url, &go_on, args.agi);
03448             if (res) {
03449                if (res < 0) {
03450                   if (!qe.handled) {
03451                      record_abandoned(&qe);
03452                      ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ABANDON",
03453                         "%d|%d|%ld", qe.pos, qe.opos,
03454                         (long) time(NULL) - qe.start);
03455                   }
03456                } else if (valid_exit(&qe, res)) {
03457                   ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHKEY",
03458                      "%s|%d", qe.digits, qe.pos);
03459                }
03460                break;
03461             }
03462 
03463             stat = get_member_status(qe.parent, qe.max_penalty);
03464 
03465             /* leave the queue if no agents, if enabled */
03466             if (qe.parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) {
03467                record_abandoned(&qe);
03468                reason = QUEUE_LEAVEEMPTY;
03469                res = 0;
03470                break;
03471             }
03472 
03473             /* leave the queue if no reachable agents, if enabled */
03474             if ((qe.parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) {
03475                record_abandoned(&qe);
03476                reason = QUEUE_LEAVEUNAVAIL;
03477                res = 0;
03478                break;
03479             }
03480 
03481             /* Leave if we have exceeded our queuetimeout */
03482             if (qe.expire && (time(NULL) > qe.expire)) {
03483                record_abandoned(&qe);
03484                reason = QUEUE_TIMEOUT;
03485                res = 0;
03486                ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
03487                break;
03488             }
03489 
03490             /* OK, we didn't get anybody; wait for 'retry' seconds; may get a digit to exit with */
03491             res = wait_a_bit(&qe);
03492             if (res < 0) {
03493                record_abandoned(&qe);
03494                ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ABANDON", "%d|%d|%ld", qe.pos, qe.opos, (long)time(NULL) - qe.start);
03495                if (option_verbose > 2) {
03496                   ast_verbose(VERBOSE_PREFIX_3 "User disconnected from queue %s when they almost made it\n", args.queuename);
03497                }
03498                res = -1;
03499                break;
03500             }
03501             if (res && valid_exit(&qe, res)) {
03502                ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%s|%d", qe.digits, qe.pos);
03503                break;
03504             }
03505             /* exit after 'timeout' cycle if 'n' option enabled */
03506             if (go_on) {
03507                if (option_verbose > 2)
03508                   ast_verbose(VERBOSE_PREFIX_3 "Exiting on time-out cycle\n");
03509                ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
03510                record_abandoned(&qe);
03511                reason = QUEUE_TIMEOUT;
03512                res = 0;
03513                break;
03514             }
03515             /* Since this is a priority queue and
03516              * it is not sure that we are still at the head
03517              * of the queue, go and check for our turn again.
03518              */
03519             if (!is_our_turn(&qe)) {
03520                if (option_debug)
03521                   ast_log(LOG_DEBUG, "Darn priorities, going back in queue (%s)!\n",
03522                      qe.chan->name);
03523                goto check_turns;
03524             }
03525          }
03526       }
03527       /* Don't allow return code > 0 */
03528       if (res >= 0 && res != AST_PBX_KEEPALIVE) {
03529          res = 0; 
03530          if (ringing) {
03531             ast_indicate(chan, -1);
03532          } else {
03533             ast_moh_stop(chan);
03534          }        
03535          ast_stopstream(chan);
03536       }
03537       leave_queue(&qe);
03538       if (reason != QUEUE_UNKNOWN)
03539          set_queue_result(chan, reason);
03540    } else {
03541       ast_log(LOG_WARNING, "Unable to join queue '%s'\n", args.queuename);
03542       set_queue_result(chan, reason);
03543       res = 0;
03544    }
03545    ast_module_user_remove(lu);
03546 
03547    return res;
03548 }

static int queue_function_qac ( struct ast_channel chan,
char *  cmd,
char *  data,
char *  buf,
size_t  len 
) [static]

Definition at line 3550 of file app_queue.c.

References AST_DEVICE_INVALID, AST_DEVICE_UNAVAILABLE, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), ast_module_user_add, ast_module_user_remove, ast_mutex_lock(), ast_mutex_unlock(), ast_strlen_zero(), call_queue::lock, LOG_ERROR, LOG_WARNING, call_queue::members, call_queue::name, member::next, and member::status.

03551 {
03552    int count = 0;
03553    struct call_queue *q;
03554    struct ast_module_user *lu;
03555    struct member *m;
03556 
03557    buf[0] = '\0';
03558    
03559    if (ast_strlen_zero(data)) {
03560       ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
03561       return -1;
03562    }
03563 
03564    lu = ast_module_user_add(chan);
03565    
03566    AST_LIST_LOCK(&queues);
03567    AST_LIST_TRAVERSE(&queues, q, list) {
03568       if (!strcasecmp(q->name, data)) {
03569          ast_mutex_lock(&q->lock);
03570          break;
03571       }
03572    }
03573    AST_LIST_UNLOCK(&queues);
03574 
03575    if (q) {
03576       for (m = q->members; m; m = m->next) {
03577          /* Count the agents who are logged in and presently answering calls */
03578          if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
03579             count++;
03580          }
03581       }
03582       ast_mutex_unlock(&q->lock);
03583    } else
03584       ast_log(LOG_WARNING, "queue %s was not found\n", data);
03585 
03586    snprintf(buf, len, "%d", count);
03587    ast_module_user_remove(lu);
03588 
03589    return 0;
03590 }

static int queue_function_queuememberlist ( struct ast_channel chan,
char *  cmd,
char *  data,
char *  buf,
size_t  len 
) [static]

Definition at line 3627 of file app_queue.c.

References AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), ast_module_user_add, ast_module_user_remove, ast_mutex_lock(), ast_mutex_unlock(), ast_strlen_zero(), member::interface, call_queue::lock, LOG_ERROR, LOG_WARNING, call_queue::members, call_queue::name, and member::next.

03628 {
03629    struct ast_module_user *u;
03630    struct call_queue *q;
03631    struct member *m;
03632 
03633    /* Ensure an otherwise empty list doesn't return garbage */
03634    buf[0] = '\0';
03635 
03636    if (ast_strlen_zero(data)) {
03637       ast_log(LOG_ERROR, "QUEUE_MEMBER_LIST requires an argument: queuename\n");
03638       return -1;
03639    }
03640    
03641    u = ast_module_user_add(chan);
03642 
03643    AST_LIST_LOCK(&queues);
03644    AST_LIST_TRAVERSE(&queues, q, list) {
03645       if (!strcasecmp(q->name, data)) {
03646          ast_mutex_lock(&q->lock);
03647          break;
03648       }
03649    }
03650    AST_LIST_UNLOCK(&queues);
03651 
03652    if (q) {
03653       int buflen = 0, count = 0;
03654 
03655       for (m = q->members; m; m = m->next) {
03656          /* strcat() is always faster than printf() */
03657          if (count++) {
03658             strncat(buf + buflen, ",", len - buflen - 1);
03659             buflen++;
03660          }
03661          strncat(buf + buflen, m->interface, len - buflen - 1);
03662          buflen += strlen(m->interface);
03663          /* Safeguard against overflow (negative length) */
03664          if (buflen >= len - 2) {
03665             ast_log(LOG_WARNING, "Truncating list\n");
03666             break;
03667          }
03668       }
03669       ast_mutex_unlock(&q->lock);
03670    } else
03671       ast_log(LOG_WARNING, "queue %s was not found\n", data);
03672 
03673    /* We should already be terminated, but let's make sure. */
03674    buf[len - 1] = '\0';
03675    ast_module_user_remove(u);
03676 
03677    return 0;
03678 }

static int queue_function_queuewaitingcount ( struct ast_channel chan,
char *  cmd,
char *  data,
char *  buf,
size_t  len 
) [static]

Definition at line 3592 of file app_queue.c.

References AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), ast_module_user_add, ast_module_user_remove, ast_mutex_lock(), ast_mutex_unlock(), ast_strlen_zero(), ast_module_user::chan, call_queue::count, call_queue::lock, LOG_ERROR, LOG_WARNING, and call_queue::name.

03593 {
03594    int count = 0;
03595    struct call_queue *q;
03596    struct ast_module_user *lu;
03597 
03598    buf[0] = '\0';
03599    
03600    if (ast_strlen_zero(data)) {
03601       ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
03602       return -1;
03603    }
03604 
03605    lu = ast_module_user_add(chan);
03606    
03607    AST_LIST_LOCK(&queues);
03608    AST_LIST_TRAVERSE(&queues, q, list) {
03609       if (!strcasecmp(q->name, data)) {
03610          ast_mutex_lock(&q->lock);
03611          break;
03612       }
03613    }
03614    AST_LIST_UNLOCK(&queues);
03615 
03616    if (q) {
03617       count = q->count;
03618       ast_mutex_unlock(&q->lock);
03619    } else
03620       ast_log(LOG_WARNING, "queue %s was not found\n", data);
03621 
03622    snprintf(buf, len, "%d", count);
03623    ast_module_user_remove(lu);
03624    return 0;
03625 }

static void queue_set_param ( struct call_queue q,
const char *  param,
const char *  val,
int  linenum,
int  failunknown 
) [static]

Configure a queue parameter.

For error reporting, line number is passed for .conf static configuration. For Realtime queues, linenum is -1. The failunknown flag is set for config files (and static realtime) to show errors for unknown parameters. It is cleared for dynamic realtime to allow extra fields in the tables.

Definition at line 778 of file app_queue.c.

References call_queue::announce, call_queue::announcefrequency, call_queue::announceholdtime, ANNOUNCEHOLDTIME_ALWAYS, ANNOUNCEHOLDTIME_ONCE, ast_log(), ast_strdupa, ast_true(), call_queue::autofill, call_queue::autopause, call_queue::context, DEFAULT_RETRY, DEFAULT_TIMEOUT, call_queue::eventwhencalled, call_queue::joinempty, call_queue::leavewhenempty, LOG_WARNING, call_queue::maskmemberstatus, MAX_PERIODIC_ANNOUNCEMENTS, call_queue::maxlen, call_queue::memberdelay, call_queue::moh, call_queue::monfmt, call_queue::monjoin, call_queue::montype, call_queue::name, call_queue::periodicannouncefrequency, QUEUE_EMPTY_NORMAL, QUEUE_EMPTY_STRICT, QUEUE_EVENT_VARIABLES, QUEUE_STRATEGY_RINGALL, call_queue::reportholdtime, call_queue::retry, call_queue::ringinuse, call_queue::roundingseconds, s, call_queue::servicelevel, call_queue::setinterfacevar, call_queue::sound_calls, call_queue::sound_holdtime, call_queue::sound_lessthan, call_queue::sound_minutes, call_queue::sound_next, call_queue::sound_periodicannounce, call_queue::sound_reporthold, call_queue::sound_seconds, call_queue::sound_thanks, call_queue::sound_thereare, strat2int(), call_queue::strategy, strsep(), call_queue::timeout, call_queue::timeoutrestart, call_queue::weight, and call_queue::wrapuptime.

Referenced by find_queue_by_name_rt(), and reload_queues().

00779 {
00780    if (!strcasecmp(param, "musicclass") || 
00781       !strcasecmp(param, "music") || !strcasecmp(param, "musiconhold")) {
00782       ast_copy_string(q->moh, val, sizeof(q->moh));
00783    } else if (!strcasecmp(param, "announce")) {
00784       ast_copy_string(q->announce, val, sizeof(q->announce));
00785    } else if (!strcasecmp(param, "context")) {
00786       ast_copy_string(q->context, val, sizeof(q->context));
00787    } else if (!strcasecmp(param, "timeout")) {
00788       q->timeout = atoi(val);
00789       if (q->timeout < 0)
00790          q->timeout = DEFAULT_TIMEOUT;
00791    } else if (!strcasecmp(param, "ringinuse")) {
00792       q->ringinuse = ast_true(val);
00793    } else if (!strcasecmp(param, "setinterfacevar")) {
00794       q->setinterfacevar = ast_true(val);
00795    } else if (!strcasecmp(param, "monitor-join")) {
00796       q->monjoin = ast_true(val);
00797    } else if (!strcasecmp(param, "monitor-format")) {
00798       ast_copy_string(q->monfmt, val, sizeof(q->monfmt));
00799    } else if (!strcasecmp(param, "queue-youarenext")) {
00800       ast_copy_string(q->sound_next, val, sizeof(q->sound_next));
00801    } else if (!strcasecmp(param, "queue-thereare")) {
00802       ast_copy_string(q->sound_thereare, val, sizeof(q->sound_thereare));
00803    } else if (!strcasecmp(param, "queue-callswaiting")) {
00804       ast_copy_string(q->sound_calls, val, sizeof(q->sound_calls));
00805    } else if (!strcasecmp(param, "queue-holdtime")) {
00806       ast_copy_string(q->sound_holdtime, val, sizeof(q->sound_holdtime));
00807    } else if (!strcasecmp(param, "queue-minutes")) {
00808       ast_copy_string(q->sound_minutes, val, sizeof(q->sound_minutes));
00809    } else if (!strcasecmp(param, "queue-seconds")) {
00810       ast_copy_string(q->sound_seconds, val, sizeof(q->sound_seconds));
00811    } else if (!strcasecmp(param, "queue-lessthan")) {
00812       ast_copy_string(q->sound_lessthan, val, sizeof(q->sound_lessthan));
00813    } else if (!strcasecmp(param, "queue-thankyou")) {
00814       ast_copy_string(q->sound_thanks, val, sizeof(q->sound_thanks));
00815    } else if (!strcasecmp(param, "queue-reporthold")) {
00816       ast_copy_string(q->sound_reporthold, val, sizeof(q->sound_reporthold));
00817    } else if (!strcasecmp(param, "announce-frequency")) {
00818       q->announcefrequency = atoi(val);
00819    } else if (!strcasecmp(param, "announce-round-seconds")) {
00820       q->roundingseconds = atoi(val);
00821       if (q->roundingseconds>60 || q->roundingseconds<0) {
00822          if (linenum >= 0) {
00823             ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
00824                "using 0 instead for queue '%s' at line %d of queues.conf\n",
00825                val, param, q->name, linenum);
00826          } else {
00827             ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
00828                "using 0 instead for queue '%s'\n", val, param, q->name);
00829          }
00830          q->roundingseconds=0;
00831       }
00832    } else if (!strcasecmp(param, "announce-holdtime")) {
00833       if (!strcasecmp(val, "once"))
00834          q->announceholdtime = ANNOUNCEHOLDTIME_ONCE;
00835       else if (ast_true(val))
00836          q->announceholdtime = ANNOUNCEHOLDTIME_ALWAYS;
00837       else
00838          q->announceholdtime = 0;
00839    } else if (!strcasecmp(param, "periodic-announce")) {
00840       if (strchr(val, '|')) {
00841          char *s, *buf = ast_strdupa(val);
00842          unsigned int i = 0;
00843 
00844          while ((s = strsep(&buf, "|"))) {
00845             ast_copy_string(q->sound_periodicannounce[i], s, sizeof(q->sound_periodicannounce[i]));
00846             i++;
00847             if (i == MAX_PERIODIC_ANNOUNCEMENTS)
00848                break;
00849          }
00850       } else {
00851          ast_copy_string(q->sound_periodicannounce[0], val, sizeof(q->sound_periodicannounce[0]));
00852       }
00853    } else if (!strcasecmp(param, "periodic-announce-frequency")) {
00854       q->periodicannouncefrequency = atoi(val);
00855    } else if (!strcasecmp(param, "retry")) {
00856       q->retry = atoi(val);
00857       if (q->retry <= 0)
00858          q->retry = DEFAULT_RETRY;
00859    } else if (!strcasecmp(param, "wrapuptime")) {
00860       q->wrapuptime = atoi(val);
00861    } else if (!strcasecmp(param, "autofill")) {
00862       q->autofill = ast_true(val);
00863    } else if (!strcasecmp(param, "monitor-type")) {
00864       if (!strcasecmp(val, "mixmonitor"))
00865          q->montype = 1;
00866    } else if (!strcasecmp(param, "autopause")) {
00867       q->autopause = ast_true(val);
00868    } else if (!strcasecmp(param, "maxlen")) {
00869       q->maxlen = atoi(val);
00870       if (q->maxlen < 0)
00871          q->maxlen = 0;
00872    } else if (!strcasecmp(param, "servicelevel")) {
00873       q->servicelevel= atoi(val);
00874    } else if (!strcasecmp(param, "strategy")) {
00875       q->strategy = strat2int(val);
00876       if (q->strategy < 0) {
00877          ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
00878             val, q->name);
00879          q->strategy = QUEUE_STRATEGY_RINGALL;
00880       }
00881    } else if (!strcasecmp(param, "joinempty")) {
00882       if (!strcasecmp(val, "strict"))
00883          q->joinempty = QUEUE_EMPTY_STRICT;
00884       else if (ast_true(val))
00885          q->joinempty = QUEUE_EMPTY_NORMAL;
00886       else
00887          q->joinempty = 0;
00888    } else if (!strcasecmp(param, "leavewhenempty")) {
00889       if (!strcasecmp(val, "strict"))
00890          q->leavewhenempty = QUEUE_EMPTY_STRICT;
00891       else if (ast_true(val))
00892          q->leavewhenempty = QUEUE_EMPTY_NORMAL;
00893       else
00894          q->leavewhenempty = 0;
00895    } else if (!strcasecmp(param, "eventmemberstatus")) {
00896       q->maskmemberstatus = !ast_true(val);
00897    } else if (!strcasecmp(param, "eventwhencalled")) {
00898       if (!strcasecmp(val, "vars")) {
00899          q->eventwhencalled = QUEUE_EVENT_VARIABLES;
00900       } else {
00901          q->eventwhencalled = ast_true(val);
00902       }
00903    } else if (!strcasecmp(param, "reportholdtime")) {
00904       q->reportholdtime = ast_true(val);
00905    } else if (!strcasecmp(param, "memberdelay")) {
00906       q->memberdelay = atoi(val);
00907    } else if (!strcasecmp(param, "weight")) {
00908       q->weight = atoi(val);
00909       if (q->weight)
00910          use_weight++;
00911       /* With Realtime queues, if the last queue using weights is deleted in realtime,
00912          we will not see any effect on use_weight until next reload. */
00913    } else if (!strcasecmp(param, "timeoutrestart")) {
00914       q->timeoutrestart = ast_true(val);
00915    } else if (failunknown) {
00916       if (linenum >= 0) {
00917          ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s at line %d of queues.conf\n",
00918             q->name, param, linenum);
00919       } else {
00920          ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s\n", q->name, param);
00921       }
00922    }
00923 }

static int queue_show ( int  fd,
int  argc,
char **  argv 
) [static]

Definition at line 4021 of file app_queue.c.

References __queues_show().

Referenced by __queues_show().

04022 {
04023    return __queues_show(NULL, 0, fd, argc, argv);
04024 }

static void recalc_holdtime ( struct queue_ent qe  )  [static]

Definition at line 1377 of file app_queue.c.

References ast_mutex_lock(), ast_mutex_unlock(), call_queue::callscompletedinsl, call_queue::holdtime, call_queue::lock, queue_ent::parent, call_queue::servicelevel, and queue_ent::start.

Referenced by try_calling().

01378 {
01379    int oldvalue, newvalue;
01380 
01381    /* Calculate holdtime using a recursive boxcar filter */
01382    /* Thanks to SRT for this contribution */
01383    /* 2^2 (4) is the filter coefficient; a higher exponent would give old entries more weight */
01384 
01385    newvalue = time(NULL) - qe->start;
01386 
01387    ast_mutex_lock(&qe->parent->lock);
01388    if (newvalue <= qe->parent->servicelevel)
01389       qe->parent->callscompletedinsl++;
01390    oldvalue = qe->parent->holdtime;
01391    qe->parent->holdtime = (((oldvalue << 2) - oldvalue) + newvalue) >> 2;
01392    ast_mutex_unlock(&qe->parent->lock);
01393 }

static void record_abandoned ( struct queue_ent qe  )  [static]

Definition at line 1828 of file app_queue.c.

References ast_mutex_lock(), ast_mutex_unlock(), call_queue::callsabandoned, queue_ent::chan, EVENT_FLAG_AGENT, call_queue::lock, manager_event(), call_queue::name, queue_ent::opos, queue_ent::parent, queue_ent::pos, and queue_ent::start.

Referenced by queue_exec(), and try_calling().

01829 {
01830    ast_mutex_lock(&qe->parent->lock);
01831    manager_event(EVENT_FLAG_AGENT, "QueueCallerAbandon",
01832       "Queue: %s\r\n"
01833       "Uniqueid: %s\r\n"
01834       "Position: %d\r\n"
01835       "OriginalPosition: %d\r\n"
01836       "HoldTime: %d\r\n",
01837       qe->parent->name, qe->chan->uniqueid, qe->pos, qe->opos, (int)(time(NULL) - qe->start));
01838 
01839    qe->parent->callsabandoned++;
01840    ast_mutex_unlock(&qe->parent->lock);
01841 }

static int reload ( void   )  [static]

Definition at line 4508 of file app_queue.c.

References reload_queues().

04509 {
04510    reload_queues();
04511    return 0;
04512 }

static void reload_queue_members ( void   )  [static]

Definition at line 2909 of file app_queue.c.

References add_to_queue(), ast_db_del(), ast_db_freetree(), ast_db_get(), ast_db_gettree(), AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), ast_mutex_lock(), ast_mutex_unlock(), ast_strlen_zero(), member::interface, ast_db_entry::key, call_queue::lock, LOG_DEBUG, LOG_ERROR, LOG_NOTICE, LOG_WARNING, member::membername, call_queue::name, ast_db_entry::next, option_debug, member::paused, member::penalty, PM_MAX_LEN, RES_OUTOFMEMORY, and strsep().

Referenced by load_module().

02910 {
02911    char *cur_ptr; 
02912    char *queue_name;
02913    char *member;
02914    char *interface;
02915    char *membername;
02916    char *penalty_tok;
02917    int penalty = 0;
02918    char *paused_tok;
02919    int paused = 0;
02920    struct ast_db_entry *db_tree;
02921    struct ast_db_entry *entry;
02922    struct call_queue *cur_queue;
02923    char queue_data[PM_MAX_LEN];
02924 
02925    AST_LIST_LOCK(&queues);
02926 
02927    /* Each key in 'pm_family' is the name of a queue */
02928    db_tree = ast_db_gettree(pm_family, NULL);
02929    for (entry = db_tree; entry; entry = entry->next) {
02930 
02931       queue_name = entry->key + strlen(pm_family) + 2;
02932 
02933       AST_LIST_TRAVERSE(&queues, cur_queue, list) {
02934          ast_mutex_lock(&cur_queue->lock);
02935          if (!strcmp(queue_name, cur_queue->name))
02936             break;
02937          ast_mutex_unlock(&cur_queue->lock);
02938       }
02939 
02940       if (!cur_queue) {
02941          /* If the queue no longer exists, remove it from the
02942           * database */
02943          ast_db_del(pm_family, queue_name);
02944          continue;
02945       } else
02946          ast_mutex_unlock(&cur_queue->lock);
02947 
02948       if (ast_db_get(pm_family, queue_name, queue_data, PM_MAX_LEN))
02949          continue;
02950 
02951       cur_ptr = queue_data;
02952       while ((member = strsep(&cur_ptr, "|"))) {
02953          if (ast_strlen_zero(member))
02954             continue;
02955 
02956          interface = strsep(&member, ";");
02957          penalty_tok = strsep(&member, ";");
02958          paused_tok = strsep(&member, ";");
02959          membername = strsep(&member, ";");
02960 
02961          if (!penalty_tok) {
02962             ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (penalty)\n", queue_name);
02963             break;
02964          }
02965          penalty = strtol(penalty_tok, NULL, 10);
02966          if (errno == ERANGE) {
02967             ast_log(LOG_WARNING, "Error converting penalty: %s: Out of range.\n", penalty_tok);
02968             break;
02969          }
02970          
02971          if (!paused_tok) {
02972             ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (paused)\n", queue_name);
02973             break;
02974          }
02975          paused = strtol(paused_tok, NULL, 10);
02976          if ((errno == ERANGE) || paused < 0 || paused > 1) {
02977             ast_log(LOG_WARNING, "Error converting paused: %s: Expected 0 or 1.\n", paused_tok);
02978             break;
02979          }
02980          if (ast_strlen_zero(membername))
02981             membername = interface;
02982 
02983          if (option_debug)
02984             ast_log(LOG_DEBUG, "Reload Members: Queue: %s  Member: %s  Name: %s  Penalty: %d  Paused: %d\n", queue_name, interface, membername, penalty, paused);
02985          
02986          if (add_to_queue(queue_name, interface, membername, penalty, paused, 0) == RES_OUTOFMEMORY) {
02987             ast_log(LOG_ERROR, "Out of Memory when reloading persistent queue member\n");
02988             break;
02989          }
02990       }
02991    }
02992 
02993    AST_LIST_UNLOCK(&queues);
02994    if (db_tree) {
02995       ast_log(LOG_NOTICE, "Queue members successfully reloaded from database.\n");
02996       ast_db_freetree(db_tree);
02997    }
02998 }

static int reload_queues ( void   )  [static]

Definition at line 3717 of file app_queue.c.

References add_to_interfaces(), alloc_queue(), AST_APP_ARG, ast_category_browse(), ast_config_load(), AST_DECLARE_APP_ARGS, AST_LIST_INSERT_HEAD, AST_LIST_LOCK, AST_LIST_TRAVERSE, ast_log(), ast_mutex_lock(), ast_mutex_unlock(), AST_NONSTANDARD_APP_ARGS, ast_strlen_zero(), ast_true(), ast_variable_browse(), ast_variable_retrieve(), clear_queue(), create_queue_member(), call_queue::dead, member::delme, member::dynamic, free, init_queue(), member::interface, call_queue::lock, LOG_NOTICE, member::membername, call_queue::members, call_queue::name, member::next, parse(), member::paused, member::penalty, queue_set_param(), QUEUE_STRATEGY_ROUNDROBIN, remove_from_interfaces(), rr_dep_warning(), call_queue::strategy, and var.

Referenced by load_module(), and reload().

03718 {
03719    struct call_queue *q;
03720    struct ast_config *cfg;
03721    char *cat, *tmp;
03722    struct ast_variable *var;
03723    struct member *prev, *cur, *newm, *next;
03724    int new;
03725    const char *general_val = NULL;
03726    char parse[80];
03727    char *interface;
03728    char *membername;
03729    int penalty;
03730    AST_DECLARE_APP_ARGS(args,
03731       AST_APP_ARG(interface);
03732       AST_APP_ARG(penalty);
03733       AST_APP_ARG(membername);
03734    );
03735    
03736    if (!(cfg = ast_config_load("queues.conf"))) {
03737       ast_log(LOG_NOTICE, "No call queueing config file (queues.conf), so no call queues\n");
03738       return 0;
03739    }
03740    AST_LIST_LOCK(&queues);
03741    use_weight=0;
03742    /* Mark all queues as dead for the moment */
03743    AST_LIST_TRAVERSE(&queues, q, list)
03744       q->dead = 1;
03745 
03746    /* Chug through config file */
03747    cat = NULL;
03748    while ((cat = ast_category_browse(cfg, cat)) ) {
03749       if (!strcasecmp(cat, "general")) {  
03750          /* Initialize global settings */
03751          queue_persistent_members = 0;
03752          if ((general_val = ast_variable_retrieve(cfg, "general", "persistentmembers")))
03753             queue_persistent_members = ast_true(general_val);
03754          autofill_default = 0;
03755          if ((general_val = ast_variable_retrieve(cfg, "general", "autofill")))
03756             autofill_default = ast_true(general_val);
03757          montype_default = 0;
03758          if ((general_val = ast_variable_retrieve(cfg, "general", "monitor-type")))
03759             if (!strcasecmp(general_val, "mixmonitor"))
03760                montype_default = 1;
03761       } else { /* Define queue */
03762          /* Look for an existing one */
03763          AST_LIST_TRAVERSE(&queues, q, list) {
03764             if (!strcmp(q->name, cat))
03765                break;
03766          }
03767          if (!q) {
03768             /* Make one then */
03769             if (!(q = alloc_queue(cat))) {
03770                /* TODO: Handle memory allocation failure */
03771             }
03772             new = 1;
03773          } else
03774             new = 0;
03775          if (q) {
03776             if (!new)
03777                ast_mutex_lock(&q->lock);
03778             /* Re-initialize the queue, and clear statistics */
03779             init_queue(q);
03780             clear_queue(q);
03781             for (cur = q->members; cur; cur = cur->next) {
03782                if (!cur->dynamic) {
03783                   cur->delme = 1;
03784                }
03785             }
03786             for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
03787                if (!strcasecmp(var->name, "member")) {
03788                   /* Add a new member */
03789                   ast_copy_string(parse, var->value, sizeof(parse));
03790                   
03791                   AST_NONSTANDARD_APP_ARGS(args, parse, ',');
03792 
03793                   interface = args.interface;
03794                   if(!ast_strlen_zero(args.penalty)) {
03795                      tmp = args.penalty;
03796                      while (*tmp && *tmp < 33) tmp++;
03797                      penalty = atoi(tmp);
03798                      if (penalty < 0) {
03799                         penalty = 0;
03800                      }
03801                   } else
03802                      penalty = 0;
03803 
03804                   if (!ast_strlen_zero(args.membername)) {
03805                      membername = args.membername;
03806                      while (*membername && *membername < 33) membername++;
03807                   } else
03808                      membername = interface;
03809 
03810                   /* Find the old position in the list */
03811                   for (prev = NULL, cur = q->members; cur; prev = cur, cur = cur->next) {
03812                      if (!strcmp(cur->interface, interface)) {
03813                         break;
03814                      }
03815                   }
03816 
03817                   newm = create_queue_member(interface, membername, penalty, cur ? cur->paused : 0);
03818 
03819                   if (cur) {
03820                      /* Delete it now */
03821                      newm->next = cur->next;
03822                      if (prev) {
03823                         prev->next = newm;
03824                      } else {
03825                         q->members = newm;
03826                      }
03827                      free(cur);
03828                   } else {
03829                      /* Add them to the master int list if necessary */
03830                      add_to_interfaces(interface);
03831                      newm->next = q->members;
03832                      q->members = newm;
03833                   }
03834                } else {
03835                   queue_set_param(q, var->name, var->value, var->lineno, 1);
03836                }
03837             }
03838 
03839             /* Free remaining members marked as delme */
03840             for (prev = NULL, cur = q->members;
03841                  cur;
03842                  cur = next) {
03843                next = cur->next;
03844 
03845                if (!cur->delme) {
03846                   prev = cur;
03847                   continue;
03848                }
03849 
03850                if (prev)
03851                   prev->next = next;
03852                else
03853                   q->members = next;
03854 
03855                remove_from_interfaces(cur->interface);
03856                free(cur);
03857             }
03858 
03859             if (q->strategy == QUEUE_STRATEGY_ROUNDROBIN)
03860                rr_dep_warning();
03861 
03862             if (new) {
03863                AST_LIST_INSERT_HEAD(&queues, q, list);
03864             } else
03865                ast_mutex_unlock(&q->lock);
03866          }
03867       }
03868    }
03869    ast_config_destroy(cfg);
03870    AST_LIST_TRAVERSE_SAFE_BEGIN(&queues, q, list) {
03871       if (q->dead) {
03872          AST_LIST_REMOVE_CURRENT(&queues, list);
03873          if (!q->count)
03874             destroy_queue(q);
03875          else
03876             ast_log(LOG_DEBUG, "XXX Leaking a little memory :( XXX\n");
03877       } else {
03878          ast_mutex_lock(&q->lock);
03879          for (cur = q->members; cur; cur = cur->next)
03880             cur->status = ast_device_state(cur->interface);
03881          ast_mutex_unlock(&q->lock);
03882       }
03883    }
03884    AST_LIST_TRAVERSE_SAFE_END;
03885    AST_LIST_UNLOCK(&queues);
03886    return 1;
03887 }

static int remove_from_interfaces ( const char *  interface  )  [static]

Definition at line 739 of file app_queue.c.

References AST_LIST_LOCK, AST_LIST_REMOVE_CURRENT, AST_LIST_TRAVERSE_SAFE_BEGIN, AST_LIST_TRAVERSE_SAFE_END, AST_LIST_UNLOCK, ast_log(), free, member_interface::interface, interface_exists_global(), LOG_DEBUG, and option_debug.

Referenced by free_members(), reload_queues(), and remove_from_queue().

00740 {
00741    struct member_interface *curint;
00742 
00743    AST_LIST_LOCK(&interfaces);
00744    AST_LIST_TRAVERSE_SAFE_BEGIN(&interfaces, curint, list) {
00745       if (!strcasecmp(curint->interface, interface)) {
00746          if (!interface_exists_global(interface)) {
00747             if (option_debug)
00748                ast_log(LOG_DEBUG, "Removing %s from the list of interfaces that make up all of our queue members.\n", interface);
00749             AST_LIST_REMOVE_CURRENT(&interfaces, list);
00750             free(curint);
00751          }
00752          break;
00753       }
00754    }
00755    AST_LIST_TRAVERSE_SAFE_END;
00756    AST_LIST_UNLOCK(&interfaces);
00757 
00758    return 0;
00759 }

static int remove_from_queue ( const char *  queuename,
const char *  interface 
) [static]

Definition at line 2762 of file app_queue.c.

References AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_mutex_lock(), ast_mutex_unlock(), dump_queue_members(), EVENT_FLAG_AGENT, free, member::interface, interface_exists(), call_queue::lock, manager_event(), member::membername, call_queue::members, call_queue::name, member::next, remove_from_interfaces(), RES_EXISTS, RES_NOSUCHQUEUE, and RES_OKAY.

Referenced by attempt_thread(), handle_queue_remove_member(), manager_remove_queue_member(), rqm_exec(), and scan_service().

02763 {
02764    struct call_queue *q;
02765    struct member *last_member, *look;
02766    int res = RES_NOSUCHQUEUE;
02767 
02768    AST_LIST_LOCK(&queues);
02769    AST_LIST_TRAVERSE(&queues, q, list) {
02770       ast_mutex_lock(&q->lock);
02771       if (strcmp(q->name, queuename)) {
02772          ast_mutex_unlock(&q->lock);
02773          continue;
02774       }
02775 
02776       if ((last_member = interface_exists(q, interface))) {
02777          if ((look = q->members) == last_member) {
02778             q->members = last_member->next;
02779          } else {
02780             while (look != NULL) {
02781                if (look->next == last_member) {
02782                   look->next = last_member->next;
02783                   break;
02784                } else {
02785                   look = look->next;
02786                }
02787             }
02788          }
02789          manager_event(EVENT_FLAG_AGENT, "QueueMemberRemoved",
02790             "Queue: %s\r\n"
02791             "Location: %s\r\n"
02792             "MemberName: %s\r\n",
02793             q->name, last_member->interface, last_member->membername);
02794          free(last_member);
02795          
02796          if (queue_persistent_members)
02797             dump_queue_members(q);
02798          
02799          res = RES_OKAY;
02800       } else {
02801          res = RES_EXISTS;
02802       }
02803       ast_mutex_unlock(&q->lock);
02804       break;
02805    }
02806 
02807    if (res == RES_OKAY)
02808       remove_from_interfaces(interface);
02809 
02810    AST_LIST_UNLOCK(&queues);
02811 
02812    return res;
02813 }

static int ring_entry ( struct queue_ent qe,
struct callattempt tmp,
int *  busies 
) [static]

Definition at line 1575 of file app_queue.c.

References ast_channel::adsicpe, ast_channel::appl, ast_call(), ast_cdr_busy(), ast_channel_inherit_variables(), AST_DEVICE_NOT_INUSE, AST_DEVICE_UNKNOWN, ast_log(), ast_mutex_lock(), ast_mutex_unlock(), ast_request(), ast_strdup, ast_verbose(), ast_channel::cdr, callattempt::chan, queue_ent::chan, ast_channel::cid, ast_callerid::cid_ani, ast_callerid::cid_name, ast_callerid::cid_num, compare_weight(), ast_channel::context, ast_channel::data, do_hang(), EVENT_FLAG_AGENT, call_queue::eventwhencalled, ast_channel::exten, free, callattempt::interface, callattempt::lastcall, call_queue::lock, LOG_DEBUG, manager_event(), callattempt::member, call_queue::name, ast_channel::nativeformats, callattempt::oldstatus, option_debug, option_verbose, queue_ent::parent, member::paused, ast_channel::priority, QUEUE_EVENT_VARIABLES, call_queue::ringinuse, call_queue::rrpos, member::status, callattempt::stillgoing, update_dial_status(), vars2manager(), VERBOSE_PREFIX_3, ast_channel::whentohangup, and call_queue::wrapuptime.

Referenced by ring_one().

01576 {
01577    int res;
01578    int status;
01579    char tech[256];
01580    char *location;
01581 
01582    /* on entry here, we know that tmp->chan == NULL */
01583    if (qe->parent->wrapuptime && (time(NULL) - tmp->lastcall < qe->parent->wrapuptime)) {
01584       if (option_debug)
01585          ast_log(LOG_DEBUG, "Wrapuptime not yet expired for %s\n", tmp->interface);
01586       if (qe->chan->cdr)
01587          ast_cdr_busy(qe->chan->cdr);
01588       tmp->stillgoing = 0;
01589       (*busies)++;
01590       return 0;
01591    }
01592 
01593    if (!qe->parent->ringinuse && (tmp->member->status != AST_DEVICE_NOT_INUSE) && (tmp->member->status != AST_DEVICE_UNKNOWN)) {
01594       if (option_debug)
01595          ast_log(LOG_DEBUG, "%s in use, can't receive call\n", tmp->interface);
01596       if (qe->chan->cdr)
01597          ast_cdr_busy(qe->chan->cdr);
01598       tmp->stillgoing = 0;
01599       return 0;
01600    }
01601 
01602    if (tmp->member->paused) {
01603       if (option_debug)
01604          ast_log(LOG_DEBUG, "%s paused, can't receive call\n", tmp->interface);
01605       if (qe->chan->cdr)
01606          ast_cdr_busy(qe->chan->cdr);
01607       tmp->stillgoing = 0;
01608       return 0;
01609    }
01610    if (use_weight && compare_weight(qe->parent,tmp->member)) {
01611       ast_log(LOG_DEBUG, "Priority queue delaying call to %s:%s\n", qe->parent->name, tmp->interface);
01612       if (qe->chan->cdr)
01613          ast_cdr_busy(qe->chan->cdr);
01614       tmp->stillgoing = 0;
01615       (*busies)++;
01616       return 0;
01617    }
01618 
01619    ast_copy_string(tech, tmp->interface, sizeof(tech));
01620    if ((location = strchr(tech, '/')))
01621       *location++ = '\0';
01622    else
01623       location = "";
01624 
01625    /* Request the peer */
01626    tmp->chan = ast_request(tech, qe->chan->nativeformats, location, &status);
01627    if (!tmp->chan) {       /* If we can't, just go on to the next call */
01628       if (qe->chan->cdr)
01629          ast_cdr_busy(qe->chan->cdr);
01630       tmp->stillgoing = 0;
01631       update_dial_status(qe->parent, tmp->member, status);
01632 
01633       ast_mutex_lock(&qe->parent->lock);
01634       qe->parent->rrpos++;
01635       ast_mutex_unlock(&qe->parent->lock);
01636 
01637       (*busies)++;
01638       return 0;
01639    } else if (status != tmp->oldstatus)
01640       update_dial_status(qe->parent, tmp->member, status);
01641    
01642    tmp->chan->appl = "AppQueue";
01643    tmp->chan->data = "(Outgoing Line)";
01644    tmp->chan->whentohangup = 0;
01645    if (tmp->chan->cid.cid_num)
01646       free(tmp->chan->cid.cid_num);
01647    tmp->chan->cid.cid_num = ast_strdup(qe->chan->cid.cid_num);
01648    if (tmp->chan->cid.cid_name)
01649       free(tmp->chan->cid.cid_name);
01650    tmp->chan->cid.cid_name = ast_strdup(qe->chan->cid.cid_name);
01651    if (tmp->chan->cid.cid_ani)
01652       free(tmp->chan->cid.cid_ani);
01653    tmp->chan->cid.cid_ani = ast_strdup(qe->chan->cid.cid_ani);
01654 
01655    /* Inherit specially named variables from parent channel */
01656    ast_channel_inherit_variables(qe->chan, tmp->chan);
01657 
01658    /* Presense of ADSI CPE on outgoing channel follows ours */
01659    tmp->chan->adsicpe = qe->chan->adsicpe;
01660 
01661    /* Place the call, but don't wait on the answer */
01662    if ((res = ast_call(tmp->chan, location, 0))) {
01663       /* Again, keep going even if there's an error */
01664       if (option_debug)
01665          ast_log(LOG_DEBUG, "ast call on peer returned %d\n", res);
01666       if (option_verbose > 2)
01667          ast_verbose(VERBOSE_PREFIX_3 "Couldn't call %s\n", tmp->interface);
01668       do_hang(tmp);
01669       (*busies)++;
01670       return 0;
01671    } else if (qe->parent->eventwhencalled) {
01672       char vars[2048];
01673 
01674       manager_event(EVENT_FLAG_AGENT, "AgentCalled",
01675                "AgentCalled: %s\r\n"
01676                "ChannelCalling: %s\r\n"
01677                "CallerID: %s\r\n"
01678                "CallerIDName: %s\r\n"
01679                "Context: %s\r\n"
01680                "Extension: %s\r\n"
01681                "Priority: %d\r\n"
01682                "%s",
01683                tmp->interface, qe->chan->name,
01684                tmp->chan->cid.cid_num ? tmp->chan->cid.cid_num : "unknown",
01685                tmp->chan->cid.cid_name ? tmp->chan->cid.cid_name : "unknown",
01686                qe->chan->context, qe->chan->exten, qe->chan->priority,
01687                qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
01688       if (option_verbose > 2)
01689          ast_verbose(VERBOSE_PREFIX_3 "Called %s\n", tmp->interface);
01690    }
01691 
01692    return 1;
01693 }

static int ring_one ( struct queue_ent qe,
struct callattempt outgoing,
int *  busies 
) [static]

Definition at line 1711 of file app_queue.c.

References ast_log(), callattempt::chan, find_best(), callattempt::interface, LOG_DEBUG, callattempt::metric, option_debug, queue_ent::parent, callattempt::q_next, QUEUE_STRATEGY_RINGALL, ring_entry(), callattempt::stillgoing, and call_queue::strategy.

Referenced by try_calling(), and wait_for_answer().

01712 {
01713    int ret = 0;
01714 
01715    while (ret == 0) {
01716       struct callattempt *best = find_best(outgoing);
01717       if (!best) {
01718          if (option_debug)
01719             ast_log(LOG_DEBUG, "Nobody left to try ringing in queue\n");
01720          break;
01721       }
01722       if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
01723          struct callattempt *cur;
01724          /* Ring everyone who shares this best metric (for ringall) */
01725          for (cur = outgoing; cur; cur = cur->q_next) {
01726             if (cur->stillgoing && !cur->chan && cur->metric <= best->metric) {
01727                if (option_debug)
01728                   ast_log(LOG_DEBUG, "(Parallel) Trying '%s' with metric %d\n", cur->interface, cur->metric);
01729                ring_entry(qe, cur, busies);
01730             }
01731          }
01732       } else {
01733          /* Ring just the best channel */
01734          if (option_debug)
01735             ast_log(LOG_DEBUG, "Trying '%s' with metric %d\n", best->interface, best->metric);
01736          ring_entry(qe, best, busies);
01737       }
01738       if (best->chan) /* break out with result = 1 */
01739          ret = 1;
01740    }
01741 
01742    return ret;
01743 }

static void rna ( int  rnatime,
struct queue_ent qe,
char *  interface,
char *  membername 
) [static]

RNA == Ring No Answer. Common code that is executed when we try a queue member and they don't answer.

Definition at line 1844 of file app_queue.c.

References ast_queue_log(), ast_verbose(), call_queue::autopause, queue_ent::chan, call_queue::name, option_verbose, queue_ent::parent, set_member_paused(), and VERBOSE_PREFIX_3.

01845 {
01846    if (option_verbose > 2)
01847       ast_verbose( VERBOSE_PREFIX_3 "Nobody picked up in %d ms\n", rnatime);
01848    ast_queue_log(qe->parent->name, qe->chan->uniqueid, membername, "RINGNOANSWER", "%d", rnatime);
01849    if (qe->parent->autopause) {
01850       if (!set_member_paused(qe->parent->name, interface, 1)) {
01851          if (option_verbose > 2)
01852             ast_verbose( VERBOSE_PREFIX_3 "Auto-Pausing Queue Member %s in queue %s since they failed to answer.\n", interface, qe->parent->name);
01853       } else {
01854          if (option_verbose > 2)
01855             ast_verbose( VERBOSE_PREFIX_3 "Failed to pause Queue Member %s in queue %s!\n", interface, qe->parent->name);
01856       }
01857    }
01858    return;
01859 }

static int rqm_exec ( struct ast_channel chan,
void *  data 
) [static]

Definition at line 3106 of file app_queue.c.

References AST_APP_ARG, AST_DECLARE_APP_ARGS, ast_goto_if_exists(), ast_log(), ast_module_user_add, ast_module_user_remove, ast_opt_priority_jumping, ast_queue_log(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), ast_module_user::chan, ast_channel::context, LOG_DEBUG, LOG_NOTICE, LOG_WARNING, parse(), pbx_builtin_setvar_helper(), remove_from_queue(), RES_EXISTS, RES_NOSUCHQUEUE, and RES_OKAY.

Referenced by load_module().

03107 {
03108    int res=-1;
03109    struct ast_module_user *lu;
03110    char *parse, *temppos = NULL;
03111    int priority_jump = 0;
03112    AST_DECLARE_APP_ARGS(args,
03113       AST_APP_ARG(queuename);
03114       AST_APP_ARG(interface);
03115       AST_APP_ARG(options);
03116    );
03117 
03118 
03119    if (ast_strlen_zero(data)) {
03120       ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename[|interface[|options]])\n");
03121       return -1;
03122    }
03123 
03124    parse = ast_strdupa(data);
03125 
03126    AST_STANDARD_APP_ARGS(args, parse);
03127 
03128    lu = ast_module_user_add(chan);
03129 
03130    if (ast_strlen_zero(args.interface)) {
03131       args.interface = ast_strdupa(chan->name);
03132       temppos = strrchr(args.interface, '-');
03133       if (temppos)
03134          *temppos = '\0';
03135    }
03136 
03137    if (args.options) {
03138       if (strchr(args.options, 'j'))
03139          priority_jump = 1;
03140    }
03141 
03142    switch (remove_from_queue(args.queuename, args.interface)) {
03143    case RES_OKAY:
03144       ast_queue_log(args.queuename, chan->uniqueid, args.interface, "REMOVEMEMBER", "%s", "");
03145       ast_log(LOG_NOTICE, "Removed interface '%s' from queue '%s'\n", args.interface, args.queuename);
03146       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "REMOVED");
03147       res = 0;
03148       break;
03149    case RES_EXISTS:
03150       ast_log(LOG_DEBUG, "Unable to remove interface '%s' from queue '%s': Not there\n", args.interface, args.queuename);
03151       if (priority_jump || ast_opt_priority_jumping)
03152          ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
03153       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTINQUEUE");
03154       res = 0;
03155       break;
03156    case RES_NOSUCHQUEUE:
03157       ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", args.queuename);
03158       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOSUCHQUEUE");
03159       res = 0;
03160       break;
03161    }
03162 
03163    ast_module_user_remove(lu);
03164 
03165    return res;
03166 }

static void rr_dep_warning ( void   )  [static]

Definition at line 406 of file app_queue.c.

References ast_log(), and LOG_NOTICE.

Referenced by reload_queues().

00407 {
00408    static unsigned int warned = 0;
00409 
00410    if (!warned) {
00411       ast_log(LOG_NOTICE, "The 'roundrobin' queue strategy is deprecated. Please use the 'rrmemory' strategy instead.\n");
00412       warned = 1;
00413    }
00414 }

static void rt_handle_member_record ( struct call_queue q,
char *  interface,
const char *  membername,
const char *  penalty_str,
const char *  paused_str 
) [static]

Definition at line 925 of file app_queue.c.

References add_to_interfaces(), create_queue_member(), member::dead, member::interface, call_queue::members, member::next, member::paused, and member::penalty.

00926 {
00927    struct member *m, *prev_m;
00928    int penalty = 0;
00929    int paused  = 0;
00930 
00931    if (penalty_str) {
00932       penalty = atoi(penalty_str);
00933       if (penalty < 0)
00934          penalty = 0;
00935    }
00936 
00937    if (paused_str) {
00938       paused = atoi(paused_str);
00939       if (paused < 0)
00940          paused = 0;
00941    }
00942 
00943    /* Find the member, or the place to put a new one. */
00944    for (m = q->members, prev_m = NULL;
00945       m && strcmp(m->interface, interface);
00946       prev_m = m, m = m->next);
00947 
00948    /* Create a new one if not found, else update penalty */
00949    if (!m) {
00950       if ((m = create_queue_member(interface, membername, penalty, paused))) {
00951          m->dead = 0;
00952          add_to_interfaces(interface);
00953          if (prev_m) {
00954             prev_m->next = m;
00955          } else {
00956             q->members = m;
00957          }
00958       }
00959    } else {
00960       m->dead = 0;   /* Do not delete this one. */
00961       if (paused_str)
00962          m->paused = paused;
00963       m->penalty = penalty;
00964    }
00965 }

static int say_periodic_announcement ( struct queue_ent qe  )  [static]

Definition at line 1789 of file app_queue.c.

References ast_moh_start(), ast_moh_stop(), ast_verbose(), background_file(), queue_ent::chan, queue_ent::last_periodic_announce_sound, queue_ent::last_periodic_announce_time, MAX_PERIODIC_ANNOUNCEMENTS, queue_ent::moh, option_verbose, queue_ent::parent, call_queue::periodicannouncefrequency, call_queue::sound_periodicannounce, and VERBOSE_PREFIX_3.

Referenced by queue_exec(), and wait_our_turn().

01790 {
01791    int res = 0;
01792    time_t now;
01793 
01794    /* Get the current time */
01795    time(&now);
01796 
01797    /* Check to see if it is time to announce */
01798    if ((now - qe->last_periodic_announce_time) < qe->parent->periodicannouncefrequency)
01799       return 0;
01800 
01801    /* Stop the music on hold so we can play our own file */
01802    ast_moh_stop(qe->chan);
01803 
01804    if (option_verbose > 2)
01805       ast_verbose(VERBOSE_PREFIX_3 "Playing periodic announcement\n");
01806 
01807    /* Check to make sure we have a sound file. If not, reset to the first sound file */
01808    if (qe->last_periodic_announce_sound >= MAX_PERIODIC_ANNOUNCEMENTS || !strlen(qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound])) {
01809       qe->last_periodic_announce_sound = 0;
01810    }
01811    
01812    /* play the announcement */
01813    res = background_file(qe, qe->chan, qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound]);
01814 
01815    /* Resume Music on Hold if the caller is going to stay in the queue */
01816    if (!res)
01817       ast_moh_start(qe->chan, qe->moh, NULL);
01818 
01819    /* update last_periodic_announce_time */
01820    qe->last_periodic_announce_time = now;
01821 
01822    /* Update the current periodic announcement to the next announcement */
01823    qe->last_periodic_announce_sound++;
01824    
01825    return res;
01826 }

static int say_position ( struct queue_ent qe  )  [static]

Definition at line 1271 of file app_queue.c.

References call_queue::announcefrequency, call_queue::announceholdtime, ANNOUNCEHOLDTIME_ONCE, AST_DIGIT_ANY, ast_moh_start(), ast_moh_stop(), ast_say_number(), ast_verbose(), queue_ent::chan, call_queue::holdtime, queue_ent::last_pos, queue_ent::last_pos_said, queue_ent::moh, call_queue::name, option_verbose, queue_ent::parent, play_file(), queue_ent::pos, call_queue::roundingseconds, call_queue::sound_calls, call_queue::sound_holdtime, call_queue::sound_lessthan, call_queue::sound_minutes, call_queue::sound_next, call_queue::sound_seconds, call_queue::sound_thanks, call_queue::sound_thereare, queue_ent::start, valid_exit(), and VERBOSE_PREFIX_3.

Referenced by queue_exec(), and wait_our_turn().

01272 {
01273    int res = 0, avgholdmins, avgholdsecs;
01274    time_t now;
01275 
01276    /* Check to see if this is ludicrous -- if we just announced position, don't do it again*/
01277    time(&now);
01278    if ((now - qe->last_pos) < 15)
01279       return 0;
01280 
01281    /* If either our position has changed, or we are over the freq timer, say position */
01282    if ((qe->last_pos_said == qe->pos) && ((now - qe->last_pos) < qe->parent->announcefrequency))
01283       return 0;
01284 
01285    ast_moh_stop(qe->chan);
01286    /* Say we're next, if we are */
01287    if (qe->pos == 1) {
01288       res = play_file(qe->chan, qe->parent->sound_next);
01289       if (res && valid_exit(qe, res))
01290          goto playout;
01291       else
01292          goto posout;
01293    } else {
01294       res = play_file(qe->chan, qe->parent->sound_thereare);
01295       if (res && valid_exit(qe, res))
01296          goto playout;
01297       res = ast_say_number(qe->chan, qe->pos, AST_DIGIT_ANY, qe->chan->language, (char *) NULL); /* Needs gender */
01298       if (res && valid_exit(qe, res))
01299          goto playout;
01300       res = play_file(qe->chan, qe->parent->sound_calls);
01301       if (res && valid_exit(qe, res))
01302          goto playout;
01303    }
01304    /* Round hold time to nearest minute */
01305    avgholdmins = abs(((qe->parent->holdtime + 30) - (now - qe->start)) / 60);
01306 
01307    /* If they have specified a rounding then round the seconds as well */
01308    if (qe->parent->roundingseconds) {
01309       avgholdsecs = (abs(((qe->parent->holdtime + 30) - (now - qe->start))) - 60 * avgholdmins) / qe->parent->roundingseconds;
01310       avgholdsecs *= qe->parent->roundingseconds;
01311    } else {
01312       avgholdsecs = 0;
01313    }
01314 
01315    if (option_verbose > 2)
01316       ast_verbose(VERBOSE_PREFIX_3 "Hold time for %s is %d minutes %d seconds\n", qe->parent->name, avgholdmins, avgholdsecs);
01317 
01318    /* If the hold time is >1 min, if it's enabled, and if it's not
01319       supposed to be only once and we have already said it, say it */
01320    if ((avgholdmins+avgholdsecs) > 0 && (qe->parent->announceholdtime) &&
01321       (!(qe->parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE) && qe->last_pos)) {
01322       res = play_file(qe->chan, qe->parent->sound_holdtime);
01323       if (res && valid_exit(qe, res))
01324          goto playout;
01325 
01326       if (avgholdmins > 0) {
01327          if (avgholdmins < 2) {
01328             res = play_file(qe->chan, qe->parent->sound_lessthan);
01329             if (res && valid_exit(qe, res))
01330                goto playout;
01331 
01332             res = ast_say_number(qe->chan, 2, AST_DIGIT_ANY, qe->chan->language, NULL);
01333             if (res && valid_exit(qe, res))
01334                goto playout;
01335          } else {
01336             res = ast_say_number(qe->chan, avgholdmins, AST_DIGIT_ANY, qe->chan->language, NULL);
01337             if (res && valid_exit(qe, res))
01338                goto playout;
01339          }
01340          
01341          res = play_file(qe->chan, qe->parent->sound_minutes);
01342          if (res && valid_exit(qe, res))
01343             goto playout;
01344       }
01345       if (avgholdsecs>0) {
01346          res = ast_say_number(qe->chan, avgholdsecs, AST_DIGIT_ANY, qe->chan->language, NULL);
01347          if (res && valid_exit(qe, res))
01348             goto playout;
01349 
01350          res = play_file(qe->chan, qe->parent->sound_seconds);
01351          if (res && valid_exit(qe, res))
01352             goto playout;
01353       }
01354 
01355    }
01356 
01357 posout:
01358    if (option_verbose > 2)
01359       ast_verbose(VERBOSE_PREFIX_3 "Told %s in %s their queue position (which was %d)\n",
01360          qe->chan->name, qe->parent->name, qe->pos);
01361    res = play_file(qe->chan, qe->parent->sound_thanks);
01362    if (res && !valid_exit(qe, res))
01363       res = 0;
01364 
01365 playout:
01366    /* Set our last_pos indicators */
01367    qe->last_pos = now;
01368    qe->last_pos_said = qe->pos;
01369 
01370    /* Don't restart music on hold if we're about to exit the caller from the queue */
01371    if (!res)
01372       ast_moh_start(qe->chan, qe->moh, NULL);
01373 
01374    return res;
01375 }

static int set_member_paused ( const char *  queuename,
const char *  interface,
int  paused 
) [static]

Definition at line 2867 of file app_queue.c.

References AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), ast_mutex_lock(), ast_mutex_unlock(), ast_queue_log(), ast_strlen_zero(), dump_queue_members(), EVENT_FLAG_AGENT, member::interface, interface_exists(), call_queue::lock, LOG_DEBUG, manager_event(), member::membername, call_queue::name, member::paused, RESULT_FAILURE, and RESULT_SUCCESS.

Referenced by manager_pause_queue_member(), pqm_exec(), rna(), and upqm_exec().

02868 {
02869    int found = 0;
02870    struct call_queue *q;
02871    struct member *mem;
02872 
02873    /* Special event for when all queues are paused - individual events still generated */
02874    /* XXX In all other cases, we use the membername, but since this affects all queues, we cannot */
02875    if (ast_strlen_zero(queuename))
02876       ast_queue_log("NONE", "NONE", interface, (paused ? "PAUSEALL" : "UNPAUSEALL"), "%s", "");
02877 
02878    AST_LIST_LOCK(&queues);
02879    AST_LIST_TRAVERSE(&queues, q, list) {
02880       ast_mutex_lock(&q->lock);
02881       if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
02882          if ((mem = interface_exists(q, interface))) {
02883             found++;
02884             if (mem->paused == paused)
02885                ast_log(LOG_DEBUG, "%spausing already-%spaused queue member %s:%s\n", (paused ? "" : "un"), (paused ? "" : "un"), q->name, interface);
02886             mem->paused = paused;
02887 
02888             if (queue_persistent_members)
02889                dump_queue_members(q);
02890 
02891             ast_queue_log(q->name, "NONE", mem->membername, (paused ? "PAUSE" : "UNPAUSE"), "%s", "");
02892 
02893             manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
02894                "Queue: %s\r\n"
02895                "Location: %s\r\n"
02896                "MemberName: %s\r\n"
02897                "Paused: %d\r\n",
02898                   q->name, mem->interface, mem->membername, paused);
02899          }
02900       }
02901       ast_mutex_unlock(&q->lock);
02902    }
02903    AST_LIST_UNLOCK(&queues);
02904 
02905    return found ? RESULT_SUCCESS : RESULT_FAILURE;
02906 }

static void set_queue_result ( struct ast_channel chan,
enum queue_result  res 
) [static]

Definition at line 416 of file app_queue.c.

References queue_ent::chan, pbx_builtin_setvar_helper(), queue_results, and text.

Referenced by queue_exec().

00417 {
00418    int i;
00419 
00420    for (i = 0; i < sizeof(queue_results) / sizeof(queue_results[0]); i++) {
00421       if (queue_results[i].id == res) {
00422          pbx_builtin_setvar_helper(chan, "QUEUESTATUS", queue_results[i].text);
00423          return;
00424       }
00425    }
00426 }

static int statechange_queue ( const char *  dev,
int  state,
void *  ign 
) [static]

Definition at line 593 of file app_queue.c.

References ast_calloc, ast_log(), ast_pthread_create_background, changethread(), free, LOG_WARNING, and t.

Referenced by load_module().

00594 {
00595    /* Avoid potential for deadlocks by spawning a new thread to handle
00596       the event */
00597    struct statechange *sc;
00598    pthread_t t;
00599    pthread_attr_t attr;
00600 
00601    if (!(sc = ast_calloc(1, sizeof(*sc) + strlen(dev) + 1)))
00602       return 0;
00603 
00604    sc->state = state;
00605    strcpy(sc->dev, dev);
00606    pthread_attr_init(&attr);
00607    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
00608    if (ast_pthread_create_background(&t, &attr, changethread, sc)) {
00609       ast_log(LOG_WARNING, "Failed to create update thread!\n");
00610       free(sc);
00611    }
00612    pthread_attr_destroy(&attr);
00613 
00614    return 0;
00615 }

static int store_next ( struct queue_ent qe,
struct callattempt outgoing 
) [static]

Definition at line 1745 of file app_queue.c.

References ast_log(), find_best(), callattempt::interface, LOG_DEBUG, callattempt::metric, option_debug, queue_ent::parent, call_queue::rrpos, and call_queue::wrapped.

Referenced by try_calling().

01746 {
01747    struct callattempt *best = find_best(outgoing);
01748 
01749    if (best) {
01750       /* Ring just the best channel */
01751       if (option_debug)
01752          ast_log(LOG_DEBUG, "Next is '%s' with metric %d\n", best->interface, best->metric);
01753       qe->parent->rrpos = best->metric % 1000;
01754    } else {
01755       /* Just increment rrpos */
01756       if (qe->parent->wrapped) {
01757          /* No more channels, start over */
01758          qe->parent->rrpos = 0;
01759       } else {
01760          /* Prioritize next entry */
01761          qe->parent->rrpos++;
01762       }
01763    }
01764    qe->parent->wrapped = 0;
01765 
01766    return 0;
01767 }

static int strat2int ( const char *  strategy  )  [static]

Definition at line 440 of file app_queue.c.

References name, and strategies.

Referenced by queue_set_param().

00441 {
00442    int x;
00443 
00444    for (x = 0; x < sizeof(strategies) / sizeof(strategies[0]); x++) {
00445       if (!strcasecmp(strategy, strategies[x].name))
00446          return strategies[x].strategy;
00447    }
00448 
00449    return -1;
00450 }

static int try_calling ( struct queue_ent qe,
const char *  options,
char *  announceoverride,
const char *  url,
int *  go_on,
const char *  agi 
) [static]

Definition at line 2296 of file app_queue.c.

References ast_channel::_softhangup, ast_channel::_state, queue_ent::announce, app, ast_autoservice_start(), ast_autoservice_stop(), ast_bridge_call(), ast_calloc, ast_cdr_setdestchan(), ast_channel_make_compatible(), ast_channel_sendurl(), ast_channel_setoption(), ast_channel_supports_html(), AST_DEVICE_NOT_INUSE, AST_DIGIT_ANY, AST_FEATURE_AUTOMON, AST_FEATURE_DISCONNECT, AST_FEATURE_REDIRECT, ast_hangup(), AST_LIST_LOCK, AST_LIST_UNLOCK, ast_log(), AST_MAX_CONTEXT, AST_MAX_EXTENSION, ast_moh_stop(), ast_monitor_setjoinfiles(), ast_monitor_start(), ast_mutex_lock(), ast_mutex_unlock(), AST_OPTION_TONE_VERIFY, AST_PBX_NO_HANGUP_PEER, ast_queue_log(), ast_random(), ast_safe_sleep(), ast_say_number(), ast_set_flag, AST_STATE_UP, ast_strdupa, ast_strlen_zero(), ast_test_flag, calc_metric(), ast_channel::cdr, callattempt::chan, queue_ent::chan, ast_channel::context, EVENT_FLAG_AGENT, call_queue::eventwhencalled, queue_ent::expire, ast_channel::exten, free, queue_ent::handled, hangupcalls(), member::interface, member::lastcall, leave_queue(), call_queue::lock, LOG_DEBUG, LOG_NOTICE, LOG_WARNING, manager_event(), callattempt::member, call_queue::memberdelay, member::membername, call_queue::members, call_queue::monfmt, call_queue::monjoin, call_queue::montype, call_queue::name, member::next, queue_ent::opos, option_debug, queue_ent::parent, pbx_builtin_getvar_helper(), pbx_builtin_setvar_helper(), pbx_exec(), pbx_findapp(), pbx_substitute_variables_helper(), play_file(), queue_ent::pos, QUEUE_EVENT_VARIABLES, recalc_holdtime(), record_abandoned(), call_queue::reportholdtime, ring_one(), call_queue::setinterfacevar, call_queue::sound_lessthan, call_queue::sound_minutes, call_queue::sound_reporthold, queue_ent::start, member::status, store_next(), call_queue::strategy, ast_channel::tech, call_queue::timeout, ast_channel_tech::type, ast_cdr::uniqueid, update_queue(), vars2manager(), and wait_for_answer().

Referenced by queue_exec().

02297 {
02298    struct member *cur;
02299    struct callattempt *outgoing = NULL; /* the list of calls we are building */
02300    int to;
02301    char oldexten[AST_MAX_EXTENSION]="";
02302    char oldcontext[AST_MAX_CONTEXT]="";
02303    char queuename[256]="";
02304    struct ast_channel *peer;
02305    struct ast_channel *which;
02306    struct callattempt *lpeer;
02307    struct member *member;
02308    struct ast_app *app;
02309    int res = 0, bridge = 0;
02310    int numbusies = 0;
02311    int x=0;
02312    char *announce = NULL;
02313    char digit = 0;
02314    time_t callstart;
02315    time_t now = time(NULL);
02316    struct ast_bridge_config bridge_config;
02317    char nondataquality = 1;
02318    char *agiexec = NULL;
02319    int ret = 0;
02320    const char *monitorfilename;
02321    const char *monitor_exec;
02322    const char *monitor_options;
02323    char tmpid[256], tmpid2[256];
02324    char meid[1024], meid2[1024];
02325    char mixmonargs[1512];
02326    struct ast_app *mixmonapp = NULL;
02327    char *p;
02328    char vars[2048];
02329    int forwardsallowed = 1;
02330 
02331    memset(&bridge_config, 0, sizeof(bridge_config));
02332    time(&now);
02333       
02334    for (; options && *options; options++)
02335       switch (*options) {
02336       case 't':
02337          ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_REDIRECT);
02338          break;
02339       case 'T':
02340          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_REDIRECT);
02341          break;
02342       case 'w':
02343          ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMON);
02344          break;
02345       case 'W':
02346          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMON);
02347          break;
02348       case 'd':
02349          nondataquality = 0;
02350          break;
02351       case 'h':
02352          ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_DISCONNECT);
02353          break;
02354       case 'H':
02355          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT);
02356          break;
02357       case 'n':
02358          if ((now - qe->start >= qe->parent->timeout))
02359             *go_on = 1;
02360          break;
02361       case 'i':
02362          forwardsallowed = 0;
02363          break;
02364       }
02365 
02366    /* Hold the lock while we setup the outgoing calls */
02367    if (use_weight)
02368       AST_LIST_LOCK(&queues);
02369    ast_mutex_lock(&qe->parent->lock);
02370    if (option_debug)
02371       ast_log(LOG_DEBUG, "%s is trying to call a queue member.\n",
02372                      qe->chan->name);
02373    ast_copy_string(queuename, qe->parent->name, sizeof(queuename));
02374    cur = qe->parent->members;
02375    if (!ast_strlen_zero(qe->announce))
02376       announce = qe->announce;
02377    if (!ast_strlen_zero(announceoverride))
02378       announce = announceoverride;
02379 
02380    for (; cur; cur = cur->next) {
02381       struct callattempt *tmp = ast_calloc(1, sizeof(*tmp));
02382 
02383       if (!tmp) {
02384          ast_mutex_unlock(&qe->parent->lock);
02385          if (use_weight)
02386             AST_LIST_UNLOCK(&queues);
02387          goto out;
02388       }
02389       tmp->stillgoing = -1;
02390       tmp->member = cur;      /* Never directly dereference!  Could change on reload */
02391       tmp->oldstatus = cur->status;
02392       tmp->lastcall = cur->lastcall;
02393       ast_copy_string(tmp->interface, cur->interface, sizeof(tmp->interface));
02394       /* Special case: If we ring everyone, go ahead and ring them, otherwise
02395          just calculate their metric for the appropriate strategy */
02396       if (!calc_metric(qe->parent, cur, x++, qe, tmp)) {
02397          /* Put them in the list of outgoing thingies...  We're ready now.
02398             XXX If we're forcibly removed, these outgoing calls won't get
02399             hung up XXX */
02400          tmp->q_next = outgoing;
02401          outgoing = tmp;      
02402          /* If this line is up, don't try anybody else */
02403          if (outgoing->chan && (outgoing->chan->_state == AST_STATE_UP))
02404             break;
02405       } else {
02406          free(tmp);
02407       }
02408    }
02409    if (qe->expire && (!qe->parent->timeout || (qe->expire - now) <= qe->parent->timeout))
02410       to = (qe->expire - now) * 1000;
02411    else
02412       to = (qe->parent->timeout) ? qe->parent->timeout * 1000 : -1;
02413    ring_one(qe, outgoing, &numbusies);
02414    ast_mutex_unlock(&qe->parent->lock);
02415    if (use_weight)
02416       AST_LIST_UNLOCK(&queues);
02417    lpeer = wait_for_answer(qe, outgoing, &to, &digit, numbusies, ast_test_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT), forwardsallowed);
02418    ast_mutex_lock(&qe->parent->lock);
02419    if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY) {
02420       store_next(qe, outgoing);
02421    }
02422    ast_mutex_unlock(&qe->parent->lock);
02423    peer = lpeer ? lpeer->chan : NULL;
02424    if (!peer) {
02425       if (to) {
02426          /* Must gotten hung up */
02427          res = -1;
02428       } else {
02429          res = digit;
02430       }
02431       if (option_debug)
02432          ast_log(LOG_DEBUG, "%s: Nobody answered.\n", qe->chan->name);
02433    } else { /* peer is valid */
02434       /* Ah ha!  Someone answered within the desired timeframe.  Of course after this
02435          we will always return with -1 so that it is hung up properly after the
02436          conversation.  */
02437       qe->handled++;
02438       if (!strcmp(qe->chan->tech->type, "Zap"))
02439          ast_channel_setoption(qe->chan, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
02440       if (!strcmp(peer->tech->type, "Zap"))
02441          ast_channel_setoption(peer, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
02442       /* Update parameters for the queue */
02443       recalc_holdtime(qe);
02444       member = lpeer->member;
02445       hangupcalls(outgoing, peer);
02446       outgoing = NULL;
02447       if (announce || qe->parent->reportholdtime || qe->parent->memberdelay) {
02448          int res2;
02449 
02450          res2 = ast_autoservice_start(qe->chan);
02451          if (!res2) {
02452             if (qe->parent->memberdelay) {
02453                ast_log(LOG_NOTICE, "Delaying member connect for %d seconds\n", qe->parent->memberdelay);
02454                res2 |= ast_safe_sleep(peer, qe->parent->memberdelay * 1000);
02455             }
02456             if (!res2 && announce) {
02457                if (play_file(peer, announce))
02458                   ast_log(LOG_WARNING, "Announcement file '%s' is unavailable, continuing anyway...\n", announce);
02459             }
02460             if (!res2 && qe->parent->reportholdtime) {
02461                if (!play_file(peer, qe->parent->sound_reporthold)) {
02462                   int holdtime;
02463 
02464                   time(&now);
02465                   holdtime = abs((now - qe->start) / 60);
02466                   if (holdtime < 2) {
02467                      play_file(peer, qe->parent->sound_lessthan);
02468                      ast_say_number(peer, 2, AST_DIGIT_ANY, peer->language, NULL);
02469                   } else
02470                      ast_say_number(peer, holdtime, AST_DIGIT_ANY, peer->language, NULL);
02471                   play_file(peer, qe->parent->sound_minutes);
02472                }
02473             }
02474          }
02475          res2 |= ast_autoservice_stop(qe->chan);
02476          if (peer->_softhangup) {
02477             /* Agent must have hung up */
02478             ast_log(LOG_WARNING, "Agent on %s hungup on the customer.  They're going to be pissed.\n", peer->name);
02479             ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "AGENTDUMP", "%s", "");
02480             record_abandoned(qe);
02481             if (qe->parent->eventwhencalled)
02482                manager_event(EVENT_FLAG_AGENT, "AgentDump",
02483                      "Queue: %s\r\n"
02484                      "Uniqueid: %s\r\n"
02485                      "Channel: %s\r\n"
02486                      "Member: %s\r\n"
02487                      "MemberName: %s\r\n"
02488                      "%s",
02489                      queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
02490                      qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
02491             ast_hangup(peer);
02492             goto out;
02493          } else if (res2) {
02494             /* Caller must have hung up just before being connected*/
02495             ast_log(LOG_NOTICE, "Caller was about to talk to agent on %s but the caller hungup.\n", peer->name);
02496             ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "ABANDON", "%d|%d|%ld", qe->pos, qe->opos, (long)time(NULL) - qe->start);
02497             record_abandoned(qe);
02498             ast_hangup(peer);
02499             return -1;
02500          }
02501       }
02502       /* Stop music on hold */
02503       ast_moh_stop(qe->chan);
02504       /* If appropriate, log that we have a destination channel */
02505       if (qe->chan->cdr)
02506          ast_cdr_setdestchan(qe->chan->cdr, peer->name);
02507       /* Make sure channels are compatible */
02508       res = ast_channel_make_compatible(qe->chan, peer);
02509       if (res < 0) {
02510          ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "SYSCOMPAT", "%s", "");
02511          ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", qe->chan->name, peer->name);
02512          record_abandoned(qe);
02513          ast_hangup(peer);
02514          return -1;
02515       }
02516       /* Begin Monitoring */
02517       if (qe->parent->monfmt && *qe->parent->monfmt) {
02518          if (!qe->parent->montype) {
02519             if (option_debug)
02520                ast_log(LOG_DEBUG, "Starting Monitor as requested.\n");
02521             monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME");
02522             if (pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC") || pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC_ARGS"))
02523                which = qe->chan;
02524             else
02525                which = peer;
02526             if (monitorfilename)
02527                ast_monitor_start(which, qe->parent->monfmt, monitorfilename, 1 );
02528             else if (qe->chan->cdr)
02529                ast_monitor_start(which, qe->parent->monfmt, qe->chan->cdr->uniqueid, 1 );
02530             else {
02531                /* Last ditch effort -- no CDR, make up something */
02532                snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random());
02533                ast_monitor_start(which, qe->parent->monfmt, tmpid, 1 );
02534             }
02535             if (qe->parent->monjoin)
02536                ast_monitor_setjoinfiles(which, 1);
02537          } else {
02538             if (option_debug)
02539                ast_log(LOG_DEBUG, "Starting MixMonitor as requested.\n");
02540             monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME");
02541             if (!monitorfilename) {
02542                if (qe->chan->cdr)
02543                   ast_copy_string(tmpid, qe->chan->cdr->uniqueid, sizeof(tmpid)-1);
02544                else
02545                   snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random());
02546             } else {
02547                ast_copy_string(tmpid2, monitorfilename, sizeof(tmpid2)-1);
02548                for (p = tmpid2; *p ; p++) {
02549                   if (*p == '^' && *(p+1) == '{') {
02550                      *p = '$';
02551                   }
02552                }
02553 
02554                memset(tmpid, 0, sizeof(tmpid));
02555                pbx_substitute_variables_helper(qe->chan, tmpid2, tmpid, sizeof(tmpid) - 1);
02556             }
02557 
02558             monitor_exec = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC");
02559             monitor_options = pbx_builtin_getvar_helper(qe->chan, "MONITOR_OPTIONS");
02560 
02561             if (monitor_exec) {
02562                ast_copy_string(meid2, monitor_exec, sizeof(meid2)-1);
02563                for (p = meid2; *p ; p++) {
02564                   if (*p == '^' && *(p+1) == '{') {
02565                      *p = '$';
02566                   }
02567                }
02568 
02569                memset(meid, 0, sizeof(meid));
02570                pbx_substitute_variables_helper(qe->chan, meid2, meid, sizeof(meid) - 1);
02571             }
02572    
02573             snprintf(tmpid2, sizeof(tmpid2)-1, "%s.%s", tmpid, qe->parent->monfmt);
02574 
02575             mixmonapp = pbx_findapp("MixMonitor");
02576 
02577             if (strchr(tmpid2, '|')) {
02578                ast_log(LOG_WARNING, "monitor-format (in queues.conf) and MONITOR_FILENAME cannot contain a '|'! Not recording.\n");
02579                mixmonapp = NULL;
02580             }
02581 
02582             if (!monitor_options)
02583                monitor_options = "";
02584             
02585             if (strchr(monitor_options, '|')) {
02586                ast_log(LOG_WARNING, "MONITOR_OPTIONS cannot contain a '|'! Not recording.\n");
02587                mixmonapp = NULL;
02588             }
02589 
02590             if (mixmonapp) {
02591                if (!ast_strlen_zero(monitor_exec))
02592                   snprintf(mixmonargs, sizeof(mixmonargs)-1, "%s|b%s|%s", tmpid2, monitor_options, monitor_exec);
02593                else
02594                   snprintf(mixmonargs, sizeof(mixmonargs)-1, "%s|b%s", tmpid2, monitor_options);
02595                   
02596                if (option_debug)
02597                   ast_log(LOG_DEBUG, "Arguments being passed to MixMonitor: %s\n", mixmonargs);
02598 
02599                ret = pbx_exec(qe->chan, mixmonapp, mixmonargs);
02600 
02601             } else
02602                ast_log(LOG_WARNING, "Asked to run MixMonitor on this call, but cannot find the MixMonitor app!\n");
02603 
02604          }
02605       }
02606       /* Drop out of the queue at this point, to prepare for next caller */
02607       leave_queue(qe);        
02608       if (!ast_strlen_zero(url) && ast_channel_supports_html(peer)) {
02609          if (option_debug)
02610             ast_log(LOG_DEBUG, "app_queue: sendurl=%s.\n", url);
02611          ast_channel_sendurl(peer, url);
02612       }
02613       if (qe->parent->setinterfacevar)
02614             pbx_builtin_setvar_helper(qe->chan, "MEMBERINTERFACE", member->interface);
02615       if (!ast_strlen_zero(agi)) {
02616          if (option_debug)
02617             ast_log(LOG_DEBUG, "app_queue: agi=%s.\n", agi);
02618          app = pbx_findapp("agi");
02619          if (app) {
02620             agiexec = ast_strdupa(agi);
02621             ret = pbx_exec(qe->chan, app, agiexec);
02622          } else
02623             ast_log(LOG_WARNING, "Asked to execute an AGI on this channel, but could not find application (agi)!\n");
02624       }
02625       ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "CONNECT", "%ld|%s", (long)time(NULL) - qe->start, peer->uniqueid);
02626       if (qe->parent->eventwhencalled)
02627          manager_event(EVENT_FLAG_AGENT, "AgentConnect",
02628                "Queue: %s\r\n"
02629                "Uniqueid: %s\r\n"
02630                "Channel: %s\r\n"
02631                "Member: %s\r\n"
02632                "MemberName: %s\r\n"
02633                "Holdtime: %ld\r\n"
02634                "BridgedChannel: %s\r\n"
02635                "%s",
02636                queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
02637                (long)time(NULL) - qe->start, peer->uniqueid,
02638                qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
02639       ast_copy_string(oldcontext, qe->chan->context, sizeof(oldcontext));
02640       ast_copy_string(oldexten, qe->chan->exten, sizeof(oldexten));
02641       time(&callstart);
02642 
02643       if (member->status == AST_DEVICE_NOT_INUSE)
02644          ast_log(LOG_WARNING, "The device state of this queue member, %s, is still 'Not in Use' when it probably should not be! Please check UPGRADE.txt for correct configuration settings.\n", member->membername);
02645          
02646 
02647       bridge = ast_bridge_call(qe->chan,peer, &bridge_config);
02648 
02649       if (strcasecmp(oldcontext, qe->chan->context) || strcasecmp(oldexten, qe->chan->exten)) {
02650          ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld",
02651             qe->chan->exten, qe->chan->context, (long) (callstart - qe->start),
02652             (long) (time(NULL) - callstart));
02653       } else if (qe->chan->_softhangup) {
02654          ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETECALLER", "%ld|%ld|%d",
02655             (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
02656          if (qe->parent->eventwhencalled)
02657             manager_event(EVENT_FLAG_AGENT, "AgentComplete",
02658                   "Queue: %s\r\n"
02659                   "Uniqueid: %s\r\n"
02660                   "Channel: %s\r\n"
02661                   "Member: %s\r\n"
02662                   "MemberName: %s\r\n"
02663                   "HoldTime: %ld\r\n"
02664                   "TalkTime: %ld\r\n"
02665                   "Reason: caller\r\n"
02666                   "%s",
02667                   queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
02668                   (long)(callstart - qe->start), (long)(time(NULL) - callstart),
02669                   qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
02670       } else {
02671          ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETEAGENT", "%ld|%ld|%d",
02672             (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
02673          if (qe->parent->eventwhencalled)
02674             manager_event(EVENT_FLAG_AGENT, "AgentComplete",
02675                   "Queue: %s\r\n"
02676                   "Uniqueid: %s\r\n"
02677                   "Channel: %s\r\n"
02678                   "MemberName: %s\r\n"
02679                   "HoldTime: %ld\r\n"
02680                   "TalkTime: %ld\r\n"
02681                   "Reason: agent\r\n"
02682                   "%s",
02683                   queuename, qe->chan->uniqueid, peer->name, member->membername, (long)(callstart - qe->start),
02684                   (long)(time(NULL) - callstart),
02685                   qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
02686       }
02687 
02688       if (bridge != AST_PBX_NO_HANGUP_PEER)
02689          ast_hangup(peer);
02690       update_queue(qe->parent, member);
02691       res = bridge ? bridge : 1;
02692    }
02693 out:
02694    hangupcalls(outgoing, NULL);
02695 
02696    return res;
02697 }

static int unload_module ( void   )  [static]

Definition at line 4451 of file app_queue.c.

References ast_cli_unregister_multiple(), ast_custom_function_unregister(), ast_manager_unregister(), ast_module_user_hangup_all, ast_unregister_application(), clear_and_free_interfaces(), cli_queue, queueagentcount_function, queuemembercount_function, queuememberlist_function, and queuewaitingcount_function.

04452 {
04453    int res;
04454 
04455    ast_cli_unregister_multiple(cli_queue, sizeof(cli_queue) / sizeof(struct ast_cli_entry));
04456    res = ast_manager_unregister("QueueStatus");
04457    res |= ast_manager_unregister("Queues");
04458    res |= ast_manager_unregister("QueueStatus");
04459    res |= ast_manager_unregister("QueueAdd");
04460    res |= ast_manager_unregister("QueueRemove");
04461    res |= ast_manager_unregister("QueuePause");
04462    res |= ast_unregister_application(app_aqm);
04463    res |= ast_unregister_application(app_rqm);
04464    res |= ast_unregister_application(app_pqm);
04465    res |= ast_unregister_application(app_upqm);
04466    res |= ast_unregister_application(app_ql);
04467    res |= ast_unregister_application(app);
04468    res |= ast_custom_function_unregister(&queueagentcount_function);
04469    res |= ast_custom_function_unregister(&queuemembercount_function);
04470    res |= ast_custom_function_unregister(&queuememberlist_function);
04471    res |= ast_custom_function_unregister(&queuewaitingcount_function);
04472 
04473    ast_module_user_hangup_all();
04474 
04475    clear_and_free_interfaces();
04476 
04477    return res;
04478 }

static int update_dial_status ( struct call_queue q,
struct member member,
int  status 
) [static]

Definition at line 1485 of file app_queue.c.

References AST_CAUSE_BUSY, AST_CAUSE_NOSUCHDRIVER, AST_CAUSE_UNREGISTERED, AST_DEVICE_BUSY, AST_DEVICE_INVALID, AST_DEVICE_UNAVAILABLE, AST_DEVICE_UNKNOWN, and update_status().

Referenced by ring_entry().

01486 {
01487    if (status == AST_CAUSE_BUSY)
01488       status = AST_DEVICE_BUSY;
01489    else if (status == AST_CAUSE_UNREGISTERED)
01490       status = AST_DEVICE_UNAVAILABLE;
01491    else if (status == AST_CAUSE_NOSUCHDRIVER)
01492       status = AST_DEVICE_INVALID;
01493    else
01494       status = AST_DEVICE_UNKNOWN;
01495    return update_status(q, member, status);
01496 }

static int update_queue ( struct call_queue q,
struct member member 
) [static]

Definition at line 2220 of file app_queue.c.

References ast_mutex_lock(), ast_mutex_unlock(), member::calls, call_queue::callscompleted, member::lastcall, call_queue::lock, call_queue::members, and member::next.

Referenced by try_calling().

02221 {
02222    struct member *cur;
02223 
02224    /* Since a reload could have taken place, we have to traverse the list to
02225       be sure it's still valid */
02226    ast_mutex_lock(&q->lock);
02227    cur = q->members;
02228    while (cur) {
02229       if (member == cur) {
02230          time(&cur->lastcall);
02231          cur->calls++;
02232          break;
02233       }
02234       cur = cur->next;
02235    }
02236    q->callscompleted++;
02237    ast_mutex_unlock(&q->lock);
02238    return 0;
02239 }

static int update_status ( struct call_queue q,
struct member member,
int  status 
) [static]

Definition at line 1454 of file app_queue.c.

References ast_mutex_lock(), ast_mutex_unlock(), member::calls, member::dynamic, EVENT_FLAG_AGENT, member::interface, member::lastcall, call_queue::lock, manager_event(), call_queue::maskmemberstatus, member::membername, call_queue::members, call_queue::name, member::next, member::paused, member::penalty, and member::status.

Referenced by update_dial_status().

01455 {
01456    struct member *cur;
01457 
01458    /* Since a reload could have taken place, we have to traverse the list to
01459       be sure it's still valid */
01460    ast_mutex_lock(&q->lock);
01461    for (cur = q->members; cur; cur = cur->next) {
01462       if (member != cur)
01463          continue;
01464 
01465       cur->status = status;
01466       if (!q->maskmemberstatus) {
01467          manager_event(EVENT_FLAG_AGENT, "QueueMemberStatus",
01468             "Queue: %s\r\n"
01469             "Location: %s\r\n"
01470             "MemberName: %s\r\n"
01471             "Membership: %s\r\n"
01472             "Penalty: %d\r\n"
01473             "CallsTaken: %d\r\n"
01474             "LastCall: %d\r\n"
01475             "Status: %d\r\n"
01476             "Paused: %d\r\n",
01477             q->name, cur->interface, cur->membername, cur->dynamic ? "dynamic" : "static",
01478             cur->penalty, cur->calls, (int)cur->lastcall, cur->status, cur->paused);
01479       }
01480    }
01481    ast_mutex_unlock(&q->lock);
01482    return 0;
01483 }

static int upqm_exec ( struct ast_channel chan,
void *  data 
) [static]

Definition at line 3053 of file app_queue.c.

References AST_APP_ARG, AST_DECLARE_APP_ARGS, ast_goto_if_exists(), ast_log(), ast_module_user_add, ast_module_user_remove, ast_opt_priority_jumping, AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), ast_module_user::chan, ast_channel::context, LOG_WARNING, parse(), pbx_builtin_setvar_helper(), and set_member_paused().

Referenced by load_module().

03054 {
03055    struct ast_module_user *lu;
03056    char *parse;
03057    int priority_jump = 0;
03058    AST_DECLARE_APP_ARGS(args,
03059       AST_APP_ARG(queuename);
03060       AST_APP_ARG(interface);
03061       AST_APP_ARG(options);
03062    );
03063 
03064    if (ast_strlen_zero(data)) {
03065       ast_log(LOG_WARNING, "UnpauseQueueMember requires an argument ([queuename]|interface[|options])\n");
03066       return -1;
03067    }
03068 
03069    parse = ast_strdupa(data);
03070 
03071    AST_STANDARD_APP_ARGS(args, parse);
03072 
03073    lu = ast_module_user_add(chan);
03074 
03075    if (args.options) {
03076       if (strchr(args.options, 'j'))
03077          priority_jump = 1;
03078    }
03079 
03080    if (ast_strlen_zero(args.interface)) {
03081       ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename]|interface[|options])\n");
03082       ast_module_user_remove(lu);
03083       return -1;
03084    }
03085 
03086    if (set_member_paused(args.queuename, args.interface, 0)) {
03087       ast_log(LOG_WARNING, "Attempt to unpause interface %s, not found\n", args.interface);
03088       if (priority_jump || ast_opt_priority_jumping) {
03089          if (ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101)) {
03090             pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND");
03091             ast_module_user_remove(lu);
03092             return 0;
03093          }
03094       }
03095       ast_module_user_remove(lu);
03096       pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND");
03097       return -1;
03098    }
03099 
03100    ast_module_user_remove(lu);
03101    pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "UNPAUSED");
03102 
03103    return 0;
03104 }

static int valid_exit ( struct queue_ent qe,
char  digit 
) [static]

Definition at line 1239 of file app_queue.c.

References ast_canmatch_extension(), ast_goto_if_exists(), ast_strlen_zero(), queue_ent::chan, ast_channel::cid, ast_callerid::cid_num, queue_ent::context, and queue_ent::digits.

Referenced by background_file(), queue_exec(), and say_position().

01240 {
01241    int digitlen = strlen(qe->digits);
01242 
01243    /* Prevent possible buffer overflow */
01244    if (digitlen < sizeof(qe->digits) - 2) {
01245       qe->digits[digitlen] = digit;
01246       qe->digits[digitlen + 1] = '\0';
01247    } else {
01248       qe->digits[0] = '\0';
01249       return 0;
01250    }
01251 
01252    /* If there's no context to goto, short-circuit */
01253    if (ast_strlen_zero(qe->context))
01254       return 0;
01255 
01256    /* If the extension is bad, then reset the digits to blank */
01257    if (!ast_canmatch_extension(qe->chan, qe->context, qe->digits, 1, qe->chan->cid.cid_num)) {
01258       qe->digits[0] = '\0';
01259       return 0;
01260    }
01261 
01262    /* We have an exact match */
01263    if (!ast_goto_if_exists(qe->chan, qe->context, qe->digits, 1)) {
01264       /* Return 1 on a successful goto */
01265       return 1;
01266    }
01267 
01268    return 0;
01269 }

static char* vars2manager ( struct ast_channel chan,
char *  vars,
size_t  len 
) [static]

Definition at line 1540 of file app_queue.c.

References pbx_builtin_serialize_variables().

Referenced by ring_entry(), and try_calling().

01541 {
01542    char *tmp = alloca(len);
01543 
01544    if (pbx_builtin_serialize_variables(chan, tmp, len)) {
01545       int i, j;
01546 
01547       /* convert "\n" to "\nVariable: " */
01548       strcpy(vars, "Variable: ");
01549 
01550       for (i = 0, j = 10; (i < len - 1) && (j < len - 1); i++, j++) {
01551          vars[j] = tmp[i];
01552 
01553          if (tmp[i + 1] == '\0')
01554             break;
01555          if (tmp[i] == '\n') {
01556             vars[j] = '\r';
01557             vars[++j] = '\n';
01558 
01559             ast_copy_string(&(vars[j]), "Variable: ", len - j);
01560             j += 9;
01561          }
01562       }
01563       if (j > len - 1)
01564          j = len - 1;
01565       vars[j - 2] = '\r';
01566       vars[j - 1] = '\n';
01567       vars[j] = '\0';
01568    } else {
01569       /* there are no channel variables; leave it blank */
01570       *vars = '\0';
01571    }
01572    return vars;
01573 }

static int wait_a_bit ( struct queue_ent qe  )  [static]

Definition at line 2699 of file app_queue.c.

References ast_waitfordigit(), queue_ent::chan, queue_ent::parent, and call_queue::retry.

Referenced by queue_exec().

02700 {
02701    /* Don't need to hold the lock while we setup the outgoing calls */
02702    int retrywait = qe->parent->retry * 1000;
02703 
02704    return ast_waitfordigit(qe->chan, retrywait);
02705 }

static struct callattempt* wait_for_answer ( struct queue_ent qe,
struct callattempt outgoing,
int *  to,
char *  digit,
int  prebusies,
int  caller_disconnect,
int  forwardsallowed 
) [static]

Definition at line 1863 of file app_queue.c.

References ast_channel::_state, ast_log(), AST_MAX_WATCHERS, AST_STATE_UP, ast_strlen_zero(), ast_verbose(), ast_waitfor_n(), callattempt::chan, queue_ent::chan, do_hang(), f, member::interface, LOG_DEBUG, LOG_NOTICE, callattempt::member, member::membername, call_queue::name, option_verbose, queue_ent::parent, callattempt::q_next, QUEUE_STRATEGY_RINGALL, ring_one(), callattempt::stillgoing, call_queue::strategy, and VERBOSE_PREFIX_3.

01864 {
01865    char *queue = qe->parent->name;
01866    struct callattempt *o;
01867    int status;
01868    int sentringing = 0;
01869    int numbusies = prebusies;
01870    int numnochan = 0;
01871    int stillgoing = 0;
01872    int orig = *to;
01873    struct ast_frame *f;
01874    struct callattempt *peer = NULL;
01875    struct ast_channel *winner;
01876    struct ast_channel *in = qe->chan;
01877    char on[80] = "";
01878    char membername[80] = "";
01879    long starttime = 0;
01880    long endtime = 0; 
01881 
01882    starttime = (long) time(NULL);
01883    
01884    while (*to && !peer) {
01885       int numlines, retry, pos = 1;
01886       struct ast_channel *watchers[AST_MAX_WATCHERS];
01887       watchers[0] = in;
01888 
01889       for (retry = 0; retry < 2; retry++) {
01890          numlines = 0;
01891          for (o = outgoing; o; o = o->q_next) { /* Keep track of important channels */
01892             if (o->stillgoing) { /* Keep track of important channels */
01893                stillgoing = 1;
01894                if (o->chan)
01895                   watchers[pos++] = o->chan;
01896             }
01897             numlines++;
01898          }
01899          if (pos > 1 /* found */ || !stillgoing /* nobody listening */ ||
01900             (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) /* ring would not be delivered */)
01901             break;
01902          /* On "ringall" strategy we only move to the next penalty level
01903             when *all* ringing phones are done in the current penalty level */
01904          ring_one(qe, outgoing, &numbusies);
01905          /* and retry... */
01906       }
01907       if (pos == 1 /* not found */) {
01908          if (numlines == (numbusies + numnochan)) {
01909             ast_log(LOG_DEBUG, "Everyone is busy at this time\n");
01910          } else {
01911             ast_log(LOG_NOTICE, "No one is answering queue '%s' (%d/%d/%d)\n", queue, numlines, numbusies, numnochan);
01912          }
01913          *to = 0;
01914          return NULL;
01915       }
01916       winner = ast_waitfor_n(watchers, pos, to);
01917       for (o = outgoing; o; o = o->q_next) {
01918          if (o->stillgoing && (o->chan) &&  (o->chan->_state == AST_STATE_UP)) {
01919             if (!peer) {
01920                if (option_verbose > 2)
01921                   ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
01922                peer = o;
01923             }
01924          } else if (o->chan && (o->chan == winner)) {
01925 
01926             ast_copy_string(on, o->member->interface, sizeof(on));
01927             ast_copy_string(membername, o->member->membername, sizeof(membername));
01928 
01929             if (!ast_strlen_zero(o->chan->call_forward) && !forwardsallowed) {
01930                if (option_verbose > 2)
01931                   ast_verbose(VERBOSE_PREFIX_3 "Forwarding %s to '%s' prevented.\n", in->name, o->chan->call_forward);
01932                numnochan++;
01933                do_hang(o);
01934                winner = NULL;
01935                continue;
01936             } else if (!ast_strlen_zero(o->chan->call_forward)) {
01937                char tmpchan[256];
01938                char *stuff;
01939                char *tech;
01940 
01941                ast_copy_string(tmpchan, o->chan->call_forward, sizeof(tmpchan));
01942                if ((stuff = strchr(tmpchan, '/'))) {
01943                   *stuff++ = '\0';
01944                   tech = tmpchan;
01945                } else {
01946                   snprintf(tmpchan, sizeof(tmpchan), "%s@%s", o->chan->call_forward, o->chan->context);
01947                   stuff = tmpchan;
01948                   tech = "Local";
01949                }
01950                /* Before processing channel, go ahead and check for forwarding */
01951                if (option_verbose > 2)
01952                   ast_verbose(VERBOSE_PREFIX_3 "Now forwarding %s to '%s/%s' (thanks to %s)\n", in->name, tech, stuff, o->chan->name);
01953                /* Setup parameters */
01954                o->chan = ast_request(tech, in->nativeformats, stuff, &status);
01955                if (status != o->oldstatus)
01956                   update_dial_status(qe->parent, o->member, status);                
01957                if (!o->chan) {
01958                   ast_log(LOG_NOTICE, "Unable to create local channel for call forward to '%s/%s'\n", tech, stuff);
01959                   o->stillgoing = 0;
01960                   numnochan++;
01961                } else {
01962                   ast_channel_inherit_variables(in, o->chan);
01963                   if (o->chan->cid.cid_num)
01964                      free(o->chan->cid.cid_num);
01965                   o->chan->cid.cid_num = ast_strdup(in->cid.cid_num);
01966 
01967                   if (o->chan->cid.cid_name)
01968                      free(o->chan->cid.cid_name);
01969                   o->chan->cid.cid_name = ast_strdup(in->cid.cid_name);
01970 
01971                   ast_string_field_set(o->chan, accountcode, in->accountcode);
01972                   o->chan->cdrflags = in->cdrflags;
01973 
01974                   if (in->cid.cid_ani) {
01975                      if (o->chan->cid.cid_ani)
01976                         free(o->chan->cid.cid_ani);
01977                      o->chan->cid.cid_ani = ast_strdup(in->cid.cid_ani);
01978                   }
01979                   if (o->chan->cid.cid_rdnis)
01980                      free(o->chan->cid.cid_rdnis);
01981                   o->chan->cid.cid_rdnis = ast_strdup(S_OR(in->macroexten, in->exten));
01982                   if (ast_call(o->chan, tmpchan, 0)) {
01983                      ast_log(LOG_NOTICE, "Failed to dial on local channel for call forward to '%s'\n", tmpchan);
01984                      do_hang(o);
01985                      numnochan++;
01986                   }
01987                }
01988                /* Hangup the original channel now, in case we needed it */
01989                ast_hangup(winner);
01990                continue;
01991             }
01992             f = ast_read(winner);
01993             if (f) {
01994                if (f->frametype == AST_FRAME_CONTROL) {
01995                   switch (f->subclass) {
01996                   case AST_CONTROL_ANSWER:
01997                      /* This is our guy if someone answered. */
01998                      if (!peer) {
01999                         if (option_verbose > 2)
02000                            ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
02001                         peer = o;
02002                      }
02003                      break;
02004                   case AST_CONTROL_BUSY:
02005                      if (option_verbose > 2)
02006                         ast_verbose( VERBOSE_PREFIX_3 "%s is busy\n", o->chan->name);
02007                      if (in->cdr)
02008                         ast_cdr_busy(in->cdr);
02009                      do_hang(o);
02010                      endtime = (long)time(NULL);
02011                      endtime -= starttime;
02012                      rna(endtime*1000, qe, on, membername);
02013                      if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
02014                         if (qe->parent->timeoutrestart)
02015                            *to = orig;
02016                         ring_one(qe, outgoing, &numbusies);
02017                      }
02018                      numbusies++;
02019                      break;
02020                   case AST_CONTROL_CONGESTION:
02021                      if (option_verbose > 2)
02022                         ast_verbose( VERBOSE_PREFIX_3 "%s is circuit-busy\n", o->chan->name);
02023                      if (in->cdr)
02024                         ast_cdr_busy(in->cdr);
02025                      endtime = (long)time(NULL);
02026                      endtime -= starttime;
02027                      rna(endtime*1000, qe, on, membername);
02028                      do_hang(o);
02029                      if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
02030                         if (qe->parent->timeoutrestart)
02031                            *to = orig;
02032                         ring_one(qe, outgoing, &numbusies);
02033                      }
02034                      numbusies++;
02035                      break;
02036                   case AST_CONTROL_RINGING:
02037                      if (option_verbose > 2)
02038                         ast_verbose( VERBOSE_PREFIX_3 "%s is ringing\n", o->chan->name);
02039                      if (!sentringing) {
02040 #if 0
02041                         ast_indicate(in, AST_CONTROL_RINGING);
02042 #endif                        
02043                         sentringing++;
02044                      }
02045                      break;
02046                   case AST_CONTROL_OFFHOOK:
02047                      /* Ignore going off hook */
02048                      break;
02049                   default:
02050                      ast_log(LOG_DEBUG, "Dunno what to do with control type %d\n", f->subclass);
02051                   }
02052                }
02053                ast_frfree(f);
02054             } else {
02055                endtime = (long) time(NULL) - starttime;
02056                rna(endtime * 1000, qe, on, membername);
02057                do_hang(o);
02058                if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
02059                   if (qe->parent->timeoutrestart)
02060                      *to = orig;
02061                   ring_one(qe, outgoing, &numbusies);
02062                }
02063             }
02064          }
02065       }
02066       if (winner == in) {
02067          f = ast_read(in);
02068          if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) {
02069             /* Got hung up */
02070             *to = -1;
02071             if (f)
02072                ast_frfree(f);
02073             return NULL;
02074          }
02075          if ((f->frametype == AST_FRAME_DTMF) && caller_disconnect && (f->subclass == '*')) {
02076             if (option_verbose > 3)
02077                ast_verbose(VERBOSE_PREFIX_3 "User hit %c to disconnect call.\n", f->subclass);
02078             *to = 0;
02079             ast_frfree(f);
02080             return NULL;
02081          }
02082          if ((f->frametype == AST_FRAME_DTMF) && valid_exit(qe, f->subclass)) {
02083             if (option_verbose > 3)
02084                ast_verbose(VERBOSE_PREFIX_3 "User pressed digit: %c\n", f->subclass);
02085             *to = 0;
02086             *digit = f->subclass;
02087             ast_frfree(f);
02088             return NULL;
02089          }
02090          ast_frfree(f);
02091       }
02092       if (!*to)
02093          rna(orig, qe, on, membername);
02094    }
02095 
02096    return peer;
02097 }

static int wait_our_turn ( struct queue_ent qe,
int  ringing,
enum queue_result reason 
) [static]

Definition at line 2167 of file app_queue.c.

References call_queue::announcefrequency, ast_queue_log(), ast_waitfordigit(), queue_ent::chan, get_member_status(), is_our_turn(), leave_queue(), call_queue::leavewhenempty, queue_ent::max_penalty, call_queue::name, queue_ent::opos, queue_ent::parent, call_queue::periodicannouncefrequency, queue_ent::pos, QUEUE_EMPTY_STRICT, QUEUE_LEAVEEMPTY, QUEUE_LEAVEUNAVAIL, QUEUE_NO_MEMBERS, QUEUE_NO_REACHABLE_MEMBERS, QUEUE_TIMEOUT, RECHECK, say_periodic_announcement(), say_position(), and queue_ent::start.

Referenced by queue_exec().

02168 {
02169    int res = 0;
02170 
02171    /* This is the holding pen for callers 2 through maxlen */
02172    for (;;) {
02173       enum queue_member_status stat;
02174 
02175       if (is_our_turn(qe))
02176          break;
02177 
02178       /* If we have timed out, break out */
02179       if (qe->expire && (time(NULL) > qe->expire)) {
02180          *reason = QUEUE_TIMEOUT;
02181          break;
02182       }
02183 
02184       stat = get_member_status(qe->parent, qe->max_penalty);
02185 
02186       /* leave the queue if no agents, if enabled */
02187       if (qe->parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) {
02188          *reason = QUEUE_LEAVEEMPTY;
02189          ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long)time(NULL) - qe->start);
02190          leave_queue(qe);
02191          break;
02192       }
02193 
02194       /* leave the queue if no reachable agents, if enabled */
02195       if ((qe->parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) {
02196          *reason = QUEUE_LEAVEUNAVAIL;
02197          ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long)time(NULL) - qe->start);
02198          leave_queue(qe);
02199          break;
02200       }
02201 
02202       /* Make a position announcement, if enabled */
02203       if (qe->parent->announcefrequency && !ringing &&
02204          (res = say_position(qe)))
02205          break;
02206 
02207       /* Make a periodic announcement, if enabled */
02208       if (qe->parent->periodicannouncefrequency && !ringing &&
02209          (res = say_periodic_announcement(qe)))
02210          break;
02211 
02212       /* Wait a second before checking again */
02213       if ((res = ast_waitfordigit(qe->chan, RECHECK * 1000)))
02214          break;
02215    }
02216 
02217    return res;
02218 }


Variable Documentation

char* app = "Queue" [static]

Definition at line 127 of file app_queue.c.

char* app_aqm = "AddQueueMember" [static]

Definition at line 161 of file app_queue.c.

char* app_aqm_descrip [static]

Definition at line 163 of file app_queue.c.

char* app_aqm_synopsis = "Dynamically adds queue members" [static]

Definition at line 162 of file app_queue.c.

char* app_pqm = "PauseQueueMember" [static]

Definition at line 193 of file app_queue.c.

char* app_pqm_descrip [static]

Definition at line 195 of file app_queue.c.

char* app_pqm_synopsis = "Pauses a queue member" [static]

Definition at line 194 of file app_queue.c.

char* app_ql = "QueueLog" [static]

Definition at line 230 of file app_queue.c.

char* app_ql_descrip [static]

Initial value:

"   QueueLog(queuename|uniqueid|agent|event[|additionalinfo]):\n"
"Allows you to write your own events into the queue log\n"
"Example: QueueLog(101|${UNIQUEID}|${AGENT}|WENTONBREAK|600)\n"

Definition at line 232 of file app_queue.c.

char* app_ql_synopsis = "Writes to the queue_log" [static]

Definition at line 231 of file app_queue.c.

char* app_rqm = "RemoveQueueMember" [static]

Definition at line 177 of file app_queue.c.

char* app_rqm_descrip [static]

Definition at line 179 of file app_queue.c.

char* app_rqm_synopsis = "Dynamically removes queue members" [static]

Definition at line 178 of file app_queue.c.

char* app_upqm = "UnpauseQueueMember" [static]

Definition at line 215 of file app_queue.c.

char* app_upqm_descrip [static]

Definition at line 217 of file app_queue.c.

char* app_upqm_synopsis = "Unpauses a queue member" [static]

Definition at line 216 of file app_queue.c.

int autofill_default = 0 [static]

queues.conf [general] option

Definition at line 249 of file app_queue.c.

struct ast_cli_entry cli_add_queue_member_deprecated [static]

Initial value:

 {
   { "add", "queue", "member", NULL },
   handle_queue_add_member, NULL,
   NULL, complete_queue_add_member }

Definition at line 4422 of file app_queue.c.

struct ast_cli_entry cli_queue[] [static]

Definition at line 4432 of file app_queue.c.

Referenced by load_module(), and unload_module().

struct ast_cli_entry cli_remove_queue_member_deprecated [static]

Initial value:

 {
   { "remove", "queue", "member", NULL },
   handle_queue_remove_member, NULL,
   NULL, complete_queue_remove_member }

Definition at line 4427 of file app_queue.c.

struct ast_cli_entry cli_show_queue_deprecated [static]

Initial value:

 {
   { "show", "queue", NULL },
   queue_show, NULL,
   NULL, complete_queue }

Definition at line 4417 of file app_queue.c.

char* descrip [static]

Definition at line 131 of file app_queue.c.

enum queue_result id

Definition at line 265 of file app_queue.c.

Referenced by _sip_show_peers(), aMYSQL_clear(), and aMYSQL_disconnect().

int montype_default = 0 [static]

queues.conf [general] option

Definition at line 252 of file app_queue.c.

const char* pm_family = "/Queue/PersistentMembers" [static]

Persistent Members astdb family.

Definition at line 238 of file app_queue.c.

char qam_cmd_usage[] [static]

Initial value:

"Usage: queue add member <channel> to <queue> [penalty <penalty>]\n"

Definition at line 4411 of file app_queue.c.

char qrm_cmd_usage[] [static]

Initial value:

"Usage: queue remove member <channel> from <queue>\n"

Definition at line 4414 of file app_queue.c.

int queue_persistent_members = 0 [static]

queues.conf [general] option

Definition at line 243 of file app_queue.c.

struct { ... } queue_results[]

Referenced by set_queue_result().

char queue_show_usage[] [static]

Initial value:

"Usage: queue show\n"
"       Provides summary information on a specified queue.\n"

Definition at line 4407 of file app_queue.c.

struct ast_custom_function queueagentcount_function [static]

Definition at line 3680 of file app_queue.c.

Referenced by load_module(), and unload_module().

struct ast_custom_function queuemembercount_function [static]

Definition at line 3690 of file app_queue.c.

Referenced by load_module(), and unload_module().

struct ast_custom_function queuememberlist_function [static]

Definition at line 3708 of file app_queue.c.

Referenced by load_module(), and unload_module().

struct ast_custom_function queuewaitingcount_function [static]

Definition at line 3699 of file app_queue.c.

Referenced by load_module(), and unload_module().

struct strategy strategies[] [static]

Referenced by int2strat(), and strat2int().

char* synopsis = "Queue a call for a call queue" [static]

Definition at line 129 of file app_queue.c.

char* text

Definition at line 266 of file app_queue.c.

Referenced by _sip_show_peer(), build_reply_digest(), check_auth(), handle_response(), method_match(), parse_sip_options(), referstatus2str(), reqprep(), sendtext_exec(), set_queue_result(), sip_alloc(), and sip_show_channel().

int use_weight = 0 [static]

queues.conf per-queue weight option

Definition at line 246 of file app_queue.c.


Generated on Mon May 14 04:44:15 2007 for Asterisk - the Open Source PBX by  doxygen 1.5.1