Mon Mar 31 07:38:37 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)
 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>:

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 2064 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)

Definition at line 143 of file app_queue.c.

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

#define RES_NOSUCHQUEUE   (-3)

Definition at line 145 of file app_queue.c.

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

#define RES_NOT_DYNAMIC   (-4)

Definition at line 146 of file app_queue.c.

Referenced by handle_queue_remove_member(), manager_remove_queue_member(), remove_from_queue(), and rqm_exec().

#define RES_OKAY   0

Definition at line 142 of file app_queue.c.

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

#define RES_OUTOFMEMORY   (-2)

Definition at line 144 of file app_queue.c.

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


Enumeration Type Documentation

anonymous enum

Enumerator:
QUEUE_STRATEGY_RINGALL 
QUEUE_STRATEGY_ROUNDROBIN 
QUEUE_STRATEGY_LEASTRECENT 
QUEUE_STRATEGY_FEWESTCALLS 
QUEUE_STRATEGY_RANDOM 
QUEUE_STRATEGY_RRMEMORY 

Definition at line 116 of file app_queue.c.

enum queue_member_status

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 };

enum queue_result

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 4279 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, s, call_queue::servicelevel, queue_ent::start, member::status, call_queue::strategy, and call_queue::weight.

Referenced by manager_queues_show(), and queue_show().

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

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

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

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

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

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

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

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

Definition at line 762 of file app_queue.c.

References ast_calloc, and ast_mutex_init().

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

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

Referenced by load_module().

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

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

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

Referenced by try_calling().

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

static void clear_and_free_interfaces ( void   )  [static]

Definition at line 935 of file app_queue.c.

References AST_LIST_LOCK, AST_LIST_REMOVE_HEAD, AST_LIST_UNLOCK, and free.

Referenced by unload_module().

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

static void clear_queue ( struct call_queue q  )  [static]

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

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

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

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

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

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

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

04422 {
04423    struct call_queue *q;
04424    char *ret = NULL;
04425    int which = 0;
04426    int wordlen = strlen(word);
04427    
04428    AST_LIST_LOCK(&queues);
04429    AST_LIST_TRAVERSE(&queues, q, list) {
04430       if (!strncasecmp(word, q->name, wordlen) && ++which > state) {
04431          ret = ast_strdup(q->name); 
04432          break;
04433       }
04434    }
04435    AST_LIST_UNLOCK(&queues);
04436 
04437    return ret;
04438 }

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

Definition at line 4715 of file app_queue.c.

References ast_malloc, ast_strdup, and complete_queue().

04716 {
04717    /* 0 - queue; 1 - add; 2 - member; 3 - <interface>; 4 - to; 5 - <queue>; 6 - penalty; 7 - <penalty>; 8 - as; 9 - <membername> */
04718    switch (pos) {
04719    case 3:  /* Don't attempt to complete name of interface (infinite possibilities) */
04720       return NULL;
04721    case 4:  /* only one possible match, "to" */
04722       return state == 0 ? ast_strdup("to") : NULL;
04723    case 5:  /* <queue> */
04724       return complete_queue(line, word, pos, state);
04725    case 6: /* only one possible match, "penalty" */
04726       return state == 0 ? ast_strdup("penalty") : NULL;
04727    case 7:
04728       if (state < 100) {   /* 0-99 */
04729          char *num;
04730          if ((num = ast_malloc(3))) {
04731             sprintf(num, "%d", state);
04732          }
04733          return num;
04734       } else {
04735          return NULL;
04736       }
04737    case 8: /* only one possible match, "as" */
04738       return state == 0 ? ast_strdup("as") : NULL;
04739    case 9:  /* Don't attempt to complete name of member (infinite possibilities) */
04740       return NULL;
04741    default:
04742       return NULL;
04743    }
04744 }

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

Definition at line 4781 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(), call_queue::lock, member::membername, and call_queue::members.

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

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

Definition at line 4440 of file app_queue.c.

References complete_queue().

04441 {
04442    if (pos == 2)
04443       return complete_queue(line, word, pos, state);
04444    return NULL;
04445 }

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]

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(), LOG_WARNING, and member::penalty.

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 1158 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(), and leave_queue().

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

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

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

Referenced by ring_entry().

01732 {
01733    o->stillgoing = 0;
01734    ast_hangup(o->chan);
01735    o->chan = NULL;
01736 }

static void dump_queue_members ( struct call_queue pm_queue  )  [static]

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

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

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

find the entry with the best metric, or NULL

Definition at line 1910 of file app_queue.c.

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

01911 {
01912    struct callattempt *best = NULL, *cur;
01913 
01914    for (cur = outgoing; cur; cur = cur->q_next) {
01915       if (cur->stillgoing &&              /* Not already done */
01916          !cur->chan &&              /* Isn't already going */
01917          (!best || cur->metric < best->metric)) {     /* We haven't found one yet, or it's better */
01918          best = cur;
01919       }
01920    }
01921 
01922    return best;
01923 }

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

Reload a single queue via realtime.

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

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

Definition at line 1169 of file app_queue.c.

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

Referenced by load_realtime_queue().

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

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

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

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

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, result, 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 4661 of file app_queue.c.

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

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

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

Definition at line 4746 of file app_queue.c.

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

04747 {
04748    char *queuename, *interface;
04749 
04750    if (argc != 6) {
04751       return RESULT_SHOWUSAGE;
04752    } else if (strcmp(argv[4], "from")) {
04753       return RESULT_SHOWUSAGE;
04754    }
04755 
04756    queuename = argv[5];
04757    interface = argv[3];
04758 
04759    switch (remove_from_queue(queuename, interface)) {
04760    case RES_OKAY:
04761       ast_queue_log(queuename, "CLI", interface, "REMOVEMEMBER", "%s", "");
04762       ast_cli(fd, "Removed interface '%s' from queue '%s'\n", interface, queuename);
04763       return RESULT_SUCCESS;
04764    case RES_EXISTS:
04765       ast_cli(fd, "Unable to remove interface '%s' from queue '%s': Not there\n", interface, queuename);
04766       return RESULT_FAILURE;
04767    case RES_NOSUCHQUEUE:
04768       ast_cli(fd, "Unable to remove interface from queue '%s': No such queue\n", queuename);
04769       return RESULT_FAILURE;
04770    case RES_OUTOFMEMORY:
04771       ast_cli(fd, "Out of memory\n");
04772       return RESULT_FAILURE;
04773    case RES_NOT_DYNAMIC:
04774       ast_cli(fd, "Member not dynamic\n");
04775       return RESULT_FAILURE;
04776    default:
04777       return RESULT_FAILURE;
04778    }
04779 }

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

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

Referenced by try_calling().

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

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->autofill = 0;
00826    q->joinempty = 0;
00827    q->leavewhenempty = 0;
00828    q->memberdelay = 0;
00829    q->maskmemberstatus = 0;
00830    q->eventwhencalled = 0;
00831    q->weight = 0;
00832    q->timeoutrestart = 0;
00833    if (!q->members)
00834       q->members = ao2_container_alloc(37, member_hash_fn, member_cmp_fn);
00835    q->membercount = 0;
00836    q->found = 1;
00837    ast_copy_string(q->sound_next, "queue-youarenext", sizeof(q->sound_next));
00838    ast_copy_string(q->sound_thereare, "queue-thereare", sizeof(q->sound_thereare));
00839    ast_copy_string(q->sound_calls, "queue-callswaiting", sizeof(q->sound_calls));
00840    ast_copy_string(q->sound_holdtime, "queue-holdtime", sizeof(q->sound_holdtime));
00841    ast_copy_string(q->sound_minutes, "queue-minutes", sizeof(q->sound_minutes));
00842    ast_copy_string(q->sound_seconds, "queue-seconds", sizeof(q->sound_seconds));
00843    ast_copy_string(q->sound_thanks, "queue-thankyou", sizeof(q->sound_thanks));
00844    ast_copy_string(q->sound_lessthan, "queue-less-than", sizeof(q->sound_lessthan));
00845    ast_copy_string(q->sound_reporthold, "queue-reporthold", sizeof(q->sound_reporthold));
00846    ast_copy_string(q->sound_periodicannounce[0], "queue-periodic-announce", sizeof(q->sound_periodicannounce[0]));
00847    for (i = 1; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) {
00848       q->sound_periodicannounce[i][0]='\0';
00849    }
00850 }

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

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

03071 {
03072    struct member *mem;
03073    struct ao2_iterator mem_iter;
03074 
03075    if (!q)
03076       return NULL;
03077 
03078    mem_iter = ao2_iterator_init(q->members, 0);
03079    while ((mem = ao2_iterator_next(&mem_iter))) {
03080       if (!strcasecmp(interface, mem->interface))
03081          return mem;
03082       ao2_ref(mem, -1);
03083    }
03084 
03085    return NULL;
03086 }

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

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

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

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 2320 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().

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

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

Definition at line 1403 of file app_queue.c.

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

Referenced by queue_exec().

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

static void leave_queue ( struct queue_ent qe  )  [static]

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

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

static int load_module ( void   )  [static]

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

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

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

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

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

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

Definition at line 4554 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(), RES_EXISTS, RES_NOSUCHQUEUE, RES_OKAY, RES_OUTOFMEMORY, and s.

Referenced by load_module().

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

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

Definition at line 4638 of file app_queue.c.

References ast_strlen_zero(), ast_true(), astman_get_header(), astman_send_ack(), astman_send_error(), s, and set_member_paused().

Referenced by load_module().

04639 {
04640    const char *queuename, *interface, *paused_s;
04641    int paused;
04642 
04643    interface = astman_get_header(m, "Interface");
04644    paused_s = astman_get_header(m, "Paused");
04645    queuename = astman_get_header(m, "Queue");   /* Optional - if not supplied, pause the given Interface in all queues */
04646 
04647    if (ast_strlen_zero(interface) || ast_strlen_zero(paused_s)) {
04648       astman_send_error(s, m, "Need 'Interface' and 'Paused' parameters.");
04649       return 0;
04650    }
04651 
04652    paused = abs(ast_true(paused_s));
04653 
04654    if (set_member_paused(queuename, interface, paused))
04655       astman_send_error(s, m, "Interface not found");
04656    else
04657       astman_send_ack(s, m, paused ? "Interface paused successfully" : "Interface unpaused successfully");
04658    return 0;
04659 }

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

Definition at line 4450 of file app_queue.c.

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

Referenced by load_module().

04451 {
04452    char *a[] = { "queue", "show" };
04453 
04454    __queues_show(s, 1, -1, 2, a);
04455    astman_append(s, "\r\n\r\n"); /* Properly terminate Manager output */
04456 
04457    return RESULT_SUCCESS;
04458 }

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

Definition at line 4461 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, S_OR, call_queue::servicelevel, queue_ent::start, member::status, and call_queue::weight.

Referenced by load_module().

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

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

Definition at line 4604 of file app_queue.c.

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

Referenced by load_module().

04605 {
04606    const char *queuename, *interface;
04607 
04608    queuename = astman_get_header(m, "Queue");
04609    interface = astman_get_header(m, "Interface");
04610 
04611    if (ast_strlen_zero(queuename) || ast_strlen_zero(interface)) {
04612       astman_send_error(s, m, "Need 'Queue' and 'Interface' parameters.");
04613       return 0;
04614    }
04615 
04616    switch (remove_from_queue(queuename, interface)) {
04617    case RES_OKAY:
04618       ast_queue_log(queuename, "MANAGER", interface, "REMOVEMEMBER", "%s", "");
04619       astman_send_ack(s, m, "Removed interface from queue");
04620       break;
04621    case RES_EXISTS:
04622       astman_send_error(s, m, "Unable to remove interface: Not there");
04623       break;
04624    case RES_NOSUCHQUEUE:
04625       astman_send_error(s, m, "Unable to remove interface from queue: No such queue");
04626       break;
04627    case RES_OUTOFMEMORY:
04628       astman_send_error(s, m, "Out of memory");
04629       break;
04630    case RES_NOT_DYNAMIC:
04631       astman_send_error(s, m, "Member not dynamic");
04632       break;
04633    }
04634 
04635    return 0;
04636 }

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

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

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

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

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

Definition at line 3383 of file app_queue.c.

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

Referenced by load_module().

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

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

Definition at line 3630 of file app_queue.c.

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

Referenced by load_module().

03631 {
03632    struct ast_module_user *u;
03633    char *parse;
03634 
03635    AST_DECLARE_APP_ARGS(args,
03636       AST_APP_ARG(queuename);
03637       AST_APP_ARG(uniqueid);
03638       AST_APP_ARG(membername);
03639       AST_APP_ARG(event);
03640       AST_APP_ARG(params);
03641    );
03642 
03643    if (ast_strlen_zero(data)) {
03644       ast_log(LOG_WARNING, "QueueLog requires arguments (queuename|uniqueid|membername|event[|additionalinfo]\n");
03645       return -1;
03646    }
03647 
03648    u = ast_module_user_add(chan);
03649 
03650    parse = ast_strdupa(data);
03651 
03652    AST_STANDARD_APP_ARGS(args, parse);
03653 
03654    if (ast_strlen_zero(args.queuename) || ast_strlen_zero(args.uniqueid)
03655        || ast_strlen_zero(args.membername) || ast_strlen_zero(args.event)) {
03656       ast_log(LOG_WARNING, "QueueLog requires arguments (queuename|uniqueid|membername|event[|additionalinfo])\n");
03657       ast_module_user_remove(u);
03658       return -1;
03659    }
03660 
03661    ast_queue_log(args.queuename, args.uniqueid, args.membername, args.event, 
03662       "%s", args.params ? args.params : "");
03663 
03664    ast_module_user_remove(u);
03665 
03666    return 0;
03667 }

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

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

Referenced by load_module().

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

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

Definition at line 3927 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.

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

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

Definition at line 4007 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.

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

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

Definition at line 3964 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.

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

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 952 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().

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

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

Definition at line 4416 of file app_queue.c.

References __queues_show().

Referenced by __queues_show().

04417 {
04418    return __queues_show(NULL, 0, fd, argc, argv);
04419 }

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

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

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

static void record_abandoned ( struct queue_ent qe  )  [static]

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

02032 {
02033    ast_mutex_lock(&qe->parent->lock);
02034    manager_event(EVENT_FLAG_AGENT, "QueueCallerAbandon",
02035       "Queue: %s\r\n"
02036       "Uniqueid: %s\r\n"
02037       "Position: %d\r\n"
02038       "OriginalPosition: %d\r\n"
02039       "HoldTime: %d\r\n",
02040       qe->parent->name, qe->chan->uniqueid, qe->pos, qe->opos, (int)(time(NULL) - qe->start));
02041 
02042    qe->parent->callsabandoned++;
02043    ast_mutex_unlock(&qe->parent->lock);
02044 }

static int reload ( void   )  [static]

Definition at line 4936 of file app_queue.c.

References reload_queues().

04937 {
04938    reload_queues();
04939    return 0;
04940 }

static void reload_queue_members ( void   )  [static]

Definition at line 3288 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, ast_db_entry::key, load_realtime_queue(), call_queue::lock, LOG_DEBUG, LOG_ERROR, LOG_NOTICE, LOG_WARNING, call_queue::name, ast_db_entry::next, option_debug, member::paused, member::penalty, PM_MAX_LEN, RES_OUTOFMEMORY, and strsep().

Referenced by load_module().

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

static int reload_queues ( void   )  [static]

Definition at line 4100 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_load(), AST_DECLARE_APP_ARGS, AST_LIST_INSERT_HEAD, AST_LIST_LOCK, AST_LIST_TRAVERSE, ast_log(), ast_mutex_lock(), ast_mutex_unlock(), AST_NONSTANDARD_APP_ARGS, ast_strlen_zero(), ast_true(), ast_variable_browse(), ast_variable_retrieve(), clear_queue(), create_queue_member(), call_queue::dead, member::delme, member::dynamic, call_queue::found, init_queue(), member::interface, call_queue::lock, LOG_NOTICE, LOG_WARNING, call_queue::membercount, call_queue::members, call_queue::name, parse(), member::paused, queue_set_param(), QUEUE_STRATEGY_ROUNDROBIN, call_queue::realtime, remove_from_interfaces(), rr_dep_warning(), call_queue::strategy, and var.

Referenced by load_module(), and reload().

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

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

Definition at line 912 of file app_queue.c.

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

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

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

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

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

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

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

References ast_channel::adsicpe, ast_channel::appl, ast_call(), ast_cdr_busy(), ast_channel_inherit_variables(), AST_DEVICE_NOT_INUSE, ast_device_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, ast_channel::macrocontext, ast_channel::macroexten, manager_event(), callattempt::member, member::membername, call_queue::name, ast_channel::nativeformats, option_debug, option_verbose, queue_ent::parent, member::paused, ast_channel::priority, QUEUE_EVENT_VARIABLES, call_queue::ringinuse, call_queue::rrpos, member::status, callattempt::stillgoing, update_status(), vars2manager(), VERBOSE_PREFIX_3, ast_channel::whentohangup, and call_queue::wrapuptime.

Referenced by ring_one().

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

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 1933 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().

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

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

02048 {
02049    if (option_verbose > 2)
02050       ast_verbose( VERBOSE_PREFIX_3 "Nobody picked up in %d ms\n", rnatime);
02051    ast_queue_log(qe->parent->name, qe->chan->uniqueid, membername, "RINGNOANSWER", "%d", rnatime);
02052    if (qe->parent->autopause) {
02053       if (!set_member_paused(qe->parent->name, interface, 1)) {
02054          if (option_verbose > 2)
02055             ast_verbose( VERBOSE_PREFIX_3 "Auto-Pausing Queue Member %s in queue %s since they failed to answer.\n", interface, qe->parent->name);
02056       } else {
02057          if (option_verbose > 2)
02058             ast_verbose( VERBOSE_PREFIX_3 "Failed to pause Queue Member %s in queue %s!\n", interface, qe->parent->name);
02059       }
02060    }
02061    return;
02062 }

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

Definition at line 3489 of file app_queue.c.

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

Referenced by load_module().

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

static void rr_dep_warning ( void   )  [static]

Definition at line 444 of file app_queue.c.

References ast_log(), and LOG_NOTICE.

Referenced by 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 1100 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 update_realtime_members().

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

static int say_periodic_announcement ( struct queue_ent qe  )  [static]

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

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

static int say_position ( struct queue_ent qe  )  [static]

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

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

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

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

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

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 queue_ent::chan, 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 
) [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 1965 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().

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

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

References ast_channel::_softhangup, ast_channel::_state, queue_ent::announce, ao2_iterator_init(), ao2_iterator_next(), ao2_ref(), app, 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, free, queue_ent::handled, hangupcalls(), ast_datastore::inheritance, member::interface, member::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, 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, QUEUE_EVENT_VARIABLES, QUEUE_STRATEGY_ROUNDROBIN, 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, 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().

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

static int unload_module ( void   )  [static]

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

04864 {
04865    int res;
04866 
04867    if (device_state.thread != AST_PTHREADT_NULL) {
04868       device_state.stop = 1;
04869       ast_mutex_lock(&device_state.lock);
04870       ast_cond_signal(&device_state.cond);
04871       ast_mutex_unlock(&device_state.lock);
04872       pthread_join(device_state.thread, NULL);
04873    }
04874 
04875    ast_cli_unregister_multiple(cli_queue, sizeof(cli_queue) / sizeof(struct ast_cli_entry));
04876    res = ast_manager_unregister("QueueStatus");
04877    res |= ast_manager_unregister("Queues");
04878    res |= ast_manager_unregister("QueueStatus");
04879    res |= ast_manager_unregister("QueueAdd");
04880    res |= ast_manager_unregister("QueueRemove");
04881    res |= ast_manager_unregister("QueuePause");
04882    res |= ast_unregister_application(app_aqm);
04883    res |= ast_unregister_application(app_rqm);
04884    res |= ast_unregister_application(app_pqm);
04885    res |= ast_unregister_application(app_upqm);
04886    res |= ast_unregister_application(app_ql);
04887    res |= ast_unregister_application(app);
04888    res |= ast_custom_function_unregister(&queueagentcount_function);
04889    res |= ast_custom_function_unregister(&queuemembercount_function);
04890    res |= ast_custom_function_unregister(&queuememberlist_function);
04891    res |= ast_custom_function_unregister(&queuewaitingcount_function);
04892    ast_devstate_del(statechange_queue, NULL);
04893 
04894    ast_module_user_hangup_all();
04895 
04896    clear_and_free_interfaces();
04897 
04898    return res;
04899 }

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

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

02463 {
02464    ast_mutex_lock(&q->lock);
02465    time(&member->lastcall);
02466    member->calls++;
02467    q->callscompleted++;
02468    if (callcompletedinsl)
02469       q->callscompletedinsl++;
02470    ast_mutex_unlock(&q->lock);
02471    return 0;
02472 }

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

Definition at line 1287 of file app_queue.c.

References ast_load_realtime(), ast_strlen_zero(), ast_update_realtime(), member::interface, and var.

Referenced by set_member_paused().

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

static void update_realtime_members ( struct call_queue q  )  [static]

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

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

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

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

Referenced by load_module().

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

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

Definition at line 1483 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(), and wait_our_turn().

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

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

Definition at line 1738 of file app_queue.c.

References pbx_builtin_serialize_variables().

Referenced by ring_entry(), and try_calling().

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

static int wait_a_bit ( struct queue_ent qe  )  [static]

Definition at line 3058 of file app_queue.c.

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

Referenced by queue_exec().

03059 {
03060    /* Don't need to hold the lock while we setup the outgoing calls */
03061    int retrywait = qe->parent->retry * 1000;
03062 
03063    int res = ast_waitfordigit(qe->chan, retrywait);
03064    if (res > 0 && !valid_exit(qe, res))
03065       res = 0;
03066 
03067    return res;
03068 }

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]

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

References AST_MAX_WATCHERS, callattempt::call_next, callattempt::chan, queue_ent::chan, f, call_queue::name, queue_ent::parent, callattempt::q_next, QUEUE_STRATEGY_RINGALL, ring_one(), starttime, callattempt::stillgoing, and call_queue::strategy.

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

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

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

Referenced by queue_exec().

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


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.

struct ast_cli_entry cli_add_queue_member_deprecated [static]

Initial value:

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

Definition at line 4834 of file app_queue.c.

struct ast_cli_entry cli_queue[] [static]

Definition at line 4844 of file app_queue.c.

Referenced by load_module(), and unload_module().

struct ast_cli_entry cli_remove_queue_member_deprecated [static]

Initial value:

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

Definition at line 4839 of file app_queue.c.

struct ast_cli_entry cli_show_queue_deprecated [static]

Initial value:

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

Definition at line 4829 of file app_queue.c.

ast_cond_t cond

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().

enum queue_result id

Definition at line 286 of file app_queue.c.

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

ast_mutex_t lock

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

char qrm_cmd_usage[] [static]

Initial value:

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

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

struct ast_custom_function queueagentcount_function [static]

Definition at line 4063 of file app_queue.c.

Referenced by load_module(), and unload_module().

struct ast_custom_function queuemembercount_function [static]

Definition at line 4073 of file app_queue.c.

Referenced by load_module(), and unload_module().

struct ast_custom_function queuememberlist_function [static]

Definition at line 4091 of file app_queue.c.

Referenced by load_module(), and unload_module().

struct ast_custom_function queuewaitingcount_function [static]

Definition at line 4082 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

Definition at line 287 of file app_queue.c.

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

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 Mon Mar 31 07:38:39 2008 for Asterisk - the Open Source PBX by  doxygen 1.5.1