Tue Sep 30 01:19:42 2008

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 "asterisk/astobj2.h"
#include "asterisk/global_datastores.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_NOT_DYNAMIC   (-4)
#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 calc_metric (struct call_queue *q, struct member *mem, int pos, struct queue_ent *qe, struct callattempt *tmp)
 Calculate the metric of each member in the outgoing callattempts.
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 char * complete_queue_show (const char *line, const char *word, int pos, int state)
static int compress_char (const char c)
static struct membercreate_queue_member (const char *interface, const char *membername, int penalty, int paused)
 allocate space for new queue member and set fields based on parameters passed
static void destroy_queue (struct call_queue *q)
static void * device_state_thread (void *data)
 Consumer of the statechange queue.
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)
 Check if members are available.
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 * handle_statechange (struct statechange *sc)
 set a member's status based on device state of that member's interface
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)
 Check if we should start attempting to call queue members.
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 member_cmp_fn (void *obj1, void *obj2, int flags)
static int member_hash_fn (const void *obj, const int flags)
static void monjoin_dep_warning (void)
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)
 The starting point for all queue calls.
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, int newholdtime)
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)
 Part 2 of ring_one.
static int ring_one (struct queue_ent *qe, struct callattempt *outgoing, int *busies)
 Place a call to a queue member.
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)
 sets the QUEUESTATUS channel variable
static int statechange_queue (const char *dev, int state, void *ign, char *cid_num, char *cid_name)
 Producer of the statechange queue.
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 *tries, int *noption, const char *agi)
 A large function which calls members, updates statistics, and bridges the caller and a member.
static int unload_module (void)
static int update_queue (struct call_queue *q, struct member *member, int callcompletedinsl)
static int update_realtime_member_field (struct member *mem, const char *queue_name, const char *field, const char *value)
static void update_realtime_members (struct call_queue *q)
static int update_status (const char *interface, const 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)
 Wait for a member to answer the call.
static int wait_our_turn (struct queue_ent *qe, int ringing, enum queue_result *reason)
 The waiting areas for callers who are not actively calling members.

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
struct {
   ast_cond_t   cond
   ast_mutex_t   lock
   unsigned int   stop:1
   pthread_t   thread
device_state
 Data used by the device state thread.
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>:

  • Per-queue holdtime calculation
  • Estimated holdtime announcement
  • Position announcement
  • Abandoned/completed call counters
  • Failout timer passed as optional app parameter
  • Optional monitoring of calls, started when call is answered
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 369 of file app_queue.c.

Referenced by queue_set_param().

#define ANNOUNCEHOLDTIME_ONCE   2

Definition at line 370 of file app_queue.c.

Referenced by queue_set_param(), and say_position().

#define AST_MAX_WATCHERS   256

Definition at line 2072 of file app_queue.c.

#define DEFAULT_RETRY   5

Definition at line 137 of file app_queue.c.

Referenced by init_queue(), and queue_set_param().

#define DEFAULT_TIMEOUT   15

Definition at line 138 of file app_queue.c.

Referenced by queue_set_param().

#define MAX_PERIODIC_ANNOUNCEMENTS   10

Definition at line 140 of file app_queue.c.

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

#define PM_MAX_LEN   8192

Definition at line 261 of file app_queue.c.

Referenced by dump_queue_members(), and reload_queue_members().

#define QUEUE_EMPTY_NORMAL   1

Definition at line 367 of file app_queue.c.

Referenced by queue_set_param().

#define QUEUE_EMPTY_STRICT   2

Definition at line 368 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 371 of file app_queue.c.

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

#define RECHECK   1

Definition at line 139 of file app_queue.c.

Referenced by wait_our_turn().

#define RES_EXISTS   (-1)

#define RES_NOSUCHQUEUE   (-3)

#define RES_NOT_DYNAMIC   (-4)

#define RES_OKAY   0

#define RES_OUTOFMEMORY   (-2)


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 116 of file app_queue.c.

Enumerator:
QUEUE_NO_MEMBERS 
QUEUE_NO_REACHABLE_MEMBERS 
QUEUE_NORMAL 

Definition at line 519 of file app_queue.c.

00519                          {
00520    QUEUE_NO_MEMBERS,
00521    QUEUE_NO_REACHABLE_MEMBERS,
00522    QUEUE_NORMAL
00523 };

Enumerator:
QUEUE_UNKNOWN 
QUEUE_TIMEOUT 
QUEUE_JOINEMPTY 
QUEUE_LEAVEEMPTY 
QUEUE_JOINUNAVAIL 
QUEUE_LEAVEUNAVAIL 
QUEUE_FULL 

Definition at line 275 of file app_queue.c.

00275                   {
00276    QUEUE_UNKNOWN = 0,
00277    QUEUE_TIMEOUT = 1,
00278    QUEUE_JOINEMPTY = 2,
00279    QUEUE_LEAVEEMPTY = 3,
00280    QUEUE_JOINUNAVAIL = 4,
00281    QUEUE_LEAVEUNAVAIL = 5,
00282    QUEUE_FULL = 6,
00283 };


Function Documentation

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

Definition at line 4291 of file app_queue.c.

References ao2_container_count(), ao2_iterator_init(), ao2_iterator_next(), ao2_ref(), 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::lastcall, load_realtime_queue(), call_queue::lock, call_queue::maxlen, member::membername, call_queue::members, call_queue::name, queue_ent::next, member::paused, member::penalty, queue_ent::prio, queue_show(), member::realtime, RESULT_SHOWUSAGE, RESULT_SUCCESS, call_queue::servicelevel, queue_ent::start, member::status, call_queue::strategy, and call_queue::weight.

Referenced by manager_queues_show(), and queue_show().

04292 {
04293    struct call_queue *q;
04294    struct queue_ent *qe;
04295    struct member *mem;
04296    int pos, queue_show;
04297    time_t now;
04298    char max_buf[80];
04299    char *max;
04300    size_t max_left;
04301    float sl = 0;
04302    char *term = manager ? "\r\n" : "\n";
04303    struct ao2_iterator mem_iter;
04304 
04305    time(&now);
04306    if (argc == 2)
04307       queue_show = 0;
04308    else if (argc == 3)
04309       queue_show = 1;
04310    else
04311       return RESULT_SHOWUSAGE;
04312 
04313    /* We only want to load realtime queues when a specific queue is asked for. */
04314    if (queue_show)
04315       load_realtime_queue(argv[2]);
04316 
04317    AST_LIST_LOCK(&queues);
04318    if (AST_LIST_EMPTY(&queues)) {
04319       AST_LIST_UNLOCK(&queues);
04320       if (queue_show) {
04321          if (s)
04322             astman_append(s, "No such queue: %s.%s",argv[2], term);
04323          else
04324             ast_cli(fd, "No such queue: %s.%s",argv[2], term);
04325       } else {
04326          if (s)
04327             astman_append(s, "No queues.%s", term);
04328          else
04329             ast_cli(fd, "No queues.%s", term);
04330       }
04331       return RESULT_SUCCESS;
04332    }
04333    AST_LIST_TRAVERSE(&queues, q, list) {
04334       ast_mutex_lock(&q->lock);
04335       if (queue_show) {
04336          if (strcasecmp(q->name, argv[2]) != 0) {
04337             ast_mutex_unlock(&q->lock);
04338             if (!AST_LIST_NEXT(q, list)) {
04339                ast_cli(fd, "No such queue: %s.%s",argv[2], term);
04340                break;
04341             }
04342             continue;
04343          }
04344       }
04345       max_buf[0] = '\0';
04346       max = max_buf;
04347       max_left = sizeof(max_buf);
04348       if (q->maxlen)
04349          ast_build_string(&max, &max_left, "%d", q->maxlen);
04350       else
04351          ast_build_string(&max, &max_left, "unlimited");
04352       sl = 0;
04353       if (q->callscompleted > 0)
04354          sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
04355       if (s)
04356          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",
04357             q->name, q->count, max_buf, int2strat(q->strategy), q->holdtime, q->weight,
04358             q->callscompleted, q->callsabandoned,sl,q->servicelevel, term);
04359       else
04360          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",
04361             q->name, q->count, max_buf, int2strat(q->strategy), q->holdtime, q->weight, q->callscompleted, q->callsabandoned,sl,q->servicelevel, term);
04362       if (ao2_container_count(q->members)) {
04363          if (s)
04364             astman_append(s, "   Members: %s", term);
04365          else
04366             ast_cli(fd, "   Members: %s", term);
04367          mem_iter = ao2_iterator_init(q->members, 0);
04368          while ((mem = ao2_iterator_next(&mem_iter))) {
04369             max_buf[0] = '\0';
04370             max = max_buf;
04371             max_left = sizeof(max_buf);
04372             if (mem->penalty)
04373                ast_build_string(&max, &max_left, " with penalty %d", mem->penalty);
04374             if (mem->dynamic)
04375                ast_build_string(&max, &max_left, " (dynamic)");
04376             if (mem->realtime)
04377                ast_build_string(&max, &max_left, " (realtime)");
04378             if (mem->paused)
04379                ast_build_string(&max, &max_left, " (paused)");
04380             ast_build_string(&max, &max_left, " (%s)", devstate2str(mem->status));
04381             if (mem->calls) {
04382                ast_build_string(&max, &max_left, " has taken %d calls (last was %ld secs ago)",
04383                   mem->calls, (long) (time(NULL) - mem->lastcall));
04384             } else
04385                ast_build_string(&max, &max_left, " has taken no calls yet");
04386             if (s)
04387                astman_append(s, "      %s%s%s", mem->membername, max_buf, term);
04388             else
04389                ast_cli(fd, "      %s%s%s", mem->membername, max_buf, term);
04390             ao2_ref(mem, -1);
04391          }
04392       } else if (s)
04393          astman_append(s, "   No Members%s", term);
04394       else  
04395          ast_cli(fd, "   No Members%s", term);
04396       if (q->head) {
04397          pos = 1;
04398          if (s)
04399             astman_append(s, "   Callers: %s", term);
04400          else
04401             ast_cli(fd, "   Callers: %s", term);
04402          for (qe = q->head; qe; qe = qe->next) {
04403             if (s)
04404                astman_append(s, "      %d. %s (wait: %ld:%2.2ld, prio: %d)%s",
04405                   pos++, qe->chan->name, (long) (now - qe->start) / 60,
04406                   (long) (now - qe->start) % 60, qe->prio, term);
04407             else
04408                ast_cli(fd, "      %d. %s (wait: %ld:%2.2ld, prio: %d)%s", pos++,
04409                   qe->chan->name, (long) (now - qe->start) / 60,
04410                   (long) (now - qe->start) % 60, qe->prio, term);
04411          }
04412       } else if (s)
04413          astman_append(s, "   No Callers%s", term);
04414       else
04415          ast_cli(fd, "   No Callers%s", term);
04416       if (s)
04417          astman_append(s, "%s", term);
04418       else
04419          ast_cli(fd, "%s", term);
04420       ast_mutex_unlock(&q->lock);
04421       if (queue_show)
04422          break;
04423    }
04424    AST_LIST_UNLOCK(&queues);
04425    return RESULT_SUCCESS;
04426 }

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

Definition at line 860 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().

00861 {
00862    struct member_interface *curint;
00863 
00864    AST_LIST_LOCK(&interfaces);
00865    AST_LIST_TRAVERSE(&interfaces, curint, list) {
00866       if (!strcasecmp(curint->interface, interface))
00867          break;
00868    }
00869 
00870    if (curint) {
00871       AST_LIST_UNLOCK(&interfaces);
00872       return 0;
00873    }
00874 
00875    if (option_debug)
00876       ast_log(LOG_DEBUG, "Adding %s to the list of interfaces that make up all of our queue members.\n", interface);
00877    
00878    if ((curint = ast_calloc(1, sizeof(*curint)))) {
00879       ast_copy_string(curint->interface, interface, sizeof(curint->interface));
00880       AST_LIST_INSERT_HEAD(&interfaces, curint, list);
00881    }
00882    AST_LIST_UNLOCK(&interfaces);
00883 
00884    return 0;
00885 }

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

Definition at line 3199 of file app_queue.c.

References add_to_interfaces(), ao2_ref(), 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(), call_queue::membercount, member::membername, call_queue::members, call_queue::name, 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().

03200 {
03201    struct call_queue *q;
03202    struct member *new_member, *old_member;
03203    int res = RES_NOSUCHQUEUE;
03204 
03205    /* \note Ensure the appropriate realtime queue is loaded.  Note that this
03206     * short-circuits if the queue is already in memory. */
03207    if (!(q = load_realtime_queue(queuename)))
03208       return res;
03209 
03210    AST_LIST_LOCK(&queues);
03211 
03212    ast_mutex_lock(&q->lock);
03213    if ((old_member = interface_exists(q, interface)) == NULL) {
03214       add_to_interfaces(interface);
03215       if ((new_member = create_queue_member(interface, membername, penalty, paused))) {
03216          new_member->dynamic = 1;
03217          ao2_link(q->members, new_member);
03218          q->membercount++;
03219          manager_event(EVENT_FLAG_AGENT, "QueueMemberAdded",
03220             "Queue: %s\r\n"
03221             "Location: %s\r\n"
03222             "MemberName: %s\r\n"
03223             "Membership: %s\r\n"
03224             "Penalty: %d\r\n"
03225             "CallsTaken: %d\r\n"
03226             "LastCall: %d\r\n"
03227             "Status: %d\r\n"
03228             "Paused: %d\r\n",
03229             q->name, new_member->interface, new_member->membername,
03230             "dynamic",
03231             new_member->penalty, new_member->calls, (int) new_member->lastcall,
03232             new_member->status, new_member->paused);
03233          
03234          ao2_ref(new_member, -1);
03235          new_member = NULL;
03236 
03237          if (dump)
03238             dump_queue_members(q);
03239          
03240          res = RES_OKAY;
03241       } else {
03242          res = RES_OUTOFMEMORY;
03243       }
03244    } else {
03245       ao2_ref(old_member, -1);
03246       res = RES_EXISTS;
03247    }
03248    ast_mutex_unlock(&q->lock);
03249    AST_LIST_UNLOCK(&queues);
03250 
03251    return res;
03252 }

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

Definition at line 762 of file app_queue.c.

References ast_calloc, ast_mutex_init(), call_queue::lock, and call_queue::name.

Referenced by find_queue_by_name_rt(), and reload_queues().

00763 {
00764    struct call_queue *q;
00765 
00766    if ((q = ast_calloc(1, sizeof(*q)))) {
00767       ast_mutex_init(&q->lock);
00768       ast_copy_string(q->name, queuename, sizeof(q->name));
00769    }
00770    return q;
00771 }

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

Definition at line 3568 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_channel::context, ast_channel::exten, member_interface::interface, LOG_ERROR, LOG_NOTICE, LOG_WARNING, parse(), pbx_builtin_setvar_helper(), ast_channel::priority, RES_EXISTS, RES_NOSUCHQUEUE, RES_OKAY, and RES_OUTOFMEMORY.

Referenced by load_module().

03569 {
03570    int res=-1;
03571    struct ast_module_user *lu;
03572    char *parse, *temppos = NULL;
03573    int priority_jump = 0;
03574    AST_DECLARE_APP_ARGS(args,
03575       AST_APP_ARG(queuename);
03576       AST_APP_ARG(interface);
03577       AST_APP_ARG(penalty);
03578       AST_APP_ARG(options);
03579       AST_APP_ARG(membername);
03580    );
03581    int penalty = 0;
03582 
03583    if (ast_strlen_zero(data)) {
03584       ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename[|[interface]|[penalty][|options][|membername]])\n");
03585       return -1;
03586    }
03587 
03588    parse = ast_strdupa(data);
03589 
03590    AST_STANDARD_APP_ARGS(args, parse);
03591 
03592    lu = ast_module_user_add(chan);
03593 
03594    if (ast_strlen_zero(args.interface)) {
03595       args.interface = ast_strdupa(chan->name);
03596       temppos = strrchr(args.interface, '-');
03597       if (temppos)
03598          *temppos = '\0';
03599    }
03600 
03601    if (!ast_strlen_zero(args.penalty)) {
03602       if ((sscanf(args.penalty, "%d", &penalty) != 1) || penalty < 0) {
03603          ast_log(LOG_WARNING, "Penalty '%s' is invalid, must be an integer >= 0\n", args.penalty);
03604          penalty = 0;
03605       }
03606    }
03607    
03608    if (args.options) {
03609       if (strchr(args.options, 'j'))
03610          priority_jump = 1;
03611    }
03612 
03613    switch (add_to_queue(args.queuename, args.interface, args.membername, penalty, 0, queue_persistent_members)) {
03614    case RES_OKAY:
03615       ast_queue_log(args.queuename, chan->uniqueid, args.interface, "ADDMEMBER", "%s", "");
03616       ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", args.interface, args.queuename);
03617       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "ADDED");
03618       res = 0;
03619       break;
03620    case RES_EXISTS:
03621       ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': Already there\n", args.interface, args.queuename);
03622       if (priority_jump || ast_opt_priority_jumping)
03623          ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
03624       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "MEMBERALREADY");
03625       res = 0;
03626       break;
03627    case RES_NOSUCHQUEUE:
03628       ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", args.queuename);
03629       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "NOSUCHQUEUE");
03630       res = 0;
03631       break;
03632    case RES_OUTOFMEMORY:
03633       ast_log(LOG_ERROR, "Out of memory adding member %s to queue %s\n", args.interface, args.queuename);
03634       break;
03635    }
03636 
03637    ast_module_user_remove(lu);
03638 
03639    return res;
03640 }

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 calc_metric ( struct call_queue q,
struct member mem,
int  pos,
struct queue_ent qe,
struct callattempt tmp 
) [static]

Calculate the metric of each member in the outgoing callattempts.

A numeric metric is given to each member depending on the ring strategy used by the queue. Members with lower metrics will be called before members with higher metrics

Definition at line 2488 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, QUEUE_STRATEGY_RRMEMORY, call_queue::rrpos, call_queue::strategy, and call_queue::wrapped.

Referenced by try_calling().

02489 {
02490    if (qe->max_penalty && (mem->penalty > qe->max_penalty))
02491       return -1;
02492 
02493    switch (q->strategy) {
02494    case QUEUE_STRATEGY_RINGALL:
02495       /* Everyone equal, except for penalty */
02496       tmp->metric = mem->penalty * 1000000;
02497       break;
02498    case QUEUE_STRATEGY_ROUNDROBIN:
02499       if (!pos) {
02500          if (!q->wrapped) {
02501             /* No more channels, start over */
02502             q->rrpos = 0;
02503          } else {
02504             /* Prioritize next entry */
02505             q->rrpos++;
02506          }
02507          q->wrapped = 0;
02508       }
02509       /* Fall through */
02510    case QUEUE_STRATEGY_RRMEMORY:
02511       if (pos < q->rrpos) {
02512          tmp->metric = 1000 + pos;
02513       } else {
02514          if (pos > q->rrpos)
02515             /* Indicate there is another priority */
02516             q->wrapped = 1;
02517          tmp->metric = pos;
02518       }
02519       tmp->metric += mem->penalty * 1000000;
02520       break;
02521    case QUEUE_STRATEGY_RANDOM:
02522       tmp->metric = ast_random() % 1000;
02523       tmp->metric += mem->penalty * 1000000;
02524       break;
02525    case QUEUE_STRATEGY_FEWESTCALLS:
02526       tmp->metric = mem->calls;
02527       tmp->metric += mem->penalty * 1000000;
02528       break;
02529    case QUEUE_STRATEGY_LEASTRECENT:
02530       if (!mem->lastcall)
02531          tmp->metric = 0;
02532       else
02533          tmp->metric = 1000000 - (time(NULL) - mem->lastcall);
02534       tmp->metric += mem->penalty * 1000000;
02535       break;
02536    default:
02537       ast_log(LOG_WARNING, "Can't calculate metric for unknown strategy %d\n", q->strategy);
02538       break;
02539    }
02540    return 0;
02541 }

static void clear_and_free_interfaces ( void   )  [static]

Definition at line 934 of file app_queue.c.

References AST_LIST_LOCK, AST_LIST_REMOVE_HEAD, AST_LIST_UNLOCK, and free.

Referenced by unload_module().

00935 {
00936    struct member_interface *curint;
00937 
00938    AST_LIST_LOCK(&interfaces);
00939    while ((curint = AST_LIST_REMOVE_HEAD(&interfaces, list)))
00940       free(curint);
00941    AST_LIST_UNLOCK(&interfaces);
00942 }

static void clear_queue ( struct call_queue q  )  [static]

Definition at line 851 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().

00852 {
00853    q->holdtime = 0;
00854    q->callscompleted = 0;
00855    q->callsabandoned = 0;
00856    q->callscompletedinsl = 0;
00857    q->wrapuptime = 0;
00858 }

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

Definition at line 1704 of file app_queue.c.

References ao2_find(), ao2_ref(), 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, and call_queue::weight.

Referenced by ring_entry().

01705 {
01706    struct call_queue *q;
01707    struct member *mem;
01708    int found = 0;
01709    
01710    /* &qlock and &rq->lock already set by try_calling()
01711     * to solve deadlock */
01712    AST_LIST_TRAVERSE(&queues, q, list) {
01713       if (q == rq) /* don't check myself, could deadlock */
01714          continue;
01715       ast_mutex_lock(&q->lock);
01716       if (q->count && q->members) {
01717          if ((mem = ao2_find(q->members, member, OBJ_POINTER))) {
01718             ast_log(LOG_DEBUG, "Found matching member %s in queue '%s'\n", mem->interface, q->name);
01719             if (q->weight > rq->weight) {
01720                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);
01721                found = 1;
01722             }
01723             ao2_ref(mem, -1);
01724          }
01725       }
01726       ast_mutex_unlock(&q->lock);
01727       if (found)
01728          break;
01729    }
01730    return found;
01731 }

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

Definition at line 4433 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(), complete_queue_remove_member(), and complete_queue_show().

04434 {
04435    struct call_queue *q;
04436    char *ret = NULL;
04437    int which = 0;
04438    int wordlen = strlen(word);
04439    
04440    AST_LIST_LOCK(&queues);
04441    AST_LIST_TRAVERSE(&queues, q, list) {
04442       if (!strncasecmp(word, q->name, wordlen) && ++which > state) {
04443          ret = ast_strdup(q->name); 
04444          break;
04445       }
04446    }
04447    AST_LIST_UNLOCK(&queues);
04448 
04449    return ret;
04450 }

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

Definition at line 4727 of file app_queue.c.

References ast_malloc, ast_strdup, and complete_queue().

04728 {
04729    /* 0 - queue; 1 - add; 2 - member; 3 - <interface>; 4 - to; 5 - <queue>; 6 - penalty; 7 - <penalty>; 8 - as; 9 - <membername> */
04730    switch (pos) {
04731    case 3:  /* Don't attempt to complete name of interface (infinite possibilities) */
04732       return NULL;
04733    case 4:  /* only one possible match, "to" */
04734       return state == 0 ? ast_strdup("to") : NULL;
04735    case 5:  /* <queue> */
04736       return complete_queue(line, word, pos, state);
04737    case 6: /* only one possible match, "penalty" */
04738       return state == 0 ? ast_strdup("penalty") : NULL;
04739    case 7:
04740       if (state < 100) {   /* 0-99 */
04741          char *num;
04742          if ((num = ast_malloc(3))) {
04743             sprintf(num, "%d", state);
04744          }
04745          return num;
04746       } else {
04747          return NULL;
04748       }
04749    case 8: /* only one possible match, "as" */
04750       return state == 0 ? ast_strdup("as") : NULL;
04751    case 9:  /* Don't attempt to complete name of member (infinite possibilities) */
04752       return NULL;
04753    default:
04754       return NULL;
04755    }
04756 }

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

Definition at line 4793 of file app_queue.c.

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

04794 {
04795    int which = 0;
04796    struct call_queue *q;
04797    struct member *m;
04798    struct ao2_iterator mem_iter;
04799 
04800    /* 0 - queue; 1 - remove; 2 - member; 3 - <member>; 4 - from; 5 - <queue> */
04801    if (pos > 5 || pos < 3)
04802       return NULL;
04803    if (pos == 4)  /* only one possible match, 'from' */
04804       return state == 0 ? ast_strdup("from") : NULL;
04805 
04806    if (pos == 5)  /* No need to duplicate code */
04807       return complete_queue(line, word, pos, state);
04808 
04809    /* here is the case for 3, <member> */
04810    if (!AST_LIST_EMPTY(&queues)) { /* XXX unnecessary ? the traverse does that for us */
04811       AST_LIST_TRAVERSE(&queues, q, list) {
04812          ast_mutex_lock(&q->lock);
04813          mem_iter = ao2_iterator_init(q->members, 0);
04814          while ((m = ao2_iterator_next(&mem_iter))) {
04815             if (++which > state) {
04816                char *tmp;
04817                ast_mutex_unlock(&q->lock);
04818                tmp = ast_strdup(m->interface);
04819                ao2_ref(m, -1);
04820                return tmp;
04821             }
04822             ao2_ref(m, -1);
04823          }
04824          ast_mutex_unlock(&q->lock);
04825       }
04826    }
04827 
04828    return NULL;
04829 }

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

Definition at line 4452 of file app_queue.c.

References complete_queue().

04453 {
04454    if (pos == 2)
04455       return complete_queue(line, word, pos, state);
04456    return NULL;
04457 }

static int compress_char ( const char  c  )  [static]

Definition at line 773 of file app_queue.c.

00774 {
00775    if (c < 32)
00776       return 0;
00777    else if (c > 96)
00778       return c - 64;
00779    else
00780       return c - 32;
00781 }

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

allocate space for new queue member and set fields based on parameters passed

Definition at line 742 of file app_queue.c.

References ao2_alloc(), ast_device_state(), ast_log(), ast_strlen_zero(), member::interface, LOG_WARNING, member::membername, member::paused, member::penalty, and member::status.

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

00743 {
00744    struct member *cur;
00745    
00746    if ((cur = ao2_alloc(sizeof(*cur), NULL))) {
00747       cur->penalty = penalty;
00748       cur->paused = paused;
00749       ast_copy_string(cur->interface, interface, sizeof(cur->interface));
00750       if (!ast_strlen_zero(membername))
00751          ast_copy_string(cur->membername, membername, sizeof(cur->membername));
00752       else
00753          ast_copy_string(cur->membername, interface, sizeof(cur->membername));
00754       if (!strchr(cur->interface, '/'))
00755          ast_log(LOG_WARNING, "No location at interface '%s'\n", interface);
00756       cur->status = ast_device_state(interface);
00757    }
00758 
00759    return cur;
00760 }

static void destroy_queue ( struct call_queue q  )  [static]

Definition at line 1157 of file app_queue.c.

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

Referenced by find_queue_by_name_rt(), leave_queue(), and reload_queues().

01158 {
01159    free_members(q, 1);
01160    ast_mutex_destroy(&q->lock);
01161    ao2_ref(q->members, -1);
01162    free(q);
01163 }

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

Consumer of the statechange queue.

Definition at line 690 of file app_queue.c.

References ast_cond_wait(), AST_LIST_REMOVE_HEAD, ast_mutex_lock(), ast_mutex_unlock(), device_state, free, and handle_statechange().

Referenced by load_module().

00691 {
00692    struct statechange *sc = NULL;
00693 
00694    while (!device_state.stop) {
00695       ast_mutex_lock(&device_state.lock);
00696       if (!(sc = AST_LIST_REMOVE_HEAD(&device_state.state_change_q, entry))) {
00697          ast_cond_wait(&device_state.cond, &device_state.lock);
00698          sc = AST_LIST_REMOVE_HEAD(&device_state.state_change_q, entry);
00699       }
00700       ast_mutex_unlock(&device_state.lock);
00701 
00702       /* Check to see if we were woken up to see the request to stop */
00703       if (device_state.stop)
00704          break;
00705 
00706       if (!sc)
00707          continue;
00708 
00709       handle_statechange(sc);
00710 
00711       free(sc);
00712       sc = NULL;
00713    }
00714 
00715    if (sc)
00716       free(sc);
00717 
00718    while ((sc = AST_LIST_REMOVE_HEAD(&device_state.state_change_q, entry)))
00719       free(sc);
00720 
00721    return NULL;
00722 }

static void do_hang ( struct callattempt o  )  [static]

common hangup actions

Definition at line 1734 of file app_queue.c.

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

Referenced by ring_entry(), and wait_for_answer().

01735 {
01736    o->stillgoing = 0;
01737    ast_hangup(o->chan);
01738    o->chan = NULL;
01739 }

static void dump_queue_members ( struct call_queue pm_queue  )  [static]

Definition at line 3106 of file app_queue.c.

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

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

03107 {
03108    struct member *cur_member;
03109    char value[PM_MAX_LEN];
03110    int value_len = 0;
03111    int res;
03112    struct ao2_iterator mem_iter;
03113 
03114    memset(value, 0, sizeof(value));
03115 
03116    if (!pm_queue)
03117       return;
03118 
03119    mem_iter = ao2_iterator_init(pm_queue->members, 0);
03120    while ((cur_member = ao2_iterator_next(&mem_iter))) {
03121       if (!cur_member->dynamic) {
03122          ao2_ref(cur_member, -1);
03123          continue;
03124       }
03125 
03126       res = snprintf(value + value_len, sizeof(value) - value_len, "%s%s;%d;%d;%s",
03127          value_len ? "|" : "", cur_member->interface, cur_member->penalty, cur_member->paused, cur_member->membername);
03128 
03129       ao2_ref(cur_member, -1);
03130 
03131       if (res != strlen(value + value_len)) {
03132          ast_log(LOG_WARNING, "Could not create persistent member string, out of space\n");
03133          break;
03134       }
03135       value_len += res;
03136    }
03137    
03138    if (value_len && !cur_member) {
03139       if (ast_db_put(pm_family, pm_queue->name, value))
03140          ast_log(LOG_WARNING, "failed to create persistent dynamic entry!\n");
03141    } else
03142       /* Delete the entry if the queue is empty or there is an error */
03143       ast_db_del(pm_family, pm_queue->name);
03144 }

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

find the entry with the best metric, or NULL

Definition at line 1918 of file app_queue.c.

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

01919 {
01920    struct callattempt *best = NULL, *cur;
01921 
01922    for (cur = outgoing; cur; cur = cur->q_next) {
01923       if (cur->stillgoing &&              /* Not already done */
01924          !cur->chan &&              /* Isn't already going */
01925          (!best || cur->metric < best->metric)) {     /* We haven't found one yet, or it's better */
01926          best = cur;
01927       }
01928    }
01929 
01930    return best;
01931 }

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

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 1168 of file app_queue.c.

References alloc_queue(), ao2_iterator_init(), ao2_iterator_next(), ao2_ref(), ao2_unlink(), ast_category_browse(), AST_LIST_INSERT_HEAD, AST_LIST_REMOVE, AST_LIST_TRAVERSE, ast_log(), ast_mutex_lock(), ast_mutex_unlock(), ast_strlen_zero(), ast_variable_retrieve(), clear_queue(), call_queue::count, member::dead, call_queue::dead, destroy_queue(), init_queue(), member::interface, member_interface::interface, call_queue::lock, LOG_DEBUG, LOG_WARNING, call_queue::membercount, call_queue::members, ast_variable::name, call_queue::name, ast_variable::next, queue_set_param(), QUEUE_STRATEGY_ROUNDROBIN, member::realtime, call_queue::realtime, remove_from_interfaces(), rr_dep_warning(), rt_handle_member_record(), call_queue::strategy, and ast_variable::value.

Referenced by load_realtime_queue().

01169 {
01170    struct ast_variable *v;
01171    struct call_queue *q;
01172    struct member *m;
01173    struct ao2_iterator mem_iter;
01174    char *interface = NULL;
01175    char *tmp, *tmp_name;
01176    char tmpbuf[64];  /* Must be longer than the longest queue param name. */
01177 
01178    /* Find the queue in the in-core list (we will create a new one if not found). */
01179    AST_LIST_TRAVERSE(&queues, q, list) {
01180       if (!strcasecmp(q->name, queuename))
01181          break;
01182    }
01183 
01184    /* Static queues override realtime. */
01185    if (q) {
01186       ast_mutex_lock(&q->lock);
01187       if (!q->realtime) {
01188          if (q->dead) {
01189             ast_mutex_unlock(&q->lock);
01190             return NULL;
01191          } else {
01192             ast_log(LOG_WARNING, "Static queue '%s' already exists. Not loading from realtime\n", q->name);
01193             ast_mutex_unlock(&q->lock);
01194             return q;
01195          }
01196       }
01197    } else if (!member_config)
01198       /* Not found in the list, and it's not realtime ... */
01199       return NULL;
01200 
01201    /* Check if queue is defined in realtime. */
01202    if (!queue_vars) {
01203       /* Delete queue from in-core list if it has been deleted in realtime. */
01204       if (q) {
01205          /*! \note Hmm, can't seem to distinguish a DB failure from a not
01206             found condition... So we might delete an in-core queue
01207             in case of DB failure. */
01208          ast_log(LOG_DEBUG, "Queue %s not found in realtime.\n", queuename);
01209 
01210          q->dead = 1;
01211          /* Delete if unused (else will be deleted when last caller leaves). */
01212          if (!q->count) {
01213             /* Delete. */
01214             AST_LIST_REMOVE(&queues, q, list);
01215             ast_mutex_unlock(&q->lock);
01216             destroy_queue(q);
01217          } else
01218             ast_mutex_unlock(&q->lock);
01219       }
01220       return NULL;
01221    }
01222 
01223    /* Create a new queue if an in-core entry does not exist yet. */
01224    if (!q) {
01225       if (!(q = alloc_queue(queuename)))
01226          return NULL;
01227       ast_mutex_lock(&q->lock);
01228       clear_queue(q);
01229       q->realtime = 1;
01230       init_queue(q);    /* Ensure defaults for all parameters not set explicitly. */
01231       AST_LIST_INSERT_HEAD(&queues, q, list);
01232    }
01233 
01234    memset(tmpbuf, 0, sizeof(tmpbuf));
01235    for (v = queue_vars; v; v = v->next) {
01236       /* Convert to dashes `-' from underscores `_' as the latter are more SQL friendly. */
01237       if ((tmp = strchr(v->name, '_'))) {
01238          ast_copy_string(tmpbuf, v->name, sizeof(tmpbuf));
01239          tmp_name = tmpbuf;
01240          tmp = tmp_name;
01241          while ((tmp = strchr(tmp, '_')))
01242             *tmp++ = '-';
01243       } else
01244          tmp_name = v->name;
01245 
01246       if (!ast_strlen_zero(v->value)) {
01247          /* Don't want to try to set the option if the value is empty */
01248          queue_set_param(q, tmp_name, v->value, -1, 0);
01249       }
01250    }
01251 
01252    if (q->strategy == QUEUE_STRATEGY_ROUNDROBIN)
01253       rr_dep_warning();
01254 
01255    /* Temporarily set realtime members dead so we can detect deleted ones. 
01256     * Also set the membercount correctly for realtime*/
01257    mem_iter = ao2_iterator_init(q->members, 0);
01258    while ((m = ao2_iterator_next(&mem_iter))) {
01259       q->membercount++;
01260       if (m->realtime)
01261          m->dead = 1;
01262       ao2_ref(m, -1);
01263    }
01264 
01265    while ((interface = ast_category_browse(member_config, interface))) {
01266       rt_handle_member_record(q, interface,
01267          ast_variable_retrieve(member_config, interface, "membername"),
01268          ast_variable_retrieve(member_config, interface, "penalty"),
01269          ast_variable_retrieve(member_config, interface, "paused"));
01270    }
01271 
01272    /* Delete all realtime members that have been deleted in DB. */
01273    mem_iter = ao2_iterator_init(q->members, 0);
01274    while ((m = ao2_iterator_next(&mem_iter))) {
01275       if (m->dead) {
01276          ao2_unlink(q->members, m);
01277          ast_mutex_unlock(&q->lock);
01278          remove_from_interfaces(m->interface);
01279          ast_mutex_lock(&q->lock);
01280          q->membercount--;
01281       }
01282       ao2_ref(m, -1);
01283    }
01284 
01285    ast_mutex_unlock(&q->lock);
01286 
01287    return q;
01288 }

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

Definition at line 1141 of file app_queue.c.

References ao2_iterator_init(), ao2_iterator_next(), ao2_ref(), ao2_unlink(), member::dynamic, member::interface, call_queue::membercount, call_queue::members, and remove_from_interfaces().

Referenced by destroy_queue().

01142 {
01143    /* Free non-dynamic members */
01144    struct member *cur;
01145    struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
01146 
01147    while ((cur = ao2_iterator_next(&mem_iter))) {
01148       if (all || !cur->dynamic) {
01149          ao2_unlink(q->members, cur);
01150          remove_from_interfaces(cur->interface);
01151          q->membercount--;
01152       }
01153       ao2_ref(cur, -1);
01154    }
01155 }

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

Check if members are available.

This function checks to see if members are available to be called. If any member is available, the function immediately returns QUEUE_NORMAL. If no members are available, the appropriate reason why is returned

Definition at line 531 of file app_queue.c.

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

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

00532 {
00533    struct member *member;
00534    struct ao2_iterator mem_iter;
00535    enum queue_member_status result = QUEUE_NO_MEMBERS;
00536 
00537    ast_mutex_lock(&q->lock);
00538    mem_iter = ao2_iterator_init(q->members, 0);
00539    while ((member = ao2_iterator_next(&mem_iter))) {
00540       if (max_penalty && (member->penalty > max_penalty)) {
00541          ao2_ref(member, -1);
00542          continue;
00543       }
00544 
00545       if (member->paused) {
00546          ao2_ref(member, -1);
00547          continue;
00548       }
00549 
00550       switch (member->status) {
00551       case AST_DEVICE_INVALID:
00552          /* nothing to do */
00553          ao2_ref(member, -1);
00554          break;
00555       case AST_DEVICE_UNAVAILABLE:
00556          result = QUEUE_NO_REACHABLE_MEMBERS;
00557          ao2_ref(member, -1);
00558          break;
00559       default:
00560          ast_mutex_unlock(&q->lock);
00561          ao2_ref(member, -1);
00562          return QUEUE_NORMAL;
00563       }
00564    }
00565 
00566    ast_mutex_unlock(&q->lock);
00567    return result;
00568 }

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

Definition at line 4673 of file app_queue.c.

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

04674 {
04675    char *queuename, *interface, *membername = NULL;
04676    int penalty;
04677 
04678    if ((argc != 6) && (argc != 8) && (argc != 10)) {
04679       return RESULT_SHOWUSAGE;
04680    } else if (strcmp(argv[4], "to")) {
04681       return RESULT_SHOWUSAGE;
04682    } else if ((argc == 8) && strcmp(argv[6], "penalty")) {
04683       return RESULT_SHOWUSAGE;
04684    } else if ((argc == 10) && strcmp(argv[8], "as")) {
04685       return RESULT_SHOWUSAGE;
04686    }
04687 
04688    queuename = argv[5];
04689    interface = argv[3];
04690    if (argc >= 8) {
04691       if (sscanf(argv[7], "%d", &penalty) == 1) {
04692          if (penalty < 0) {
04693             ast_cli(fd, "Penalty must be >= 0\n");
04694             penalty = 0;
04695          }
04696       } else {
04697          ast_cli(fd, "Penalty must be an integer >= 0\n");
04698          penalty = 0;
04699       }
04700    } else {
04701       penalty = 0;
04702    }
04703 
04704    if (argc >= 10) {
04705       membername = argv[9];
04706    }
04707 
04708    switch (add_to_queue(queuename, interface, membername, penalty, 0, queue_persistent_members)) {
04709    case RES_OKAY:
04710       ast_queue_log(queuename, "CLI", interface, "ADDMEMBER", "%s", "");
04711       ast_cli(fd, "Added interface '%s' to queue '%s'\n", interface, queuename);
04712       return RESULT_SUCCESS;
04713    case RES_EXISTS:
04714       ast_cli(fd, "Unable to add interface '%s' to queue '%s': Already there\n", interface, queuename);
04715       return RESULT_FAILURE;
04716    case RES_NOSUCHQUEUE:
04717       ast_cli(fd, "Unable to add interface to queue '%s': No such queue\n", queuename);
04718       return RESULT_FAILURE;
04719    case RES_OUTOFMEMORY:
04720       ast_cli(fd, "Out of memory\n");
04721       return RESULT_FAILURE;
04722    default:
04723       return RESULT_FAILURE;
04724    }
04725 }

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

Definition at line 4758 of file app_queue.c.

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

04759 {
04760    char *queuename, *interface;
04761 
04762    if (argc != 6) {
04763       return RESULT_SHOWUSAGE;
04764    } else if (strcmp(argv[4], "from")) {
04765       return RESULT_SHOWUSAGE;
04766    }
04767 
04768    queuename = argv[5];
04769    interface = argv[3];
04770 
04771    switch (remove_from_queue(queuename, interface)) {
04772    case RES_OKAY:
04773       ast_queue_log(queuename, "CLI", interface, "REMOVEMEMBER", "%s", "");
04774       ast_cli(fd, "Removed interface '%s' from queue '%s'\n", interface, queuename);
04775       return RESULT_SUCCESS;
04776    case RES_EXISTS:
04777       ast_cli(fd, "Unable to remove interface '%s' from queue '%s': Not there\n", interface, queuename);
04778       return RESULT_FAILURE;
04779    case RES_NOSUCHQUEUE:
04780       ast_cli(fd, "Unable to remove interface from queue '%s': No such queue\n", queuename);
04781       return RESULT_FAILURE;
04782    case RES_OUTOFMEMORY:
04783       ast_cli(fd, "Out of memory\n");
04784       return RESULT_FAILURE;
04785    case RES_NOT_DYNAMIC:
04786       ast_cli(fd, "Member not dynamic\n");
04787       return RESULT_FAILURE;
04788    default:
04789       return RESULT_FAILURE;
04790    }
04791 }

static void* handle_statechange ( struct statechange sc  )  [static]

set a member's status based on device state of that member's interface

Definition at line 629 of file app_queue.c.

References AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), ast_strdupa, devstate2str(), member_interface::interface, LOG_DEBUG, option_debug, and update_status().

Referenced by device_state_thread().

00630 {
00631    struct member_interface *curint;
00632    char *loc;
00633    char *technology;
00634 
00635    technology = ast_strdupa(sc->dev);
00636    loc = strchr(technology, '/');
00637    if (loc) {
00638       *loc++ = '\0';
00639    } else {
00640       return NULL;
00641    }
00642 
00643    AST_LIST_LOCK(&interfaces);
00644    AST_LIST_TRAVERSE(&interfaces, curint, list) {
00645       char *interface;
00646       char *slash_pos;
00647       interface = ast_strdupa(curint->interface);
00648       if ((slash_pos = strchr(interface, '/')))
00649          if ((slash_pos = strchr(slash_pos + 1, '/')))
00650             *slash_pos = '\0';
00651 
00652       if (!strcasecmp(interface, sc->dev))
00653          break;
00654    }
00655    AST_LIST_UNLOCK(&interfaces);
00656 
00657    if (!curint) {
00658       if (option_debug > 2)
00659          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));
00660       return NULL;
00661    }
00662 
00663    if (option_debug)
00664       ast_log(LOG_DEBUG, "Device '%s/%s' changed to state '%d' (%s)\n", technology, loc, sc->state, devstate2str(sc->state));
00665 
00666    update_status(sc->dev, sc->state);
00667 
00668    return NULL;
00669 }

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

Definition at line 1685 of file app_queue.c.

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

Referenced by try_calling().

01686 {
01687    struct callattempt *oo;
01688 
01689    while (outgoing) {
01690       /* Hangup any existing lines we have open */
01691       if (outgoing->chan && (outgoing->chan != exception))
01692          ast_hangup(outgoing->chan);
01693       oo = outgoing;
01694       outgoing = outgoing->q_next;
01695       if (oo->member)
01696          ao2_ref(oo->member, -1);
01697       free(oo);
01698    }
01699 }

static void init_queue ( struct call_queue q  )  [static]

Definition at line 801 of file app_queue.c.

References call_queue::announce, call_queue::announcefrequency, call_queue::announceholdtime, ao2_container_alloc(), call_queue::autofill, call_queue::context, call_queue::dead, DEFAULT_RETRY, call_queue::eventwhencalled, call_queue::found, call_queue::joinempty, call_queue::leavewhenempty, call_queue::maskmemberstatus, MAX_PERIODIC_ANNOUNCEMENTS, call_queue::maxlen, member_cmp_fn(), member_hash_fn(), call_queue::membercount, call_queue::memberdelay, call_queue::members, call_queue::moh, call_queue::monfmt, call_queue::monjoin, call_queue::montype, call_queue::periodicannouncefrequency, call_queue::reportholdtime, 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, call_queue::timeout, call_queue::timeoutrestart, call_queue::weight, and call_queue::wrapuptime.

Referenced by find_queue_by_name_rt(), and reload_queues().

00802 {
00803    int i;
00804 
00805    q->dead = 0;
00806    q->retry = DEFAULT_RETRY;
00807    q->timeout = -1;
00808    q->maxlen = 0;
00809    q->announcefrequency = 0;
00810    q->announceholdtime = 0;
00811    q->roundingseconds = 0; /* Default - don't announce seconds */
00812    q->servicelevel = 0;
00813    q->ringinuse = 1;
00814    q->setinterfacevar = 0;
00815    q->autofill = autofill_default;
00816    q->montype = montype_default;
00817    q->moh[0] = '\0';
00818    q->announce[0] = '\0';
00819    q->context[0] = '\0';
00820    q->monfmt[0] = '\0';
00821    q->periodicannouncefrequency = 0;
00822    q->reportholdtime = 0;
00823    q->monjoin = 0;
00824    q->wrapuptime = 0;
00825    q->joinempty = 0;
00826    q->leavewhenempty = 0;
00827    q->memberdelay = 0;
00828    q->maskmemberstatus = 0;
00829    q->eventwhencalled = 0;
00830    q->weight = 0;
00831    q->timeoutrestart = 0;
00832    if (!q->members)
00833       q->members = ao2_container_alloc(37, member_hash_fn, member_cmp_fn);
00834    q->membercount = 0;
00835    q->found = 1;
00836    ast_copy_string(q->sound_next, "queue-youarenext", sizeof(q->sound_next));
00837    ast_copy_string(q->sound_thereare, "queue-thereare", sizeof(q->sound_thereare));
00838    ast_copy_string(q->sound_calls, "queue-callswaiting", sizeof(q->sound_calls));
00839    ast_copy_string(q->sound_holdtime, "queue-holdtime", sizeof(q->sound_holdtime));
00840    ast_copy_string(q->sound_minutes, "queue-minutes", sizeof(q->sound_minutes));
00841    ast_copy_string(q->sound_seconds, "queue-seconds", sizeof(q->sound_seconds));
00842    ast_copy_string(q->sound_thanks, "queue-thankyou", sizeof(q->sound_thanks));
00843    ast_copy_string(q->sound_lessthan, "queue-less-than", sizeof(q->sound_lessthan));
00844    ast_copy_string(q->sound_reporthold, "queue-reporthold", sizeof(q->sound_reporthold));
00845    ast_copy_string(q->sound_periodicannounce[0], "queue-periodic-announce", sizeof(q->sound_periodicannounce[0]));
00846    for (i = 1; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) {
00847       q->sound_periodicannounce[i][0]='\0';
00848    }
00849 }

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 500 of file app_queue.c.

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

Referenced by join_queue().

00501 {
00502    struct queue_ent *cur;
00503 
00504    if (!q || !new)
00505       return;
00506    if (prev) {
00507       cur = prev->next;
00508       prev->next = new;
00509    } else {
00510       cur = q->head;
00511       q->head = new;
00512    }
00513    new->next = cur;
00514    new->parent = q;
00515    new->pos = ++(*pos);
00516    new->opos = *pos;
00517 }

static char* int2strat ( int  strategy  )  [static]

Definition at line 475 of file app_queue.c.

References strategy::name, and strategies.

Referenced by __queues_show().

00476 {
00477    int x;
00478 
00479    for (x = 0; x < sizeof(strategies) / sizeof(strategies[0]); x++) {
00480       if (strategy == strategies[x].strategy)
00481          return strategies[x].name;
00482    }
00483 
00484    return "<unknown>";
00485 }

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

Definition at line 3082 of file app_queue.c.

References ao2_iterator_init(), ao2_iterator_next(), ao2_ref(), member::interface, and call_queue::members.

Referenced by add_to_queue(), and set_member_paused().

03083 {
03084    struct member *mem;
03085    struct ao2_iterator mem_iter;
03086 
03087    if (!q)
03088       return NULL;
03089 
03090    mem_iter = ao2_iterator_init(q->members, 0);
03091    while ((mem = ao2_iterator_next(&mem_iter))) {
03092       if (!strcasecmp(interface, mem->interface))
03093          return mem;
03094       ao2_ref(mem, -1);
03095    }
03096 
03097    return NULL;
03098 }

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

Definition at line 887 of file app_queue.c.

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

Referenced by remove_from_interfaces().

00888 {
00889    struct call_queue *q;
00890    struct member *mem, tmpmem;
00891    int ret = 0;
00892 
00893    ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
00894 
00895    AST_LIST_LOCK(&queues);
00896    AST_LIST_TRAVERSE(&queues, q, list) {
00897       ast_mutex_lock(&q->lock);
00898       if ((mem = ao2_find(q->members, &tmpmem, OBJ_POINTER))) {
00899          ao2_ref(mem, -1);
00900          ret = 1;
00901       }
00902       ast_mutex_unlock(&q->lock);
00903       if (ret)
00904          break;
00905    }
00906    AST_LIST_UNLOCK(&queues);
00907 
00908    return ret;
00909 }

static int is_our_turn ( struct queue_ent qe  )  [static]

Check if we should start attempting to call queue members.

The behavior of this function is dependent first on whether autofill is enabled and second on whether the ring strategy is ringall. If autofill is not enabled, then return true if we're the head of the queue. If autofill is enabled, then we count the available members and see if the number of available members is enough that given our position in the queue, we would theoretically be able to connect to one of those available members

Definition at line 2328 of file app_queue.c.

References ao2_iterator_init(), ao2_iterator_next(), ao2_ref(), AST_DEVICE_INUSE, 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, option_debug, queue_ent::parent, member::paused, queue_ent::pending, QUEUE_STRATEGY_RINGALL, call_queue::ringinuse, member::status, and call_queue::strategy.

Referenced by queue_exec(), and wait_our_turn().

02329 {
02330    struct queue_ent *ch;
02331    struct member *cur;
02332    int avl = 0;
02333    int idx = 0;
02334    int res;
02335 
02336    if (!qe->parent->autofill) {
02337       /* Atomically read the parent head -- does not need a lock */
02338       ch = qe->parent->head;
02339       /* If we are now at the top of the head, break out */
02340       if (ch == qe) {
02341          if (option_debug)
02342             ast_log(LOG_DEBUG, "It's our turn (%s).\n", qe->chan->name);
02343          res = 1;
02344       } else {
02345          if (option_debug)
02346             ast_log(LOG_DEBUG, "It's not our turn (%s).\n", qe->chan->name);
02347          res = 0;
02348       }  
02349 
02350    } else {
02351       /* This needs a lock. How many members are available to be served? */
02352       ast_mutex_lock(&qe->parent->lock);
02353          
02354       ch = qe->parent->head;
02355    
02356       if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
02357          if (option_debug)
02358             ast_log(LOG_DEBUG, "Even though there may be multiple members available, the strategy is ringall so only the head call is allowed in\n");
02359          avl = 1;
02360       } else {
02361          struct ao2_iterator mem_iter = ao2_iterator_init(qe->parent->members, 0);
02362          while ((cur = ao2_iterator_next(&mem_iter))) {
02363             switch (cur->status) {
02364             case AST_DEVICE_INUSE:
02365                if (!qe->parent->ringinuse)
02366                   break;
02367                /* else fall through */
02368             case AST_DEVICE_NOT_INUSE:
02369             case AST_DEVICE_UNKNOWN:
02370                if (!cur->paused)
02371                   avl++;
02372                break;
02373             }
02374             ao2_ref(cur, -1);
02375          }
02376       }
02377 
02378       if (option_debug)
02379          ast_log(LOG_DEBUG, "There are %d available members.\n", avl);
02380    
02381       while ((idx < avl) && (ch) && (ch != qe)) {
02382          if (!ch->pending)
02383             idx++;
02384          ch = ch->next;       
02385       }
02386    
02387       /* If the queue entry is within avl [the number of available members] calls from the top ... */
02388       if (ch && idx < avl) {
02389          if (option_debug)
02390             ast_log(LOG_DEBUG, "It's our turn (%s).\n", qe->chan->name);
02391          res = 1;
02392       } else {
02393          if (option_debug)
02394             ast_log(LOG_DEBUG, "It's not our turn (%s).\n", qe->chan->name);
02395          res = 0;
02396       }
02397       
02398       ast_mutex_unlock(&qe->parent->lock);
02399    }
02400 
02401    return res;
02402 }

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

Definition at line 1406 of file app_queue.c.

References call_queue::announce, 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, call_queue::context, 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, call_queue::moh, queue_ent::moh, call_queue::name, 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().

01407 {
01408    struct call_queue *q;
01409    struct queue_ent *cur, *prev = NULL;
01410    int res = -1;
01411    int pos = 0;
01412    int inserted = 0;
01413    enum queue_member_status stat;
01414 
01415    if (!(q = load_realtime_queue(queuename)))
01416       return res;
01417 
01418    AST_LIST_LOCK(&queues);
01419    ast_mutex_lock(&q->lock);
01420 
01421    /* This is our one */
01422    stat = get_member_status(q, qe->max_penalty);
01423    if (!q->joinempty && (stat == QUEUE_NO_MEMBERS))
01424       *reason = QUEUE_JOINEMPTY;
01425    else if ((q->joinempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS || stat == QUEUE_NO_MEMBERS))
01426       *reason = QUEUE_JOINUNAVAIL;
01427    else if (q->maxlen && (q->count >= q->maxlen))
01428       *reason = QUEUE_FULL;
01429    else {
01430       /* There's space for us, put us at the right position inside
01431        * the queue.
01432        * Take into account the priority of the calling user */
01433       inserted = 0;
01434       prev = NULL;
01435       cur = q->head;
01436       while (cur) {
01437          /* We have higher priority than the current user, enter
01438           * before him, after all the other users with priority
01439           * higher or equal to our priority. */
01440          if ((!inserted) && (qe->prio > cur->prio)) {
01441             insert_entry(q, prev, qe, &pos);
01442             inserted = 1;
01443          }
01444          cur->pos = ++pos;
01445          prev = cur;
01446          cur = cur->next;
01447       }
01448       /* No luck, join at the end of the queue */
01449       if (!inserted)
01450          insert_entry(q, prev, qe, &pos);
01451       ast_copy_string(qe->moh, q->moh, sizeof(qe->moh));
01452       ast_copy_string(qe->announce, q->announce, sizeof(qe->announce));
01453       ast_copy_string(qe->context, q->context, sizeof(qe->context));
01454       q->count++;
01455       res = 0;
01456       manager_event(EVENT_FLAG_CALL, "Join",
01457          "Channel: %s\r\nCallerID: %s\r\nCallerIDName: %s\r\nQueue: %s\r\nPosition: %d\r\nCount: %d\r\nUniqueid: %s\r\n",
01458          qe->chan->name,
01459          S_OR(qe->chan->cid.cid_num, "unknown"), /* XXX somewhere else it is <unknown> */
01460          S_OR(qe->chan->cid.cid_name, "unknown"),
01461          q->name, qe->pos, q->count, qe->chan->uniqueid );
01462       if (option_debug)
01463          ast_log(LOG_DEBUG, "Queue '%s' Join, Channel '%s', Position '%d'\n", q->name, qe->chan->name, qe->pos );
01464    }
01465    ast_mutex_unlock(&q->lock);
01466    AST_LIST_UNLOCK(&queues);
01467 
01468    return res;
01469 }

static void leave_queue ( struct queue_ent qe  )  [static]

Definition at line 1641 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().

01642 {
01643    struct call_queue *q;
01644    struct queue_ent *cur, *prev = NULL;
01645    int pos = 0;
01646 
01647    if (!(q = qe->parent))
01648       return;
01649    ast_mutex_lock(&q->lock);
01650 
01651    prev = NULL;
01652    for (cur = q->head; cur; cur = cur->next) {
01653       if (cur == qe) {
01654          q->count--;
01655 
01656          /* Take us out of the queue */
01657          manager_event(EVENT_FLAG_CALL, "Leave",
01658             "Channel: %s\r\nQueue: %s\r\nCount: %d\r\nUniqueid: %s\r\n",
01659             qe->chan->name, q->name,  q->count, qe->chan->uniqueid);
01660          if (option_debug)
01661             ast_log(LOG_DEBUG, "Queue '%s' Leave, Channel '%s'\n", q->name, qe->chan->name );
01662          /* Take us out of the queue */
01663          if (prev)
01664             prev->next = cur->next;
01665          else
01666             q->head = cur->next;
01667       } else {
01668          /* Renumber the people after us in the queue based on a new count */
01669          cur->pos = ++pos;
01670          prev = cur;
01671       }
01672    }
01673    ast_mutex_unlock(&q->lock);
01674 
01675    if (q->dead && !q->count) {   
01676       /* It's dead and nobody is in it, so kill it */
01677       AST_LIST_LOCK(&queues);
01678       AST_LIST_REMOVE(&queues, q, list);
01679       AST_LIST_UNLOCK(&queues);
01680       destroy_queue(q);
01681    }
01682 }

static int load_module ( void   )  [static]

Definition at line 4913 of file app_queue.c.

References aqm_exec(), ast_cli_register_multiple(), ast_cond_init(), ast_custom_function_register(), ast_devstate_add(), ast_manager_register, AST_MODULE_LOAD_DECLINE, ast_mutex_init(), ast_pthread_create, ast_register_application(), cli_queue, device_state, device_state_thread(), 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().

04914 {
04915    int res;
04916 
04917    if (!reload_queues())
04918       return AST_MODULE_LOAD_DECLINE;
04919 
04920    if (queue_persistent_members)
04921       reload_queue_members();
04922 
04923    ast_mutex_init(&device_state.lock);
04924    ast_cond_init(&device_state.cond, NULL);
04925    ast_pthread_create(&device_state.thread, NULL, device_state_thread, NULL);
04926 
04927    ast_cli_register_multiple(cli_queue, sizeof(cli_queue) / sizeof(struct ast_cli_entry));
04928    res = ast_register_application(app, queue_exec, synopsis, descrip);
04929    res |= ast_register_application(app_aqm, aqm_exec, app_aqm_synopsis, app_aqm_descrip);
04930    res |= ast_register_application(app_rqm, rqm_exec, app_rqm_synopsis, app_rqm_descrip);
04931    res |= ast_register_application(app_pqm, pqm_exec, app_pqm_synopsis, app_pqm_descrip);
04932    res |= ast_register_application(app_upqm, upqm_exec, app_upqm_synopsis, app_upqm_descrip);
04933    res |= ast_register_application(app_ql, ql_exec, app_ql_synopsis, app_ql_descrip);
04934    res |= ast_manager_register("Queues", 0, manager_queues_show, "Queues");
04935    res |= ast_manager_register("QueueStatus", 0, manager_queues_status, "Queue Status");
04936    res |= ast_manager_register("QueueAdd", EVENT_FLAG_AGENT, manager_add_queue_member, "Add interface to queue.");
04937    res |= ast_manager_register("QueueRemove", EVENT_FLAG_AGENT, manager_remove_queue_member, "Remove interface from queue.");
04938    res |= ast_manager_register("QueuePause", EVENT_FLAG_AGENT, manager_pause_queue_member, "Makes a queue member temporarily unavailable");
04939    res |= ast_custom_function_register(&queueagentcount_function);
04940    res |= ast_custom_function_register(&queuemembercount_function);
04941    res |= ast_custom_function_register(&queuememberlist_function);
04942    res |= ast_custom_function_register(&queuewaitingcount_function);
04943    res |= ast_devstate_add(statechange_queue, NULL);
04944 
04945    return res;
04946 }

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

Note:
Load from realtime before taking the global qlock, to avoid blocking all queue operations while waiting for the DB.
This will be two separate database transactions, so we might see queue parameters as they were before another process changed the queue and member list as it was after the change. Thus we might see an empty member list when a queue is deleted. In practise, this is unlikely to cause a problem.

Definition at line 1356 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, call_queue::realtime, and update_realtime_members().

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

01357 {
01358    struct ast_variable *queue_vars;
01359    struct ast_config *member_config = NULL;
01360    struct call_queue *q;
01361 
01362    /* Find the queue in the in-core list first. */
01363    AST_LIST_LOCK(&queues);
01364    AST_LIST_TRAVERSE(&queues, q, list) {
01365       if (!strcasecmp(q->name, queuename)) {
01366          break;
01367       }
01368    }
01369    AST_LIST_UNLOCK(&queues);
01370 
01371    if (!q || q->realtime) {
01372       /*! \note Load from realtime before taking the global qlock, to avoid blocking all
01373          queue operations while waiting for the DB.
01374 
01375          This will be two separate database transactions, so we might
01376          see queue parameters as they were before another process
01377          changed the queue and member list as it was after the change.
01378          Thus we might see an empty member list when a queue is
01379          deleted. In practise, this is unlikely to cause a problem. */
01380 
01381       queue_vars = ast_load_realtime("queues", "name", queuename, NULL);
01382       if (queue_vars) {
01383          member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", queuename, NULL);
01384          if (!member_config) {
01385             ast_log(LOG_ERROR, "no queue_members defined in your config (extconfig.conf).\n");
01386             ast_variables_destroy(queue_vars);
01387             return NULL;
01388          }
01389       }
01390 
01391       AST_LIST_LOCK(&queues);
01392 
01393       q = find_queue_by_name_rt(queuename, queue_vars, member_config);
01394       if (member_config)
01395          ast_config_destroy(member_config);
01396       if (queue_vars)
01397          ast_variables_destroy(queue_vars);
01398 
01399       AST_LIST_UNLOCK(&queues);
01400    } else { 
01401       update_realtime_members(q);
01402    }
01403    return q;
01404 }

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

Definition at line 4566 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::interface, RES_EXISTS, RES_NOSUCHQUEUE, RES_OKAY, and RES_OUTOFMEMORY.

Referenced by load_module().

04567 {
04568    const char *queuename, *interface, *penalty_s, *paused_s, *membername;
04569    int paused, penalty = 0;
04570 
04571    queuename = astman_get_header(m, "Queue");
04572    interface = astman_get_header(m, "Interface");
04573    penalty_s = astman_get_header(m, "Penalty");
04574    paused_s = astman_get_header(m, "Paused");
04575    membername = astman_get_header(m, "MemberName");
04576 
04577    if (ast_strlen_zero(queuename)) {
04578       astman_send_error(s, m, "'Queue' not specified.");
04579       return 0;
04580    }
04581 
04582    if (ast_strlen_zero(interface)) {
04583       astman_send_error(s, m, "'Interface' not specified.");
04584       return 0;
04585    }
04586 
04587    if (ast_strlen_zero(penalty_s))
04588       penalty = 0;
04589    else if (sscanf(penalty_s, "%d", &penalty) != 1 || penalty < 0)
04590       penalty = 0;
04591 
04592    if (ast_strlen_zero(paused_s))
04593       paused = 0;
04594    else
04595       paused = abs(ast_true(paused_s));
04596 
04597    switch (add_to_queue(queuename, interface, membername, penalty, paused, queue_persistent_members)) {
04598    case RES_OKAY:
04599       ast_queue_log(queuename, "MANAGER", interface, "ADDMEMBER", "%s", "");
04600       astman_send_ack(s, m, "Added interface to queue");
04601       break;
04602    case RES_EXISTS:
04603       astman_send_error(s, m, "Unable to add interface: Already there");
04604       break;
04605    case RES_NOSUCHQUEUE:
04606       astman_send_error(s, m, "Unable to add interface to queue: No such queue");
04607       break;
04608    case RES_OUTOFMEMORY:
04609       astman_send_error(s, m, "Out of memory");
04610       break;
04611    }
04612 
04613    return 0;
04614 }

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

Definition at line 4650 of file app_queue.c.

References ast_strlen_zero(), ast_true(), astman_get_header(), astman_send_ack(), astman_send_error(), member_interface::interface, and set_member_paused().

Referenced by load_module().

04651 {
04652    const char *queuename, *interface, *paused_s;
04653    int paused;
04654 
04655    interface = astman_get_header(m, "Interface");
04656    paused_s = astman_get_header(m, "Paused");
04657    queuename = astman_get_header(m, "Queue");   /* Optional - if not supplied, pause the given Interface in all queues */
04658 
04659    if (ast_strlen_zero(interface) || ast_strlen_zero(paused_s)) {
04660       astman_send_error(s, m, "Need 'Interface' and 'Paused' parameters.");
04661       return 0;
04662    }
04663 
04664    paused = abs(ast_true(paused_s));
04665 
04666    if (set_member_paused(queuename, interface, paused))
04667       astman_send_error(s, m, "Interface not found");
04668    else
04669       astman_send_ack(s, m, paused ? "Interface paused successfully" : "Interface unpaused successfully");
04670    return 0;
04671 }

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

Definition at line 4462 of file app_queue.c.

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

Referenced by load_module().

04463 {
04464    char *a[] = { "queue", "show" };
04465 
04466    __queues_show(s, 1, -1, 2, a);
04467    astman_append(s, "\r\n\r\n"); /* Properly terminate Manager output */
04468 
04469    return RESULT_SUCCESS;
04470 }

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

Definition at line 4473 of file app_queue.c.

References ao2_iterator_init(), ao2_iterator_next(), ao2_ref(), 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, member::membername, call_queue::members, call_queue::name, queue_ent::next, member::paused, member::penalty, RESULT_SUCCESS, S_OR, call_queue::servicelevel, queue_ent::start, member::status, and call_queue::weight.

Referenced by load_module().

04474 {
04475    time_t now;
04476    int pos;
04477    const char *id = astman_get_header(m,"ActionID");
04478    const char *queuefilter = astman_get_header(m,"Queue");
04479    const char *memberfilter = astman_get_header(m,"Member");
04480    char idText[256] = "";
04481    struct call_queue *q;
04482    struct queue_ent *qe;
04483    float sl = 0;
04484    struct member *mem;
04485    struct ao2_iterator mem_iter;
04486 
04487    astman_send_ack(s, m, "Queue status will follow");
04488    time(&now);
04489    AST_LIST_LOCK(&queues);
04490    if (!ast_strlen_zero(id))
04491       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
04492 
04493    AST_LIST_TRAVERSE(&queues, q, list) {
04494       ast_mutex_lock(&q->lock);
04495 
04496       /* List queue properties */
04497       if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) {
04498          sl = ((q->callscompleted > 0) ? 100 * ((float)q->callscompletedinsl / (float)q->callscompleted) : 0);
04499          astman_append(s, "Event: QueueParams\r\n"
04500             "Queue: %s\r\n"
04501             "Max: %d\r\n"
04502             "Calls: %d\r\n"
04503             "Holdtime: %d\r\n"
04504             "Completed: %d\r\n"
04505             "Abandoned: %d\r\n"
04506             "ServiceLevel: %d\r\n"
04507             "ServicelevelPerf: %2.1f\r\n"
04508             "Weight: %d\r\n"
04509             "%s"
04510             "\r\n",
04511             q->name, q->maxlen, q->count, q->holdtime, q->callscompleted,
04512             q->callsabandoned, q->servicelevel, sl, q->weight, idText);
04513          /* List Queue Members */
04514          mem_iter = ao2_iterator_init(q->members, 0);
04515          while ((mem = ao2_iterator_next(&mem_iter))) {
04516             if (ast_strlen_zero(memberfilter) || !strcmp(mem->interface, memberfilter)) {
04517                astman_append(s, "Event: QueueMember\r\n"
04518                   "Queue: %s\r\n"
04519                   "Name: %s\r\n"
04520                   "Location: %s\r\n"
04521                   "Membership: %s\r\n"
04522                   "Penalty: %d\r\n"
04523                   "CallsTaken: %d\r\n"
04524                   "LastCall: %d\r\n"
04525                   "Status: %d\r\n"
04526                   "Paused: %d\r\n"
04527                   "%s"
04528                   "\r\n",
04529                   q->name, mem->membername, mem->interface, mem->dynamic ? "dynamic" : "static",
04530                   mem->penalty, mem->calls, (int)mem->lastcall, mem->status, mem->paused, idText);
04531             }
04532             ao2_ref(mem, -1);
04533          }
04534          /* List Queue Entries */
04535          pos = 1;
04536          for (qe = q->head; qe; qe = qe->next) {
04537             astman_append(s, "Event: QueueEntry\r\n"
04538                "Queue: %s\r\n"
04539                "Position: %d\r\n"
04540                "Channel: %s\r\n"
04541                "CallerID: %s\r\n"
04542                "CallerIDName: %s\r\n"
04543                "Wait: %ld\r\n"
04544                "%s"
04545                "\r\n",
04546                q->name, pos++, qe->chan->name,
04547                S_OR(qe->chan->cid.cid_num, "unknown"),
04548                S_OR(qe->chan->cid.cid_name, "unknown"),
04549                (long) (now - qe->start), idText);
04550          }
04551       }
04552       ast_mutex_unlock(&q->lock);
04553    }
04554 
04555    astman_append(s,
04556       "Event: QueueStatusComplete\r\n"
04557       "%s"
04558       "\r\n",idText);
04559 
04560    AST_LIST_UNLOCK(&queues);
04561 
04562 
04563    return RESULT_SUCCESS;
04564 }

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

Definition at line 4616 of file app_queue.c.

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

Referenced by load_module().

04617 {
04618    const char *queuename, *interface;
04619 
04620    queuename = astman_get_header(m, "Queue");
04621    interface = astman_get_header(m, "Interface");
04622 
04623    if (ast_strlen_zero(queuename) || ast_strlen_zero(interface)) {
04624       astman_send_error(s, m, "Need 'Queue' and 'Interface' parameters.");
04625       return 0;
04626    }
04627 
04628    switch (remove_from_queue(queuename, interface)) {
04629    case RES_OKAY:
04630       ast_queue_log(queuename, "MANAGER", interface, "REMOVEMEMBER", "%s", "");
04631       astman_send_ack(s, m, "Removed interface from queue");
04632       break;
04633    case RES_EXISTS:
04634       astman_send_error(s, m, "Unable to remove interface: Not there");
04635       break;
04636    case RES_NOSUCHQUEUE:
04637       astman_send_error(s, m, "Unable to remove interface from queue: No such queue");
04638       break;
04639    case RES_OUTOFMEMORY:
04640       astman_send_error(s, m, "Out of memory");
04641       break;
04642    case RES_NOT_DYNAMIC:
04643       astman_send_error(s, m, "Member not dynamic");
04644       break;
04645    }
04646 
04647    return 0;
04648 }

static int member_cmp_fn ( void *  obj1,
void *  obj2,
int  flags 
) [static]

Definition at line 795 of file app_queue.c.

References member::interface.

Referenced by init_queue().

00796 {
00797    struct member *mem1 = obj1, *mem2 = obj2;
00798    return strcmp(mem1->interface, mem2->interface) ? 0 : CMP_MATCH;
00799 }

static int member_hash_fn ( const void *  obj,
const int  flags 
) [static]

Definition at line 783 of file app_queue.c.

References compress_char(), and member::interface.

Referenced by init_queue().

00784 {
00785    const struct member *mem = obj;
00786    const char *chname = strchr(mem->interface, '/');
00787    int ret = 0, i;
00788    if (!chname)
00789       chname = mem->interface;
00790    for (i = 0; i < 5 && chname[i]; i++)
00791       ret += compress_char(chname[i]) << (i * 6);
00792    return ret;
00793 }

static void monjoin_dep_warning ( void   )  [static]

Definition at line 454 of file app_queue.c.

References ast_log(), and LOG_NOTICE.

Referenced by queue_set_param().

00455 {
00456    static unsigned int warned = 0;
00457    if (!warned) {
00458       ast_log(LOG_NOTICE, "The 'monitor-join' queue option is deprecated. Please use monitor-type=mixmonitor instead.\n");
00459       warned = 1;
00460    }
00461 }

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

Definition at line 1471 of file app_queue.c.

References AST_DIGIT_ANY, ast_stopstream(), ast_streamfile(), and ast_waitstream().

Referenced by say_periodic_announcement(), say_position(), and try_calling().

01472 {
01473    int res;
01474 
01475    ast_stopstream(chan);
01476 
01477    res = ast_streamfile(chan, filename, chan->language);
01478    if (!res)
01479       res = ast_waitstream(chan, AST_DIGIT_ANY);
01480 
01481    ast_stopstream(chan);
01482 
01483    return res;
01484 }

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

Definition at line 3395 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_channel::context, ast_channel::exten, member_interface::interface, LOG_WARNING, parse(), pbx_builtin_setvar_helper(), ast_channel::priority, and set_member_paused().

Referenced by load_module().

03396 {
03397    struct ast_module_user *lu;
03398    char *parse;
03399    int priority_jump = 0;
03400    AST_DECLARE_APP_ARGS(args,
03401       AST_APP_ARG(queuename);
03402       AST_APP_ARG(interface);
03403       AST_APP_ARG(options);
03404    );
03405 
03406    if (ast_strlen_zero(data)) {
03407       ast_log(LOG_WARNING, "PauseQueueMember requires an argument ([queuename]|interface[|options])\n");
03408       return -1;
03409    }
03410 
03411    parse = ast_strdupa(data);
03412 
03413    AST_STANDARD_APP_ARGS(args, parse);
03414 
03415    lu = ast_module_user_add(chan);
03416 
03417    if (args.options) {
03418       if (strchr(args.options, 'j'))
03419          priority_jump = 1;
03420    }
03421 
03422    if (ast_strlen_zero(args.interface)) {
03423       ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename]|interface[|options])\n");
03424       ast_module_user_remove(lu);
03425       return -1;
03426    }
03427 
03428    if (set_member_paused(args.queuename, args.interface, 1)) {
03429       ast_log(LOG_WARNING, "Attempt to pause interface %s, not found\n", args.interface);
03430       if (priority_jump || ast_opt_priority_jumping) {
03431          if (ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101)) {
03432             pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND");
03433             ast_module_user_remove(lu);
03434             return 0;
03435          }
03436       }
03437       ast_module_user_remove(lu);
03438       pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND");
03439       return 0;
03440    }
03441 
03442    ast_module_user_remove(lu);
03443    pbx_builtin_setvar_helper(chan, "PQMSTATUS", "PAUSED");
03444 
03445    return 0;
03446 }

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

Definition at line 3642 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(), event, LOG_WARNING, and parse().

Referenced by load_module().

03643 {
03644    struct ast_module_user *u;
03645    char *parse;
03646 
03647    AST_DECLARE_APP_ARGS(args,
03648       AST_APP_ARG(queuename);
03649       AST_APP_ARG(uniqueid);
03650       AST_APP_ARG(membername);
03651       AST_APP_ARG(event);
03652       AST_APP_ARG(params);
03653    );
03654 
03655    if (ast_strlen_zero(data)) {
03656       ast_log(LOG_WARNING, "QueueLog requires arguments (queuename|uniqueid|membername|event[|additionalinfo]\n");
03657       return -1;
03658    }
03659 
03660    u = ast_module_user_add(chan);
03661 
03662    parse = ast_strdupa(data);
03663 
03664    AST_STANDARD_APP_ARGS(args, parse);
03665 
03666    if (ast_strlen_zero(args.queuename) || ast_strlen_zero(args.uniqueid)
03667        || ast_strlen_zero(args.membername) || ast_strlen_zero(args.event)) {
03668       ast_log(LOG_WARNING, "QueueLog requires arguments (queuename|uniqueid|membername|event[|additionalinfo])\n");
03669       ast_module_user_remove(u);
03670       return -1;
03671    }
03672 
03673    ast_queue_log(args.queuename, args.uniqueid, args.membername, args.event, 
03674       "%s", args.params ? args.params : "");
03675 
03676    ast_module_user_remove(u);
03677 
03678    return 0;
03679 }

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

The starting point for all queue calls.

The process involved here is to 1. Parse the options specified in the call to Queue() 2. Join the queue 3. Wait in a loop until it is our turn to try calling a queue member 4. Attempt to call a queue member 5. If 4. did not result in a bridged call, then check for between call options such as periodic announcements etc. 6. Try 4 again uless some condition (such as an expiration time) causes us to exit the queue.

Definition at line 3693 of file app_queue.c.

References call_queue::announcefrequency, AST_APP_ARG, AST_CONTROL_RINGING, AST_DECLARE_APP_ARGS, ast_indicate(), ast_log(), ast_module_user_add, ast_module_user_remove, 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, queue_ent::digits, queue_ent::expire, get_member_status(), queue_ent::handled, is_our_turn(), join_queue(), queue_ent::last_periodic_announce_sound, queue_ent::last_periodic_announce_time, queue_ent::last_pos, queue_ent::last_pos_said, leave_queue(), call_queue::leavewhenempty, LOG_DEBUG, LOG_WARNING, queue_ent::max_penalty, call_queue::membercount, queue_ent::moh, queue_ent::opos, option_debug, option_verbose, queue_ent::parent, parse(), pbx_builtin_getvar_helper(), call_queue::periodicannouncefrequency, queue_ent::pos, queue_ent::prio, 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(), queue_ent::start, stop, try_calling(), update_realtime_members(), queue_ent::valid_digits, VERBOSE_PREFIX_3, wait_a_bit(), and wait_our_turn().

Referenced by load_module().

03694 {
03695    int res=-1;
03696    int ringing=0;
03697    struct ast_module_user *lu;
03698    const char *user_priority;
03699    const char *max_penalty_str;
03700    int prio;
03701    int max_penalty;
03702    enum queue_result reason = QUEUE_UNKNOWN;
03703    /* whether to exit Queue application after the timeout hits */
03704    int tries = 0;
03705    int noption = 0;
03706    char *parse;
03707    AST_DECLARE_APP_ARGS(args,
03708       AST_APP_ARG(queuename);
03709       AST_APP_ARG(options);
03710       AST_APP_ARG(url);
03711       AST_APP_ARG(announceoverride);
03712       AST_APP_ARG(queuetimeoutstr);
03713       AST_APP_ARG(agi);
03714    );
03715    /* Our queue entry */
03716    struct queue_ent qe;
03717    
03718    if (ast_strlen_zero(data)) {
03719       ast_log(LOG_WARNING, "Queue requires an argument: queuename[|options[|URL[|announceoverride[|timeout[|agi]]]]]\n");
03720       return -1;
03721    }
03722    
03723    parse = ast_strdupa(data);
03724    AST_STANDARD_APP_ARGS(args, parse);
03725 
03726    lu = ast_module_user_add(chan);
03727 
03728    /* Setup our queue entry */
03729    memset(&qe, 0, sizeof(qe));
03730    qe.start = time(NULL);
03731 
03732    /* set the expire time based on the supplied timeout; */
03733    if (!ast_strlen_zero(args.queuetimeoutstr))
03734       qe.expire = qe.start + atoi(args.queuetimeoutstr);
03735    else
03736       qe.expire = 0;
03737 
03738    /* Get the priority from the variable ${QUEUE_PRIO} */
03739    user_priority = pbx_builtin_getvar_helper(chan, "QUEUE_PRIO");
03740    if (user_priority) {
03741       if (sscanf(user_priority, "%d", &prio) == 1) {
03742          if (option_debug)
03743             ast_log(LOG_DEBUG, "%s: Got priority %d from ${QUEUE_PRIO}.\n",
03744                chan->name, prio);
03745       } else {
03746          ast_log(LOG_WARNING, "${QUEUE_PRIO}: Invalid value (%s), channel %s.\n",
03747             user_priority, chan->name);
03748          prio = 0;
03749       }
03750    } else {
03751       if (option_debug > 2)
03752          ast_log(LOG_DEBUG, "NO QUEUE_PRIO variable found. Using default.\n");
03753       prio = 0;
03754    }
03755 
03756    /* Get the maximum penalty from the variable ${QUEUE_MAX_PENALTY} */
03757    if ((max_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MAX_PENALTY"))) {
03758       if (sscanf(max_penalty_str, "%d", &max_penalty) == 1) {
03759          if (option_debug)
03760             ast_log(LOG_DEBUG, "%s: Got max penalty %d from ${QUEUE_MAX_PENALTY}.\n",
03761                chan->name, max_penalty);
03762       } else {
03763          ast_log(LOG_WARNING, "${QUEUE_MAX_PENALTY}: Invalid value (%s), channel %s.\n",
03764             max_penalty_str, chan->name);
03765          max_penalty = 0;
03766       }
03767    } else {
03768       max_penalty = 0;
03769    }
03770 
03771    if (args.options && (strchr(args.options, 'r')))
03772       ringing = 1;
03773 
03774    if (option_debug)
03775       ast_log(LOG_DEBUG, "queue: %s, options: %s, url: %s, announce: %s, expires: %ld, priority: %d\n",
03776          args.queuename, args.options, args.url, args.announceoverride, (long)qe.expire, prio);
03777 
03778    qe.chan = chan;
03779    qe.prio = prio;
03780    qe.max_penalty = max_penalty;
03781    qe.last_pos_said = 0;
03782    qe.last_pos = 0;
03783    qe.last_periodic_announce_time = time(NULL);
03784    qe.last_periodic_announce_sound = 0;
03785    qe.valid_digits = 0;
03786    if (!join_queue(args.queuename, &qe, &reason)) {
03787       int makeannouncement = 0;
03788 
03789       ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ENTERQUEUE", "%s|%s", S_OR(args.url, ""),
03790          S_OR(chan->cid.cid_num, ""));
03791 check_turns:
03792       if (ringing) {
03793          ast_indicate(chan, AST_CONTROL_RINGING);
03794       } else {
03795          ast_moh_start(chan, qe.moh, NULL);
03796       }
03797 
03798       /* This is the wait loop for callers 2 through maxlen */
03799       res = wait_our_turn(&qe, ringing, &reason);
03800       if (res)
03801          goto stop;
03802 
03803       for (;;) {
03804          /* This is the wait loop for the head caller*/
03805          /* To exit, they may get their call answered; */
03806          /* they may dial a digit from the queue context; */
03807          /* or, they may timeout. */
03808 
03809          enum queue_member_status stat;
03810 
03811          /* Leave if we have exceeded our queuetimeout */
03812          if (qe.expire && (time(NULL) > qe.expire)) {
03813             record_abandoned(&qe);
03814             reason = QUEUE_TIMEOUT;
03815             res = 0;
03816             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
03817             break;
03818          }
03819 
03820          if (makeannouncement) {
03821             /* Make a position announcement, if enabled */
03822             if (qe.parent->announcefrequency && !ringing)
03823                if ((res = say_position(&qe)))
03824                   goto stop;
03825 
03826          }
03827          makeannouncement = 1;
03828 
03829          /* Make a periodic announcement, if enabled */
03830          if (qe.parent->periodicannouncefrequency && !ringing)
03831             if ((res = say_periodic_announcement(&qe)))
03832                goto stop;
03833 
03834          /* Try calling all queue members for 'timeout' seconds */
03835          res = try_calling(&qe, args.options, args.announceoverride, args.url, &tries, &noption, args.agi);
03836          if (res)
03837             goto stop;
03838 
03839          stat = get_member_status(qe.parent, qe.max_penalty);
03840 
03841          /* exit after 'timeout' cycle if 'n' option enabled */
03842          if (noption && tries >= qe.parent->membercount) {
03843             if (option_verbose > 2)
03844                ast_verbose(VERBOSE_PREFIX_3 "Exiting on time-out cycle\n");
03845             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
03846             record_abandoned(&qe);
03847             reason = QUEUE_TIMEOUT;
03848             res = 0;
03849             break;
03850          }
03851 
03852          /* leave the queue if no agents, if enabled */
03853          if (qe.parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) {
03854             record_abandoned(&qe);
03855             reason = QUEUE_LEAVEEMPTY;
03856             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
03857             res = 0;
03858             break;
03859          }
03860 
03861          /* leave the queue if no reachable agents, if enabled */
03862          if ((qe.parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) {
03863             record_abandoned(&qe);
03864             reason = QUEUE_LEAVEUNAVAIL;
03865             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
03866             res = 0;
03867             break;
03868          }
03869 
03870          /* Leave if we have exceeded our queuetimeout */
03871          if (qe.expire && (time(NULL) > qe.expire)) {
03872             record_abandoned(&qe);
03873             reason = QUEUE_TIMEOUT;
03874             res = 0;
03875             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
03876             break;
03877          }
03878 
03879          /* If using dynamic realtime members, we should regenerate the member list for this queue */
03880          update_realtime_members(qe.parent);
03881 
03882          /* OK, we didn't get anybody; wait for 'retry' seconds; may get a digit to exit with */
03883          res = wait_a_bit(&qe);
03884          if (res)
03885             goto stop;
03886 
03887 
03888          /* Since this is a priority queue and
03889           * it is not sure that we are still at the head
03890           * of the queue, go and check for our turn again.
03891           */
03892          if (!is_our_turn(&qe)) {
03893             if (option_debug)
03894                ast_log(LOG_DEBUG, "Darn priorities, going back in queue (%s)!\n",
03895                   qe.chan->name);
03896             goto check_turns;
03897          }
03898       }
03899 
03900 stop:
03901       if (res) {
03902          if (res < 0) {
03903             if (!qe.handled) {
03904                record_abandoned(&qe);
03905                ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ABANDON",
03906                   "%d|%d|%ld", qe.pos, qe.opos,
03907                   (long) time(NULL) - qe.start);
03908             }
03909             res = -1;
03910          } else if (qe.valid_digits) {
03911             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHKEY",
03912                "%s|%d", qe.digits, qe.pos);
03913          }
03914       }
03915 
03916       /* Don't allow return code > 0 */
03917       if (res >= 0 && res != AST_PBX_KEEPALIVE) {
03918          res = 0; 
03919          if (ringing) {
03920             ast_indicate(chan, -1);
03921          } else {
03922             ast_moh_stop(chan);
03923          }        
03924          ast_stopstream(chan);
03925       }
03926       leave_queue(&qe);
03927       if (reason != QUEUE_UNKNOWN)
03928          set_queue_result(chan, reason);
03929    } else {
03930       ast_log(LOG_WARNING, "Unable to join queue '%s'\n", args.queuename);
03931       set_queue_result(chan, reason);
03932       res = 0;
03933    }
03934    ast_module_user_remove(lu);
03935 
03936    return res;
03937 }

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

Definition at line 3939 of file app_queue.c.

References ao2_iterator_init(), ao2_iterator_next(), ao2_ref(), AST_DEVICE_INVALID, AST_DEVICE_UNAVAILABLE, ast_log(), ast_module_user_add, ast_module_user_remove, ast_mutex_lock(), ast_mutex_unlock(), ast_strlen_zero(), load_realtime_queue(), call_queue::lock, LOG_ERROR, LOG_WARNING, call_queue::members, and member::status.

03940 {
03941    int count = 0;
03942    struct call_queue *q;
03943    struct ast_module_user *lu;
03944    struct member *m;
03945    struct ao2_iterator mem_iter;
03946 
03947    buf[0] = '\0';
03948    
03949    if (ast_strlen_zero(data)) {
03950       ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
03951       return -1;
03952    }
03953 
03954    lu = ast_module_user_add(chan);
03955 
03956    if ((q = load_realtime_queue(data))) {
03957       ast_mutex_lock(&q->lock);
03958       mem_iter = ao2_iterator_init(q->members, 0);
03959       while ((m = ao2_iterator_next(&mem_iter))) {
03960          /* Count the agents who are logged in and presently answering calls */
03961          if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
03962             count++;
03963          }
03964          ao2_ref(m, -1);
03965       }
03966       ast_mutex_unlock(&q->lock);
03967    } else
03968       ast_log(LOG_WARNING, "queue %s was not found\n", data);
03969 
03970    snprintf(buf, len, "%d", count);
03971    ast_module_user_remove(lu);
03972 
03973    return 0;
03974 }

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

Definition at line 4019 of file app_queue.c.

References ao2_iterator_init(), ao2_iterator_next(), ao2_ref(), 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, member::membername, call_queue::members, and call_queue::name.

04020 {
04021    struct ast_module_user *u;
04022    struct call_queue *q;
04023    struct member *m;
04024 
04025    /* Ensure an otherwise empty list doesn't return garbage */
04026    buf[0] = '\0';
04027 
04028    if (ast_strlen_zero(data)) {
04029       ast_log(LOG_ERROR, "QUEUE_MEMBER_LIST requires an argument: queuename\n");
04030       return -1;
04031    }
04032    
04033    u = ast_module_user_add(chan);
04034 
04035    AST_LIST_LOCK(&queues);
04036    AST_LIST_TRAVERSE(&queues, q, list) {
04037       if (!strcasecmp(q->name, data)) {
04038          ast_mutex_lock(&q->lock);
04039          break;
04040       }
04041    }
04042    AST_LIST_UNLOCK(&queues);
04043 
04044    if (q) {
04045       int buflen = 0, count = 0;
04046       struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
04047 
04048       while ((m = ao2_iterator_next(&mem_iter))) {
04049          /* strcat() is always faster than printf() */
04050          if (count++) {
04051             strncat(buf + buflen, ",", len - buflen - 1);
04052             buflen++;
04053          }
04054          strncat(buf + buflen, m->membername, len - buflen - 1);
04055          buflen += strlen(m->membername);
04056          /* Safeguard against overflow (negative length) */
04057          if (buflen >= len - 2) {
04058             ao2_ref(m, -1);
04059             ast_log(LOG_WARNING, "Truncating list\n");
04060             break;
04061          }
04062          ao2_ref(m, -1);
04063       }
04064       ast_mutex_unlock(&q->lock);
04065    } else
04066       ast_log(LOG_WARNING, "queue %s was not found\n", data);
04067 
04068    /* We should already be terminated, but let's make sure. */
04069    buf[len - 1] = '\0';
04070    ast_module_user_remove(u);
04071 
04072    return 0;
04073 }

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

Definition at line 3976 of file app_queue.c.

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

03977 {
03978    int count = 0;
03979    struct call_queue *q;
03980    struct ast_module_user *lu;
03981    struct ast_variable *var = NULL;
03982 
03983    buf[0] = '\0';
03984    
03985    if (ast_strlen_zero(data)) {
03986       ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
03987       return -1;
03988    }
03989 
03990    lu = ast_module_user_add(chan);
03991    
03992    AST_LIST_LOCK(&queues);
03993    AST_LIST_TRAVERSE(&queues, q, list) {
03994       if (!strcasecmp(q->name, data)) {
03995          ast_mutex_lock(&q->lock);
03996          break;
03997       }
03998    }
03999    AST_LIST_UNLOCK(&queues);
04000 
04001    if (q) {
04002       count = q->count;
04003       ast_mutex_unlock(&q->lock);
04004    } else if ((var = ast_load_realtime("queues", "name", data, NULL))) {
04005       /* if the queue is realtime but was not found in memory, this
04006        * means that the queue had been deleted from memory since it was 
04007        * "dead." This means it has a 0 waiting count
04008        */
04009       count = 0;
04010       ast_variables_destroy(var);
04011    } else
04012       ast_log(LOG_WARNING, "queue %s was not found\n", data);
04013 
04014    snprintf(buf, len, "%d", count);
04015    ast_module_user_remove(lu);
04016    return 0;
04017 }

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 951 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, monjoin_dep_warning(), 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().

00952 {
00953    if (!strcasecmp(param, "musicclass") || 
00954       !strcasecmp(param, "music") || !strcasecmp(param, "musiconhold")) {
00955       ast_copy_string(q->moh, val, sizeof(q->moh));
00956    } else if (!strcasecmp(param, "announce")) {
00957       ast_copy_string(q->announce, val, sizeof(q->announce));
00958    } else if (!strcasecmp(param, "context")) {
00959       ast_copy_string(q->context, val, sizeof(q->context));
00960    } else if (!strcasecmp(param, "timeout")) {
00961       q->timeout = atoi(val);
00962       if (q->timeout < 0)
00963          q->timeout = DEFAULT_TIMEOUT;
00964    } else if (!strcasecmp(param, "ringinuse")) {
00965       q->ringinuse = ast_true(val);
00966    } else if (!strcasecmp(param, "setinterfacevar")) {
00967       q->setinterfacevar = ast_true(val);
00968    } else if (!strcasecmp(param, "monitor-join")) {
00969       monjoin_dep_warning();
00970       q->monjoin = ast_true(val);
00971    } else if (!strcasecmp(param, "monitor-format")) {
00972       ast_copy_string(q->monfmt, val, sizeof(q->monfmt));
00973    } else if (!strcasecmp(param, "queue-youarenext")) {
00974       ast_copy_string(q->sound_next, val, sizeof(q->sound_next));
00975    } else if (!strcasecmp(param, "queue-thereare")) {
00976       ast_copy_string(q->sound_thereare, val, sizeof(q->sound_thereare));
00977    } else if (!strcasecmp(param, "queue-callswaiting")) {
00978       ast_copy_string(q->sound_calls, val, sizeof(q->sound_calls));
00979    } else if (!strcasecmp(param, "queue-holdtime")) {
00980       ast_copy_string(q->sound_holdtime, val, sizeof(q->sound_holdtime));
00981    } else if (!strcasecmp(param, "queue-minutes")) {
00982       ast_copy_string(q->sound_minutes, val, sizeof(q->sound_minutes));
00983    } else if (!strcasecmp(param, "queue-seconds")) {
00984       ast_copy_string(q->sound_seconds, val, sizeof(q->sound_seconds));
00985    } else if (!strcasecmp(param, "queue-lessthan")) {
00986       ast_copy_string(q->sound_lessthan, val, sizeof(q->sound_lessthan));
00987    } else if (!strcasecmp(param, "queue-thankyou")) {
00988       ast_copy_string(q->sound_thanks, val, sizeof(q->sound_thanks));
00989    } else if (!strcasecmp(param, "queue-reporthold")) {
00990       ast_copy_string(q->sound_reporthold, val, sizeof(q->sound_reporthold));
00991    } else if (!strcasecmp(param, "announce-frequency")) {
00992       q->announcefrequency = atoi(val);
00993    } else if (!strcasecmp(param, "announce-round-seconds")) {
00994       q->roundingseconds = atoi(val);
00995       if (q->roundingseconds>60 || q->roundingseconds<0) {
00996          if (linenum >= 0) {
00997             ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
00998                "using 0 instead for queue '%s' at line %d of queues.conf\n",
00999                val, param, q->name, linenum);
01000          } else {
01001             ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
01002                "using 0 instead for queue '%s'\n", val, param, q->name);
01003          }
01004          q->roundingseconds=0;
01005       }
01006    } else if (!strcasecmp(param, "announce-holdtime")) {
01007       if (!strcasecmp(val, "once"))
01008          q->announceholdtime = ANNOUNCEHOLDTIME_ONCE;
01009       else if (ast_true(val))
01010          q->announceholdtime = ANNOUNCEHOLDTIME_ALWAYS;
01011       else
01012          q->announceholdtime = 0;
01013    } else if (!strcasecmp(param, "periodic-announce")) {
01014       if (strchr(val, '|')) {
01015          char *s, *buf = ast_strdupa(val);
01016          unsigned int i = 0;
01017 
01018          while ((s = strsep(&buf, "|"))) {
01019             ast_copy_string(q->sound_periodicannounce[i], s, sizeof(q->sound_periodicannounce[i]));
01020             i++;
01021             if (i == MAX_PERIODIC_ANNOUNCEMENTS)
01022                break;
01023          }
01024       } else {
01025          ast_copy_string(q->sound_periodicannounce[0], val, sizeof(q->sound_periodicannounce[0]));
01026       }
01027    } else if (!strcasecmp(param, "periodic-announce-frequency")) {
01028       q->periodicannouncefrequency = atoi(val);
01029    } else if (!strcasecmp(param, "retry")) {
01030       q->retry = atoi(val);
01031       if (q->retry <= 0)
01032          q->retry = DEFAULT_RETRY;
01033    } else if (!strcasecmp(param, "wrapuptime")) {
01034       q->wrapuptime = atoi(val);
01035    } else if (!strcasecmp(param, "autofill")) {
01036       q->autofill = ast_true(val);
01037    } else if (!strcasecmp(param, "monitor-type")) {
01038       if (!strcasecmp(val, "mixmonitor"))
01039          q->montype = 1;
01040    } else if (!strcasecmp(param, "autopause")) {
01041       q->autopause = ast_true(val);
01042    } else if (!strcasecmp(param, "maxlen")) {
01043       q->maxlen = atoi(val);
01044       if (q->maxlen < 0)
01045          q->maxlen = 0;
01046    } else if (!strcasecmp(param, "servicelevel")) {
01047       q->servicelevel= atoi(val);
01048    } else if (!strcasecmp(param, "strategy")) {
01049       q->strategy = strat2int(val);
01050       if (q->strategy < 0) {
01051          ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
01052             val, q->name);
01053          q->strategy = QUEUE_STRATEGY_RINGALL;
01054       }
01055    } else if (!strcasecmp(param, "joinempty")) {
01056       if (!strcasecmp(val, "strict"))
01057          q->joinempty = QUEUE_EMPTY_STRICT;
01058       else if (ast_true(val))
01059          q->joinempty = QUEUE_EMPTY_NORMAL;
01060       else
01061          q->joinempty = 0;
01062    } else if (!strcasecmp(param, "leavewhenempty")) {
01063       if (!strcasecmp(val, "strict"))
01064          q->leavewhenempty = QUEUE_EMPTY_STRICT;
01065       else if (ast_true(val))
01066          q->leavewhenempty = QUEUE_EMPTY_NORMAL;
01067       else
01068          q->leavewhenempty = 0;
01069    } else if (!strcasecmp(param, "eventmemberstatus")) {
01070       q->maskmemberstatus = !ast_true(val);
01071    } else if (!strcasecmp(param, "eventwhencalled")) {
01072       if (!strcasecmp(val, "vars")) {
01073          q->eventwhencalled = QUEUE_EVENT_VARIABLES;
01074       } else {
01075          q->eventwhencalled = ast_true(val) ? 1 : 0;
01076       }
01077    } else if (!strcasecmp(param, "reportholdtime")) {
01078       q->reportholdtime = ast_true(val);
01079    } else if (!strcasecmp(param, "memberdelay")) {
01080       q->memberdelay = atoi(val);
01081    } else if (!strcasecmp(param, "weight")) {
01082       q->weight = atoi(val);
01083       if (q->weight)
01084          use_weight++;
01085       /* With Realtime queues, if the last queue using weights is deleted in realtime,
01086          we will not see any effect on use_weight until next reload. */
01087    } else if (!strcasecmp(param, "timeoutrestart")) {
01088       q->timeoutrestart = ast_true(val);
01089    } else if (failunknown) {
01090       if (linenum >= 0) {
01091          ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s at line %d of queues.conf\n",
01092             q->name, param, linenum);
01093       } else {
01094          ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s\n", q->name, param);
01095       }
01096    }
01097 }

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

Definition at line 4428 of file app_queue.c.

References __queues_show().

Referenced by __queues_show().

04429 {
04430    return __queues_show(NULL, 0, fd, argc, argv);
04431 }

static void recalc_holdtime ( struct queue_ent qe,
int  newholdtime 
) [static]

Definition at line 1626 of file app_queue.c.

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

Referenced by try_calling().

01627 {
01628    int oldvalue;
01629 
01630    /* Calculate holdtime using a recursive boxcar filter */
01631    /* Thanks to SRT for this contribution */
01632    /* 2^2 (4) is the filter coefficient; a higher exponent would give old entries more weight */
01633 
01634    ast_mutex_lock(&qe->parent->lock);
01635    oldvalue = qe->parent->holdtime;
01636    qe->parent->holdtime = (((oldvalue << 2) - oldvalue) + newholdtime) >> 2;
01637    ast_mutex_unlock(&qe->parent->lock);
01638 }

static void record_abandoned ( struct queue_ent qe  )  [static]

Definition at line 2039 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().

02040 {
02041    ast_mutex_lock(&qe->parent->lock);
02042    manager_event(EVENT_FLAG_AGENT, "QueueCallerAbandon",
02043       "Queue: %s\r\n"
02044       "Uniqueid: %s\r\n"
02045       "Position: %d\r\n"
02046       "OriginalPosition: %d\r\n"
02047       "HoldTime: %d\r\n",
02048       qe->parent->name, qe->chan->uniqueid, qe->pos, qe->opos, (int)(time(NULL) - qe->start));
02049 
02050    qe->parent->callsabandoned++;
02051    ast_mutex_unlock(&qe->parent->lock);
02052 }

static int reload ( void   )  [static]

Definition at line 4948 of file app_queue.c.

References reload_queues().

04949 {
04950    reload_queues();
04951    return 0;
04952 }

static void reload_queue_members ( void   )  [static]

Definition at line 3300 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(), ERANGE, errno, member_interface::interface, ast_db_entry::key, load_realtime_queue(), 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().

03301 {
03302    char *cur_ptr; 
03303    char *queue_name;
03304    char *member;
03305    char *interface;
03306    char *membername = NULL;
03307    char *penalty_tok;
03308    int penalty = 0;
03309    char *paused_tok;
03310    int paused = 0;
03311    struct ast_db_entry *db_tree;
03312    struct ast_db_entry *entry;
03313    struct call_queue *cur_queue;
03314    char queue_data[PM_MAX_LEN];
03315 
03316    AST_LIST_LOCK(&queues);
03317 
03318    /* Each key in 'pm_family' is the name of a queue */
03319    db_tree = ast_db_gettree(pm_family, NULL);
03320    for (entry = db_tree; entry; entry = entry->next) {
03321 
03322       queue_name = entry->key + strlen(pm_family) + 2;
03323 
03324       AST_LIST_TRAVERSE(&queues, cur_queue, list) {
03325          ast_mutex_lock(&cur_queue->lock);
03326          if (!strcmp(queue_name, cur_queue->name))
03327             break;
03328          ast_mutex_unlock(&cur_queue->lock);
03329       }
03330       
03331       if (!cur_queue)
03332          cur_queue = load_realtime_queue(queue_name);
03333 
03334       if (!cur_queue) {
03335          /* If the queue no longer exists, remove it from the
03336           * database */
03337          ast_log(LOG_WARNING, "Error loading persistent queue: '%s': it does not exist\n", queue_name);
03338          ast_db_del(pm_family, queue_name);
03339          continue;
03340       } else
03341          ast_mutex_unlock(&cur_queue->lock);
03342 
03343       if (ast_db_get(pm_family, queue_name, queue_data, PM_MAX_LEN))
03344          continue;
03345 
03346       cur_ptr = queue_data;
03347       while ((member = strsep(&cur_ptr, "|"))) {
03348          if (ast_strlen_zero(member))
03349             continue;
03350 
03351          interface = strsep(&member, ";");
03352          penalty_tok = strsep(&member, ";");
03353          paused_tok = strsep(&member, ";");
03354          membername = strsep(&member, ";");
03355 
03356          if (!penalty_tok) {
03357             ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (penalty)\n", queue_name);
03358             break;
03359          }
03360          penalty = strtol(penalty_tok, NULL, 10);
03361          if (errno == ERANGE) {
03362             ast_log(LOG_WARNING, "Error converting penalty: %s: Out of range.\n", penalty_tok);
03363             break;
03364          }
03365          
03366          if (!paused_tok) {
03367             ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (paused)\n", queue_name);
03368             break;
03369          }
03370          paused = strtol(paused_tok, NULL, 10);
03371          if ((errno == ERANGE) || paused < 0 || paused > 1) {
03372             ast_log(LOG_WARNING, "Error converting paused: %s: Expected 0 or 1.\n", paused_tok);
03373             break;
03374          }
03375          if (ast_strlen_zero(membername))
03376             membername = interface;
03377 
03378          if (option_debug)
03379             ast_log(LOG_DEBUG, "Reload Members: Queue: %s  Member: %s  Name: %s  Penalty: %d  Paused: %d\n", queue_name, interface, membername, penalty, paused);
03380          
03381          if (add_to_queue(queue_name, interface, membername, penalty, paused, 0) == RES_OUTOFMEMORY) {
03382             ast_log(LOG_ERROR, "Out of Memory when reloading persistent queue member\n");
03383             break;
03384          }
03385       }
03386    }
03387 
03388    AST_LIST_UNLOCK(&queues);
03389    if (db_tree) {
03390       ast_log(LOG_NOTICE, "Queue members successfully reloaded from database.\n");
03391       ast_db_freetree(db_tree);
03392    }
03393 }

static int reload_queues ( void   )  [static]

Definition at line 4112 of file app_queue.c.

References add_to_interfaces(), alloc_queue(), ao2_find(), ao2_iterator_init(), ao2_iterator_next(), ao2_ref(), ao2_unlink(), AST_APP_ARG, ast_category_browse(), ast_config_destroy(), ast_config_load(), AST_DECLARE_APP_ARGS, ast_device_state(), AST_LIST_INSERT_HEAD, AST_LIST_LOCK, AST_LIST_REMOVE_CURRENT, AST_LIST_TRAVERSE, AST_LIST_TRAVERSE_SAFE_BEGIN, AST_LIST_TRAVERSE_SAFE_END, AST_LIST_UNLOCK, 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(), call_queue::count, create_queue_member(), call_queue::dead, member::delme, destroy_queue(), member::dynamic, call_queue::found, init_queue(), member::interface, member_interface::interface, ast_variable::lineno, call_queue::lock, LOG_DEBUG, LOG_NOTICE, LOG_WARNING, call_queue::membercount, call_queue::members, ast_variable::name, call_queue::name, ast_variable::next, parse(), member::paused, queue_set_param(), QUEUE_STRATEGY_ROUNDROBIN, call_queue::realtime, remove_from_interfaces(), rr_dep_warning(), member::status, call_queue::strategy, ast_variable::value, and var.

Referenced by load_module(), and reload().

04113 {
04114    struct call_queue *q;
04115    struct ast_config *cfg;
04116    char *cat, *tmp;
04117    struct ast_variable *var;
04118    struct member *cur, *newm;
04119    struct ao2_iterator mem_iter;
04120    int new;
04121    const char *general_val = NULL;
04122    char parse[80];
04123    char *interface;
04124    char *membername = NULL;
04125    int penalty;
04126    AST_DECLARE_APP_ARGS(args,
04127       AST_APP_ARG(interface);
04128       AST_APP_ARG(penalty);
04129       AST_APP_ARG(membername);
04130    );
04131    
04132    if (!(cfg = ast_config_load("queues.conf"))) {
04133       ast_log(LOG_NOTICE, "No call queueing config file (queues.conf), so no call queues\n");
04134       return 0;
04135    }
04136    AST_LIST_LOCK(&queues);
04137    use_weight=0;
04138    /* Mark all non-realtime queues as dead for the moment */
04139    AST_LIST_TRAVERSE(&queues, q, list) {
04140       if (!q->realtime) {
04141          q->dead = 1;
04142          q->found = 0;
04143       }
04144    }
04145 
04146    /* Chug through config file */
04147    cat = NULL;
04148    while ((cat = ast_category_browse(cfg, cat)) ) {
04149       if (!strcasecmp(cat, "general")) {  
04150          /* Initialize global settings */
04151          queue_persistent_members = 0;
04152          if ((general_val = ast_variable_retrieve(cfg, "general", "persistentmembers")))
04153             queue_persistent_members = ast_true(general_val);
04154          autofill_default = 0;
04155          if ((general_val = ast_variable_retrieve(cfg, "general", "autofill")))
04156             autofill_default = ast_true(general_val);
04157          montype_default = 0;
04158          if ((general_val = ast_variable_retrieve(cfg, "general", "monitor-type")))
04159             if (!strcasecmp(general_val, "mixmonitor"))
04160                montype_default = 1;
04161       } else { /* Define queue */
04162          /* Look for an existing one */
04163          AST_LIST_TRAVERSE(&queues, q, list) {
04164             if (!strcmp(q->name, cat))
04165                break;
04166          }
04167          if (!q) {
04168             /* Make one then */
04169             if (!(q = alloc_queue(cat))) {
04170                /* TODO: Handle memory allocation failure */
04171             }
04172             new = 1;
04173          } else
04174             new = 0;
04175          if (q) {
04176             if (!new)
04177                ast_mutex_lock(&q->lock);
04178             /* Check if a queue with this name already exists */
04179             if (q->found) {
04180                ast_log(LOG_WARNING, "Queue '%s' already defined! Skipping!\n", cat);
04181                if (!new)
04182                   ast_mutex_unlock(&q->lock);
04183                continue;
04184             }
04185             /* Re-initialize the queue, and clear statistics */
04186             init_queue(q);
04187             clear_queue(q);
04188             mem_iter = ao2_iterator_init(q->members, 0);
04189             while ((cur = ao2_iterator_next(&mem_iter))) {
04190                if (!cur->dynamic) {
04191                   cur->delme = 1;
04192                }
04193                ao2_ref(cur, -1);
04194             }
04195             for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
04196                if (!strcasecmp(var->name, "member")) {
04197                   struct member tmpmem;
04198                   membername = NULL;
04199 
04200                   /* Add a new member */
04201                   ast_copy_string(parse, var->value, sizeof(parse));
04202                   
04203                   AST_NONSTANDARD_APP_ARGS(args, parse, ',');
04204 
04205                   interface = args.interface;
04206                   if (!ast_strlen_zero(args.penalty)) {
04207                      tmp = args.penalty;
04208                      while (*tmp && *tmp < 33) tmp++;
04209                      penalty = atoi(tmp);
04210                      if (penalty < 0) {
04211                         penalty = 0;
04212                      }
04213                   } else
04214                      penalty = 0;
04215 
04216                   if (!ast_strlen_zero(args.membername)) {
04217                      membername = args.membername;
04218                      while (*membername && *membername < 33) membername++;
04219                   }
04220 
04221                   /* Find the old position in the list */
04222                   ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
04223                   cur = ao2_find(q->members, &tmpmem, OBJ_POINTER | OBJ_UNLINK);
04224 
04225                   newm = create_queue_member(interface, membername, penalty, cur ? cur->paused : 0);
04226                   ao2_link(q->members, newm);
04227                   ao2_ref(newm, -1);
04228                   newm = NULL;
04229 
04230                   if (cur)
04231                      ao2_ref(cur, -1);
04232                   else {
04233                      /* Add them to the master int list if necessary */
04234                      add_to_interfaces(interface);
04235                      q->membercount++;
04236                   }
04237                } else {
04238                   queue_set_param(q, var->name, var->value, var->lineno, 1);
04239                }
04240             }
04241 
04242             /* Free remaining members marked as delme */
04243             mem_iter = ao2_iterator_init(q->members, 0);
04244             while ((cur = ao2_iterator_next(&mem_iter))) {
04245                if (! cur->delme) {
04246                   ao2_ref(cur, -1);
04247                   continue;
04248                }
04249 
04250                q->membercount--;
04251                ao2_unlink(q->members, cur);
04252                remove_from_interfaces(cur->interface);
04253                ao2_ref(cur, -1);
04254             }
04255 
04256             if (q->strategy == QUEUE_STRATEGY_ROUNDROBIN)
04257                rr_dep_warning();
04258 
04259             if (new) {
04260                AST_LIST_INSERT_HEAD(&queues, q, list);
04261             } else
04262                ast_mutex_unlock(&q->lock);
04263          }
04264       }
04265    }
04266    ast_config_destroy(cfg);
04267    AST_LIST_TRAVERSE_SAFE_BEGIN(&queues, q, list) {
04268       if (q->dead) {
04269          AST_LIST_REMOVE_CURRENT(&queues, list);
04270          if (!q->count)
04271             destroy_queue(q);
04272          else
04273             ast_log(LOG_DEBUG, "XXX Leaking a little memory :( XXX\n");
04274       } else {
04275          ast_mutex_lock(&q->lock);
04276          mem_iter = ao2_iterator_init(q->members, 0);
04277          while ((cur = ao2_iterator_next(&mem_iter))) {
04278             if (cur->dynamic)
04279                q->membercount++;
04280             cur->status = ast_device_state(cur->interface);
04281             ao2_ref(cur, -1);
04282          }
04283          ast_mutex_unlock(&q->lock);
04284       }
04285    }
04286    AST_LIST_TRAVERSE_SAFE_END;
04287    AST_LIST_UNLOCK(&queues);
04288    return 1;
04289 }

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

Definition at line 911 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 find_queue_by_name_rt(), free_members(), reload_queues(), remove_from_queue(), and update_realtime_members().

00912 {
00913    struct member_interface *curint;
00914 
00915    if (interface_exists_global(interface))
00916       return 0;
00917 
00918    AST_LIST_LOCK(&interfaces);
00919    AST_LIST_TRAVERSE_SAFE_BEGIN(&interfaces, curint, list) {
00920       if (!strcasecmp(curint->interface, interface)) {
00921          if (option_debug)
00922             ast_log(LOG_DEBUG, "Removing %s from the list of interfaces that make up all of our queue members.\n", interface);
00923          AST_LIST_REMOVE_CURRENT(&interfaces, list);
00924          free(curint);
00925          break;
00926       }
00927    }
00928    AST_LIST_TRAVERSE_SAFE_END;
00929    AST_LIST_UNLOCK(&interfaces);
00930 
00931    return 0;
00932 }

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

Definition at line 3146 of file app_queue.c.

References ao2_find(), ao2_ref(), ao2_unlink(), AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_mutex_lock(), ast_mutex_unlock(), dump_queue_members(), member::dynamic, EVENT_FLAG_AGENT, member::interface, call_queue::lock, manager_event(), call_queue::membercount, member::membername, call_queue::members, call_queue::name, remove_from_interfaces(), RES_EXISTS, RES_NOSUCHQUEUE, RES_NOT_DYNAMIC, and RES_OKAY.

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

03147 {
03148    struct call_queue *q;
03149    struct member *mem, tmpmem;
03150    int res = RES_NOSUCHQUEUE;
03151 
03152    ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
03153 
03154    AST_LIST_LOCK(&queues);
03155    AST_LIST_TRAVERSE(&queues, q, list) {
03156       ast_mutex_lock(&q->lock);
03157       if (strcmp(q->name, queuename)) {
03158          ast_mutex_unlock(&q->lock);
03159          continue;
03160       }
03161 
03162       if ((mem = ao2_find(q->members, &tmpmem, OBJ_POINTER))) {
03163          /* XXX future changes should beware of this assumption!! */
03164          if (!mem->dynamic) {
03165             res = RES_NOT_DYNAMIC;
03166             ao2_ref(mem, -1);
03167             ast_mutex_unlock(&q->lock);
03168             break;
03169          }
03170          q->membercount--;
03171          manager_event(EVENT_FLAG_AGENT, "QueueMemberRemoved",
03172             "Queue: %s\r\n"
03173             "Location: %s\r\n"
03174             "MemberName: %s\r\n",
03175             q->name, mem->interface, mem->membername);
03176          ao2_unlink(q->members, mem);
03177          ao2_ref(mem, -1);
03178 
03179          if (queue_persistent_members)
03180             dump_queue_members(q);
03181          
03182          res = RES_OKAY;
03183       } else {
03184          res = RES_EXISTS;
03185       }
03186       ast_mutex_unlock(&q->lock);
03187       break;
03188    }
03189 
03190    if (res == RES_OKAY)
03191       remove_from_interfaces(interface);
03192 
03193    AST_LIST_UNLOCK(&queues);
03194 
03195    return res;
03196 }

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

Part 2 of ring_one.

Does error checking before attempting to request a channel and call a member. This function is only called from ring_one

Definition at line 1781 of file app_queue.c.

References ast_channel::adsicpe, ast_channel::appl, ast_call(), ast_cdr_busy(), ast_channel_inherit_variables(), ast_channel_lock, ast_channel_unlock, AST_DEVICE_NOT_INUSE, ast_device_state(), AST_DEVICE_UNKNOWN, ast_log(), ast_mutex_lock(), ast_mutex_unlock(), ast_request(), ast_strdup, ast_strlen_zero(), 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, ast_channel::dialcontext, do_hang(), EVENT_FLAG_AGENT, call_queue::eventwhencalled, ast_channel::exten, free, member::interface, callattempt::interface, callattempt::lastcall, call_queue::lock, LOG_DEBUG, manager_event(), callattempt::member, member::membername, call_queue::name, ast_channel::nativeformats, option_debug, option_verbose, queue_ent::parent, member::paused, pbx_builtin_getvar_helper(), ast_channel::priority, QUEUE_EVENT_VARIABLES, call_queue::ringinuse, call_queue::rrpos, member::status, callattempt::stillgoing, update_status(), vars2manager(), VERBOSE_PREFIX_3, ast_channel::whentohangup, and call_queue::wrapuptime.

Referenced by ring_one().

01782 {
01783    int res;
01784    int status;
01785    char tech[256];
01786    char *location;
01787    const char *macrocontext, *macroexten;
01788 
01789    /* on entry here, we know that tmp->chan == NULL */
01790    if (qe->parent->wrapuptime && (time(NULL) - tmp->lastcall < qe->parent->wrapuptime)) {
01791       if (option_debug)
01792          ast_log(LOG_DEBUG, "Wrapuptime not yet expired for %s\n", tmp->interface);
01793       if (qe->chan->cdr)
01794          ast_cdr_busy(qe->chan->cdr);
01795       tmp->stillgoing = 0;
01796       (*busies)++;
01797       return 0;
01798    }
01799 
01800    if (!qe->parent->ringinuse && (tmp->member->status != AST_DEVICE_NOT_INUSE) && (tmp->member->status != AST_DEVICE_UNKNOWN)) {
01801       if (option_debug)
01802          ast_log(LOG_DEBUG, "%s in use, can't receive call\n", tmp->interface);
01803       if (qe->chan->cdr)
01804          ast_cdr_busy(qe->chan->cdr);
01805       tmp->stillgoing = 0;
01806       return 0;
01807    }
01808 
01809    if (tmp->member->paused) {
01810       if (option_debug)
01811          ast_log(LOG_DEBUG, "%s paused, can't receive call\n", tmp->interface);
01812       if (qe->chan->cdr)
01813          ast_cdr_busy(qe->chan->cdr);
01814       tmp->stillgoing = 0;
01815       return 0;
01816    }
01817    if (use_weight && compare_weight(qe->parent,tmp->member)) {
01818       ast_log(LOG_DEBUG, "Priority queue delaying call to %s:%s\n", qe->parent->name, tmp->interface);
01819       if (qe->chan->cdr)
01820          ast_cdr_busy(qe->chan->cdr);
01821       tmp->stillgoing = 0;
01822       (*busies)++;
01823       return 0;
01824    }
01825 
01826    ast_copy_string(tech, tmp->interface, sizeof(tech));
01827    if ((location = strchr(tech, '/')))
01828       *location++ = '\0';
01829    else
01830       location = "";
01831 
01832    /* Request the peer */
01833    tmp->chan = ast_request(tech, qe->chan->nativeformats, location, &status);
01834    if (!tmp->chan) {       /* If we can't, just go on to the next call */
01835       if (qe->chan->cdr)
01836          ast_cdr_busy(qe->chan->cdr);
01837       tmp->stillgoing = 0;
01838 
01839       update_status(tmp->member->interface, ast_device_state(tmp->member->interface));
01840 
01841       ast_mutex_lock(&qe->parent->lock);
01842       qe->parent->rrpos++;
01843       ast_mutex_unlock(&qe->parent->lock);
01844 
01845       (*busies)++;
01846       return 0;
01847    }
01848    
01849    tmp->chan->appl = "AppQueue";
01850    tmp->chan->data = "(Outgoing Line)";
01851    tmp->chan->whentohangup = 0;
01852    if (tmp->chan->cid.cid_num)
01853       free(tmp->chan->cid.cid_num);
01854    tmp->chan->cid.cid_num = ast_strdup(qe->chan->cid.cid_num);
01855    if (tmp->chan->cid.cid_name)
01856       free(tmp->chan->cid.cid_name);
01857    tmp->chan->cid.cid_name = ast_strdup(qe->chan->cid.cid_name);
01858    if (tmp->chan->cid.cid_ani)
01859       free(tmp->chan->cid.cid_ani);
01860    tmp->chan->cid.cid_ani = ast_strdup(qe->chan->cid.cid_ani);
01861 
01862    /* Inherit specially named variables from parent channel */
01863    ast_channel_inherit_variables(qe->chan, tmp->chan);
01864 
01865    /* Presense of ADSI CPE on outgoing channel follows ours */
01866    tmp->chan->adsicpe = qe->chan->adsicpe;
01867 
01868    /* Inherit context and extension */
01869    ast_channel_lock(qe->chan);
01870    macrocontext = pbx_builtin_getvar_helper(qe->chan, "MACRO_CONTEXT");
01871    if (!ast_strlen_zero(macrocontext))
01872       ast_copy_string(tmp->chan->dialcontext, macrocontext, sizeof(tmp->chan->dialcontext));
01873    else
01874       ast_copy_string(tmp->chan->dialcontext, qe->chan->context, sizeof(tmp->chan->dialcontext));
01875    macroexten = pbx_builtin_getvar_helper(qe->chan, "MACRO_EXTEN");
01876    if (!ast_strlen_zero(macroexten))
01877       ast_copy_string(tmp->chan->exten, macroexten, sizeof(tmp->chan->exten));
01878    else
01879       ast_copy_string(tmp->chan->exten, qe->chan->exten, sizeof(tmp->chan->exten));
01880    ast_channel_unlock(qe->chan);
01881 
01882    /* Place the call, but don't wait on the answer */
01883    if ((res = ast_call(tmp->chan, location, 0))) {
01884       /* Again, keep going even if there's an error */
01885       if (option_debug)
01886          ast_log(LOG_DEBUG, "ast call on peer returned %d\n", res);
01887       if (option_verbose > 2)
01888          ast_verbose(VERBOSE_PREFIX_3 "Couldn't call %s\n", tmp->interface);
01889       do_hang(tmp);
01890       (*busies)++;
01891       return 0;
01892    } else if (qe->parent->eventwhencalled) {
01893       char vars[2048];
01894 
01895       manager_event(EVENT_FLAG_AGENT, "AgentCalled",
01896                "AgentCalled: %s\r\n"
01897                "AgentName: %s\r\n"
01898                "ChannelCalling: %s\r\n"
01899                "CallerID: %s\r\n"
01900                "CallerIDName: %s\r\n"
01901                "Context: %s\r\n"
01902                "Extension: %s\r\n"
01903                "Priority: %d\r\n"
01904                "%s",
01905                tmp->interface, tmp->member->membername, qe->chan->name,
01906                tmp->chan->cid.cid_num ? tmp->chan->cid.cid_num : "unknown",
01907                tmp->chan->cid.cid_name ? tmp->chan->cid.cid_name : "unknown",
01908                qe->chan->context, qe->chan->exten, qe->chan->priority,
01909                qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
01910       if (option_verbose > 2)
01911          ast_verbose(VERBOSE_PREFIX_3 "Called %s\n", tmp->interface);
01912    }
01913 
01914    return 1;
01915 }

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

Place a call to a queue member.

Once metrics have been calculated for each member, this function is used to place a call to the appropriate member (or members). The low-level channel-handling and error detection is handled in ring_entry

Returns 1 if a member was called successfully, 0 otherwise

Definition at line 1941 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().

01942 {
01943    int ret = 0;
01944 
01945    while (ret == 0) {
01946       struct callattempt *best = find_best(outgoing);
01947       if (!best) {
01948          if (option_debug)
01949             ast_log(LOG_DEBUG, "Nobody left to try ringing in queue\n");
01950          break;
01951       }
01952       if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
01953          struct callattempt *cur;
01954          /* Ring everyone who shares this best metric (for ringall) */
01955          for (cur = outgoing; cur; cur = cur->q_next) {
01956             if (cur->stillgoing && !cur->chan && cur->metric <= best->metric) {
01957                if (option_debug)
01958                   ast_log(LOG_DEBUG, "(Parallel) Trying '%s' with metric %d\n", cur->interface, cur->metric);
01959                ret |= ring_entry(qe, cur, busies);
01960             }
01961          }
01962       } else {
01963          /* Ring just the best channel */
01964          if (option_debug)
01965             ast_log(LOG_DEBUG, "Trying '%s' with metric %d\n", best->interface, best->metric);
01966          ret = ring_entry(qe, best, busies);
01967       }
01968    }
01969 
01970    return ret;
01971 }

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 2055 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.

Referenced by wait_for_answer().

02056 {
02057    if (option_verbose > 2)
02058       ast_verbose( VERBOSE_PREFIX_3 "Nobody picked up in %d ms\n", rnatime);
02059    ast_queue_log(qe->parent->name, qe->chan->uniqueid, membername, "RINGNOANSWER", "%d", rnatime);
02060    if (qe->parent->autopause) {
02061       if (!set_member_paused(qe->parent->name, interface, 1)) {
02062          if (option_verbose > 2)
02063             ast_verbose( VERBOSE_PREFIX_3 "Auto-Pausing Queue Member %s in queue %s since they failed to answer.\n", interface, qe->parent->name);
02064       } else {
02065          if (option_verbose > 2)
02066             ast_verbose( VERBOSE_PREFIX_3 "Failed to pause Queue Member %s in queue %s!\n", interface, qe->parent->name);
02067       }
02068    }
02069    return;
02070 }

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

Definition at line 3501 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_channel::context, ast_channel::exten, member_interface::interface, LOG_DEBUG, LOG_NOTICE, LOG_WARNING, parse(), pbx_builtin_setvar_helper(), ast_channel::priority, remove_from_queue(), RES_EXISTS, RES_NOSUCHQUEUE, RES_NOT_DYNAMIC, and RES_OKAY.

Referenced by load_module().

03502 {
03503    int res=-1;
03504    struct ast_module_user *lu;
03505    char *parse, *temppos = NULL;
03506    int priority_jump = 0;
03507    AST_DECLARE_APP_ARGS(args,
03508       AST_APP_ARG(queuename);
03509       AST_APP_ARG(interface);
03510       AST_APP_ARG(options);
03511    );
03512 
03513 
03514    if (ast_strlen_zero(data)) {
03515       ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename[|interface[|options]])\n");
03516       return -1;
03517    }
03518 
03519    parse = ast_strdupa(data);
03520 
03521    AST_STANDARD_APP_ARGS(args, parse);
03522 
03523    lu = ast_module_user_add(chan);
03524 
03525    if (ast_strlen_zero(args.interface)) {
03526       args.interface = ast_strdupa(chan->name);
03527       temppos = strrchr(args.interface, '-');
03528       if (temppos)
03529          *temppos = '\0';
03530    }
03531 
03532    if (args.options) {
03533       if (strchr(args.options, 'j'))
03534          priority_jump = 1;
03535    }
03536 
03537    switch (remove_from_queue(args.queuename, args.interface)) {
03538    case RES_OKAY:
03539       ast_queue_log(args.queuename, chan->uniqueid, args.interface, "REMOVEMEMBER", "%s", "");
03540       ast_log(LOG_NOTICE, "Removed interface '%s' from queue '%s'\n", args.interface, args.queuename);
03541       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "REMOVED");
03542       res = 0;
03543       break;
03544    case RES_EXISTS:
03545       ast_log(LOG_DEBUG, "Unable to remove interface '%s' from queue '%s': Not there\n", args.interface, args.queuename);
03546       if (priority_jump || ast_opt_priority_jumping)
03547          ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
03548       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTINQUEUE");
03549       res = 0;
03550       break;
03551    case RES_NOSUCHQUEUE:
03552       ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", args.queuename);
03553       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOSUCHQUEUE");
03554       res = 0;
03555       break;
03556    case RES_NOT_DYNAMIC:
03557       ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': '%s' is not a dynamic member\n", args.queuename, args.interface);
03558       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTDYNAMIC");
03559       res = 0;
03560       break;
03561    }
03562 
03563    ast_module_user_remove(lu);
03564 
03565    return res;
03566 }

static void rr_dep_warning ( void   )  [static]

Definition at line 444 of file app_queue.c.

References ast_log(), and LOG_NOTICE.

Referenced by find_queue_by_name_rt(), and reload_queues().

00445 {
00446    static unsigned int warned = 0;
00447 
00448    if (!warned) {
00449       ast_log(LOG_NOTICE, "The 'roundrobin' queue strategy is deprecated. Please use the 'rrmemory' strategy instead.\n");
00450       warned = 1;
00451    }
00452 }

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 1099 of file app_queue.c.

References add_to_interfaces(), ao2_find(), ao2_ref(), create_queue_member(), member::dead, member::interface, call_queue::membercount, call_queue::members, member::paused, member::penalty, and member::realtime.

Referenced by find_queue_by_name_rt(), and update_realtime_members().

01100 {
01101    struct member *m, tmpmem;
01102    int penalty = 0;
01103    int paused  = 0;
01104 
01105    if (penalty_str) {
01106       penalty = atoi(penalty_str);
01107       if (penalty < 0)
01108          penalty = 0;
01109    }
01110 
01111    if (paused_str) {
01112       paused = atoi(paused_str);
01113       if (paused < 0)
01114          paused = 0;
01115    }
01116 
01117    /* Find the member, or the place to put a new one. */
01118    ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
01119    m = ao2_find(q->members, &tmpmem, OBJ_POINTER);
01120 
01121    /* Create a new one if not found, else update penalty */
01122    if (!m) {
01123       if ((m = create_queue_member(interface, membername, penalty, paused))) {
01124          m->dead = 0;
01125          m->realtime = 1;
01126          add_to_interfaces(interface);
01127          ao2_link(q->members, m);
01128          ao2_ref(m, -1);
01129          m = NULL;
01130          q->membercount++;
01131       }
01132    } else {
01133       m->dead = 0;   /* Do not delete this one. */
01134       if (paused_str)
01135          m->paused = paused;
01136       m->penalty = penalty;
01137       ao2_ref(m, -1);
01138    }
01139 }

static int say_periodic_announcement ( struct queue_ent qe  )  [static]

Definition at line 1997 of file app_queue.c.

References ast_moh_start(), ast_moh_stop(), ast_verbose(), 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, play_file(), call_queue::sound_periodicannounce, valid_exit(), and VERBOSE_PREFIX_3.

Referenced by queue_exec(), and wait_our_turn().

01998 {
01999    int res = 0;
02000    time_t now;
02001 
02002    /* Get the current time */
02003    time(&now);
02004 
02005    /* Check to see if it is time to announce */
02006    if ((now - qe->last_periodic_announce_time) < qe->parent->periodicannouncefrequency)
02007       return 0;
02008 
02009    /* Stop the music on hold so we can play our own file */
02010    ast_moh_stop(qe->chan);
02011 
02012    if (option_verbose > 2)
02013       ast_verbose(VERBOSE_PREFIX_3 "Playing periodic announcement\n");
02014 
02015    /* Check to make sure we have a sound file. If not, reset to the first sound file */
02016    if (qe->last_periodic_announce_sound >= MAX_PERIODIC_ANNOUNCEMENTS || !strlen(qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound])) {
02017       qe->last_periodic_announce_sound = 0;
02018    }
02019    
02020    /* play the announcement */
02021    res = play_file(qe->chan, qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound]);
02022 
02023    if ((res > 0 && !valid_exit(qe, res)) || res < 0)
02024       res = 0;
02025 
02026    /* Resume Music on Hold if the caller is going to stay in the queue */
02027    if (!res)
02028       ast_moh_start(qe->chan, qe->moh, NULL);
02029 
02030    /* update last_periodic_announce_time */
02031    qe->last_periodic_announce_time = now;
02032 
02033    /* Update the current periodic announcement to the next announcement */
02034    qe->last_periodic_announce_sound++;
02035    
02036    return res;
02037 }

static int say_position ( struct queue_ent qe  )  [static]

Definition at line 1519 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().

01520 {
01521    int res = 0, avgholdmins, avgholdsecs;
01522    time_t now;
01523 
01524    /* Check to see if this is ludicrous -- if we just announced position, don't do it again*/
01525    time(&now);
01526    if ((now - qe->last_pos) < 15)
01527       return 0;
01528 
01529    /* If either our position has changed, or we are over the freq timer, say position */
01530    if ((qe->last_pos_said == qe->pos) && ((now - qe->last_pos) < qe->parent->announcefrequency))
01531       return 0;
01532 
01533    ast_moh_stop(qe->chan);
01534    /* Say we're next, if we are */
01535    if (qe->pos == 1) {
01536       res = play_file(qe->chan, qe->parent->sound_next);
01537       if (res)
01538          goto playout;
01539       else
01540          goto posout;
01541    } else {
01542       res = play_file(qe->chan, qe->parent->sound_thereare);
01543       if (res)
01544          goto playout;
01545       res = ast_say_number(qe->chan, qe->pos, AST_DIGIT_ANY, qe->chan->language, (char *) NULL); /* Needs gender */
01546       if (res)
01547          goto playout;
01548       res = play_file(qe->chan, qe->parent->sound_calls);
01549       if (res)
01550          goto playout;
01551    }
01552    /* Round hold time to nearest minute */
01553    avgholdmins = abs(((qe->parent->holdtime + 30) - (now - qe->start)) / 60);
01554 
01555    /* If they have specified a rounding then round the seconds as well */
01556    if (qe->parent->roundingseconds) {
01557       avgholdsecs = (abs(((qe->parent->holdtime + 30) - (now - qe->start))) - 60 * avgholdmins) / qe->parent->roundingseconds;
01558       avgholdsecs *= qe->parent->roundingseconds;
01559    } else {
01560       avgholdsecs = 0;
01561    }
01562 
01563    if (option_verbose > 2)
01564       ast_verbose(VERBOSE_PREFIX_3 "Hold time for %s is %d minutes %d seconds\n", qe->parent->name, avgholdmins, avgholdsecs);
01565 
01566    /* If the hold time is >1 min, if it's enabled, and if it's not
01567       supposed to be only once and we have already said it, say it */
01568    if ((avgholdmins+avgholdsecs) > 0 && (qe->parent->announceholdtime) &&
01569       (!(qe->parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE) && qe->last_pos)) {
01570       res = play_file(qe->chan, qe->parent->sound_holdtime);
01571       if (res)
01572          goto playout;
01573 
01574       if (avgholdmins > 0) {
01575          if (avgholdmins < 2) {
01576             res = play_file(qe->chan, qe->parent->sound_lessthan);
01577             if (res)
01578                goto playout;
01579 
01580             res = ast_say_number(qe->chan, 2, AST_DIGIT_ANY, qe->chan->language, NULL);
01581             if (res)
01582                goto playout;
01583          } else {
01584             res = ast_say_number(qe->chan, avgholdmins, AST_DIGIT_ANY, qe->chan->language, NULL);
01585             if (res)
01586                goto playout;
01587          }
01588          
01589          res = play_file(qe->chan, qe->parent->sound_minutes);
01590          if (res)
01591             goto playout;
01592       }
01593       if (avgholdsecs>0) {
01594          res = ast_say_number(qe->chan, avgholdsecs, AST_DIGIT_ANY, qe->chan->language, NULL);
01595          if (res)
01596             goto playout;
01597 
01598          res = play_file(qe->chan, qe->parent->sound_seconds);
01599          if (res)
01600             goto playout;
01601       }
01602 
01603    }
01604 
01605 posout:
01606    if (option_verbose > 2)
01607       ast_verbose(VERBOSE_PREFIX_3 "Told %s in %s their queue position (which was %d)\n",
01608          qe->chan->name, qe->parent->name, qe->pos);
01609    res = play_file(qe->chan, qe->parent->sound_thanks);
01610 
01611 playout:
01612    if ((res > 0 && !valid_exit(qe, res)) || res < 0)
01613       res = 0;
01614 
01615    /* Set our last_pos indicators */
01616    qe->last_pos = now;
01617    qe->last_pos_said = qe->pos;
01618 
01619    /* Don't restart music on hold if we're about to exit the caller from the queue */
01620    if (!res)
01621       ast_moh_start(qe->chan, qe->moh, NULL);
01622 
01623    return res;
01624 }

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

Definition at line 3254 of file app_queue.c.

References ao2_ref(), 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, member::realtime, RESULT_FAILURE, RESULT_SUCCESS, and update_realtime_member_field().

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

03255 {
03256    int found = 0;
03257    struct call_queue *q;
03258    struct member *mem;
03259 
03260    /* Special event for when all queues are paused - individual events still generated */
03261    /* XXX In all other cases, we use the membername, but since this affects all queues, we cannot */
03262    if (ast_strlen_zero(queuename))
03263       ast_queue_log("NONE", "NONE", interface, (paused ? "PAUSEALL" : "UNPAUSEALL"), "%s", "");
03264 
03265    AST_LIST_LOCK(&queues);
03266    AST_LIST_TRAVERSE(&queues, q, list) {
03267       ast_mutex_lock(&q->lock);
03268       if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
03269          if ((mem = interface_exists(q, interface))) {
03270             found++;
03271             if (mem->paused == paused)
03272                ast_log(LOG_DEBUG, "%spausing already-%spaused queue member %s:%s\n", (paused ? "" : "un"), (paused ? "" : "un"), q->name, interface);
03273             mem->paused = paused;
03274 
03275             if (queue_persistent_members)
03276                dump_queue_members(q);
03277 
03278             if (mem->realtime)
03279                update_realtime_member_field(mem, q->name, "paused", paused ? "1" : "0");
03280 
03281             ast_queue_log(q->name, "NONE", mem->membername, (paused ? "PAUSE" : "UNPAUSE"), "%s", "");
03282 
03283             manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
03284                "Queue: %s\r\n"
03285                "Location: %s\r\n"
03286                "MemberName: %s\r\n"
03287                "Paused: %d\r\n",
03288                   q->name, mem->interface, mem->membername, paused);
03289             ao2_ref(mem, -1);
03290          }
03291       }
03292       ast_mutex_unlock(&q->lock);
03293    }
03294    AST_LIST_UNLOCK(&queues);
03295 
03296    return found ? RESULT_SUCCESS : RESULT_FAILURE;
03297 }

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

sets the QUEUESTATUS channel variable

Definition at line 463 of file app_queue.c.

References pbx_builtin_setvar_helper(), queue_results, and text.

Referenced by queue_exec().

00464 {
00465    int i;
00466 
00467    for (i = 0; i < sizeof(queue_results) / sizeof(queue_results[0]); i++) {
00468       if (queue_results[i].id == res) {
00469          pbx_builtin_setvar_helper(chan, "QUEUESTATUS", queue_results[i].text);
00470          return;
00471       }
00472    }
00473 }

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

Producer of the statechange queue.

Definition at line 724 of file app_queue.c.

References ast_calloc, ast_cond_signal(), AST_LIST_INSERT_TAIL, ast_mutex_lock(), ast_mutex_unlock(), and device_state.

Referenced by load_module(), and unload_module().

00725 {
00726    struct statechange *sc;
00727 
00728    if (!(sc = ast_calloc(1, sizeof(*sc) + strlen(dev) + 1)))
00729       return 0;
00730 
00731    sc->state = state;
00732    strcpy(sc->dev, dev);
00733 
00734    ast_mutex_lock(&device_state.lock);
00735    AST_LIST_INSERT_TAIL(&device_state.state_change_q, sc, entry);
00736    ast_cond_signal(&device_state.cond);
00737    ast_mutex_unlock(&device_state.lock);
00738 
00739    return 0;
00740 }

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

Definition at line 1973 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().

01974 {
01975    struct callattempt *best = find_best(outgoing);
01976 
01977    if (best) {
01978       /* Ring just the best channel */
01979       if (option_debug)
01980          ast_log(LOG_DEBUG, "Next is '%s' with metric %d\n", best->interface, best->metric);
01981       qe->parent->rrpos = best->metric % 1000;
01982    } else {
01983       /* Just increment rrpos */
01984       if (qe->parent->wrapped) {
01985          /* No more channels, start over */
01986          qe->parent->rrpos = 0;
01987       } else {
01988          /* Prioritize next entry */
01989          qe->parent->rrpos++;
01990       }
01991    }
01992    qe->parent->wrapped = 0;
01993 
01994    return 0;
01995 }

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

Definition at line 487 of file app_queue.c.

References name, and strategies.

Referenced by queue_set_param().

00488 {
00489    int x;
00490 
00491    for (x = 0; x < sizeof(strategies) / sizeof(strategies[0]); x++) {
00492       if (!strcasecmp(strategy, strategies[x].name))
00493          return strategies[x].strategy;
00494    }
00495 
00496    return -1;
00497 }

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

A large function which calls members, updates statistics, and bridges the caller and a member.

Here is the process of this function 1. Process any options passed to the Queue() application. Options here mean the third argument to Queue() 2. Iterate trough the members of the queue, creating a callattempt corresponding to each member. During this iteration, we also check the dialed_interfaces datastore to see if we have already attempted calling this member. If we have, we do not create a callattempt. This is in place to prevent call forwarding loops. Also during each iteration, we call calc_metric to determine which members should be rung when. 3. Call ring_one to place a call to the appropriate member(s) 4. Call wait_for_answer to wait for an answer. If no one answers, return. 5. Take care of any holdtime announcements, member delays, or other options which occur after a call has been answered. 6. Start the monitor or mixmonitor if the option is set 7. Remove the caller from the queue to allow other callers to advance 8. Bridge the call. 9. Do any post processing after the call has disconnected.

Parameters:
[in] qe the queue_ent structure which corresponds to the caller attempting to reach members
[in] options the options passed as the third parameter to the Queue() application
[in] url the url passed as the fourth parameter to the Queue() application
[in,out] tries the number of times we have tried calling queue members
[out] noption set if the call to Queue() has the 'n' option set.
[in] agi the agi passed as the fifth parameter to the Queue() application

Definition at line 2566 of file app_queue.c.

References ast_channel::_softhangup, ast_channel::_state, queue_ent::announce, ao2_iterator_init(), ao2_iterator_next(), ao2_ref(), ast_autoservice_start(), ast_autoservice_stop(), ast_bridge_call(), ast_calloc, AST_CDR_FLAG_LOCKED, ast_cdr_setdestchan(), ast_channel_datastore_add(), ast_channel_datastore_alloc(), ast_channel_datastore_find(), ast_channel_datastore_free(), ast_channel_datastore_remove(), ast_channel_lock, ast_channel_make_compatible(), ast_channel_sendurl(), ast_channel_setoption(), ast_channel_supports_html(), ast_channel_unlock, ast_clear_flag, AST_DEVICE_NOT_INUSE, AST_DIGIT_ANY, AST_FEATURE_AUTOMON, AST_FEATURE_DISCONNECT, AST_FEATURE_REDIRECT, ast_hangup(), AST_LIST_HEAD, AST_LIST_HEAD_INIT, AST_LIST_INSERT_TAIL, AST_LIST_LOCK, AST_LIST_TRAVERSE, 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, ast_datastore::data, DATASTORE_INHERIT_FOREVER, di, dialed_interface_info, EVENT_FLAG_AGENT, call_queue::eventwhencalled, queue_ent::expire, ast_channel::exten, ast_bridge_config::features_callee, ast_bridge_config::features_caller, free, queue_ent::handled, hangupcalls(), ast_datastore::inheritance, callattempt::interface, ast_dialed_interface::interface, member::interface, member::lastcall, callattempt::lastcall, leave_queue(), call_queue::lock, LOG_DEBUG, LOG_NOTICE, LOG_WARNING, manager_event(), callattempt::member, call_queue::membercount, call_queue::memberdelay, member::membername, call_queue::members, call_queue::monfmt, call_queue::monjoin, call_queue::montype, call_queue::name, callattempt::oldstatus, queue_ent::opos, option_debug, queue_ent::parent, pbx_builtin_getvar_helper(), pbx_builtin_setvar_helper(), pbx_exec(), pbx_findapp(), pbx_substitute_variables_helper(), queue_ent::pending, play_file(), queue_ent::pos, callattempt::q_next, QUEUE_EVENT_VARIABLES, QUEUE_STRATEGY_ROUNDROBIN, QUEUE_STRATEGY_RRMEMORY, recalc_holdtime(), record_abandoned(), call_queue::reportholdtime, ring_one(), call_queue::servicelevel, call_queue::setinterfacevar, call_queue::sound_lessthan, call_queue::sound_minutes, call_queue::sound_reporthold, queue_ent::start, member::status, callattempt::stillgoing, 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().

02567 {
02568    struct member *cur;
02569    struct callattempt *outgoing = NULL; /* the list of calls we are building */
02570    int to;
02571    char oldexten[AST_MAX_EXTENSION]="";
02572    char oldcontext[AST_MAX_CONTEXT]="";
02573    char queuename[256]="";
02574    struct ast_channel *peer;
02575    struct ast_channel *which;
02576    struct callattempt *lpeer;
02577    struct member *member;
02578    struct ast_app *app;
02579    int res = 0, bridge = 0;
02580    int numbusies = 0;
02581    int x=0;
02582    char *announce = NULL;
02583    char digit = 0;
02584    time_t callstart;
02585    time_t now = time(NULL);
02586    struct ast_bridge_config bridge_config;
02587    char nondataquality = 1;
02588    char *agiexec = NULL;
02589    int ret = 0;
02590    const char *monitorfilename;
02591    const char *monitor_exec;
02592    const char *monitor_options;
02593    char tmpid[256], tmpid2[256];
02594    char meid[1024], meid2[1024];
02595    char mixmonargs[1512];
02596    struct ast_app *mixmonapp = NULL;
02597    char *p;
02598    char vars[2048];
02599    int forwardsallowed = 1;
02600    int callcompletedinsl;
02601    struct ao2_iterator memi;
02602    struct ast_datastore *datastore;
02603 
02604    ast_channel_lock(qe->chan);
02605    datastore = ast_channel_datastore_find(qe->chan, &dialed_interface_info, NULL);
02606    ast_channel_unlock(qe->chan);
02607 
02608    memset(&bridge_config, 0, sizeof(bridge_config));
02609    time(&now);
02610       
02611    for (; options && *options; options++)
02612       switch (*options) {
02613       case 't':
02614          ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_REDIRECT);
02615          break;
02616       case 'T':
02617          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_REDIRECT);
02618          break;
02619       case 'w':
02620          ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMON);
02621          break;
02622       case 'W':
02623          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMON);
02624          break;
02625       case 'd':
02626          nondataquality = 0;
02627          break;
02628       case 'h':
02629          ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_DISCONNECT);
02630          break;
02631       case 'H':
02632          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT);
02633          break;
02634       case 'n':
02635          if (qe->parent->strategy == QUEUE_STRATEGY_ROUNDROBIN || qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY)
02636             (*tries)++;
02637          else
02638             *tries = qe->parent->membercount;
02639          *noption = 1;
02640          break;
02641       case 'i':
02642          forwardsallowed = 0;
02643          break;
02644       }
02645 
02646    /* Hold the lock while we setup the outgoing calls */
02647    if (use_weight)
02648       AST_LIST_LOCK(&queues);
02649    ast_mutex_lock(&qe->parent->lock);
02650    if (option_debug)
02651       ast_log(LOG_DEBUG, "%s is trying to call a queue member.\n",
02652                      qe->chan->name);
02653    ast_copy_string(queuename, qe->parent->name, sizeof(queuename));
02654    if (!ast_strlen_zero(qe->announce))
02655       announce = qe->announce;
02656    if (!ast_strlen_zero(announceoverride))
02657       announce = announceoverride;
02658 
02659    memi = ao2_iterator_init(qe->parent->members, 0);
02660    while ((cur = ao2_iterator_next(&memi))) {
02661       struct callattempt *tmp = ast_calloc(1, sizeof(*tmp));
02662       struct ast_dialed_interface *di;
02663       AST_LIST_HEAD(, ast_dialed_interface) *dialed_interfaces;
02664       if (!tmp) {
02665          ao2_ref(cur, -1);
02666          ast_mutex_unlock(&qe->parent->lock);
02667          if (use_weight)
02668             AST_LIST_UNLOCK(&queues);
02669          goto out;
02670       }
02671       if (!datastore) {
02672          if (!(datastore = ast_channel_datastore_alloc(&dialed_interface_info, NULL))) {
02673             ao2_ref(cur, -1);
02674             ast_mutex_unlock(&qe->parent->lock);
02675             if (use_weight)
02676                AST_LIST_UNLOCK(&queues);
02677             free(tmp);
02678             goto out;
02679          }
02680          datastore->inheritance = DATASTORE_INHERIT_FOREVER;
02681          if (!(dialed_interfaces = ast_calloc(1, sizeof(*dialed_interfaces)))) {
02682             ao2_ref(cur, -1);
02683             ast_mutex_unlock(&qe->parent->lock);
02684             if (use_weight)
02685                AST_LIST_UNLOCK(&queues);
02686             free(tmp);
02687             goto out;
02688          }
02689          datastore->data = dialed_interfaces;
02690          AST_LIST_HEAD_INIT(dialed_interfaces);
02691 
02692          ast_channel_lock(qe->chan);
02693          ast_channel_datastore_add(qe->chan, datastore);
02694          ast_channel_unlock(qe->chan);
02695       } else
02696          dialed_interfaces = datastore->data;
02697 
02698       AST_LIST_LOCK(dialed_interfaces);
02699       AST_LIST_TRAVERSE(dialed_interfaces, di, list) {
02700          if (!strcasecmp(cur->interface, di->interface)) {
02701             ast_log(LOG_DEBUG, "Skipping dialing interface '%s' since it has already been dialed\n", 
02702                di->interface);
02703             break;
02704          }
02705       }
02706       AST_LIST_UNLOCK(dialed_interfaces);
02707       
02708       if (di) {
02709          free(tmp);
02710          continue;
02711       }
02712 
02713       /* It is always ok to dial a Local interface.  We only keep track of
02714        * which "real" interfaces have been dialed.  The Local channel will
02715        * inherit this list so that if it ends up dialing a real interface,
02716        * it won't call one that has already been called. */
02717       if (strncasecmp(cur->interface, "Local/", 6)) {
02718          if (!(di = ast_calloc(1, sizeof(*di) + strlen(cur->interface)))) {
02719             ao2_ref(cur, -1);
02720             ast_mutex_unlock(&qe->parent->lock);
02721             if (use_weight)
02722                AST_LIST_UNLOCK(&queues);
02723             free(tmp);
02724             goto out;
02725          }
02726          strcpy(di->interface, cur->interface);
02727 
02728          AST_LIST_LOCK(dialed_interfaces);
02729          AST_LIST_INSERT_TAIL(dialed_interfaces, di, list);
02730          AST_LIST_UNLOCK(dialed_interfaces);
02731       }
02732 
02733       tmp->stillgoing = -1;
02734       tmp->member = cur;
02735       tmp->oldstatus = cur->status;
02736       tmp->lastcall = cur->lastcall;
02737       ast_copy_string(tmp->interface, cur->interface, sizeof(tmp->interface));
02738       /* Special case: If we ring everyone, go ahead and ring them, otherwise
02739          just calculate their metric for the appropriate strategy */
02740       if (!calc_metric(qe->parent, cur, x++, qe, tmp)) {
02741          /* Put them in the list of outgoing thingies...  We're ready now.
02742             XXX If we're forcibly removed, these outgoing calls won't get
02743             hung up XXX */
02744          tmp->q_next = outgoing;
02745          outgoing = tmp;      
02746          /* If this line is up, don't try anybody else */
02747          if (outgoing->chan && (outgoing->chan->_state == AST_STATE_UP))
02748             break;
02749       } else {
02750          ao2_ref(cur, -1);
02751          free(tmp);
02752       }
02753    }
02754    if (qe->expire && (!qe->parent->timeout || (qe->expire - now) <= qe->parent->timeout))
02755       to = (qe->expire - now) * 1000;
02756    else
02757       to = (qe->parent->timeout) ? qe->parent->timeout * 1000 : -1;
02758    ++qe->pending;
02759    ast_mutex_unlock(&qe->parent->lock);
02760    ring_one(qe, outgoing, &numbusies);
02761    if (use_weight)
02762       AST_LIST_UNLOCK(&queues);
02763    lpeer = wait_for_answer(qe, outgoing, &to, &digit, numbusies, ast_test_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT), forwardsallowed);
02764    /* The ast_channel_datastore_remove() function could fail here if the
02765     * datastore was moved to another channel during a masquerade. If this is
02766     * the case, don't free the datastore here because later, when the channel
02767     * to which the datastore was moved hangs up, it will attempt to free this
02768     * datastore again, causing a crash
02769     */
02770    if (datastore && !ast_channel_datastore_remove(qe->chan, datastore)) {
02771       ast_channel_datastore_free(datastore);
02772    }
02773    ast_mutex_lock(&qe->parent->lock);
02774    if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY) {
02775       store_next(qe, outgoing);
02776    }
02777    ast_mutex_unlock(&qe->parent->lock);
02778    peer = lpeer ? lpeer->chan : NULL;
02779    if (!peer) {
02780       qe->pending = 0;
02781       if (to) {
02782          /* Must gotten hung up */
02783          res = -1;
02784       } else {
02785          /* User exited by pressing a digit */
02786          res = digit;
02787       }
02788       if (option_debug && res == -1)
02789          ast_log(LOG_DEBUG, "%s: Nobody answered.\n", qe->chan->name);
02790    } else { /* peer is valid */
02791       /* Ah ha!  Someone answered within the desired timeframe.  Of course after this
02792          we will always return with -1 so that it is hung up properly after the
02793          conversation.  */
02794       if (!strcmp(qe->chan->tech->type, "Zap"))
02795          ast_channel_setoption(qe->chan, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
02796       if (!strcmp(peer->tech->type, "Zap"))
02797          ast_channel_setoption(peer, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
02798       /* Update parameters for the queue */
02799       time(&now);
02800       recalc_holdtime(qe, (now - qe->start));
02801       ast_mutex_lock(&qe->parent->lock);
02802       callcompletedinsl = ((now - qe->start) <= qe->parent->servicelevel);
02803       ast_mutex_unlock(&qe->parent->lock);
02804       member = lpeer->member;
02805       /* Increment the refcount for this member, since we're going to be using it for awhile in here. */
02806       ao2_ref(member, 1);
02807       hangupcalls(outgoing, peer);
02808       outgoing = NULL;
02809       if (announce || qe->parent->reportholdtime || qe->parent->memberdelay) {
02810          int res2;
02811 
02812          res2 = ast_autoservice_start(qe->chan);
02813          if (!res2) {
02814             if (qe->parent->memberdelay) {
02815                ast_log(LOG_NOTICE, "Delaying member connect for %d seconds\n", qe->parent->memberdelay);
02816                res2 |= ast_safe_sleep(peer, qe->parent->memberdelay * 1000);
02817             }
02818             if (!res2 && announce) {
02819                play_file(peer, announce);
02820             }
02821             if (!res2 && qe->parent->reportholdtime) {
02822                if (!play_file(peer, qe->parent->sound_reporthold)) {
02823                   int holdtime;
02824 
02825                   time(&now);
02826                   holdtime = abs((now - qe->start) / 60);
02827                   if (holdtime < 2) {
02828                      play_file(peer, qe->parent->sound_lessthan);
02829                      ast_say_number(peer, 2, AST_DIGIT_ANY, peer->language, NULL);
02830                   } else
02831                      ast_say_number(peer, holdtime, AST_DIGIT_ANY, peer->language, NULL);
02832                   play_file(peer, qe->parent->sound_minutes);
02833                }
02834             }
02835          }
02836          res2 |= ast_autoservice_stop(qe->chan);
02837          if (peer->_softhangup) {
02838             /* Agent must have hung up */
02839             ast_log(LOG_WARNING, "Agent on %s hungup on the customer.\n", peer->name);
02840             ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "AGENTDUMP", "%s", "");
02841             if (qe->parent->eventwhencalled)
02842                manager_event(EVENT_FLAG_AGENT, "AgentDump",
02843                      "Queue: %s\r\n"
02844                      "Uniqueid: %s\r\n"
02845                      "Channel: %s\r\n"
02846                      "Member: %s\r\n"
02847                      "MemberName: %s\r\n"
02848                      "%s",
02849                      queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
02850                      qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
02851             ast_hangup(peer);
02852             ao2_ref(member, -1);
02853             goto out;
02854          } else if (res2) {
02855             /* Caller must have hung up just before being connected*/
02856             ast_log(LOG_NOTICE, "Caller was about to talk to agent on %s but the caller hungup.\n", peer->name);
02857             ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "ABANDON", "%d|%d|%ld", qe->pos, qe->opos, (long)time(NULL) - qe->start);
02858             record_abandoned(qe);
02859             ast_hangup(peer);
02860             ao2_ref(member, -1);
02861             return -1;
02862          }
02863       }
02864       /* Stop music on hold */
02865       ast_moh_stop(qe->chan);
02866       /* If appropriate, log that we have a destination channel */
02867       if (qe->chan->cdr)
02868          ast_cdr_setdestchan(qe->chan->cdr, peer->name);
02869       /* Make sure channels are compatible */
02870       res = ast_channel_make_compatible(qe->chan, peer);
02871       if (res < 0) {
02872          ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "SYSCOMPAT", "%s", "");
02873          ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", qe->chan->name, peer->name);
02874          record_abandoned(qe);
02875          ast_hangup(peer);
02876          ao2_ref(member, -1);
02877          return -1;
02878       }
02879 
02880       if (qe->parent->setinterfacevar)
02881             pbx_builtin_setvar_helper(qe->chan, "MEMBERINTERFACE", member->interface);
02882 
02883       /* Begin Monitoring */
02884       if (qe->parent->monfmt && *qe->parent->monfmt) {
02885          if (!qe->parent->montype) {
02886             if (option_debug)
02887                ast_log(LOG_DEBUG, "Starting Monitor as requested.\n");
02888             monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME");
02889             if (pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC") || pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC_ARGS"))
02890                which = qe->chan;
02891             else
02892                which = peer;
02893             if (monitorfilename)
02894                ast_monitor_start(which, qe->parent->monfmt, monitorfilename, NULL, NULL, 1 );
02895             else if (qe->chan->cdr)
02896                ast_monitor_start(which, qe->parent->monfmt, qe->chan->cdr->uniqueid, NULL, NULL, 1 );
02897             else {
02898                /* Last ditch effort -- no CDR, make up something */
02899                snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random());
02900                ast_monitor_start(which, qe->parent->monfmt, tmpid, NULL, NULL, 1 );
02901             }
02902             if (qe->parent->monjoin)
02903                ast_monitor_setjoinfiles(which, 1);
02904          } else {
02905             if (option_debug)
02906                ast_log(LOG_DEBUG, "Starting MixMonitor as requested.\n");
02907             monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME");
02908             if (!monitorfilename) {
02909                if (qe->chan->cdr)
02910                   ast_copy_string(tmpid, qe->chan->cdr->uniqueid, sizeof(tmpid)-1);
02911                else
02912                   snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random());
02913             } else {
02914                ast_copy_string(tmpid2, monitorfilename, sizeof(tmpid2)-1);
02915                for (p = tmpid2; *p ; p++) {
02916                   if (*p == '^' && *(p+1) == '{') {
02917                      *p = '$';
02918                   }
02919                }
02920 
02921                memset(tmpid, 0, sizeof(tmpid));
02922                pbx_substitute_variables_helper(qe->chan, tmpid2, tmpid, sizeof(tmpid) - 1);
02923             }
02924 
02925             monitor_exec = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC");
02926             monitor_options = pbx_builtin_getvar_helper(qe->chan, "MONITOR_OPTIONS");
02927 
02928             if (monitor_exec) {
02929                ast_copy_string(meid2, monitor_exec, sizeof(meid2)-1);
02930                for (p = meid2; *p ; p++) {
02931                   if (*p == '^' && *(p+1) == '{') {
02932                      *p = '$';
02933                   }
02934                }
02935 
02936                memset(meid, 0, sizeof(meid));
02937                pbx_substitute_variables_helper(qe->chan, meid2, meid, sizeof(meid) - 1);
02938             }
02939    
02940             snprintf(tmpid2, sizeof(tmpid2)-1, "%s.%s", tmpid, qe->parent->monfmt);
02941 
02942             mixmonapp = pbx_findapp("MixMonitor");
02943 
02944             if (strchr(tmpid2, '|')) {
02945                ast_log(LOG_WARNING, "monitor-format (in queues.conf) and MONITOR_FILENAME cannot contain a '|'! Not recording.\n");
02946                mixmonapp = NULL;
02947             }
02948 
02949             if (!monitor_options)
02950                monitor_options = "";
02951             
02952             if (strchr(monitor_options, '|')) {
02953                ast_log(LOG_WARNING, "MONITOR_OPTIONS cannot contain a '|'! Not recording.\n");
02954                mixmonapp = NULL;
02955             }
02956 
02957             if (mixmonapp) {
02958                if (!ast_strlen_zero(monitor_exec))
02959                   snprintf(mixmonargs, sizeof(mixmonargs)-1, "%s|b%s|%s", tmpid2, monitor_options, monitor_exec);
02960                else
02961                   snprintf(mixmonargs, sizeof(mixmonargs)-1, "%s|b%s", tmpid2, monitor_options);
02962                   
02963                if (option_debug)
02964                   ast_log(LOG_DEBUG, "Arguments being passed to MixMonitor: %s\n", mixmonargs);
02965                /* We purposely lock the CDR so that pbx_exec does not update the application data */
02966                if (qe->chan->cdr)
02967                   ast_set_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED);
02968                ret = pbx_exec(qe->chan, mixmonapp, mixmonargs);
02969                if (qe->chan->cdr)
02970                   ast_clear_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED);
02971 
02972             } else
02973                ast_log(LOG_WARNING, "Asked to run MixMonitor on this call, but cannot find the MixMonitor app!\n");
02974 
02975          }
02976       }
02977       /* Drop out of the queue at this point, to prepare for next caller */
02978       leave_queue(qe);        
02979       if (!ast_strlen_zero(url) && ast_channel_supports_html(peer)) {
02980          if (option_debug)
02981             ast_log(LOG_DEBUG, "app_queue: sendurl=%s.\n", url);
02982          ast_channel_sendurl(peer, url);
02983       }
02984       if (!ast_strlen_zero(agi)) {
02985          if (option_debug)
02986             ast_log(LOG_DEBUG, "app_queue: agi=%s.\n", agi);
02987          app = pbx_findapp("agi");
02988          if (app) {
02989             agiexec = ast_strdupa(agi);
02990             ret = pbx_exec(qe->chan, app, agiexec);
02991          } else
02992             ast_log(LOG_WARNING, "Asked to execute an AGI on this channel, but could not find application (agi)!\n");
02993       }
02994       qe->handled++;
02995       ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "CONNECT", "%ld|%s", (long)time(NULL) - qe->start, peer->uniqueid);
02996       if (qe->parent->eventwhencalled)
02997          manager_event(EVENT_FLAG_AGENT, "AgentConnect",
02998                "Queue: %s\r\n"
02999                "Uniqueid: %s\r\n"
03000                "Channel: %s\r\n"
03001                "Member: %s\r\n"
03002                "MemberName: %s\r\n"
03003                "Holdtime: %ld\r\n"
03004                "BridgedChannel: %s\r\n"
03005                "%s",
03006                queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
03007                (long)time(NULL) - qe->start, peer->uniqueid,
03008                qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
03009       ast_copy_string(oldcontext, qe->chan->context, sizeof(oldcontext));
03010       ast_copy_string(oldexten, qe->chan->exten, sizeof(oldexten));
03011       time(&callstart);
03012 
03013       if (member->status == AST_DEVICE_NOT_INUSE)
03014          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);
03015          
03016 
03017       bridge = ast_bridge_call(qe->chan,peer, &bridge_config);
03018 
03019       if (strcasecmp(oldcontext, qe->chan->context) || strcasecmp(oldexten, qe->chan->exten)) {
03020          ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld",
03021             qe->chan->exten, qe->chan->context, (long) (callstart - qe->start),
03022             (long) (time(NULL) - callstart));
03023       } else if (qe->chan->_softhangup) {
03024          ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETECALLER", "%ld|%ld|%d",
03025             (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
03026          if (qe->parent->eventwhencalled)
03027             manager_event(EVENT_FLAG_AGENT, "AgentComplete",
03028                   "Queue: %s\r\n"
03029                   "Uniqueid: %s\r\n"
03030                   "Channel: %s\r\n"
03031                   "Member: %s\r\n"
03032                   "MemberName: %s\r\n"
03033                   "HoldTime: %ld\r\n"
03034                   "TalkTime: %ld\r\n"
03035                   "Reason: caller\r\n"
03036                   "%s",
03037                   queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
03038                   (long)(callstart - qe->start), (long)(time(NULL) - callstart),
03039                   qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
03040       } else {
03041          ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETEAGENT", "%ld|%ld|%d",
03042             (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
03043          if (qe->parent->eventwhencalled)
03044             manager_event(EVENT_FLAG_AGENT, "AgentComplete",
03045                   "Queue: %s\r\n"
03046                   "Uniqueid: %s\r\n"
03047                   "Channel: %s\r\n"
03048                   "MemberName: %s\r\n"
03049                   "HoldTime: %ld\r\n"
03050                   "TalkTime: %ld\r\n"
03051                   "Reason: agent\r\n"
03052                   "%s",
03053                   queuename, qe->chan->uniqueid, peer->name, member->membername, (long)(callstart - qe->start),
03054                   (long)(time(NULL) - callstart),
03055                   qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
03056       }
03057 
03058       if (bridge != AST_PBX_NO_HANGUP_PEER)
03059          ast_hangup(peer);
03060       update_queue(qe->parent, member, callcompletedinsl);
03061       res = bridge ? bridge : 1;
03062       ao2_ref(member, -1);
03063    }
03064 out:
03065    hangupcalls(outgoing, NULL);
03066 
03067    return res;
03068 }

static int unload_module ( void   )  [static]

Definition at line 4875 of file app_queue.c.

References ast_cli_unregister_multiple(), ast_cond_signal(), ast_custom_function_unregister(), ast_devstate_del(), ast_manager_unregister(), ast_module_user_hangup_all, ast_mutex_lock(), ast_mutex_unlock(), AST_PTHREADT_NULL, ast_unregister_application(), clear_and_free_interfaces(), cli_queue, device_state, queueagentcount_function, queuemembercount_function, queuememberlist_function, queuewaitingcount_function, and statechange_queue().

04876 {
04877    int res;
04878 
04879    if (device_state.thread != AST_PTHREADT_NULL) {
04880       device_state.stop = 1;
04881       ast_mutex_lock(&device_state.lock);
04882       ast_cond_signal(&device_state.cond);
04883       ast_mutex_unlock(&device_state.lock);
04884       pthread_join(device_state.thread, NULL);
04885    }
04886 
04887    ast_cli_unregister_multiple(cli_queue, sizeof(cli_queue) / sizeof(struct ast_cli_entry));
04888    res = ast_manager_unregister("QueueStatus");
04889    res |= ast_manager_unregister("Queues");
04890    res |= ast_manager_unregister("QueueStatus");
04891    res |= ast_manager_unregister("QueueAdd");
04892    res |= ast_manager_unregister("QueueRemove");
04893    res |= ast_manager_unregister("QueuePause");
04894    res |= ast_unregister_application(app_aqm);
04895    res |= ast_unregister_application(app_rqm);
04896    res |= ast_unregister_application(app_pqm);
04897    res |= ast_unregister_application(app_upqm);
04898    res |= ast_unregister_application(app_ql);
04899    res |= ast_unregister_application(app);
04900    res |= ast_custom_function_unregister(&queueagentcount_function);
04901    res |= ast_custom_function_unregister(&queuemembercount_function);
04902    res |= ast_custom_function_unregister(&queuememberlist_function);
04903    res |= ast_custom_function_unregister(&queuewaitingcount_function);
04904    ast_devstate_del(statechange_queue, NULL);
04905 
04906    ast_module_user_hangup_all();
04907 
04908    clear_and_free_interfaces();
04909 
04910    return res;
04911 }

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

Definition at line 2470 of file app_queue.c.

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

Referenced by try_calling().

02471 {
02472    ast_mutex_lock(&q->lock);
02473    time(&member->lastcall);
02474    member->calls++;
02475    q->callscompleted++;
02476    if (callcompletedinsl)
02477       q->callscompletedinsl++;
02478    ast_mutex_unlock(&q->lock);
02479    return 0;
02480 }

static int update_realtime_member_field ( struct member mem,
const char *  queue_name,
const char *  field,
const char *  value 
) [static]

Definition at line 1290 of file app_queue.c.

References ast_load_realtime(), ast_strlen_zero(), ast_update_realtime(), member::interface, ast_variable::name, ast_variable::next, ast_variable::value, and var.

Referenced by set_member_paused().

01291 {
01292    struct ast_variable *var;
01293    int ret = -1;
01294 
01295    if (!(var = ast_load_realtime("queue_members", "interface", mem->interface, "queue_name", queue_name, NULL))) 
01296       return ret;
01297    while (var) {
01298       if (!strcmp(var->name, "uniqueid"))
01299          break;
01300       var = var->next;
01301    }
01302    if (var && !ast_strlen_zero(var->value)) {
01303       if ((ast_update_realtime("queue_members", "uniqueid", var->value, field, value, NULL)) > -1)
01304          ret = 0;
01305    }
01306    return ret;
01307 }

static void update_realtime_members ( struct call_queue q  )  [static]

Definition at line 1309 of file app_queue.c.

References ao2_iterator_init(), ao2_iterator_next(), ao2_ref(), ao2_unlink(), ast_category_browse(), ast_config_destroy(), ast_load_realtime_multientry(), ast_log(), ast_mutex_lock(), ast_mutex_unlock(), ast_variable_retrieve(), member::dead, member::interface, member_interface::interface, call_queue::lock, LOG_DEBUG, call_queue::membercount, call_queue::members, call_queue::name, option_debug, member::realtime, remove_from_interfaces(), rt_handle_member_record(), and S_OR.

Referenced by load_realtime_queue(), and queue_exec().

01310 {
01311    struct ast_config *member_config = NULL;
01312    struct member *m;
01313    char *interface = NULL;
01314    struct ao2_iterator mem_iter;
01315 
01316    if (!(member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", q->name , NULL))) {
01317       /*This queue doesn't have realtime members*/
01318       if (option_debug > 2)
01319          ast_log(LOG_DEBUG, "Queue %s has no realtime members defined. No need for update\n", q->name);
01320       return;
01321    }
01322 
01323    ast_mutex_lock(&q->lock);
01324    
01325    /* Temporarily set realtime  members dead so we can detect deleted ones.*/ 
01326    mem_iter = ao2_iterator_init(q->members, 0);
01327    while ((m = ao2_iterator_next(&mem_iter))) {
01328       if (m->realtime)
01329          m->dead = 1;
01330       ao2_ref(m, -1);
01331    }
01332 
01333    while ((interface = ast_category_browse(member_config, interface))) {
01334       rt_handle_member_record(q, interface,
01335          S_OR(ast_variable_retrieve(member_config, interface, "membername"), interface),
01336          ast_variable_retrieve(member_config, interface, "penalty"),
01337          ast_variable_retrieve(member_config, interface, "paused"));
01338    }
01339 
01340    /* Delete all realtime members that have been deleted in DB. */
01341    mem_iter = ao2_iterator_init(q->members, 0);
01342    while ((m = ao2_iterator_next(&mem_iter))) {
01343       if (m->dead) {
01344          ao2_unlink(q->members, m);
01345          ast_mutex_unlock(&q->lock);
01346          remove_from_interfaces(m->interface);
01347          ast_mutex_lock(&q->lock);
01348          q->membercount--;
01349       }
01350       ao2_ref(m, -1);
01351    }
01352    ast_mutex_unlock(&q->lock);
01353    ast_config_destroy(member_config);
01354 }

static int update_status ( const char *  interface,
const int  status 
) [static]

Definition at line 576 of file app_queue.c.

References ao2_iterator_init(), ao2_iterator_next(), ao2_ref(), AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_mutex_lock(), ast_mutex_unlock(), ast_strdupa, 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::paused, member::penalty, member::realtime, and member::status.

Referenced by handle_statechange(), and ring_entry().

00577 {
00578    struct member *cur;
00579    struct ao2_iterator mem_iter;
00580    struct call_queue *q;
00581 
00582    AST_LIST_LOCK(&queues);
00583    AST_LIST_TRAVERSE(&queues, q, list) {
00584       ast_mutex_lock(&q->lock);
00585       mem_iter = ao2_iterator_init(q->members, 0);
00586       while ((cur = ao2_iterator_next(&mem_iter))) {
00587          char *tmp_interface;
00588          char *slash_pos;
00589          tmp_interface = ast_strdupa(cur->interface);
00590          if ((slash_pos = strchr(tmp_interface, '/')))
00591             if ((slash_pos = strchr(slash_pos + 1, '/')))
00592                *slash_pos = '\0';
00593 
00594          if (strcasecmp(interface, tmp_interface)) {
00595             ao2_ref(cur, -1);
00596             continue;
00597          }
00598 
00599          if (cur->status != status) {
00600             cur->status = status;
00601             if (q->maskmemberstatus) {
00602                ao2_ref(cur, -1);
00603                continue;
00604             }
00605 
00606             manager_event(EVENT_FLAG_AGENT, "QueueMemberStatus",
00607                "Queue: %s\r\n"
00608                "Location: %s\r\n"
00609                "MemberName: %s\r\n"
00610                "Membership: %s\r\n"
00611                "Penalty: %d\r\n"
00612                "CallsTaken: %d\r\n"
00613                "LastCall: %d\r\n"
00614                "Status: %d\r\n"
00615                "Paused: %d\r\n",
00616                q->name, cur->interface, cur->membername, cur->dynamic ? "dynamic" : cur->realtime ? "realtime" : "static",
00617                cur->penalty, cur->calls, (int)cur->lastcall, cur->status, cur->paused);
00618          }
00619          ao2_ref(cur, -1);
00620       }
00621       ast_mutex_unlock(&q->lock);
00622    }
00623    AST_LIST_UNLOCK(&queues);
00624 
00625    return 0;
00626 }

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

Definition at line 3448 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_channel::context, ast_channel::exten, member_interface::interface, LOG_WARNING, parse(), pbx_builtin_setvar_helper(), ast_channel::priority, and set_member_paused().

Referenced by load_module().

03449 {
03450    struct ast_module_user *lu;
03451    char *parse;
03452    int priority_jump = 0;
03453    AST_DECLARE_APP_ARGS(args,
03454       AST_APP_ARG(queuename);
03455       AST_APP_ARG(interface);
03456       AST_APP_ARG(options);
03457    );
03458 
03459    if (ast_strlen_zero(data)) {
03460       ast_log(LOG_WARNING, "UnpauseQueueMember requires an argument ([queuename]|interface[|options])\n");
03461       return -1;
03462    }
03463 
03464    parse = ast_strdupa(data);
03465 
03466    AST_STANDARD_APP_ARGS(args, parse);
03467 
03468    lu = ast_module_user_add(chan);
03469 
03470    if (args.options) {
03471       if (strchr(args.options, 'j'))
03472          priority_jump = 1;
03473    }
03474 
03475    if (ast_strlen_zero(args.interface)) {
03476       ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename]|interface[|options])\n");
03477       ast_module_user_remove(lu);
03478       return -1;
03479    }
03480 
03481    if (set_member_paused(args.queuename, args.interface, 0)) {
03482       ast_log(LOG_WARNING, "Attempt to unpause interface %s, not found\n", args.interface);
03483       if (priority_jump || ast_opt_priority_jumping) {
03484          if (ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101)) {
03485             pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND");
03486             ast_module_user_remove(lu);
03487             return 0;
03488          }
03489       }
03490       ast_module_user_remove(lu);
03491       pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND");
03492       return 0;
03493    }
03494 
03495    ast_module_user_remove(lu);
03496    pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "UNPAUSED");
03497 
03498    return 0;
03499 }

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

Definition at line 1486 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, queue_ent::digits, and queue_ent::valid_digits.

Referenced by say_periodic_announcement(), say_position(), wait_a_bit(), wait_for_answer(), and wait_our_turn().

01487 {
01488    int digitlen = strlen(qe->digits);
01489 
01490    /* Prevent possible buffer overflow */
01491    if (digitlen < sizeof(qe->digits) - 2) {
01492       qe->digits[digitlen] = digit;
01493       qe->digits[digitlen + 1] = '\0';
01494    } else {
01495       qe->digits[0] = '\0';
01496       return 0;
01497    }
01498 
01499    /* If there's no context to goto, short-circuit */
01500    if (ast_strlen_zero(qe->context))
01501       return 0;
01502 
01503    /* If the extension is bad, then reset the digits to blank */
01504    if (!ast_canmatch_extension(qe->chan, qe->context, qe->digits, 1, qe->chan->cid.cid_num)) {
01505       qe->digits[0] = '\0';
01506       return 0;
01507    }
01508 
01509    /* We have an exact match */
01510    if (!ast_goto_if_exists(qe->chan, qe->context, qe->digits, 1)) {
01511       qe->valid_digits = 1;
01512       /* Return 1 on a successful goto */
01513       return 1;
01514    }
01515 
01516    return 0;
01517 }

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

Definition at line 1741 of file app_queue.c.

References pbx_builtin_serialize_variables().

Referenced by ring_entry(), and try_calling().

01742 {
01743    char *tmp = alloca(len);
01744 
01745    if (pbx_builtin_serialize_variables(chan, tmp, len)) {
01746       int i, j;
01747 
01748       /* convert "\n" to "\nVariable: " */
01749       strcpy(vars, "Variable: ");
01750 
01751       for (i = 0, j = 10; (i < len - 1) && (j < len - 1); i++, j++) {
01752          vars[j] = tmp[i];
01753 
01754          if (tmp[i + 1] == '\0')
01755             break;
01756          if (tmp[i] == '\n') {
01757             vars[j++] = '\r';
01758             vars[j++] = '\n';
01759 
01760             ast_copy_string(&(vars[j]), "Variable: ", len - j);
01761             j += 9;
01762          }
01763       }
01764       if (j > len - 3)
01765          j = len - 3;
01766       vars[j++] = '\r';
01767       vars[j++] = '\n';
01768       vars[j] = '\0';
01769    } else {
01770       /* there are no channel variables; leave it blank */
01771       *vars = '\0';
01772    }
01773    return vars;
01774 }

static int wait_a_bit ( struct queue_ent qe  )  [static]

Definition at line 3070 of file app_queue.c.

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

Referenced by queue_exec().

03071 {
03072    /* Don't need to hold the lock while we setup the outgoing calls */
03073    int retrywait = qe->parent->retry * 1000;
03074 
03075    int res = ast_waitfordigit(qe->chan, retrywait);
03076    if (res > 0 && !valid_exit(qe, res))
03077       res = 0;
03078 
03079    return res;
03080 }

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, read]

Wait for a member to answer the call.

Parameters:
[in] qe the queue_ent corresponding to the caller in the queue
[in] outgoing the list of callattempts. Relevant ones will have their chan and stillgoing parameters non-zero
[in] to the amount of time (in milliseconds) to wait for a response
[out] digit if a user presses a digit to exit the queue, this is the digit the caller pressed
[in] prebusies number of busy members calculated prior to calling wait_for_answer
[in] caller_disconnect if the 'H' option is used when calling Queue(), this is used to detect if the caller pressed * to disconnect the call
[in] forwardsallowed used to detect if we should allow call forwarding, based on the 'i' option to Queue()

Definition at line 2083 of file app_queue.c.

References ast_channel::_state, accountcode, ast_call(), ast_cdr_busy(), ast_channel_datastore_inherit(), ast_channel_inherit_variables(), AST_CONTROL_ANSWER, AST_CONTROL_BUSY, AST_CONTROL_CONGESTION, AST_CONTROL_HANGUP, AST_CONTROL_OFFHOOK, AST_CONTROL_RINGING, AST_FRAME_CONTROL, AST_FRAME_DTMF, ast_frfree, ast_hangup(), ast_log(), AST_MAX_WATCHERS, ast_read(), ast_request(), AST_STATE_UP, ast_strdup, ast_string_field_set, ast_strlen_zero(), ast_verbose(), ast_waitfor_n(), callattempt::call_next, ast_channel::cdr, ast_channel::cdrflags, callattempt::chan, queue_ent::chan, ast_channel::cid, ast_callerid::cid_ani, ast_callerid::cid_name, ast_callerid::cid_num, ast_callerid::cid_rdnis, ast_channel::context, do_hang(), ast_channel::exten, f, ast_frame::frametype, free, callattempt::interface, member::interface, LOG_DEBUG, LOG_NOTICE, ast_channel::macroexten, callattempt::member, member::membername, call_queue::name, ast_channel::nativeformats, option_verbose, queue_ent::parent, callattempt::q_next, QUEUE_STRATEGY_RINGALL, ring_one(), rna(), S_OR, starttime, callattempt::stillgoing, call_queue::strategy, ast_frame::subclass, ast_channel::tech, call_queue::timeoutrestart, valid_exit(), and VERBOSE_PREFIX_3.

02084 {
02085    char *queue = qe->parent->name;
02086    struct callattempt *o, *start = NULL, *prev = NULL;
02087    int status;
02088    int numbusies = prebusies;
02089    int numnochan = 0;
02090    int stillgoing = 0;
02091    int orig = *to;
02092    struct ast_frame *f;
02093    struct callattempt *peer = NULL;
02094    struct ast_channel *winner;
02095    struct ast_channel *in = qe->chan;
02096    char on[80] = "";
02097    char membername[80] = "";
02098    long starttime = 0;
02099    long endtime = 0; 
02100 
02101    starttime = (long) time(NULL);
02102    
02103    while (*to && !peer) {
02104       int numlines, retry, pos = 1;
02105       struct ast_channel *watchers[AST_MAX_WATCHERS];
02106       watchers[0] = in;
02107       start = NULL;
02108 
02109       for (retry = 0; retry < 2; retry++) {
02110          numlines = 0;
02111          for (o = outgoing; o; o = o->q_next) { /* Keep track of important channels */
02112             if (o->stillgoing) { /* Keep track of important channels */
02113                stillgoing = 1;
02114                if (o->chan) {
02115                   watchers[pos++] = o->chan;
02116                   if (!start)
02117                      start = o;
02118                   else
02119                      prev->call_next = o;
02120                   prev = o;
02121                }
02122             }
02123             numlines++;
02124          }
02125          if (pos > 1 /* found */ || !stillgoing /* nobody listening */ ||
02126             (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) /* ring would not be delivered */)
02127             break;
02128          /* On "ringall" strategy we only move to the next penalty level
02129             when *all* ringing phones are done in the current penalty level */
02130          ring_one(qe, outgoing, &numbusies);
02131          /* and retry... */
02132       }
02133       if (pos == 1 /* not found */) {
02134          if (numlines == (numbusies + numnochan)) {
02135             ast_log(LOG_DEBUG, "Everyone is busy at this time\n");
02136          } else {
02137             ast_log(LOG_NOTICE, "No one is answering queue '%s' (%d/%d/%d)\n", queue, numlines, numbusies, numnochan);
02138          }
02139          *to = 0;
02140          return NULL;
02141       }
02142       winner = ast_waitfor_n(watchers, pos, to);
02143       for (o = start; o; o = o->call_next) {
02144          if (o->stillgoing && (o->chan) &&  (o->chan->_state == AST_STATE_UP)) {
02145             if (!peer) {
02146                if (option_verbose > 2)
02147                   ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
02148                peer = o;
02149             }
02150          } else if (o->chan && (o->chan == winner)) {
02151 
02152             ast_copy_string(on, o->member->interface, sizeof(on));
02153             ast_copy_string(membername, o->member->membername, sizeof(membername));
02154 
02155             if (!ast_strlen_zero(o->chan->call_forward) && !forwardsallowed) {
02156                if (option_verbose > 2)
02157                   ast_verbose(VERBOSE_PREFIX_3 "Forwarding %s to '%s' prevented.\n", in->name, o->chan->call_forward);
02158                numnochan++;
02159                do_hang(o);
02160                winner = NULL;
02161                continue;
02162             } else if (!ast_strlen_zero(o->chan->call_forward)) {
02163                char tmpchan[256];
02164                char *stuff;
02165                char *tech;
02166 
02167                ast_copy_string(tmpchan, o->chan->call_forward, sizeof(tmpchan));
02168                if ((stuff = strchr(tmpchan, '/'))) {
02169                   *stuff++ = '\0';
02170                   tech = tmpchan;
02171                } else {
02172                   snprintf(tmpchan, sizeof(tmpchan), "%s@%s", o->chan->call_forward, o->chan->context);
02173                   stuff = tmpchan;
02174                   tech = "Local";
02175                }
02176                /* Before processing channel, go ahead and check for forwarding */
02177                if (option_verbose > 2)
02178                   ast_verbose(VERBOSE_PREFIX_3 "Now forwarding %s to '%s/%s' (thanks to %s)\n", in->name, tech, stuff, o->chan->name);
02179                /* Setup parameters */
02180                o->chan = ast_request(tech, in->nativeformats, stuff, &status);
02181                if (!o->chan) {
02182                   ast_log(LOG_NOTICE, "Unable to create local channel for call forward to '%s/%s'\n", tech, stuff);
02183                   o->stillgoing = 0;
02184                   numnochan++;
02185                } else {
02186                   ast_channel_inherit_variables(in, o->chan);
02187                   ast_channel_datastore_inherit(in, o->chan);
02188                   if (o->chan->cid.cid_num)
02189                      free(o->chan->cid.cid_num);
02190                   o->chan->cid.cid_num = ast_strdup(in->cid.cid_num);
02191 
02192                   if (o->chan->cid.cid_name)
02193                      free(o->chan->cid.cid_name);
02194                   o->chan->cid.cid_name = ast_strdup(in->cid.cid_name);
02195 
02196                   ast_string_field_set(o->chan, accountcode, in->accountcode);
02197                   o->chan->cdrflags = in->cdrflags;
02198 
02199                   if (in->cid.cid_ani) {
02200                      if (o->chan->cid.cid_ani)
02201                         free(o->chan->cid.cid_ani);
02202                      o->chan->cid.cid_ani = ast_strdup(in->cid.cid_ani);
02203                   }
02204                   if (o->chan->cid.cid_rdnis)
02205                      free(o->chan->cid.cid_rdnis);
02206                   o->chan->cid.cid_rdnis = ast_strdup(S_OR(in->macroexten, in->exten));
02207                   if (ast_call(o->chan, tmpchan, 0)) {
02208                      ast_log(LOG_NOTICE, "Failed to dial on local channel for call forward to '%s'\n", tmpchan);
02209                      do_hang(o);
02210                      numnochan++;
02211                   }
02212                }
02213                /* Hangup the original channel now, in case we needed it */
02214                ast_hangup(winner);
02215                continue;
02216             }
02217             f = ast_read(winner);
02218             if (f) {
02219                if (f->frametype == AST_FRAME_CONTROL) {
02220                   switch (f->subclass) {
02221                   case AST_CONTROL_ANSWER:
02222                      /* This is our guy if someone answered. */
02223                      if (!peer) {
02224                         if (option_verbose > 2)
02225                            ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
02226                         peer = o;
02227                      }
02228                      break;
02229                   case AST_CONTROL_BUSY:
02230                      if (option_verbose > 2)
02231                         ast_verbose( VERBOSE_PREFIX_3 "%s is busy\n", o->chan->name);
02232                      if (in->cdr)
02233                         ast_cdr_busy(in->cdr);
02234                      do_hang(o);
02235                      endtime = (long)time(NULL);
02236                      endtime -= starttime;
02237                      rna(endtime*1000, qe, on, membername);
02238                      if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
02239                         if (qe->parent->timeoutrestart)
02240                            *to = orig;
02241                         ring_one(qe, outgoing, &numbusies);
02242                      }
02243                      numbusies++;
02244                      break;
02245                   case AST_CONTROL_CONGESTION:
02246                      if (option_verbose > 2)
02247                         ast_verbose( VERBOSE_PREFIX_3 "%s is circuit-busy\n", o->chan->name);
02248                      if (in->cdr)
02249                         ast_cdr_busy(in->cdr);
02250                      endtime = (long)time(NULL);
02251                      endtime -= starttime;
02252                      rna(endtime*1000, qe, on, membername);
02253                      do_hang(o);
02254                      if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
02255                         if (qe->parent->timeoutrestart)
02256                            *to = orig;
02257                         ring_one(qe, outgoing, &numbusies);
02258                      }
02259                      numbusies++;
02260                      break;
02261                   case AST_CONTROL_RINGING:
02262                      if (option_verbose > 2)
02263                         ast_verbose( VERBOSE_PREFIX_3 "%s is ringing\n", o->chan->name);
02264                      break;
02265                   case AST_CONTROL_OFFHOOK:
02266                      /* Ignore going off hook */
02267                      break;
02268                   default:
02269                      ast_log(LOG_DEBUG, "Dunno what to do with control type %d\n", f->subclass);
02270                   }
02271                }
02272                ast_frfree(f);
02273             } else {
02274                endtime = (long) time(NULL) - starttime;
02275                rna(endtime * 1000, qe, on, membername);
02276                do_hang(o);
02277                if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
02278                   if (qe->parent->timeoutrestart)
02279                      *to = orig;
02280                   ring_one(qe, outgoing, &numbusies);
02281                }
02282             }
02283          }
02284       }
02285       if (winner == in) {
02286          f = ast_read(in);
02287          if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) {
02288             /* Got hung up */
02289             *to = -1;
02290             if (f)
02291                ast_frfree(f);
02292             return NULL;
02293          }
02294          if ((f->frametype == AST_FRAME_DTMF) && caller_disconnect && (f->subclass == '*')) {
02295             if (option_verbose > 3)
02296                ast_verbose(VERBOSE_PREFIX_3 "User hit %c to disconnect call.\n", f->subclass);
02297             *to = 0;
02298             ast_frfree(f);
02299             return NULL;
02300          }
02301          if ((f->frametype == AST_FRAME_DTMF) && valid_exit(qe, f->subclass)) {
02302             if (option_verbose > 3)
02303                ast_verbose(VERBOSE_PREFIX_3 "User pressed digit: %c\n", f->subclass);
02304             *to = 0;
02305             *digit = f->subclass;
02306             ast_frfree(f);
02307             return NULL;
02308          }
02309          ast_frfree(f);
02310       }
02311       if (!*to) {
02312          for (o = start; o; o = o->call_next)
02313             rna(orig, qe, o->interface, o->member->membername);
02314       }
02315    }
02316 
02317    return peer;
02318 }

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

The waiting areas for callers who are not actively calling members.

This function is one large loop. This function will return if a caller either exits the queue or it becomes that caller's turn to attempt calling queue members. Inside the loop, we service the caller with periodic announcements, holdtime announcements, etc. as configured in queues.conf

Return values:
0 if the caller's turn has arrived
-1 if the caller should exit the queue.

Definition at line 2413 of file app_queue.c.

References call_queue::announcefrequency, ast_queue_log(), ast_waitfordigit(), queue_ent::chan, queue_ent::expire, 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(), queue_ent::start, and valid_exit().

Referenced by queue_exec().

02414 {
02415    int res = 0;
02416 
02417    /* This is the holding pen for callers 2 through maxlen */
02418    for (;;) {
02419       enum queue_member_status stat;
02420 
02421       if (is_our_turn(qe))
02422          break;
02423 
02424       /* If we have timed out, break out */
02425       if (qe->expire && (time(NULL) > qe->expire)) {
02426          *reason = QUEUE_TIMEOUT;
02427          break;
02428       }
02429 
02430       stat = get_member_status(qe->parent, qe->max_penalty);
02431 
02432       /* leave the queue if no agents, if enabled */
02433       if (qe->parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) {
02434          *reason = QUEUE_LEAVEEMPTY;
02435          ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long)time(NULL) - qe->start);
02436          leave_queue(qe);
02437          break;
02438       }
02439 
02440       /* leave the queue if no reachable agents, if enabled */
02441       if ((qe->parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) {
02442          *reason = QUEUE_LEAVEUNAVAIL;
02443          ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long)time(NULL) - qe->start);
02444          leave_queue(qe);
02445          break;
02446       }
02447 
02448       /* Make a position announcement, if enabled */
02449       if (qe->parent->announcefrequency && !ringing &&
02450          (res = say_position(qe)))
02451          break;
02452 
02453       /* Make a periodic announcement, if enabled */
02454       if (qe->parent->periodicannouncefrequency && !ringing &&
02455          (res = say_periodic_announcement(qe)))
02456          break;
02457 
02458       /* Wait a second before checking again */
02459       if ((res = ast_waitfordigit(qe->chan, RECHECK * 1000))) {
02460          if (res > 0 && !valid_exit(qe, res))
02461             res = 0;
02462          else
02463             break;
02464       }
02465    }
02466 
02467    return res;
02468 }


Variable Documentation

char* app = "Queue" [static]

Definition at line 148 of file app_queue.c.

char* app_aqm = "AddQueueMember" [static]

Definition at line 182 of file app_queue.c.

char* app_aqm_descrip [static]

Definition at line 184 of file app_queue.c.

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

Definition at line 183 of file app_queue.c.

char* app_pqm = "PauseQueueMember" [static]

Definition at line 214 of file app_queue.c.

char* app_pqm_descrip [static]

Definition at line 216 of file app_queue.c.

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

Definition at line 215 of file app_queue.c.

char* app_ql = "QueueLog" [static]

Definition at line 251 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 253 of file app_queue.c.

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

Definition at line 252 of file app_queue.c.

char* app_rqm = "RemoveQueueMember" [static]

Definition at line 198 of file app_queue.c.

char* app_rqm_descrip [static]

Definition at line 200 of file app_queue.c.

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

Definition at line 199 of file app_queue.c.

char* app_upqm = "UnpauseQueueMember" [static]

Definition at line 236 of file app_queue.c.

char* app_upqm_descrip [static]

Definition at line 238 of file app_queue.c.

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

Definition at line 237 of file app_queue.c.

int autofill_default = 0 [static]

queues.conf [general] option

Definition at line 270 of file app_queue.c.

Initial value:

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

Definition at line 4846 of file app_queue.c.

struct ast_cli_entry cli_queue[] [static]

Definition at line 4856 of file app_queue.c.

Referenced by load_module(), and unload_module().

Initial value:

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

Definition at line 4851 of file app_queue.c.

Initial value:

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

Definition at line 4841 of file app_queue.c.

Condition for the state change queue

Definition at line 682 of file app_queue.c.

char* descrip [static]

Definition at line 152 of file app_queue.c.

struct { ... } device_state [static]

Data used by the device state thread.

Referenced by device_state_thread(), load_module(), statechange_queue(), and unload_module().

Definition at line 286 of file app_queue.c.

Referenced by _sip_show_peers().

Lock for the state change queue

Definition at line 680 of file app_queue.c.

int montype_default = 0 [static]

queues.conf [general] option

Definition at line 273 of file app_queue.c.

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

Persistent Members astdb family.

Definition at line 259 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 4835 of file app_queue.c.

char qrm_cmd_usage[] [static]

Initial value:

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

Definition at line 4838 of file app_queue.c.

int queue_persistent_members = 0 [static]

queues.conf [general] option

Definition at line 264 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 4831 of file app_queue.c.

Definition at line 4075 of file app_queue.c.

Referenced by load_module(), and unload_module().

Definition at line 4085 of file app_queue.c.

Referenced by load_module(), and unload_module().

Definition at line 4103 of file app_queue.c.

Referenced by load_module(), and unload_module().

Definition at line 4094 of file app_queue.c.

Referenced by load_module(), and unload_module().

unsigned int stop

Set to 1 to stop the thread

Definition at line 676 of file app_queue.c.

Referenced by handle_controlstreamfile(), and queue_exec().

struct strategy strategies[] [static]

Referenced by int2strat(), and strat2int().

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

Definition at line 150 of file app_queue.c.

char* text

pthread_t thread

The device state monitoring thread

Definition at line 678 of file app_queue.c.

int use_weight = 0 [static]

queues.conf per-queue weight option

Definition at line 267 of file app_queue.c.


Generated on Tue Sep 30 01:19:42 2008 for Asterisk - the Open Source PBX by  doxygen 1.5.6