Wed Feb 11 12:00:04 2009

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"

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  interfaces
struct  member
struct  member_interface
struct  queue_ent
struct  queue_transfer_ds
struct  queues
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  qmc_status {
  QMC_VALID = 0, QMC_PAUSED, QMC_ACTIVE, QMC_FREE,
  QMC_ALL
}
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 void __reg_module (void)
static void __unreg_module (void)
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 int attended_transfer_occurred (struct ast_channel *chan)
 mechanism to tell if a queue caller was atxferred by a queue member.
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_queuemembercount (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 queue_transfer_destroy (void *data)
static void queue_transfer_fixup (void *data, struct ast_channel *old_chan, struct ast_channel *new_chan)
 Log an attended transfer when a queue caller channel is masqueraded.
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 struct ast_datastoresetup_transfer_datastore (struct queue_ent *qe, struct member *member, time_t starttime, int callcompletedinsl)
 create a datastore for storing relevant info to log attended transfers in the queue_log
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 struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT | AST_MODFLAG_BUILDSUM, .description = "True Call Queueing" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "f450f61f60e761b3aa089ebed76ca8a5" , .load = load_module, .unload = unload_module, .reload = reload, }
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 const struct ast_module_infoast_module_info = &__mod_info
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
   struct {
      statechange *   first
      statechange *   last
   }   state_change_q
   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_debug = 0
 queues.conf [general] extra debug option
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_datastore_info queue_transfer_info
 a datastore used to help correctly log attended transfers of queue callers
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 386 of file app_queue.c.

Referenced by queue_set_param().

#define ANNOUNCEHOLDTIME_ONCE   2

Definition at line 387 of file app_queue.c.

Referenced by queue_set_param(), and say_position().

#define AST_MAX_WATCHERS   256

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

Referenced by dump_queue_members(), and reload_queue_members().

#define QUEUE_EMPTY_NORMAL   1

Definition at line 384 of file app_queue.c.

Referenced by queue_set_param().

#define QUEUE_EMPTY_STRICT   2

Definition at line 385 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 388 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 qmc_status

Enumerator:
QMC_VALID 
QMC_PAUSED 
QMC_ACTIVE 
QMC_FREE 
QMC_ALL 

Definition at line 4166 of file app_queue.c.

04166                 {
04167    QMC_VALID = 0, /* Count valid members */
04168    QMC_PAUSED,    /* Count paused members */
04169    QMC_ACTIVE,    /* Count active members */
04170    QMC_FREE,      /* Count free members */
04171    QMC_ALL        /* Count all queue members */
04172 };

enum queue_member_status

Enumerator:
QUEUE_NO_MEMBERS 
QUEUE_NO_REACHABLE_MEMBERS 
QUEUE_NORMAL 

Definition at line 539 of file app_queue.c.

00539                          {
00540    QUEUE_NO_MEMBERS,
00541    QUEUE_NO_REACHABLE_MEMBERS,
00542    QUEUE_NORMAL
00543 };

enum queue_result

Enumerator:
QUEUE_UNKNOWN 
QUEUE_TIMEOUT 
QUEUE_JOINEMPTY 
QUEUE_LEAVEEMPTY 
QUEUE_JOINUNAVAIL 
QUEUE_LEAVEUNAVAIL 
QUEUE_FULL 

Definition at line 290 of file app_queue.c.

00290                   {
00291    QUEUE_UNKNOWN = 0,
00292    QUEUE_TIMEOUT = 1,
00293    QUEUE_JOINEMPTY = 2,
00294    QUEUE_LEAVEEMPTY = 3,
00295    QUEUE_JOINUNAVAIL = 4,
00296    QUEUE_LEAVEUNAVAIL = 5,
00297    QUEUE_FULL = 6,
00298 };


Function Documentation

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

Definition at line 4583 of file app_queue.c.

References ao2_container_count(), ao2_iterator_init(), ao2_iterator_next(), ao2_ref(), ast_build_string(), ast_category_browse(), ast_check_realtime(), ast_cli(), ast_config_destroy(), AST_LIST_EMPTY, AST_LIST_LOCK, AST_LIST_NEXT, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_load_realtime_multientry(), ast_mutex_lock(), ast_mutex_unlock(), ast_strlen_zero(), astman_append(), member::calls, call_queue::callsabandoned, call_queue::callscompleted, call_queue::callscompletedinsl, queue_ent::chan, call_queue::count, devstate2str(), member::dynamic, call_queue::head, call_queue::holdtime, int2strat(), member::interface, member::lastcall, load_realtime_queue(), call_queue::lock, call_queue::maxlen, member::membername, call_queue::members, ast_channel::name, call_queue::name, queue_ent::next, member::paused, member::penalty, queue_ent::prio, queue_show(), member::realtime, RESULT_SHOWUSAGE, RESULT_SUCCESS, call_queue::ringlimit, s, call_queue::servicelevel, queue_ent::start, member::status, call_queue::strategy, and call_queue::weight.

Referenced by manager_queues_show(), and queue_show().

04584 {
04585    struct call_queue *q;
04586    struct queue_ent *qe;
04587    struct member *mem;
04588    int pos, queue_show;
04589    time_t now;
04590    char max_buf[150];
04591    char *max;
04592    size_t max_left;
04593    float sl = 0;
04594    char *term = manager ? "\r\n" : "\n";
04595    struct ao2_iterator mem_iter;
04596 
04597    time(&now);
04598    if (argc == 2)
04599       queue_show = 0;
04600    else if (argc == 3)
04601       queue_show = 1;
04602    else
04603       return RESULT_SHOWUSAGE;
04604 
04605    /* We only want to load realtime queues when a specific queue is asked for. */
04606    if (queue_show) {
04607       load_realtime_queue(argv[2]);
04608    } else if (ast_check_realtime("queues")) {
04609       struct ast_config *cfg = ast_load_realtime_multientry("queues", "name LIKE", "%", (char *) NULL);
04610       char *queuename;
04611       if (cfg) {
04612          for (queuename = ast_category_browse(cfg, NULL); !ast_strlen_zero(queuename); queuename = ast_category_browse(cfg, queuename)) {
04613             load_realtime_queue(queuename);
04614          }
04615          ast_config_destroy(cfg);
04616       }
04617    }
04618 
04619    AST_LIST_LOCK(&queues);
04620    if (AST_LIST_EMPTY(&queues)) {
04621       AST_LIST_UNLOCK(&queues);
04622       if (queue_show) {
04623          if (s)
04624             astman_append(s, "No such queue: %s.%s",argv[2], term);
04625          else
04626             ast_cli(fd, "No such queue: %s.%s",argv[2], term);
04627       } else {
04628          if (s)
04629             astman_append(s, "No queues.%s", term);
04630          else
04631             ast_cli(fd, "No queues.%s", term);
04632       }
04633       return RESULT_SUCCESS;
04634    }
04635    AST_LIST_TRAVERSE(&queues, q, list) {
04636       ast_mutex_lock(&q->lock);
04637       if (queue_show) {
04638          if (strcasecmp(q->name, argv[2]) != 0) {
04639             ast_mutex_unlock(&q->lock);
04640             if (!AST_LIST_NEXT(q, list)) {
04641                ast_cli(fd, "No such queue: %s.%s",argv[2], term);
04642                break;
04643             }
04644             continue;
04645          }
04646       }
04647       max_buf[0] = '\0';
04648       max = max_buf;
04649       max_left = sizeof(max_buf);
04650       if (q->maxlen)
04651          ast_build_string(&max, &max_left, "%d", q->maxlen);
04652       else
04653          ast_build_string(&max, &max_left, "unlimited");
04654       sl = 0;
04655       if (q->callscompleted > 0)
04656          sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
04657       if (s)
04658          astman_append(s, "%-12.12s has %d calls (max %s) in '%s' strategy (%ds holdtime), R:%d, W:%d, C:%d, A:%d, SL:%2.1f%% within %ds%s",
04659                               q->name, q->count, max_buf, int2strat(q->strategy), q->holdtime, q->ringlimit,
04660                               q->weight, q->callscompleted, q->callsabandoned, sl, q->servicelevel, term);
04661       else
04662          ast_cli(fd, "%-12.12s has %d calls (max %s) in '%s' strategy (%ds holdtime), R:%d, W:%d, C:%d, A:%d, SL:%2.1f%% within %ds%s",
04663                      q->name, q->count, max_buf, int2strat(q->strategy), q->holdtime, q->ringlimit,
04664                      q->weight, q->callscompleted, q->callsabandoned, sl, q->servicelevel, term);
04665       if (ao2_container_count(q->members)) {
04666          if (s)
04667             astman_append(s, "   Members: %s", term);
04668          else
04669             ast_cli(fd, "   Members: %s", term);
04670          mem_iter = ao2_iterator_init(q->members, 0);
04671          while ((mem = ao2_iterator_next(&mem_iter))) {
04672             max_buf[0] = '\0';
04673             max = max_buf;
04674             max_left = sizeof(max_buf);
04675             if (strcasecmp(mem->membername, mem->interface)) {
04676                ast_build_string(&max, &max_left, " (%s)", mem->interface);
04677             }
04678             if (mem->penalty)
04679                ast_build_string(&max, &max_left, " with penalty %d", mem->penalty);
04680             if (mem->dynamic)
04681                ast_build_string(&max, &max_left, " (dynamic)");
04682             if (mem->realtime)
04683                ast_build_string(&max, &max_left, " (realtime)");
04684             if (mem->paused)
04685                ast_build_string(&max, &max_left, " (paused)");
04686             ast_build_string(&max, &max_left, " (%s)", devstate2str(mem->status));
04687             if (mem->calls) {
04688                ast_build_string(&max, &max_left, " has taken %d calls (last was %ld secs ago)",
04689                   mem->calls, (long) (time(NULL) - mem->lastcall));
04690             } else
04691                ast_build_string(&max, &max_left, " has taken no calls yet");
04692             if (s)
04693                astman_append(s, "      %s%s%s", mem->membername, max_buf, term);
04694             else
04695                ast_cli(fd, "      %s%s%s", mem->membername, max_buf, term);
04696             ao2_ref(mem, -1);
04697          }
04698       } else if (s)
04699          astman_append(s, "   No Members%s", term);
04700       else  
04701          ast_cli(fd, "   No Members%s", term);
04702       if (q->head) {
04703          pos = 1;
04704          if (s)
04705             astman_append(s, "   Callers: %s", term);
04706          else
04707             ast_cli(fd, "   Callers: %s", term);
04708          for (qe = q->head; qe; qe = qe->next) {
04709             if (s)
04710                astman_append(s, "      %d. %s (wait: %ld:%2.2ld, prio: %d)%s",
04711                   pos++, qe->chan->name, (long) (now - qe->start) / 60,
04712                   (long) (now - qe->start) % 60, qe->prio, term);
04713             else
04714                ast_cli(fd, "      %d. %s (wait: %ld:%2.2ld, prio: %d)%s", pos++,
04715                   qe->chan->name, (long) (now - qe->start) / 60,
04716                   (long) (now - qe->start) % 60, qe->prio, term);
04717          }
04718       } else if (s)
04719          astman_append(s, "   No Callers%s", term);
04720       else
04721          ast_cli(fd, "   No Callers%s", term);
04722       if (s)
04723          astman_append(s, "%s", term);
04724       else
04725          ast_cli(fd, "%s", term);
04726       ast_mutex_unlock(&q->lock);
04727       if (queue_show)
04728          break;
04729    }
04730    AST_LIST_UNLOCK(&queues);
04731    return RESULT_SUCCESS;
04732 }

static void __reg_module ( void   )  [static]

Definition at line 5264 of file app_queue.c.

static void __unreg_module ( void   )  [static]

Definition at line 5264 of file app_queue.c.

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

Definition at line 881 of file app_queue.c.

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

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

00882 {
00883    struct member_interface *curint;
00884 
00885    AST_LIST_LOCK(&interfaces);
00886    AST_LIST_TRAVERSE(&interfaces, curint, list) {
00887       if (!strcasecmp(curint->interface, interface))
00888          break;
00889    }
00890 
00891    if (curint) {
00892       AST_LIST_UNLOCK(&interfaces);
00893       return 0;
00894    }
00895 
00896    if (option_debug)
00897       ast_log(LOG_DEBUG, "Adding %s to the list of interfaces that make up all of our queue members.\n", interface);
00898    
00899    if ((curint = ast_calloc(1, sizeof(*curint)))) {
00900       ast_copy_string(curint->interface, interface, sizeof(curint->interface));
00901       AST_LIST_INSERT_HEAD(&interfaces, curint, list);
00902    }
00903    AST_LIST_UNLOCK(&interfaces);
00904 
00905    return 0;
00906 }

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

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

03402 {
03403    struct call_queue *q;
03404    struct member *new_member, *old_member;
03405    int res = RES_NOSUCHQUEUE;
03406 
03407    /* \note Ensure the appropriate realtime queue is loaded.  Note that this
03408     * short-circuits if the queue is already in memory. */
03409    if (!(q = load_realtime_queue(queuename)))
03410       return res;
03411 
03412    AST_LIST_LOCK(&queues);
03413 
03414    ast_mutex_lock(&q->lock);
03415    if ((old_member = interface_exists(q, interface)) == NULL) {
03416       add_to_interfaces(interface);
03417       if ((new_member = create_queue_member(interface, membername, penalty, paused))) {
03418          new_member->dynamic = 1;
03419          ao2_link(q->members, new_member);
03420          q->membercount++;
03421          manager_event(EVENT_FLAG_AGENT, "QueueMemberAdded",
03422             "Queue: %s\r\n"
03423             "Location: %s\r\n"
03424             "MemberName: %s\r\n"
03425             "Membership: %s\r\n"
03426             "Penalty: %d\r\n"
03427             "CallsTaken: %d\r\n"
03428             "LastCall: %d\r\n"
03429             "Status: %d\r\n"
03430             "Paused: %d\r\n",
03431             q->name, new_member->interface, new_member->membername,
03432             "dynamic",
03433             new_member->penalty, new_member->calls, (int) new_member->lastcall,
03434             new_member->status, new_member->paused);
03435          
03436          ao2_ref(new_member, -1);
03437          new_member = NULL;
03438 
03439          if (dump)
03440             dump_queue_members(q);
03441          
03442          res = RES_OKAY;
03443       } else {
03444          res = RES_OUTOFMEMORY;
03445       }
03446    } else {
03447       ao2_ref(old_member, -1);
03448       res = RES_EXISTS;
03449    }
03450    ast_mutex_unlock(&q->lock);
03451    AST_LIST_UNLOCK(&queues);
03452 
03453    return res;
03454 }

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

Definition at line 782 of file app_queue.c.

References ast_calloc, ast_copy_string(), and ast_mutex_init().

Referenced by find_queue_by_name_rt(), and reload_queues().

00783 {
00784    struct call_queue *q;
00785 
00786    if ((q = ast_calloc(1, sizeof(*q)))) {
00787       ast_mutex_init(&q->lock);
00788       ast_copy_string(q->name, queuename, sizeof(q->name));
00789    }
00790    return q;
00791 }

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

Definition at line 3780 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, ast_channel::name, parse(), pbx_builtin_setvar_helper(), RES_EXISTS, RES_NOSUCHQUEUE, RES_OKAY, RES_OUTOFMEMORY, and ast_channel::uniqueid.

Referenced by load_module().

03781 {
03782    int res=-1;
03783    struct ast_module_user *lu;
03784    char *parse, *temppos = NULL;
03785    int priority_jump = 0;
03786    AST_DECLARE_APP_ARGS(args,
03787       AST_APP_ARG(queuename);
03788       AST_APP_ARG(interface);
03789       AST_APP_ARG(penalty);
03790       AST_APP_ARG(options);
03791       AST_APP_ARG(membername);
03792    );
03793    int penalty = 0;
03794 
03795    if (ast_strlen_zero(data)) {
03796       ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename[|[interface]|[penalty][|options][|membername]])\n");
03797       return -1;
03798    }
03799 
03800    parse = ast_strdupa(data);
03801 
03802    AST_STANDARD_APP_ARGS(args, parse);
03803 
03804    lu = ast_module_user_add(chan);
03805 
03806    if (ast_strlen_zero(args.interface)) {
03807       args.interface = ast_strdupa(chan->name);
03808       temppos = strrchr(args.interface, '-');
03809       if (temppos)
03810          *temppos = '\0';
03811    }
03812 
03813    if (!ast_strlen_zero(args.penalty)) {
03814       if ((sscanf(args.penalty, "%d", &penalty) != 1) || penalty < 0) {
03815          ast_log(LOG_WARNING, "Penalty '%s' is invalid, must be an integer >= 0\n", args.penalty);
03816          penalty = 0;
03817       }
03818    }
03819    
03820    if (args.options) {
03821       if (strchr(args.options, 'j'))
03822          priority_jump = 1;
03823    }
03824 
03825    switch (add_to_queue(args.queuename, args.interface, args.membername, penalty, 0, queue_persistent_members)) {
03826    case RES_OKAY:
03827       ast_queue_log(args.queuename, chan->uniqueid, args.interface, "ADDMEMBER", "%s", "");
03828       ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", args.interface, args.queuename);
03829       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "ADDED");
03830       res = 0;
03831       break;
03832    case RES_EXISTS:
03833       ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': Already there\n", args.interface, args.queuename);
03834       if (priority_jump || ast_opt_priority_jumping)
03835          ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
03836       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "MEMBERALREADY");
03837       res = 0;
03838       break;
03839    case RES_NOSUCHQUEUE:
03840       ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", args.queuename);
03841       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "NOSUCHQUEUE");
03842       res = 0;
03843       break;
03844    case RES_OUTOFMEMORY:
03845       ast_log(LOG_ERROR, "Out of memory adding member %s to queue %s\n", args.interface, args.queuename);
03846       break;
03847    }
03848 
03849    ast_module_user_remove(lu);
03850 
03851    return res;
03852 }

static int attended_transfer_occurred ( struct ast_channel chan  )  [static]

mechanism to tell if a queue caller was atxferred by a queue member.

When a caller is atxferred, then the queue_transfer_info datastore is removed from the channel. If it's still there after the bridge is broken, then the caller was not atxferred.

Note:
Only call this with chan locked

Definition at line 2665 of file app_queue.c.

References ast_channel_datastore_find(), and queue_transfer_info.

Referenced by try_calling().

02666 {
02667    return ast_channel_datastore_find(chan, &queue_transfer_info, NULL) ? 0 : 1;
02668 }

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

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

Referenced by try_calling().

02544 {
02545    if (qe->max_penalty && (mem->penalty > qe->max_penalty))
02546       return -1;
02547 
02548    switch (q->strategy) {
02549    case QUEUE_STRATEGY_RINGALL:
02550       /* Everyone equal, except for penalty */
02551       tmp->metric = mem->penalty * 1000000;
02552       break;
02553    case QUEUE_STRATEGY_ROUNDROBIN:
02554       if (!pos) {
02555          if (!q->wrapped) {
02556             /* No more channels, start over */
02557             q->rrpos = 0;
02558          } else {
02559             /* Prioritize next entry */
02560             q->rrpos++;
02561          }
02562          q->wrapped = 0;
02563       }
02564       /* Fall through */
02565    case QUEUE_STRATEGY_RRMEMORY:
02566       if (pos < q->rrpos) {
02567          tmp->metric = 1000 + pos;
02568       } else {
02569          if (pos > q->rrpos)
02570             /* Indicate there is another priority */
02571             q->wrapped = 1;
02572          tmp->metric = pos;
02573       }
02574       tmp->metric += mem->penalty * 1000000;
02575       break;
02576    case QUEUE_STRATEGY_RANDOM:
02577       tmp->metric = ast_random() % 1000;
02578       tmp->metric += mem->penalty * 1000000;
02579       break;
02580    case QUEUE_STRATEGY_FEWESTCALLS:
02581       tmp->metric = mem->calls;
02582       tmp->metric += mem->penalty * 1000000;
02583       break;
02584    case QUEUE_STRATEGY_LEASTRECENT:
02585       if (!mem->lastcall)
02586          tmp->metric = 0;
02587       else
02588          tmp->metric = 1000000 - (time(NULL) - mem->lastcall);
02589       tmp->metric += mem->penalty * 1000000;
02590       break;
02591    default:
02592       ast_log(LOG_WARNING, "Can't calculate metric for unknown strategy %d\n", q->strategy);
02593       break;
02594    }
02595    if (q->ringlimit && (mem->ringcount >= q->ringlimit)) {
02596       tmp->metric += (mem->ringcount / q->ringlimit) * 10000000;
02597    }
02598    if (option_debug)
02599       ast_log(LOG_DEBUG, "New metric %d for member %s with %d rings (limit %d)\n", 
02600                   tmp->metric, mem->interface, mem->ringcount, q->ringlimit);
02601    return 0;
02602 }

static void clear_and_free_interfaces ( void   )  [static]

Definition at line 955 of file app_queue.c.

References AST_LIST_LOCK, AST_LIST_REMOVE_HEAD, AST_LIST_UNLOCK, free, and member_interface::list.

Referenced by unload_module().

00956 {
00957    struct member_interface *curint;
00958 
00959    AST_LIST_LOCK(&interfaces);
00960    while ((curint = AST_LIST_REMOVE_HEAD(&interfaces, list)))
00961       free(curint);
00962    AST_LIST_UNLOCK(&interfaces);
00963 }

static void clear_queue ( struct call_queue q  )  [static]

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

00873 {
00874    q->holdtime = 0;
00875    q->callscompleted = 0;
00876    q->callsabandoned = 0;
00877    q->callscompletedinsl = 0;
00878    q->wrapuptime = 0;
00879 }

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

Definition at line 1732 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, member_interface::list, call_queue::lock, LOG_DEBUG, call_queue::members, call_queue::name, and call_queue::weight.

Referenced by ring_entry().

01733 {
01734    struct call_queue *q;
01735    struct member *mem;
01736    int found = 0;
01737    
01738    /* &qlock and &rq->lock already set by try_calling()
01739     * to solve deadlock */
01740    AST_LIST_TRAVERSE(&queues, q, list) {
01741       if (q == rq) /* don't check myself, could deadlock */
01742          continue;
01743       ast_mutex_lock(&q->lock);
01744       if (q->count && q->members) {
01745          if ((mem = ao2_find(q->members, member, OBJ_POINTER))) {
01746             ast_log(LOG_DEBUG, "Found matching member %s in queue '%s'\n", mem->interface, q->name);
01747             if (q->weight > rq->weight) {
01748                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);
01749                found = 1;
01750             }
01751             ao2_ref(mem, -1);
01752          }
01753       }
01754       ast_mutex_unlock(&q->lock);
01755       if (found)
01756          break;
01757    }
01758    return found;
01759 }

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

Definition at line 4739 of file app_queue.c.

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

Referenced by complete_queue_add_member(), complete_queue_remove_member(), and complete_queue_show().

04740 {
04741    struct call_queue *q;
04742    char *ret = NULL;
04743    int which = 0;
04744    int wordlen = strlen(word);
04745    
04746    AST_LIST_LOCK(&queues);
04747    AST_LIST_TRAVERSE(&queues, q, list) {
04748       if (!strncasecmp(word, q->name, wordlen) && ++which > state) {
04749          ret = ast_strdup(q->name); 
04750          break;
04751       }
04752    }
04753    AST_LIST_UNLOCK(&queues);
04754 
04755    return ret;
04756 }

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

Definition at line 5034 of file app_queue.c.

References ast_malloc, ast_strdup, and complete_queue().

05035 {
05036    /* 0 - queue; 1 - add; 2 - member; 3 - <interface>; 4 - to; 5 - <queue>; 6 - penalty; 7 - <penalty>; 8 - as; 9 - <membername> */
05037    switch (pos) {
05038    case 3:  /* Don't attempt to complete name of interface (infinite possibilities) */
05039       return NULL;
05040    case 4:  /* only one possible match, "to" */
05041       return state == 0 ? ast_strdup("to") : NULL;
05042    case 5:  /* <queue> */
05043       return complete_queue(line, word, pos, state);
05044    case 6: /* only one possible match, "penalty" */
05045       return state == 0 ? ast_strdup("penalty") : NULL;
05046    case 7:
05047       if (state < 100) {   /* 0-99 */
05048          char *num;
05049          if ((num = ast_malloc(3))) {
05050             sprintf(num, "%d", state);
05051          }
05052          return num;
05053       } else {
05054          return NULL;
05055       }
05056    case 8: /* only one possible match, "as" */
05057       return state == 0 ? ast_strdup("as") : NULL;
05058    case 9:  /* Don't attempt to complete name of member (infinite possibilities) */
05059       return NULL;
05060    default:
05061       return NULL;
05062    }
05063 }

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

Definition at line 5100 of file app_queue.c.

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

05101 {
05102    int which = 0;
05103    struct call_queue *q;
05104    struct member *m;
05105    struct ao2_iterator mem_iter;
05106 
05107    /* 0 - queue; 1 - remove; 2 - member; 3 - <member>; 4 - from; 5 - <queue> */
05108    if (pos > 5 || pos < 3)
05109       return NULL;
05110    if (pos == 4)  /* only one possible match, 'from' */
05111       return state == 0 ? ast_strdup("from") : NULL;
05112 
05113    if (pos == 5)  /* No need to duplicate code */
05114       return complete_queue(line, word, pos, state);
05115 
05116    /* here is the case for 3, <member> */
05117    if (!AST_LIST_EMPTY(&queues)) { /* XXX unnecessary ? the traverse does that for us */
05118       AST_LIST_TRAVERSE(&queues, q, list) {
05119          ast_mutex_lock(&q->lock);
05120          mem_iter = ao2_iterator_init(q->members, 0);
05121          while ((m = ao2_iterator_next(&mem_iter))) {
05122             if (++which > state) {
05123                char *tmp;
05124                ast_mutex_unlock(&q->lock);
05125                tmp = ast_strdup(m->interface);
05126                ao2_ref(m, -1);
05127                return tmp;
05128             }
05129             ao2_ref(m, -1);
05130          }
05131          ast_mutex_unlock(&q->lock);
05132       }
05133    }
05134 
05135    return NULL;
05136 }

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

Definition at line 4758 of file app_queue.c.

References complete_queue().

04759 {
04760    if (pos == 2)
04761       return complete_queue(line, word, pos, state);
04762    return NULL;
04763 }

static int compress_char ( const char  c  )  [static]

Definition at line 793 of file app_queue.c.

00794 {
00795    if (c < 32)
00796       return 0;
00797    else if (c > 96)
00798       return c - 64;
00799    else
00800       return c - 32;
00801 }

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

References ao2_alloc(), ast_copy_string(), 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().

00763 {
00764    struct member *cur;
00765    
00766    if ((cur = ao2_alloc(sizeof(*cur), NULL))) {
00767       cur->penalty = penalty;
00768       cur->paused = paused;
00769       ast_copy_string(cur->interface, interface, sizeof(cur->interface));
00770       if (!ast_strlen_zero(membername))
00771          ast_copy_string(cur->membername, membername, sizeof(cur->membername));
00772       else
00773          ast_copy_string(cur->membername, interface, sizeof(cur->membername));
00774       if (!strchr(cur->interface, '/'))
00775          ast_log(LOG_WARNING, "No location at interface '%s'\n", interface);
00776       cur->status = ast_device_state(interface);
00777    }
00778 
00779    return cur;
00780 }

static void destroy_queue ( struct call_queue q  )  [static]

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

01183 {
01184    free_members(q, 1);
01185    ast_mutex_destroy(&q->lock);
01186    ao2_ref(q->members, -1);
01187    free(q);
01188 }

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

Consumer of the statechange queue.

Definition at line 710 of file app_queue.c.

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

Referenced by load_module().

00711 {
00712    struct statechange *sc = NULL;
00713 
00714    while (!device_state.stop) {
00715       ast_mutex_lock(&device_state.lock);
00716       if (!(sc = AST_LIST_REMOVE_HEAD(&device_state.state_change_q, entry))) {
00717          ast_cond_wait(&device_state.cond, &device_state.lock);
00718          sc = AST_LIST_REMOVE_HEAD(&device_state.state_change_q, entry);
00719       }
00720       ast_mutex_unlock(&device_state.lock);
00721 
00722       /* Check to see if we were woken up to see the request to stop */
00723       if (device_state.stop)
00724          break;
00725 
00726       if (!sc)
00727          continue;
00728 
00729       handle_statechange(sc);
00730 
00731       free(sc);
00732       sc = NULL;
00733    }
00734 
00735    if (sc)
00736       free(sc);
00737 
00738    while ((sc = AST_LIST_REMOVE_HEAD(&device_state.state_change_q, entry)))
00739       free(sc);
00740 
00741    return NULL;
00742 }

static void do_hang ( struct callattempt o  )  [static]

common hangup actions

Definition at line 1762 of file app_queue.c.

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

Referenced by ring_entry().

01763 {
01764    o->stillgoing = 0;
01765    ast_hangup(o->chan);
01766    o->chan = NULL;
01767 }

static void dump_queue_members ( struct call_queue pm_queue  )  [static]

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

03309 {
03310    struct member *cur_member;
03311    char value[PM_MAX_LEN];
03312    int value_len = 0;
03313    int res;
03314    struct ao2_iterator mem_iter;
03315 
03316    memset(value, 0, sizeof(value));
03317 
03318    if (!pm_queue)
03319       return;
03320 
03321    mem_iter = ao2_iterator_init(pm_queue->members, 0);
03322    while ((cur_member = ao2_iterator_next(&mem_iter))) {
03323       if (!cur_member->dynamic) {
03324          ao2_ref(cur_member, -1);
03325          continue;
03326       }
03327 
03328       res = snprintf(value + value_len, sizeof(value) - value_len, "%s%s;%d;%d;%s",
03329          value_len ? "|" : "", cur_member->interface, cur_member->penalty, cur_member->paused, cur_member->membername);
03330 
03331       ao2_ref(cur_member, -1);
03332 
03333       if (res != strlen(value + value_len)) {
03334          ast_log(LOG_WARNING, "Could not create persistent member string, out of space\n");
03335          break;
03336       }
03337       value_len += res;
03338    }
03339    
03340    if (value_len && !cur_member) {
03341       if (ast_db_put(pm_family, pm_queue->name, value))
03342          ast_log(LOG_WARNING, "failed to create persistent dynamic entry!\n");
03343    } else
03344       /* Delete the entry if the queue is empty or there is an error */
03345       ast_db_del(pm_family, pm_queue->name);
03346 }

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

find the entry with the best metric, or NULL

Definition at line 1953 of file app_queue.c.

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

01954 {
01955    struct callattempt *best = NULL, *cur;
01956 
01957    for (cur = outgoing; cur; cur = cur->q_next) {
01958       if (cur->stillgoing &&              /* Not already done */
01959          !cur->chan &&              /* Isn't already going */
01960          (!best || cur->metric < best->metric)) {     /* We haven't found one yet, or it's better */
01961          best = cur;
01962       }
01963    }
01964 
01965    return best;
01966 }

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

References alloc_queue(), ast_copy_string(), AST_LIST_INSERT_HEAD, AST_LIST_REMOVE, AST_LIST_TRAVERSE, ast_log(), ast_mutex_lock(), ast_mutex_unlock(), ast_strlen_zero(), clear_queue(), call_queue::dead, destroy_queue(), init_queue(), member_interface::interface, member_interface::list, 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().

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

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

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

01167 {
01168    /* Free non-dynamic members */
01169    struct member *cur;
01170    struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
01171 
01172    while ((cur = ao2_iterator_next(&mem_iter))) {
01173       if (all || !cur->dynamic) {
01174          ao2_unlink(q->members, cur);
01175          remove_from_interfaces(cur->interface);
01176          q->membercount--;
01177       }
01178       ao2_ref(cur, -1);
01179    }
01180 }

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

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

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

00552 {
00553    struct member *member;
00554    struct ao2_iterator mem_iter;
00555    enum queue_member_status result = QUEUE_NO_MEMBERS;
00556 
00557    ast_mutex_lock(&q->lock);
00558    mem_iter = ao2_iterator_init(q->members, 0);
00559    while ((member = ao2_iterator_next(&mem_iter))) {
00560       if (max_penalty && (member->penalty > max_penalty)) {
00561          ao2_ref(member, -1);
00562          continue;
00563       }
00564 
00565       if (member->paused) {
00566          ao2_ref(member, -1);
00567          continue;
00568       }
00569 
00570       switch (member->status) {
00571       case AST_DEVICE_INVALID:
00572          /* nothing to do */
00573          ao2_ref(member, -1);
00574          break;
00575       case AST_DEVICE_UNAVAILABLE:
00576          result = QUEUE_NO_REACHABLE_MEMBERS;
00577          ao2_ref(member, -1);
00578          break;
00579       default:
00580          ast_mutex_unlock(&q->lock);
00581          ao2_ref(member, -1);
00582          return QUEUE_NORMAL;
00583       }
00584    }
00585 
00586    ast_mutex_unlock(&q->lock);
00587    return result;
00588 }

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

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

04981 {
04982    char *queuename, *interface, *membername = NULL;
04983    int penalty;
04984 
04985    if ((argc != 6) && (argc != 8) && (argc != 10)) {
04986       return RESULT_SHOWUSAGE;
04987    } else if (strcmp(argv[4], "to")) {
04988       return RESULT_SHOWUSAGE;
04989    } else if ((argc == 8) && strcmp(argv[6], "penalty")) {
04990       return RESULT_SHOWUSAGE;
04991    } else if ((argc == 10) && strcmp(argv[8], "as")) {
04992       return RESULT_SHOWUSAGE;
04993    }
04994 
04995    queuename = argv[5];
04996    interface = argv[3];
04997    if (argc >= 8) {
04998       if (sscanf(argv[7], "%d", &penalty) == 1) {
04999          if (penalty < 0) {
05000             ast_cli(fd, "Penalty must be >= 0\n");
05001             penalty = 0;
05002          }
05003       } else {
05004          ast_cli(fd, "Penalty must be an integer >= 0\n");
05005          penalty = 0;
05006       }
05007    } else {
05008       penalty = 0;
05009    }
05010 
05011    if (argc >= 10) {
05012       membername = argv[9];
05013    }
05014 
05015    switch (add_to_queue(queuename, interface, membername, penalty, 0, queue_persistent_members)) {
05016    case RES_OKAY:
05017       ast_queue_log(queuename, "CLI", interface, "ADDMEMBER", "%s", "");
05018       ast_cli(fd, "Added interface '%s' to queue '%s'\n", interface, queuename);
05019       return RESULT_SUCCESS;
05020    case RES_EXISTS:
05021       ast_cli(fd, "Unable to add interface '%s' to queue '%s': Already there\n", interface, queuename);
05022       return RESULT_FAILURE;
05023    case RES_NOSUCHQUEUE:
05024       ast_cli(fd, "Unable to add interface to queue '%s': No such queue\n", queuename);
05025       return RESULT_FAILURE;
05026    case RES_OUTOFMEMORY:
05027       ast_cli(fd, "Out of memory\n");
05028       return RESULT_FAILURE;
05029    default:
05030       return RESULT_FAILURE;
05031    }
05032 }

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

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

05066 {
05067    char *queuename, *interface;
05068 
05069    if (argc != 6) {
05070       return RESULT_SHOWUSAGE;
05071    } else if (strcmp(argv[4], "from")) {
05072       return RESULT_SHOWUSAGE;
05073    }
05074 
05075    queuename = argv[5];
05076    interface = argv[3];
05077 
05078    switch (remove_from_queue(queuename, interface)) {
05079    case RES_OKAY:
05080       ast_queue_log(queuename, "CLI", interface, "REMOVEMEMBER", "%s", "");
05081       ast_cli(fd, "Removed interface '%s' from queue '%s'\n", interface, queuename);
05082       return RESULT_SUCCESS;
05083    case RES_EXISTS:
05084       ast_cli(fd, "Unable to remove interface '%s' from queue '%s': Not there\n", interface, queuename);
05085       return RESULT_FAILURE;
05086    case RES_NOSUCHQUEUE:
05087       ast_cli(fd, "Unable to remove interface from queue '%s': No such queue\n", queuename);
05088       return RESULT_FAILURE;
05089    case RES_OUTOFMEMORY:
05090       ast_cli(fd, "Out of memory\n");
05091       return RESULT_FAILURE;
05092    case RES_NOT_DYNAMIC:
05093       ast_cli(fd, "Member not dynamic\n");
05094       return RESULT_FAILURE;
05095    default:
05096       return RESULT_FAILURE;
05097    }
05098 }

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

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

Referenced by device_state_thread().

00650 {
00651    struct member_interface *curint;
00652    char *loc;
00653    char *technology;
00654 
00655    technology = ast_strdupa(sc->dev);
00656    loc = strchr(technology, '/');
00657    if (loc) {
00658       *loc++ = '\0';
00659    } else {
00660       return NULL;
00661    }
00662 
00663    AST_LIST_LOCK(&interfaces);
00664    AST_LIST_TRAVERSE(&interfaces, curint, list) {
00665       char *interface;
00666       char *slash_pos;
00667       interface = ast_strdupa(curint->interface);
00668       if ((slash_pos = strchr(interface, '/')))
00669          if ((slash_pos = strchr(slash_pos + 1, '/')))
00670             *slash_pos = '\0';
00671 
00672       if (!strcasecmp(interface, sc->dev))
00673          break;
00674    }
00675    AST_LIST_UNLOCK(&interfaces);
00676 
00677    if (!curint) {
00678       if (option_debug > 2)
00679          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));
00680       return NULL;
00681    }
00682 
00683    if (option_debug)
00684       ast_log(LOG_DEBUG, "Device '%s/%s' changed to state '%d' (%s)\n", technology, loc, sc->state, devstate2str(sc->state));
00685 
00686    update_status(sc->dev, sc->state);
00687 
00688    return NULL;
00689 }

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

Definition at line 1713 of file app_queue.c.

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

Referenced by try_calling().

01714 {
01715    struct callattempt *oo;
01716 
01717    while (outgoing) {
01718       /* Hangup any existing lines we have open */
01719       if (outgoing->chan && (outgoing->chan != exception))
01720          ast_hangup(outgoing->chan);
01721       oo = outgoing;
01722       outgoing = outgoing->q_next;
01723       if (oo->member)
01724          ao2_ref(oo->member, -1);
01725       free(oo);
01726    }
01727 }

static void init_queue ( struct call_queue q  )  [static]

Definition at line 821 of file app_queue.c.

References call_queue::announce, call_queue::announcefrequency, call_queue::announceholdtime, ao2_container_alloc(), ast_copy_string(), 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::ringlimit, 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().

00822 {
00823    int i;
00824 
00825    q->dead = 0;
00826    q->retry = DEFAULT_RETRY;
00827    q->timeout = -1;
00828    q->maxlen = 0;
00829    q->ringlimit = 0;
00830    q->announcefrequency = 0;
00831    q->announceholdtime = 0;
00832    q->roundingseconds = 0; /* Default - don't announce seconds */
00833    q->servicelevel = 0;
00834    q->ringinuse = 1;
00835    q->setinterfacevar = 0;
00836    q->autofill = autofill_default;
00837    q->montype = montype_default;
00838    q->moh[0] = '\0';
00839    q->announce[0] = '\0';
00840    q->context[0] = '\0';
00841    q->monfmt[0] = '\0';
00842    q->periodicannouncefrequency = 0;
00843    q->reportholdtime = 0;
00844    q->monjoin = 0;
00845    q->wrapuptime = 0;
00846    q->joinempty = 0;
00847    q->leavewhenempty = 0;
00848    q->memberdelay = 0;
00849    q->maskmemberstatus = 0;
00850    q->eventwhencalled = 0;
00851    q->weight = 0;
00852    q->timeoutrestart = 0;
00853    if (!q->members)
00854       q->members = ao2_container_alloc(37, member_hash_fn, member_cmp_fn);
00855    q->membercount = 0;
00856    q->found = 1;
00857    ast_copy_string(q->sound_next, "queue-youarenext", sizeof(q->sound_next));
00858    ast_copy_string(q->sound_thereare, "queue-thereare", sizeof(q->sound_thereare));
00859    ast_copy_string(q->sound_calls, "queue-callswaiting", sizeof(q->sound_calls));
00860    ast_copy_string(q->sound_holdtime, "queue-holdtime", sizeof(q->sound_holdtime));
00861    ast_copy_string(q->sound_minutes, "queue-minutes", sizeof(q->sound_minutes));
00862    ast_copy_string(q->sound_seconds, "queue-seconds", sizeof(q->sound_seconds));
00863    ast_copy_string(q->sound_thanks, "queue-thankyou", sizeof(q->sound_thanks));
00864    ast_copy_string(q->sound_lessthan, "queue-less-than", sizeof(q->sound_lessthan));
00865    ast_copy_string(q->sound_reporthold, "queue-reporthold", sizeof(q->sound_reporthold));
00866    ast_copy_string(q->sound_periodicannounce[0], "queue-periodic-announce", sizeof(q->sound_periodicannounce[0]));
00867    for (i = 1; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) {
00868       q->sound_periodicannounce[i][0]='\0';
00869    }
00870 }

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

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

Referenced by join_queue().

00521 {
00522    struct queue_ent *cur;
00523 
00524    if (!q || !new)
00525       return;
00526    if (prev) {
00527       cur = prev->next;
00528       prev->next = new;
00529    } else {
00530       cur = q->head;
00531       q->head = new;
00532    }
00533    new->next = cur;
00534    new->parent = q;
00535    new->pos = ++(*pos);
00536    new->opos = *pos;
00537 }

static char* int2strat ( int  strategy  )  [static]

Definition at line 495 of file app_queue.c.

References name, and strategies.

Referenced by __queues_show().

00496 {
00497    int x;
00498 
00499    for (x = 0; x < sizeof(strategies) / sizeof(strategies[0]); x++) {
00500       if (strategy == strategies[x].strategy)
00501          return strategies[x].name;
00502    }
00503 
00504    return "<unknown>";
00505 }

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

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

03285 {
03286    struct member *mem;
03287    struct ao2_iterator mem_iter;
03288 
03289    if (!q)
03290       return NULL;
03291 
03292    mem_iter = ao2_iterator_init(q->members, 0);
03293    while ((mem = ao2_iterator_next(&mem_iter))) {
03294       if (!strcasecmp(interface, mem->interface))
03295          return mem;
03296       ao2_ref(mem, -1);
03297    }
03298 
03299    return NULL;
03300 }

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

Definition at line 908 of file app_queue.c.

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

Referenced by remove_from_interfaces().

00909 {
00910    struct call_queue *q;
00911    struct member *mem, tmpmem;
00912    int ret = 0;
00913 
00914    ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
00915 
00916    AST_LIST_LOCK(&queues);
00917    AST_LIST_TRAVERSE(&queues, q, list) {
00918       ast_mutex_lock(&q->lock);
00919       if ((mem = ao2_find(q->members, &tmpmem, OBJ_POINTER))) {
00920          ao2_ref(mem, -1);
00921          ret = 1;
00922       }
00923       ast_mutex_unlock(&q->lock);
00924       if (ret)
00925          break;
00926    }
00927    AST_LIST_UNLOCK(&queues);
00928 
00929    return ret;
00930 }

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 2365 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, ast_channel::name, 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().

02366 {
02367    struct queue_ent *ch;
02368    struct member *cur;
02369    int avl = 0;
02370    int idx = 0;
02371    int res;
02372 
02373    if (!qe->parent->autofill) {
02374       /* Atomically read the parent head -- does not need a lock */
02375       ch = qe->parent->head;
02376       /* If we are now at the top of the head, break out */
02377       if (ch == qe) {
02378          if (option_debug)
02379             ast_log(LOG_DEBUG, "It's our turn (%s).\n", qe->chan->name);
02380          res = 1;
02381       } else {
02382          if (option_debug)
02383             ast_log(LOG_DEBUG, "It's not our turn (%s).\n", qe->chan->name);
02384          res = 0;
02385       }  
02386 
02387    } else {
02388       /* This needs a lock. How many members are available to be served? */
02389       ast_mutex_lock(&qe->parent->lock);
02390          
02391       ch = qe->parent->head;
02392    
02393       if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
02394          if (option_debug)
02395             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");
02396          avl = 1;
02397       } else {
02398          struct ao2_iterator mem_iter = ao2_iterator_init(qe->parent->members, 0);
02399          while ((cur = ao2_iterator_next(&mem_iter))) {
02400             switch (cur->status) {
02401             case AST_DEVICE_INUSE:
02402                if (!qe->parent->ringinuse)
02403                   break;
02404                /* else fall through */
02405             case AST_DEVICE_NOT_INUSE:
02406             case AST_DEVICE_UNKNOWN:
02407                if (!cur->paused)
02408                   avl++;
02409                break;
02410             }
02411             ao2_ref(cur, -1);
02412          }
02413       }
02414 
02415       if (option_debug)
02416          ast_log(LOG_DEBUG, "There are %d available members.\n", avl);
02417    
02418       while ((idx < avl) && (ch) && (ch != qe)) {
02419          if (!ch->pending)
02420             idx++;
02421          ch = ch->next;       
02422       }
02423    
02424       /* If the queue entry is within avl [the number of available members] calls from the top ... */
02425       if (ch && idx < avl) {
02426          if (option_debug)
02427             ast_log(LOG_DEBUG, "It's our turn (%s).\n", qe->chan->name);
02428          res = 1;
02429       } else {
02430          if (option_debug)
02431             ast_log(LOG_DEBUG, "It's not our turn (%s).\n", qe->chan->name);
02432          res = 0;
02433       }
02434       
02435       ast_mutex_unlock(&qe->parent->lock);
02436    }
02437 
02438    return res;
02439 }

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

Definition at line 1433 of file app_queue.c.

References queue_ent::announce, ast_copy_string(), 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, ast_channel::name, queue_ent::next, option_debug, queue_ent::pos, queue_ent::prio, QUEUE_EMPTY_STRICT, QUEUE_FULL, QUEUE_JOINEMPTY, QUEUE_JOINUNAVAIL, QUEUE_NO_MEMBERS, QUEUE_NO_REACHABLE_MEMBERS, S_OR, and ast_channel::uniqueid.

Referenced by queue_exec().

01434 {
01435    struct call_queue *q;
01436    struct queue_ent *cur, *prev = NULL;
01437    int res = -1;
01438    int pos = 0;
01439    int inserted = 0;
01440    enum queue_member_status stat;
01441 
01442    if (!(q = load_realtime_queue(queuename)))
01443       return res;
01444 
01445    AST_LIST_LOCK(&queues);
01446    ast_mutex_lock(&q->lock);
01447 
01448    /* This is our one */
01449    stat = get_member_status(q, qe->max_penalty);
01450    if (!q->joinempty && (stat == QUEUE_NO_MEMBERS))
01451       *reason = QUEUE_JOINEMPTY;
01452    else if ((q->joinempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS || stat == QUEUE_NO_MEMBERS))
01453       *reason = QUEUE_JOINUNAVAIL;
01454    else if (q->maxlen && (q->count >= q->maxlen))
01455       *reason = QUEUE_FULL;
01456    else {
01457       /* There's space for us, put us at the right position inside
01458        * the queue.
01459        * Take into account the priority of the calling user */
01460       inserted = 0;
01461       prev = NULL;
01462       cur = q->head;
01463       while (cur) {
01464          /* We have higher priority than the current user, enter
01465           * before him, after all the other users with priority
01466           * higher or equal to our priority. */
01467          if ((!inserted) && (qe->prio > cur->prio)) {
01468             insert_entry(q, prev, qe, &pos);
01469             inserted = 1;
01470          }
01471          cur->pos = ++pos;
01472          prev = cur;
01473          cur = cur->next;
01474       }
01475       /* No luck, join at the end of the queue */
01476       if (!inserted)
01477          insert_entry(q, prev, qe, &pos);
01478       ast_copy_string(qe->moh, q->moh, sizeof(qe->moh));
01479       ast_copy_string(qe->announce, q->announce, sizeof(qe->announce));
01480       ast_copy_string(qe->context, q->context, sizeof(qe->context));
01481       q->count++;
01482       res = 0;
01483       manager_event(EVENT_FLAG_CALL, "Join",
01484          "Channel: %s\r\nCallerID: %s\r\nCallerIDName: %s\r\nQueue: %s\r\nPosition: %d\r\nCount: %d\r\nUniqueid: %s\r\n",
01485          qe->chan->name,
01486          S_OR(qe->chan->cid.cid_num, "unknown"), /* XXX somewhere else it is <unknown> */
01487          S_OR(qe->chan->cid.cid_name, "unknown"),
01488          q->name, qe->pos, q->count, qe->chan->uniqueid );
01489       if (option_debug)
01490          ast_log(LOG_DEBUG, "Queue '%s' Join, Channel '%s', Position '%d'\n", q->name, qe->chan->name, qe->pos );
01491    }
01492    ast_mutex_unlock(&q->lock);
01493    AST_LIST_UNLOCK(&queues);
01494 
01495    return res;
01496 }

static void leave_queue ( struct queue_ent qe  )  [static]

Definition at line 1669 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, member_interface::list, call_queue::lock, LOG_DEBUG, manager_event(), call_queue::name, ast_channel::name, queue_ent::next, option_debug, queue_ent::parent, queue_ent::pos, and ast_channel::uniqueid.

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

01670 {
01671    struct call_queue *q;
01672    struct queue_ent *cur, *prev = NULL;
01673    int pos = 0;
01674 
01675    if (!(q = qe->parent))
01676       return;
01677    ast_mutex_lock(&q->lock);
01678 
01679    prev = NULL;
01680    for (cur = q->head; cur; cur = cur->next) {
01681       if (cur == qe) {
01682          q->count--;
01683 
01684          /* Take us out of the queue */
01685          manager_event(EVENT_FLAG_CALL, "Leave",
01686             "Channel: %s\r\nQueue: %s\r\nCount: %d\r\nUniqueid: %s\r\n",
01687             qe->chan->name, q->name,  q->count, qe->chan->uniqueid);
01688          if (option_debug)
01689             ast_log(LOG_DEBUG, "Queue '%s' Leave, Channel '%s'\n", q->name, qe->chan->name );
01690          /* Take us out of the queue */
01691          if (prev)
01692             prev->next = cur->next;
01693          else
01694             q->head = cur->next;
01695       } else {
01696          /* Renumber the people after us in the queue based on a new count */
01697          cur->pos = ++pos;
01698          prev = cur;
01699       }
01700    }
01701    ast_mutex_unlock(&q->lock);
01702 
01703    if (q->dead && !q->count) {   
01704       /* It's dead and nobody is in it, so kill it */
01705       AST_LIST_LOCK(&queues);
01706       AST_LIST_REMOVE(&queues, q, list);
01707       AST_LIST_UNLOCK(&queues);
01708       destroy_queue(q);
01709    }
01710 }

static int load_module ( void   )  [static]

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

05220 {
05221    int res;
05222 
05223    if (!reload_queues())
05224       return AST_MODULE_LOAD_DECLINE;
05225 
05226    if (queue_persistent_members)
05227       reload_queue_members();
05228 
05229    ast_mutex_init(&device_state.lock);
05230    ast_cond_init(&device_state.cond, NULL);
05231    ast_pthread_create(&device_state.thread, NULL, device_state_thread, NULL);
05232 
05233    ast_cli_register_multiple(cli_queue, sizeof(cli_queue) / sizeof(struct ast_cli_entry));
05234    res = ast_register_application(app, queue_exec, synopsis, descrip);
05235    res |= ast_register_application(app_aqm, aqm_exec, app_aqm_synopsis, app_aqm_descrip);
05236    res |= ast_register_application(app_rqm, rqm_exec, app_rqm_synopsis, app_rqm_descrip);
05237    res |= ast_register_application(app_pqm, pqm_exec, app_pqm_synopsis, app_pqm_descrip);
05238    res |= ast_register_application(app_upqm, upqm_exec, app_upqm_synopsis, app_upqm_descrip);
05239    res |= ast_register_application(app_ql, ql_exec, app_ql_synopsis, app_ql_descrip);
05240    res |= ast_manager_register("Queues", 0, manager_queues_show, "Queues");
05241    res |= ast_manager_register("QueueStatus", 0, manager_queues_status, "Queue Status");
05242    res |= ast_manager_register("QueueAdd", EVENT_FLAG_AGENT, manager_add_queue_member, "Add interface to queue.");
05243    res |= ast_manager_register("QueueRemove", EVENT_FLAG_AGENT, manager_remove_queue_member, "Remove interface from queue.");
05244    res |= ast_manager_register("QueuePause", EVENT_FLAG_AGENT, manager_pause_queue_member, "Makes a queue member temporarily unavailable");
05245    res |= ast_custom_function_register(&queueagentcount_function);
05246    res |= ast_custom_function_register(&queuemembercount_function);
05247    res |= ast_custom_function_register(&queuememberlist_function);
05248    res |= ast_custom_function_register(&queuewaitingcount_function);
05249    res |= ast_devstate_add(statechange_queue, NULL);
05250 
05251    return res;
05252 }

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

Definition at line 1383 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(), member_interface::list, LOG_ERROR, call_queue::name, call_queue::realtime, and update_realtime_members().

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

01384 {
01385    struct ast_variable *queue_vars;
01386    struct ast_config *member_config = NULL;
01387    struct call_queue *q;
01388 
01389    /* Find the queue in the in-core list first. */
01390    AST_LIST_LOCK(&queues);
01391    AST_LIST_TRAVERSE(&queues, q, list) {
01392       if (!strcasecmp(q->name, queuename)) {
01393          break;
01394       }
01395    }
01396    AST_LIST_UNLOCK(&queues);
01397 
01398    if (!q || q->realtime) {
01399       /*! \note Load from realtime before taking the global qlock, to avoid blocking all
01400          queue operations while waiting for the DB.
01401 
01402          This will be two separate database transactions, so we might
01403          see queue parameters as they were before another process
01404          changed the queue and member list as it was after the change.
01405          Thus we might see an empty member list when a queue is
01406          deleted. In practise, this is unlikely to cause a problem. */
01407 
01408       queue_vars = ast_load_realtime("queues", "name", queuename, NULL);
01409       if (queue_vars) {
01410          member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", queuename, NULL);
01411          if (!member_config) {
01412             ast_log(LOG_ERROR, "no queue_members defined in your config (extconfig.conf).\n");
01413             ast_variables_destroy(queue_vars);
01414             return NULL;
01415          }
01416       }
01417 
01418       AST_LIST_LOCK(&queues);
01419 
01420       q = find_queue_by_name_rt(queuename, queue_vars, member_config);
01421       if (member_config)
01422          ast_config_destroy(member_config);
01423       if (queue_vars)
01424          ast_variables_destroy(queue_vars);
01425 
01426       AST_LIST_UNLOCK(&queues);
01427    } else { 
01428       update_realtime_members(q);
01429    }
01430    return q;
01431 }

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

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

04874 {
04875    const char *queuename, *interface, *penalty_s, *paused_s, *membername;
04876    int paused, penalty = 0;
04877 
04878    queuename = astman_get_header(m, "Queue");
04879    interface = astman_get_header(m, "Interface");
04880    penalty_s = astman_get_header(m, "Penalty");
04881    paused_s = astman_get_header(m, "Paused");
04882    membername = astman_get_header(m, "MemberName");
04883 
04884    if (ast_strlen_zero(queuename)) {
04885       astman_send_error(s, m, "'Queue' not specified.");
04886       return 0;
04887    }
04888 
04889    if (ast_strlen_zero(interface)) {
04890       astman_send_error(s, m, "'Interface' not specified.");
04891       return 0;
04892    }
04893 
04894    if (ast_strlen_zero(penalty_s))
04895       penalty = 0;
04896    else if (sscanf(penalty_s, "%d", &penalty) != 1 || penalty < 0)
04897       penalty = 0;
04898 
04899    if (ast_strlen_zero(paused_s))
04900       paused = 0;
04901    else
04902       paused = abs(ast_true(paused_s));
04903 
04904    switch (add_to_queue(queuename, interface, membername, penalty, paused, queue_persistent_members)) {
04905    case RES_OKAY:
04906       ast_queue_log(queuename, "MANAGER", interface, "ADDMEMBER", "%s", "");
04907       astman_send_ack(s, m, "Added interface to queue");
04908       break;
04909    case RES_EXISTS:
04910       astman_send_error(s, m, "Unable to add interface: Already there");
04911       break;
04912    case RES_NOSUCHQUEUE:
04913       astman_send_error(s, m, "Unable to add interface to queue: No such queue");
04914       break;
04915    case RES_OUTOFMEMORY:
04916       astman_send_error(s, m, "Out of memory");
04917       break;
04918    }
04919 
04920    return 0;
04921 }

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

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

04958 {
04959    const char *queuename, *interface, *paused_s;
04960    int paused;
04961 
04962    interface = astman_get_header(m, "Interface");
04963    paused_s = astman_get_header(m, "Paused");
04964    queuename = astman_get_header(m, "Queue");   /* Optional - if not supplied, pause the given Interface in all queues */
04965 
04966    if (ast_strlen_zero(interface) || ast_strlen_zero(paused_s)) {
04967       astman_send_error(s, m, "Need 'Interface' and 'Paused' parameters.");
04968       return 0;
04969    }
04970 
04971    paused = abs(ast_true(paused_s));
04972 
04973    if (set_member_paused(queuename, interface, paused))
04974       astman_send_error(s, m, "Interface not found");
04975    else
04976       astman_send_ack(s, m, paused ? "Interface paused successfully" : "Interface unpaused successfully");
04977    return 0;
04978 }

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

Definition at line 4768 of file app_queue.c.

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

Referenced by load_module().

04769 {
04770    char *a[] = { "queue", "show" };
04771 
04772    __queues_show(s, 1, -1, 2, a);
04773    astman_append(s, "\r\n\r\n"); /* Properly terminate Manager output */
04774 
04775    return RESULT_SUCCESS;
04776 }

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

Definition at line 4779 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, ast_channel::name, call_queue::name, queue_ent::next, member::paused, member::penalty, RESULT_SUCCESS, call_queue::ringlimit, s, S_OR, call_queue::servicelevel, queue_ent::start, member::status, and call_queue::weight.

Referenced by load_module().

04780 {
04781    time_t now;
04782    int pos;
04783    const char *id = astman_get_header(m,"ActionID");
04784    const char *queuefilter = astman_get_header(m,"Queue");
04785    const char *memberfilter = astman_get_header(m,"Member");
04786    char idText[256] = "";
04787    struct call_queue *q;
04788    struct queue_ent *qe;
04789    float sl = 0;
04790    struct member *mem;
04791    struct ao2_iterator mem_iter;
04792 
04793    astman_send_ack(s, m, "Queue status will follow");
04794    time(&now);
04795    AST_LIST_LOCK(&queues);
04796    if (!ast_strlen_zero(id))
04797       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
04798 
04799    AST_LIST_TRAVERSE(&queues, q, list) {
04800       ast_mutex_lock(&q->lock);
04801 
04802       /* List queue properties */
04803       if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) {
04804          sl = ((q->callscompleted > 0) ? 100 * ((float)q->callscompletedinsl / (float)q->callscompleted) : 0);
04805          astman_append(s, "Event: QueueParams\r\n"
04806             "Queue: %s\r\n"
04807             "Max: %d\r\n"
04808             "Calls: %d\r\n"
04809             "Holdtime: %d\r\n"
04810             "Completed: %d\r\n"
04811             "Abandoned: %d\r\n"
04812             "ServiceLevel: %d\r\n"
04813             "ServicelevelPerf: %2.1f\r\n"
04814             "RingLimit: %d\r\n"
04815             "Weight: %d\r\n"
04816             "%s"
04817             "\r\n",
04818             q->name, q->maxlen, q->count, q->holdtime, q->callscompleted,
04819             q->callsabandoned, q->servicelevel, sl,  q->ringlimit, q->weight, idText);
04820          /* List Queue Members */
04821          mem_iter = ao2_iterator_init(q->members, 0);
04822          while ((mem = ao2_iterator_next(&mem_iter))) {
04823             if (ast_strlen_zero(memberfilter) || !strcmp(mem->interface, memberfilter)) {
04824                astman_append(s, "Event: QueueMember\r\n"
04825                   "Queue: %s\r\n"
04826                   "Name: %s\r\n"
04827                   "Location: %s\r\n"
04828                   "Membership: %s\r\n"
04829                   "Penalty: %d\r\n"
04830                   "CallsTaken: %d\r\n"
04831                   "LastCall: %d\r\n"
04832                   "Status: %d\r\n"
04833                   "Paused: %d\r\n"
04834                   "%s"
04835                   "\r\n",
04836                   q->name, mem->membername, mem->interface, mem->dynamic ? "dynamic" : "static",
04837                   mem->penalty, mem->calls, (int)mem->lastcall, mem->status, mem->paused, idText);
04838             }
04839             ao2_ref(mem, -1);
04840          }
04841          /* List Queue Entries */
04842          pos = 1;
04843          for (qe = q->head; qe; qe = qe->next) {
04844             astman_append(s, "Event: QueueEntry\r\n"
04845                "Queue: %s\r\n"
04846                "Position: %d\r\n"
04847                "Channel: %s\r\n"
04848                "CallerID: %s\r\n"
04849                "CallerIDName: %s\r\n"
04850                "Wait: %ld\r\n"
04851                "%s"
04852                "\r\n",
04853                q->name, pos++, qe->chan->name,
04854                S_OR(qe->chan->cid.cid_num, "unknown"),
04855                S_OR(qe->chan->cid.cid_name, "unknown"),
04856                (long) (now - qe->start), idText);
04857          }
04858       }
04859       ast_mutex_unlock(&q->lock);
04860    }
04861 
04862    astman_append(s,
04863       "Event: QueueStatusComplete\r\n"
04864       "%s"
04865       "\r\n",idText);
04866 
04867    AST_LIST_UNLOCK(&queues);
04868 
04869 
04870    return RESULT_SUCCESS;
04871 }

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

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

04924 {
04925    const char *queuename, *interface;
04926 
04927    queuename = astman_get_header(m, "Queue");
04928    interface = astman_get_header(m, "Interface");
04929 
04930    if (ast_strlen_zero(queuename) || ast_strlen_zero(interface)) {
04931       astman_send_error(s, m, "Need 'Queue' and 'Interface' parameters.");
04932       return 0;
04933    }
04934 
04935    switch (remove_from_queue(queuename, interface)) {
04936    case RES_OKAY:
04937       ast_queue_log(queuename, "MANAGER", interface, "REMOVEMEMBER", "%s", "");
04938       astman_send_ack(s, m, "Removed interface from queue");
04939       break;
04940    case RES_EXISTS:
04941       astman_send_error(s, m, "Unable to remove interface: Not there");
04942       break;
04943    case RES_NOSUCHQUEUE:
04944       astman_send_error(s, m, "Unable to remove interface from queue: No such queue");
04945       break;
04946    case RES_OUTOFMEMORY:
04947       astman_send_error(s, m, "Out of memory");
04948       break;
04949    case RES_NOT_DYNAMIC:
04950       astman_send_error(s, m, "Member not dynamic");
04951       break;
04952    }
04953 
04954    return 0;
04955 }

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

Definition at line 815 of file app_queue.c.

References member::interface.

Referenced by init_queue().

00816 {
00817    struct member *mem1 = obj1, *mem2 = obj2;
00818    return strcmp(mem1->interface, mem2->interface) ? 0 : CMP_MATCH | CMP_STOP;
00819 }

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

Definition at line 803 of file app_queue.c.

References compress_char(), and member::interface.

Referenced by init_queue().

00804 {
00805    const struct member *mem = obj;
00806    const char *chname = strchr(mem->interface, '/');
00807    int ret = 0, i;
00808    if (!chname)
00809       chname = mem->interface;
00810    for (i = 0; i < 5 && chname[i]; i++)
00811       ret += compress_char(chname[i]) << (i * 6);
00812    return ret;
00813 }

static void monjoin_dep_warning ( void   )  [static]

Definition at line 474 of file app_queue.c.

References ast_log(), and LOG_NOTICE.

Referenced by queue_set_param().

00475 {
00476    static unsigned int warned = 0;
00477    if (!warned) {
00478       ast_log(LOG_NOTICE, "The 'monitor-join' queue option is deprecated. Please use monitor-type=mixmonitor instead.\n");
00479       warned = 1;
00480    }
00481 }

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

Definition at line 1498 of file app_queue.c.

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

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

01499 {
01500    int res;
01501 
01502    ast_stopstream(chan);
01503 
01504    res = ast_streamfile(chan, filename, chan->language);
01505    if (!res)
01506       res = ast_waitstream(chan, AST_DIGIT_ANY);
01507 
01508    ast_stopstream(chan);
01509 
01510    return res;
01511 }

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

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

Referenced by load_module().

03598 {
03599    struct ast_module_user *lu;
03600    char *parse;
03601    int priority_jump = 0;
03602    int ignore_fail = 0;
03603    AST_DECLARE_APP_ARGS(args,
03604       AST_APP_ARG(queuename);
03605       AST_APP_ARG(interface);
03606       AST_APP_ARG(options);
03607    );
03608 
03609    if (ast_strlen_zero(data)) {
03610       ast_log(LOG_WARNING, "PauseQueueMember requires an argument ([queuename]|interface[|options])\n");
03611       return -1;
03612    }
03613 
03614    parse = ast_strdupa(data);
03615 
03616    AST_STANDARD_APP_ARGS(args, parse);
03617 
03618    lu = ast_module_user_add(chan);
03619 
03620    if (args.options) {
03621       if (strchr(args.options, 'j'))
03622          priority_jump = 1;
03623       if (strchr(args.options, 'i'))
03624          ignore_fail = 1;
03625    }
03626 
03627    if (ast_strlen_zero(args.interface)) {
03628       ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename]|interface[|options])\n");
03629       ast_module_user_remove(lu);
03630       return -1;
03631    }
03632 
03633    if (set_member_paused(args.queuename, args.interface, 1)) {
03634       ast_log(LOG_WARNING, "Attempt to pause interface %s, not found\n", args.interface);
03635       pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND");
03636       if (priority_jump || ast_opt_priority_jumping) {
03637          if (!ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101)) {
03638             ast_module_user_remove(lu);
03639             return 0;
03640          }
03641       }
03642       ast_module_user_remove(lu);
03643       if (ignore_fail) {
03644          return 0;
03645       } else {
03646          return -1;
03647       }
03648    }
03649 
03650    ast_module_user_remove(lu);
03651    pbx_builtin_setvar_helper(chan, "PQMSTATUS", "PAUSED");
03652    return 0;
03653 }

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

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

Referenced by load_module().

03855 {
03856    struct ast_module_user *u;
03857    char *parse;
03858 
03859    AST_DECLARE_APP_ARGS(args,
03860       AST_APP_ARG(queuename);
03861       AST_APP_ARG(uniqueid);
03862       AST_APP_ARG(membername);
03863       AST_APP_ARG(event);
03864       AST_APP_ARG(params);
03865    );
03866 
03867    if (ast_strlen_zero(data)) {
03868       ast_log(LOG_WARNING, "QueueLog requires arguments (queuename|uniqueid|membername|event[|additionalinfo]\n");
03869       return -1;
03870    }
03871 
03872    u = ast_module_user_add(chan);
03873 
03874    parse = ast_strdupa(data);
03875 
03876    AST_STANDARD_APP_ARGS(args, parse);
03877 
03878    if (ast_strlen_zero(args.queuename) || ast_strlen_zero(args.uniqueid)
03879        || ast_strlen_zero(args.membername) || ast_strlen_zero(args.event)) {
03880       ast_log(LOG_WARNING, "QueueLog requires arguments (queuename|uniqueid|membername|event[|additionalinfo])\n");
03881       ast_module_user_remove(u);
03882       return -1;
03883    }
03884 
03885    ast_queue_log(args.queuename, args.uniqueid, args.membername, args.event, 
03886       "%s", args.params ? args.params : "");
03887 
03888    ast_module_user_remove(u);
03889 
03890    return 0;
03891 }

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 3905 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_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, ast_channel::name, 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(), ast_channel::uniqueid, update_realtime_members(), VERBOSE_PREFIX_3, wait_a_bit(), and wait_our_turn().

Referenced by load_module().

03906 {
03907    int res=-1;
03908    int ringing=0;
03909    struct ast_module_user *lu;
03910    const char *user_priority;
03911    const char *max_penalty_str;
03912    int prio;
03913    int max_penalty;
03914    enum queue_result reason = QUEUE_UNKNOWN;
03915    /* whether to exit Queue application after the timeout hits */
03916    int tries = 0;
03917    int noption = 0;
03918    char *parse;
03919    AST_DECLARE_APP_ARGS(args,
03920       AST_APP_ARG(queuename);
03921       AST_APP_ARG(options);
03922       AST_APP_ARG(url);
03923       AST_APP_ARG(announceoverride);
03924       AST_APP_ARG(queuetimeoutstr);
03925       AST_APP_ARG(agi);
03926    );
03927    /* Our queue entry */
03928    struct queue_ent qe;
03929    
03930    if (ast_strlen_zero(data)) {
03931       ast_log(LOG_WARNING, "Queue requires an argument: queuename[|options[|URL[|announceoverride[|timeout[|agi]]]]]\n");
03932       return -1;
03933    }
03934    
03935    parse = ast_strdupa(data);
03936    AST_STANDARD_APP_ARGS(args, parse);
03937 
03938    lu = ast_module_user_add(chan);
03939 
03940    /* Setup our queue entry */
03941    memset(&qe, 0, sizeof(qe));
03942    qe.start = time(NULL);
03943 
03944    /* set the expire time based on the supplied timeout; */
03945    if (!ast_strlen_zero(args.queuetimeoutstr))
03946       qe.expire = qe.start + atoi(args.queuetimeoutstr);
03947    else
03948       qe.expire = 0;
03949 
03950    /* Get the priority from the variable ${QUEUE_PRIO} */
03951    user_priority = pbx_builtin_getvar_helper(chan, "QUEUE_PRIO");
03952    if (user_priority) {
03953       if (sscanf(user_priority, "%d", &prio) == 1) {
03954          if (option_debug)
03955             ast_log(LOG_DEBUG, "%s: Got priority %d from ${QUEUE_PRIO}.\n",
03956                chan->name, prio);
03957       } else {
03958          ast_log(LOG_WARNING, "${QUEUE_PRIO}: Invalid value (%s), channel %s.\n",
03959             user_priority, chan->name);
03960          prio = 0;
03961       }
03962    } else {
03963       if (option_debug > 2)
03964          ast_log(LOG_DEBUG, "NO QUEUE_PRIO variable found. Using default.\n");
03965       prio = 0;
03966    }
03967 
03968    /* Get the maximum penalty from the variable ${QUEUE_MAX_PENALTY} */
03969    if ((max_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MAX_PENALTY"))) {
03970       if (sscanf(max_penalty_str, "%d", &max_penalty) == 1) {
03971          if (option_debug)
03972             ast_log(LOG_DEBUG, "%s: Got max penalty %d from ${QUEUE_MAX_PENALTY}.\n",
03973                chan->name, max_penalty);
03974       } else {
03975          ast_log(LOG_WARNING, "${QUEUE_MAX_PENALTY}: Invalid value (%s), channel %s.\n",
03976             max_penalty_str, chan->name);
03977          max_penalty = 0;
03978       }
03979    } else {
03980       max_penalty = 0;
03981    }
03982 
03983    if (args.options && (strchr(args.options, 'r')))
03984       ringing = 1;
03985 
03986    if (option_debug)
03987       ast_log(LOG_DEBUG, "queue: %s, options: %s, url: %s, announce: %s, expires: %ld, priority: %d\n",
03988          args.queuename, args.options, args.url, args.announceoverride, (long)qe.expire, prio);
03989 
03990    qe.chan = chan;
03991    qe.prio = prio;
03992    qe.max_penalty = max_penalty;
03993    qe.last_pos_said = 0;
03994    qe.last_pos = 0;
03995    qe.last_periodic_announce_time = time(NULL);
03996    qe.last_periodic_announce_sound = 0;
03997    qe.valid_digits = 0;
03998    if (!join_queue(args.queuename, &qe, &reason)) {
03999       int makeannouncement = 0;
04000 
04001       ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ENTERQUEUE", "%s|%s", S_OR(args.url, ""),
04002          S_OR(chan->cid.cid_num, ""));
04003 check_turns:
04004       if (ringing) {
04005          ast_indicate(chan, AST_CONTROL_RINGING);
04006       } else {
04007          ast_moh_start(chan, qe.moh, NULL);
04008       }
04009 
04010       /* This is the wait loop for callers 2 through maxlen */
04011       res = wait_our_turn(&qe, ringing, &reason);
04012       if (res)
04013          goto stop;
04014 
04015       for (;;) {
04016          /* This is the wait loop for the head caller*/
04017          /* To exit, they may get their call answered; */
04018          /* they may dial a digit from the queue context; */
04019          /* or, they may timeout. */
04020 
04021          enum queue_member_status stat;
04022 
04023          /* Leave if we have exceeded our queuetimeout */
04024          if (qe.expire && (time(NULL) >= qe.expire)) {
04025             record_abandoned(&qe);
04026             reason = QUEUE_TIMEOUT;
04027             res = 0;
04028             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
04029             break;
04030          }
04031 
04032          if (makeannouncement) {
04033             /* Make a position announcement, if enabled */
04034             if (qe.parent->announcefrequency && !ringing)
04035                if ((res = say_position(&qe)))
04036                   goto stop;
04037 
04038          }
04039          makeannouncement = 1;
04040 
04041          /* Leave if we have exceeded our queuetimeout */
04042          if (qe.expire && (time(NULL) >= qe.expire)) {
04043             record_abandoned(&qe);
04044             reason = QUEUE_TIMEOUT;
04045             res = 0;
04046             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
04047             break;
04048          }
04049          /* Make a periodic announcement, if enabled */
04050          if (qe.parent->periodicannouncefrequency && !ringing)
04051             if ((res = say_periodic_announcement(&qe)))
04052                goto stop;
04053 
04054          /* Leave if we have exceeded our queuetimeout */
04055          if (qe.expire && (time(NULL) >= qe.expire)) {
04056             record_abandoned(&qe);
04057             reason = QUEUE_TIMEOUT;
04058             res = 0;
04059             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
04060             break;
04061          }
04062          /* Try calling all queue members for 'timeout' seconds */
04063          res = try_calling(&qe, args.options, args.announceoverride, args.url, &tries, &noption, args.agi);
04064          if (res)
04065             goto stop;
04066 
04067          stat = get_member_status(qe.parent, qe.max_penalty);
04068 
04069          /* exit after 'timeout' cycle if 'n' option enabled */
04070          if (noption && tries >= qe.parent->membercount) {
04071             if (option_verbose > 2)
04072                ast_verbose(VERBOSE_PREFIX_3 "Exiting on time-out cycle\n");
04073             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
04074             record_abandoned(&qe);
04075             reason = QUEUE_TIMEOUT;
04076             res = 0;
04077             break;
04078          }
04079 
04080          /* leave the queue if no agents, if enabled */
04081          if (qe.parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) {
04082             record_abandoned(&qe);
04083             reason = QUEUE_LEAVEEMPTY;
04084             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
04085             res = 0;
04086             break;
04087          }
04088 
04089          /* leave the queue if no reachable agents, if enabled */
04090          if ((qe.parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) {
04091             record_abandoned(&qe);
04092             reason = QUEUE_LEAVEUNAVAIL;
04093             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
04094             res = 0;
04095             break;
04096          }
04097 
04098          /* Leave if we have exceeded our queuetimeout */
04099          if (qe.expire && (time(NULL) >= qe.expire)) {
04100             record_abandoned(&qe);
04101             reason = QUEUE_TIMEOUT;
04102             res = 0;
04103             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
04104             break;
04105          }
04106 
04107          /* If using dynamic realtime members, we should regenerate the member list for this queue */
04108          update_realtime_members(qe.parent);
04109 
04110          /* OK, we didn't get anybody; wait for 'retry' seconds; may get a digit to exit with */
04111          res = wait_a_bit(&qe);
04112          if (res)
04113             goto stop;
04114 
04115          /* Since this is a priority queue and
04116           * it is not sure that we are still at the head
04117           * of the queue, go and check for our turn again.
04118           */
04119          if (!is_our_turn(&qe)) {
04120             if (option_debug)
04121                ast_log(LOG_DEBUG, "Darn priorities, going back in queue (%s)!\n",
04122                   qe.chan->name);
04123             goto check_turns;
04124          }
04125       }
04126 
04127 stop:
04128       if (res) {
04129          if (res < 0) {
04130             if (!qe.handled) {
04131                record_abandoned(&qe);
04132                ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ABANDON",
04133                   "%d|%d|%ld", qe.pos, qe.opos,
04134                   (long) time(NULL) - qe.start);
04135             }
04136             res = -1;
04137          } else if (qe.valid_digits) {
04138             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHKEY",
04139                "%s|%d", qe.digits, qe.pos);
04140          }
04141       }
04142 
04143       /* Don't allow return code > 0 */
04144       if (res >= 0) {
04145          res = 0; 
04146          if (ringing) {
04147             ast_indicate(chan, -1);
04148          } else {
04149             ast_moh_stop(chan);
04150          }        
04151          ast_stopstream(chan);
04152       }
04153       leave_queue(&qe);
04154       if (reason != QUEUE_UNKNOWN)
04155          set_queue_result(chan, reason);
04156    } else {
04157       ast_log(LOG_WARNING, "Unable to join queue '%s'\n", args.queuename);
04158       set_queue_result(chan, reason);
04159       res = 0;
04160    }
04161    ast_module_user_remove(lu);
04162 
04163    return res;
04164 }

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

Definition at line 4174 of file app_queue.c.

References ao2_iterator_init(), ao2_iterator_next(), ao2_ref(), AST_DEVICE_INVALID, AST_DEVICE_NOT_INUSE, AST_DEVICE_UNAVAILABLE, AST_DEVICE_UNKNOWN, ast_log(), ast_module_user_add, ast_module_user_remove, ast_mutex_lock(), ast_mutex_unlock(), ast_strdupa, ast_strlen_zero(), load_realtime_queue(), call_queue::lock, LOG_ERROR, LOG_WARNING, call_queue::members, name, member::paused, QMC_ACTIVE, QMC_FREE, QMC_PAUSED, QMC_VALID, and member::status.

04175 {
04176    int count = 0;
04177    struct call_queue *q;
04178    struct ast_module_user *lu;
04179    struct member *m;
04180    struct ao2_iterator mem_iter;
04181    char *name, *item;
04182    enum qmc_status mode = QMC_VALID;
04183 
04184    buf[0] = '\0';
04185    
04186    if (ast_strlen_zero(data)) {
04187       ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
04188       return -1;
04189    }
04190 
04191    name = ast_strdupa(data);
04192  
04193    lu = ast_module_user_add(chan);
04194 
04195    if ((item = strchr(name, ':'))) {
04196       *item = '\0';
04197       item++;
04198    } else {
04199       item = "";
04200    }
04201 
04202    if (!strcasecmp(item, "valid")) {
04203       mode = QMC_VALID;
04204    } else  if (!strcasecmp(item, "paused")) {
04205       mode = QMC_PAUSED;
04206    } else  if (!strcasecmp(item, "active")) {
04207       mode = QMC_ACTIVE;
04208    } else  if (!strcasecmp(item, "free")) {
04209       mode = QMC_FREE;
04210    } else  if (!strcasecmp(item, "all")) {
04211       mode = QMC_ALL;
04212    }
04213 
04214    if ((q = load_realtime_queue(name))) {
04215       ast_mutex_lock(&q->lock);
04216       mem_iter = ao2_iterator_init(q->members, 0);
04217       while ((m = ao2_iterator_next(&mem_iter))) {
04218          switch (mode) {
04219          case QMC_VALID:
04220             /* Count the queue members who are logged in and presently answering calls */
04221             if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
04222                count++;
04223             }
04224             break;
04225          case QMC_PAUSED:
04226             /* Count paused members */
04227             if (m->paused) {
04228                count++;
04229             }
04230             break;
04231          case QMC_ACTIVE:
04232             /* Count not paused members who are logged in and presently answering calls */
04233             if (!m->paused && (m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
04234                count++;
04235             }
04236             break;
04237          case QMC_FREE:
04238             /* Count free members in the queue */
04239             if (!m->paused && ((m->status == AST_DEVICE_UNKNOWN) || (m->status == AST_DEVICE_NOT_INUSE))) {
04240                count++;
04241             }
04242             break;
04243          default:
04244             count++;
04245             break;
04246          }
04247          ao2_ref(m, -1);
04248       }
04249       ast_mutex_unlock(&q->lock);
04250    } else
04251       ast_log(LOG_WARNING, "queue %s was not found\n", name);
04252 
04253    snprintf(buf, len, "%d", count);
04254    ast_module_user_remove(lu);
04255 
04256    return 0;
04257 }

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

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

04303 {
04304    struct ast_module_user *u;
04305    struct call_queue *q;
04306    struct member *m;
04307 
04308    /* Ensure an otherwise empty list doesn't return garbage */
04309    buf[0] = '\0';
04310 
04311    if (ast_strlen_zero(data)) {
04312       ast_log(LOG_ERROR, "QUEUE_MEMBER_LIST requires an argument: queuename\n");
04313       return -1;
04314    }
04315    
04316    u = ast_module_user_add(chan);
04317 
04318    AST_LIST_LOCK(&queues);
04319    AST_LIST_TRAVERSE(&queues, q, list) {
04320       if (!strcasecmp(q->name, data)) {
04321          ast_mutex_lock(&q->lock);
04322          break;
04323       }
04324    }
04325    AST_LIST_UNLOCK(&queues);
04326 
04327    if (q) {
04328       int buflen = 0, count = 0;
04329       struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
04330 
04331       while ((m = ao2_iterator_next(&mem_iter))) {
04332          /* strcat() is always faster than printf() */
04333          if (count++) {
04334             strncat(buf + buflen, ",", len - buflen - 1);
04335             buflen++;
04336          }
04337          strncat(buf + buflen, m->membername, len - buflen - 1);
04338          buflen += strlen(m->membername);
04339          /* Safeguard against overflow (negative length) */
04340          if (buflen >= len - 2) {
04341             ao2_ref(m, -1);
04342             ast_log(LOG_WARNING, "Truncating list\n");
04343             break;
04344          }
04345          ao2_ref(m, -1);
04346       }
04347       ast_mutex_unlock(&q->lock);
04348    } else
04349       ast_log(LOG_WARNING, "queue %s was not found\n", data);
04350 
04351    /* We should already be terminated, but let's make sure. */
04352    buf[len - 1] = '\0';
04353    ast_module_user_remove(u);
04354 
04355    return 0;
04356 }

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

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

04260 {
04261    int count = 0;
04262    struct call_queue *q;
04263    struct ast_module_user *lu;
04264    struct ast_variable *var = NULL;
04265 
04266    buf[0] = '\0';
04267    
04268    if (ast_strlen_zero(data)) {
04269       ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
04270       return -1;
04271    }
04272 
04273    lu = ast_module_user_add(chan);
04274    
04275    AST_LIST_LOCK(&queues);
04276    AST_LIST_TRAVERSE(&queues, q, list) {
04277       if (!strcasecmp(q->name, data)) {
04278          ast_mutex_lock(&q->lock);
04279          break;
04280       }
04281    }
04282    AST_LIST_UNLOCK(&queues);
04283 
04284    if (q) {
04285       count = q->count;
04286       ast_mutex_unlock(&q->lock);
04287    } else if ((var = ast_load_realtime("queues", "name", data, NULL))) {
04288       /* if the queue is realtime but was not found in memory, this
04289        * means that the queue had been deleted from memory since it was 
04290        * "dead." This means it has a 0 waiting count
04291        */
04292       count = 0;
04293       ast_variables_destroy(var);
04294    } else
04295       ast_log(LOG_WARNING, "queue %s was not found\n", data);
04296 
04297    snprintf(buf, len, "%d", count);
04298    ast_module_user_remove(lu);
04299    return 0;
04300 }

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

References call_queue::announce, call_queue::announcefrequency, call_queue::announceholdtime, ANNOUNCEHOLDTIME_ALWAYS, ANNOUNCEHOLDTIME_ONCE, ast_copy_string(), 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::ringlimit, 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, call_queue::timeout, call_queue::timeoutrestart, call_queue::weight, and call_queue::wrapuptime.

Referenced by find_queue_by_name_rt(), and reload_queues().

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

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

Definition at line 4734 of file app_queue.c.

References __queues_show().

Referenced by __queues_show().

04735 {
04736    return __queues_show(NULL, 0, fd, argc, argv);
04737 }

static void queue_transfer_destroy ( void *  data  )  [static]

Definition at line 2611 of file app_queue.c.

References ast_free.

02612 {
02613    struct queue_transfer_ds *qtds = data;
02614    ast_free(qtds);
02615 }

static void queue_transfer_fixup ( void *  data,
struct ast_channel old_chan,
struct ast_channel new_chan 
) [static]

Log an attended transfer when a queue caller channel is masqueraded.

When a caller is masqueraded, we want to log a transfer. Fixup time is the closest we can come to when the actual transfer occurs. This happens during the masquerade after datastores are moved from old_chan to new_chan. This is why new_chan is referenced for exten, context, and datastore information.

At the end of this, we want to remove the datastore so that this fixup function is not called on any future masquerades of the caller during the current call.

Definition at line 2634 of file app_queue.c.

References ast_channel_datastore_find(), ast_channel_datastore_remove(), ast_log(), ast_queue_log(), queue_transfer_ds::callcompletedinsl, queue_ent::chan, ast_channel::context, ast_channel::exten, LOG_WARNING, queue_transfer_ds::member, member::membername, call_queue::name, queue_ent::parent, queue_transfer_ds::qe, queue_transfer_info, queue_ent::start, queue_transfer_ds::starttime, ast_channel::uniqueid, and update_queue().

02635 {
02636    struct queue_transfer_ds *qtds = data;
02637    struct queue_ent *qe = qtds->qe;
02638    struct member *member = qtds->member;
02639    time_t callstart = qtds->starttime;
02640    int callcompletedinsl = qtds->callcompletedinsl;
02641    struct ast_datastore *datastore;
02642 
02643    ast_queue_log(qe->parent->name, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld",
02644             new_chan->exten, new_chan->context, (long) (callstart - qe->start),
02645             (long) (time(NULL) - callstart));
02646 
02647    update_queue(qe->parent, member, callcompletedinsl);
02648    
02649    /* No need to lock the channels because they are already locked in ast_do_masquerade */
02650    if ((datastore = ast_channel_datastore_find(old_chan, &queue_transfer_info, NULL))) {
02651       ast_channel_datastore_remove(old_chan, datastore);
02652    } else {
02653       ast_log(LOG_WARNING, "Can't find the queue_transfer datastore.\n");
02654    }
02655 }

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

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

01655 {
01656    int oldvalue;
01657 
01658    /* Calculate holdtime using an exponential average */
01659    /* Thanks to SRT for this contribution */
01660    /* 2^2 (4) is the filter coefficient; a higher exponent would give old entries more weight */
01661 
01662    ast_mutex_lock(&qe->parent->lock);
01663    oldvalue = qe->parent->holdtime;
01664    qe->parent->holdtime = (((oldvalue << 2) - oldvalue) + newholdtime) >> 2;
01665    ast_mutex_unlock(&qe->parent->lock);
01666 }

static void record_abandoned ( struct queue_ent qe  )  [static]

Definition at line 2074 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, queue_ent::start, and ast_channel::uniqueid.

Referenced by queue_exec(), and try_calling().

02075 {
02076    ast_mutex_lock(&qe->parent->lock);
02077    manager_event(EVENT_FLAG_AGENT, "QueueCallerAbandon",
02078       "Queue: %s\r\n"
02079       "Uniqueid: %s\r\n"
02080       "Position: %d\r\n"
02081       "OriginalPosition: %d\r\n"
02082       "HoldTime: %d\r\n",
02083       qe->parent->name, qe->chan->uniqueid, qe->pos, qe->opos, (int)(time(NULL) - qe->start));
02084 
02085    qe->parent->callsabandoned++;
02086    ast_mutex_unlock(&qe->parent->lock);
02087 }

static int reload ( void   )  [static]

Definition at line 5254 of file app_queue.c.

References reload_queues().

05255 {
05256    reload_queues();
05257    return 0;
05258 }

static void reload_queue_members ( void   )  [static]

Definition at line 3502 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(), errno, member::interface, ast_db_entry::key, call_queue::list, 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, and RES_OUTOFMEMORY.

Referenced by load_module().

03503 {
03504    char *cur_ptr;
03505    char *queue_name;
03506    char *member;
03507    char *interface;
03508    char *membername = NULL;
03509    char *penalty_tok;
03510    int penalty = 0;
03511    char *paused_tok;
03512    int paused = 0;
03513    struct ast_db_entry *db_tree;
03514    struct ast_db_entry *entry;
03515    struct call_queue *cur_queue;
03516    char queue_data[PM_MAX_LEN];
03517 
03518    AST_LIST_LOCK(&queues);
03519 
03520    /* Each key in 'pm_family' is the name of a queue */
03521    db_tree = ast_db_gettree(pm_family, NULL);
03522    for (entry = db_tree; entry; entry = entry->next) {
03523 
03524       queue_name = entry->key + strlen(pm_family) + 2;
03525 
03526       AST_LIST_TRAVERSE(&queues, cur_queue, list) {
03527          ast_mutex_lock(&cur_queue->lock);
03528          if (!strcmp(queue_name, cur_queue->name))
03529             break;
03530          ast_mutex_unlock(&cur_queue->lock);
03531       }
03532       
03533       if (!cur_queue)
03534          cur_queue = load_realtime_queue(queue_name);
03535 
03536       if (!cur_queue) {
03537          /* If the queue no longer exists, remove it from the
03538           * database */
03539          ast_log(LOG_WARNING, "Error loading persistent queue: '%s': it does not exist\n", queue_name);
03540          ast_db_del(pm_family, queue_name);
03541          continue;
03542       } else
03543          ast_mutex_unlock(&cur_queue->lock);
03544 
03545       if (ast_db_get(pm_family, queue_name, queue_data, PM_MAX_LEN))
03546          continue;
03547 
03548       cur_ptr = queue_data;
03549       while ((member = strsep(&cur_ptr, "|"))) {
03550          if (ast_strlen_zero(member))
03551             continue;
03552 
03553          interface = strsep(&member, ";");
03554          penalty_tok = strsep(&member, ";");
03555          paused_tok = strsep(&member, ";");
03556          membername = strsep(&member, ";");
03557 
03558          if (!penalty_tok) {
03559             ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (penalty)\n", queue_name);
03560             break;
03561          }
03562          penalty = strtol(penalty_tok, NULL, 10);
03563          if (errno == ERANGE) {
03564             ast_log(LOG_WARNING, "Error converting penalty: %s: Out of range.\n", penalty_tok);
03565             break;
03566          }
03567          
03568          if (!paused_tok) {
03569             ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (paused)\n", queue_name);
03570             break;
03571          }
03572          paused = strtol(paused_tok, NULL, 10);
03573          if ((errno == ERANGE) || paused < 0 || paused > 1) {
03574             ast_log(LOG_WARNING, "Error converting paused: %s: Expected 0 or 1.\n", paused_tok);
03575             break;
03576          }
03577          if (ast_strlen_zero(membername))
03578             membername = interface;
03579 
03580          if (option_debug)
03581             ast_log(LOG_DEBUG, "Reload Members: Queue: %s  Member: %s  Name: %s  Penalty: %d  Paused: %d\n", queue_name, interface, membername, penalty, paused);
03582          
03583          if (add_to_queue(queue_name, interface, membername, penalty, paused, 0) == RES_OUTOFMEMORY) {
03584             ast_log(LOG_ERROR, "Out of Memory when reloading persistent queue member\n");
03585             break;
03586          }
03587       }
03588    }
03589 
03590    AST_LIST_UNLOCK(&queues);
03591    if (db_tree) {
03592       ast_log(LOG_NOTICE, "Queue members successfully reloaded from database.\n");
03593       ast_db_freetree(db_tree);
03594    }
03595 }

static int reload_queues ( void   )  [static]

Definition at line 4401 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_copy_string(), 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().

04402 {
04403    struct call_queue *q;
04404    struct ast_config *cfg;
04405    char *cat, *tmp;
04406    struct ast_variable *var;
04407    struct member *cur, *newm;
04408    struct ao2_iterator mem_iter;
04409    int new;
04410    const char *general_val = NULL;
04411    char parse[80];
04412    char *interface;
04413    char *membername = NULL;
04414    int penalty;
04415    AST_DECLARE_APP_ARGS(args,
04416       AST_APP_ARG(interface);
04417       AST_APP_ARG(penalty);
04418       AST_APP_ARG(membername);
04419    );
04420    
04421    if (!(cfg = ast_config_load("queues.conf"))) {
04422       ast_log(LOG_NOTICE, "No call queueing config file (queues.conf), so no call queues\n");
04423       return 0;
04424    }
04425    AST_LIST_LOCK(&queues);
04426    use_weight=0;
04427    /* Mark all non-realtime queues as dead for the moment */
04428    AST_LIST_TRAVERSE(&queues, q, list) {
04429       if (!q->realtime) {
04430          q->dead = 1;
04431          q->found = 0;
04432       }
04433    }
04434 
04435    /* Chug through config file */
04436    cat = NULL;
04437    while ((cat = ast_category_browse(cfg, cat)) ) {
04438       if (!strcasecmp(cat, "general")) {  
04439          /* Initialize global settings */
04440          queue_debug = 0;
04441          if ((general_val = ast_variable_retrieve(cfg, "general", "debug")))
04442             queue_debug = ast_true(general_val);
04443          queue_persistent_members = 0;
04444          if ((general_val = ast_variable_retrieve(cfg, "general", "persistentmembers")))
04445             queue_persistent_members = ast_true(general_val);
04446          autofill_default = 0;
04447          if ((general_val = ast_variable_retrieve(cfg, "general", "autofill")))
04448             autofill_default = ast_true(general_val);
04449          montype_default = 0;
04450          if ((general_val = ast_variable_retrieve(cfg, "general", "monitor-type")))
04451             if (!strcasecmp(general_val, "mixmonitor"))
04452                montype_default = 1;
04453       } else { /* Define queue */
04454          /* Look for an existing one */
04455          AST_LIST_TRAVERSE(&queues, q, list) {
04456             if (!strcmp(q->name, cat))
04457                break;
04458          }
04459          if (!q) {
04460             /* Make one then */
04461             if (!(q = alloc_queue(cat))) {
04462                /* TODO: Handle memory allocation failure */
04463             }
04464             new = 1;
04465          } else
04466             new = 0;
04467          if (q) {
04468             if (!new)
04469                ast_mutex_lock(&q->lock);
04470             /* Check if a queue with this name already exists */
04471             if (q->found) {
04472                ast_log(LOG_WARNING, "Queue '%s' already defined! Skipping!\n", cat);
04473                if (!new)
04474                   ast_mutex_unlock(&q->lock);
04475                continue;
04476             }
04477             /* Re-initialize the queue, and clear statistics */
04478             init_queue(q);
04479             clear_queue(q);
04480             mem_iter = ao2_iterator_init(q->members, 0);
04481             while ((cur = ao2_iterator_next(&mem_iter))) {
04482                if (!cur->dynamic) {
04483                   cur->delme = 1;
04484                }
04485                ao2_ref(cur, -1);
04486             }
04487             for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
04488                if (!strcasecmp(var->name, "member")) {
04489                   struct member tmpmem;
04490                   membername = NULL;
04491 
04492                   /* Add a new member */
04493                   ast_copy_string(parse, var->value, sizeof(parse));
04494                   
04495                   AST_NONSTANDARD_APP_ARGS(args, parse, ',');
04496 
04497                   interface = args.interface;
04498                   if (!ast_strlen_zero(args.penalty)) {
04499                      tmp = args.penalty;
04500                      while (*tmp && *tmp < 33) tmp++;
04501                      penalty = atoi(tmp);
04502                      if (penalty < 0) {
04503                         penalty = 0;
04504                      }
04505                   } else
04506                      penalty = 0;
04507 
04508                   if (!ast_strlen_zero(args.membername)) {
04509                      membername = args.membername;
04510                      while (*membername && *membername < 33) membername++;
04511                   }
04512 
04513                   /* Find the old position in the list */
04514                   ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
04515                   cur = ao2_find(q->members, &tmpmem, OBJ_POINTER | OBJ_UNLINK);
04516 
04517                   newm = create_queue_member(interface, membername, penalty, cur ? cur->paused : 0);
04518                   ao2_link(q->members, newm);
04519                   ao2_ref(newm, -1);
04520                   newm = NULL;
04521 
04522                   if (cur)
04523                      ao2_ref(cur, -1);
04524                   else {
04525                      /* Add them to the master int list if necessary */
04526                      add_to_interfaces(interface);
04527                      q->membercount++;
04528                   }
04529                } else {
04530                   queue_set_param(q, var->name, var->value, var->lineno, 1);
04531                }
04532             }
04533 
04534             /* Free remaining members marked as delme */
04535             mem_iter = ao2_iterator_init(q->members, 0);
04536             while ((cur = ao2_iterator_next(&mem_iter))) {
04537                if (! cur->delme) {
04538                   ao2_ref(cur, -1);
04539                   continue;
04540                }
04541 
04542                q->membercount--;
04543                ao2_unlink(q->members, cur);
04544                remove_from_interfaces(cur->interface);
04545                ao2_ref(cur, -1);
04546             }
04547 
04548             if (q->strategy == QUEUE_STRATEGY_ROUNDROBIN)
04549                rr_dep_warning();
04550 
04551             if (new) {
04552                AST_LIST_INSERT_HEAD(&queues, q, list);
04553             } else
04554                ast_mutex_unlock(&q->lock);
04555          }
04556       }
04557    }
04558    ast_config_destroy(cfg);
04559    AST_LIST_TRAVERSE_SAFE_BEGIN(&queues, q, list) {
04560       if (q->dead) {
04561          AST_LIST_REMOVE_CURRENT(&queues, list);
04562          if (!q->count)
04563             destroy_queue(q);
04564          else
04565             ast_log(LOG_DEBUG, "XXX Leaking a little memory :( XXX\n");
04566       } else {
04567          ast_mutex_lock(&q->lock);
04568          mem_iter = ao2_iterator_init(q->members, 0);
04569          while ((cur = ao2_iterator_next(&mem_iter))) {
04570             if (cur->dynamic)
04571                q->membercount++;
04572             cur->status = ast_device_state(cur->interface);
04573             ao2_ref(cur, -1);
04574          }
04575          ast_mutex_unlock(&q->lock);
04576       }
04577    }
04578    AST_LIST_TRAVERSE_SAFE_END;
04579    AST_LIST_UNLOCK(&queues);
04580    return 1;
04581 }

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

Definition at line 932 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(), member_interface::list, LOG_DEBUG, and option_debug.

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

00933 {
00934    struct member_interface *curint;
00935 
00936    if (interface_exists_global(interface))
00937       return 0;
00938 
00939    AST_LIST_LOCK(&interfaces);
00940    AST_LIST_TRAVERSE_SAFE_BEGIN(&interfaces, curint, list) {
00941       if (!strcasecmp(curint->interface, interface)) {
00942          if (option_debug)
00943             ast_log(LOG_DEBUG, "Removing %s from the list of interfaces that make up all of our queue members.\n", interface);
00944          AST_LIST_REMOVE_CURRENT(&interfaces, list);
00945          free(curint);
00946          break;
00947       }
00948    }
00949    AST_LIST_TRAVERSE_SAFE_END;
00950    AST_LIST_UNLOCK(&interfaces);
00951 
00952    return 0;
00953 }

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

Definition at line 3348 of file app_queue.c.

References ao2_find(), ao2_ref(), ao2_unlink(), ast_copy_string(), 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().

03349 {
03350    struct call_queue *q;
03351    struct member *mem, tmpmem;
03352    int res = RES_NOSUCHQUEUE;
03353 
03354    ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
03355 
03356    AST_LIST_LOCK(&queues);
03357    AST_LIST_TRAVERSE(&queues, q, list) {
03358       ast_mutex_lock(&q->lock);
03359       if (strcmp(q->name, queuename)) {
03360          ast_mutex_unlock(&q->lock);
03361          continue;
03362       }
03363 
03364       if ((mem = ao2_find(q->members, &tmpmem, OBJ_POINTER))) {
03365          /* XXX future changes should beware of this assumption!! */
03366          if (!mem->dynamic) {
03367             res = RES_NOT_DYNAMIC;
03368             ao2_ref(mem, -1);
03369             ast_mutex_unlock(&q->lock);
03370             break;
03371          }
03372          q->membercount--;
03373          manager_event(EVENT_FLAG_AGENT, "QueueMemberRemoved",
03374             "Queue: %s\r\n"
03375             "Location: %s\r\n"
03376             "MemberName: %s\r\n",
03377             q->name, mem->interface, mem->membername);
03378          ao2_unlink(q->members, mem);
03379          ao2_ref(mem, -1);
03380 
03381          if (queue_persistent_members)
03382             dump_queue_members(q);
03383          
03384          res = RES_OKAY;
03385       } else {
03386          res = RES_EXISTS;
03387       }
03388       ast_mutex_unlock(&q->lock);
03389       break;
03390    }
03391 
03392    if (res == RES_OKAY)
03393       remove_from_interfaces(interface);
03394 
03395    AST_LIST_UNLOCK(&queues);
03396 
03397    return res;
03398 }

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

References ast_channel::adsicpe, ast_channel::appl, ast_call(), ast_cdr_busy(), ast_channel_inherit_variables(), ast_channel_lock, ast_channel_unlock, ast_copy_string(), 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, LOG_NOTICE, manager_event(), callattempt::member, member::membername, ast_channel::name, call_queue::name, ast_channel::nativeformats, option_debug, option_verbose, queue_ent::parent, member::paused, pbx_builtin_getvar_helper(), ast_channel::priority, QUEUE_EVENT_VARIABLES, member::ringcount, 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().

01810 {
01811    int res;
01812    int status;
01813    char tech[256];
01814    char *location;
01815    const char *macrocontext, *macroexten;
01816 
01817    /* on entry here, we know that tmp->chan == NULL */
01818    if (qe->parent->wrapuptime && (time(NULL) - tmp->lastcall < qe->parent->wrapuptime)) {
01819       if (queue_debug)
01820          ast_log(LOG_NOTICE, "Wrapuptime not yet expired for %s\n", tmp->interface);
01821       if (qe->chan->cdr)
01822          ast_cdr_busy(qe->chan->cdr);
01823       tmp->stillgoing = 0;
01824       (*busies)++;
01825       return 0;
01826    }
01827 
01828    if (!qe->parent->ringinuse && (tmp->member->status != AST_DEVICE_NOT_INUSE) && (tmp->member->status != AST_DEVICE_UNKNOWN)) {
01829       if (queue_debug)
01830          ast_log(LOG_NOTICE, "%s in use, can't receive call\n", tmp->interface);
01831       if (qe->chan->cdr)
01832          ast_cdr_busy(qe->chan->cdr);
01833       tmp->stillgoing = 0;
01834       return 0;
01835    }
01836 
01837    if (tmp->member->paused) {
01838       if (queue_debug)
01839          ast_log(LOG_NOTICE, "%s paused, can't receive call\n", tmp->interface);
01840       if (qe->chan->cdr)
01841          ast_cdr_busy(qe->chan->cdr);
01842       tmp->stillgoing = 0;
01843       return 0;
01844    }
01845    if (use_weight && compare_weight(qe->parent,tmp->member)) {
01846       if (queue_debug)
01847          ast_log(LOG_NOTICE, "Priority queue delaying call to %s:%s\n", qe->parent->name, tmp->interface);
01848       if (qe->chan->cdr)
01849          ast_cdr_busy(qe->chan->cdr);
01850       tmp->stillgoing = 0;
01851       (*busies)++;
01852       return 0;
01853    }
01854 
01855    ast_copy_string(tech, tmp->interface, sizeof(tech));
01856    if ((location = strchr(tech, '/')))
01857       *location++ = '\0';
01858    else
01859       location = "";
01860 
01861    /* Request the peer */
01862    tmp->chan = ast_request(tech, qe->chan->nativeformats, location, &status);
01863    if (!tmp->chan) {       /* If we can't, just go on to the next call */
01864       if (queue_debug)
01865          ast_log(LOG_NOTICE, "Unable to create channel of type '%s' for Queue\n", tech);
01866       if (qe->chan->cdr)
01867          ast_cdr_busy(qe->chan->cdr);
01868       tmp->stillgoing = 0;
01869 
01870       update_status(tmp->member->interface, ast_device_state(tmp->member->interface));
01871 
01872       ast_mutex_lock(&qe->parent->lock);
01873       qe->parent->rrpos++;
01874       ast_mutex_unlock(&qe->parent->lock);
01875 
01876       (*busies)++;
01877       return 0;
01878    }
01879    
01880    /* Increment ring count */
01881    tmp->member->ringcount++;
01882    tmp->chan->appl = "AppQueue";
01883    tmp->chan->data = "(Outgoing Line)";
01884    tmp->chan->whentohangup = 0;
01885    if (tmp->chan->cid.cid_num)
01886       free(tmp->chan->cid.cid_num);
01887    tmp->chan->cid.cid_num = ast_strdup(qe->chan->cid.cid_num);
01888    if (tmp->chan->cid.cid_name)
01889       free(tmp->chan->cid.cid_name);
01890    tmp->chan->cid.cid_name = ast_strdup(qe->chan->cid.cid_name);
01891    if (tmp->chan->cid.cid_ani)
01892       free(tmp->chan->cid.cid_ani);
01893    tmp->chan->cid.cid_ani = ast_strdup(qe->chan->cid.cid_ani);
01894 
01895    /* Inherit specially named variables from parent channel */
01896    ast_channel_inherit_variables(qe->chan, tmp->chan);
01897 
01898    /* Presense of ADSI CPE on outgoing channel follows ours */
01899    tmp->chan->adsicpe = qe->chan->adsicpe;
01900 
01901    /* Inherit context and extension */
01902    ast_channel_lock(qe->chan);
01903    macrocontext = pbx_builtin_getvar_helper(qe->chan, "MACRO_CONTEXT");
01904    if (!ast_strlen_zero(macrocontext))
01905       ast_copy_string(tmp->chan->dialcontext, macrocontext, sizeof(tmp->chan->dialcontext));
01906    else
01907       ast_copy_string(tmp->chan->dialcontext, qe->chan->context, sizeof(tmp->chan->dialcontext));
01908    macroexten = pbx_builtin_getvar_helper(qe->chan, "MACRO_EXTEN");
01909    if (!ast_strlen_zero(macroexten))
01910       ast_copy_string(tmp->chan->exten, macroexten, sizeof(tmp->chan->exten));
01911    else
01912       ast_copy_string(tmp->chan->exten, qe->chan->exten, sizeof(tmp->chan->exten));
01913    ast_channel_unlock(qe->chan);
01914 
01915    /* Place the call, but don't wait on the answer */
01916    if ((res = ast_call(tmp->chan, location, 0))) {
01917       /* Again, keep going even if there's an error */
01918       if (option_debug)
01919          ast_log(LOG_DEBUG, "ast call on peer returned %d\n", res);
01920       if (option_verbose > 2)
01921          ast_verbose(VERBOSE_PREFIX_3 "Couldn't call %s\n", tmp->interface);
01922       do_hang(tmp);
01923       (*busies)++;
01924       update_status(tmp->member->interface, ast_device_state(tmp->member->interface));
01925       return 0;
01926    } else if (qe->parent->eventwhencalled) {
01927       char vars[2048];
01928 
01929       manager_event(EVENT_FLAG_AGENT, "AgentCalled",
01930                "AgentCalled: %s\r\n"
01931                "AgentName: %s\r\n"
01932                "ChannelCalling: %s\r\n"
01933                "CallerID: %s\r\n"
01934                "CallerIDName: %s\r\n"
01935                "Context: %s\r\n"
01936                "Extension: %s\r\n"
01937                "Priority: %d\r\n"
01938                "%s",
01939                tmp->interface, tmp->member->membername, qe->chan->name,
01940                tmp->chan->cid.cid_num ? tmp->chan->cid.cid_num : "unknown",
01941                tmp->chan->cid.cid_name ? tmp->chan->cid.cid_name : "unknown",
01942                qe->chan->context, qe->chan->exten, qe->chan->priority,
01943                qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
01944       if (option_verbose > 2)
01945          ast_verbose(VERBOSE_PREFIX_3 "Called %s\n", tmp->interface);
01946    }
01947 
01948    update_status(tmp->member->interface, ast_device_state(tmp->member->interface));
01949    return 1;
01950 }

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

01977 {
01978    int ret = 0;
01979 
01980    while (ret == 0) {
01981       struct callattempt *best = find_best(outgoing);
01982       if (!best) {
01983          if (option_debug)
01984             ast_log(LOG_DEBUG, "Nobody left to try ringing in queue\n");
01985          break;
01986       }
01987       if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
01988          struct callattempt *cur;
01989          /* Ring everyone who shares this best metric (for ringall) */
01990          for (cur = outgoing; cur; cur = cur->q_next) {
01991             if (cur->stillgoing && !cur->chan && cur->metric <= best->metric) {
01992                if (option_debug)
01993                   ast_log(LOG_DEBUG, "(Parallel) Trying '%s' with metric %d\n", cur->interface, cur->metric);
01994                ret |= ring_entry(qe, cur, busies);
01995             }
01996          }
01997       } else {
01998          /* Ring just the best channel */
01999          if (option_debug)
02000             ast_log(LOG_DEBUG, "Trying '%s' with metric %d\n", best->interface, best->metric);
02001          ret = ring_entry(qe, best, busies);
02002       }
02003    }
02004 
02005    return ret;
02006 }

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 2090 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(), ast_channel::uniqueid, and VERBOSE_PREFIX_3.

02091 {
02092    if (option_verbose > 2)
02093       ast_verbose( VERBOSE_PREFIX_3 "Nobody picked up in %d ms\n", rnatime);
02094    ast_queue_log(qe->parent->name, qe->chan->uniqueid, membername, "RINGNOANSWER", "%d", rnatime);
02095    if (qe->parent->autopause) {
02096       if (!set_member_paused(qe->parent->name, interface, 1)) {
02097          if (option_verbose > 2)
02098             ast_verbose( VERBOSE_PREFIX_3 "Auto-Pausing Queue Member %s in queue %s since they failed to answer.\n", interface, qe->parent->name);
02099       } else {
02100          if (option_verbose > 2)
02101             ast_verbose( VERBOSE_PREFIX_3 "Failed to pause Queue Member %s in queue %s!\n", interface, qe->parent->name);
02102       }
02103    }
02104    return;
02105 }

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

Definition at line 3713 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, ast_channel::name, parse(), pbx_builtin_setvar_helper(), remove_from_queue(), RES_EXISTS, RES_NOSUCHQUEUE, RES_NOT_DYNAMIC, RES_OKAY, and ast_channel::uniqueid.

Referenced by load_module().

03714 {
03715    int res=-1;
03716    struct ast_module_user *lu;
03717    char *parse, *temppos = NULL;
03718    int priority_jump = 0;
03719    AST_DECLARE_APP_ARGS(args,
03720       AST_APP_ARG(queuename);
03721       AST_APP_ARG(interface);
03722       AST_APP_ARG(options);
03723    );
03724 
03725 
03726    if (ast_strlen_zero(data)) {
03727       ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename[|interface[|options]])\n");
03728       return -1;
03729    }
03730 
03731    parse = ast_strdupa(data);
03732 
03733    AST_STANDARD_APP_ARGS(args, parse);
03734 
03735    lu = ast_module_user_add(chan);
03736 
03737    if (ast_strlen_zero(args.interface)) {
03738       args.interface = ast_strdupa(chan->name);
03739       temppos = strrchr(args.interface, '-');
03740       if (temppos)
03741          *temppos = '\0';
03742    }
03743 
03744    if (args.options) {
03745       if (strchr(args.options, 'j'))
03746          priority_jump = 1;
03747    }
03748 
03749    switch (remove_from_queue(args.queuename, args.interface)) {
03750    case RES_OKAY:
03751       ast_queue_log(args.queuename, chan->uniqueid, args.interface, "REMOVEMEMBER", "%s", "");
03752       ast_log(LOG_NOTICE, "Removed interface '%s' from queue '%s'\n", args.interface, args.queuename);
03753       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "REMOVED");
03754       res = 0;
03755       break;
03756    case RES_EXISTS:
03757       ast_log(LOG_DEBUG, "Unable to remove interface '%s' from queue '%s': Not there\n", args.interface, args.queuename);
03758       if (priority_jump || ast_opt_priority_jumping)
03759          ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
03760       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTINQUEUE");
03761       res = 0;
03762       break;
03763    case RES_NOSUCHQUEUE:
03764       ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", args.queuename);
03765       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOSUCHQUEUE");
03766       res = 0;
03767       break;
03768    case RES_NOT_DYNAMIC:
03769       ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': '%s' is not a dynamic member\n", args.queuename, args.interface);
03770       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTDYNAMIC");
03771       res = 0;
03772       break;
03773    }
03774 
03775    ast_module_user_remove(lu);
03776 
03777    return res;
03778 }

static void rr_dep_warning ( void   )  [static]

Definition at line 464 of file app_queue.c.

References ast_log(), and LOG_NOTICE.

Referenced by reload_queues().

00465 {
00466    static unsigned int warned = 0;
00467 
00468    if (!warned) {
00469       ast_log(LOG_NOTICE, "The 'roundrobin' queue strategy is deprecated. Please use the 'rrmemory' strategy instead.\n");
00470       warned = 1;
00471    }
00472 }

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

References add_to_interfaces(), ao2_find(), ao2_ref(), ast_copy_string(), 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().

01125 {
01126    struct member *m, tmpmem;
01127    int penalty = 0;
01128    int paused  = 0;
01129 
01130    if (penalty_str) {
01131       penalty = atoi(penalty_str);
01132       if (penalty < 0)
01133          penalty = 0;
01134    }
01135 
01136    if (paused_str) {
01137       paused = atoi(paused_str);
01138       if (paused < 0)
01139          paused = 0;
01140    }
01141 
01142    /* Find the member, or the place to put a new one. */
01143    ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
01144    m = ao2_find(q->members, &tmpmem, OBJ_POINTER);
01145 
01146    /* Create a new one if not found, else update penalty */
01147    if (!m) {
01148       if ((m = create_queue_member(interface, membername, penalty, paused))) {
01149          m->dead = 0;
01150          m->realtime = 1;
01151          add_to_interfaces(interface);
01152          ao2_link(q->members, m);
01153          ao2_ref(m, -1);
01154          m = NULL;
01155          q->membercount++;
01156       }
01157    } else {
01158       m->dead = 0;   /* Do not delete this one. */
01159       if (paused_str)
01160          m->paused = paused;
01161       m->penalty = penalty;
01162       ao2_ref(m, -1);
01163    }
01164 }

static int say_periodic_announcement ( struct queue_ent qe  )  [static]

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

02033 {
02034    int res = 0;
02035    time_t now;
02036 
02037    /* Get the current time */
02038    time(&now);
02039 
02040    /* Check to see if it is time to announce */
02041    if ((now - qe->last_periodic_announce_time) < qe->parent->periodicannouncefrequency)
02042       return 0;
02043 
02044    /* Stop the music on hold so we can play our own file */
02045    ast_moh_stop(qe->chan);
02046 
02047    if (option_verbose > 2)
02048       ast_verbose(VERBOSE_PREFIX_3 "Playing periodic announcement\n");
02049 
02050    /* Check to make sure we have a sound file. If not, reset to the first sound file */
02051    if (qe->last_periodic_announce_sound >= MAX_PERIODIC_ANNOUNCEMENTS || !strlen(qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound])) {
02052       qe->last_periodic_announce_sound = 0;
02053    }
02054    
02055    /* play the announcement */
02056    res = play_file(qe->chan, qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound]);
02057 
02058    if ((res > 0 && !valid_exit(qe, res)) || res < 0)
02059       res = 0;
02060 
02061    /* Resume Music on Hold if the caller is going to stay in the queue */
02062    if (!res)
02063       ast_moh_start(qe->chan, qe->moh, NULL);
02064 
02065    /* update last_periodic_announce_time */
02066    qe->last_periodic_announce_time = now;
02067 
02068    /* Update the current periodic announcement to the next announcement */
02069    qe->last_periodic_announce_sound++;
02070    
02071    return res;
02072 }

static int say_position ( struct queue_ent qe  )  [static]

Definition at line 1546 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, ast_channel::language, queue_ent::last_pos, queue_ent::last_pos_said, queue_ent::moh, ast_channel::name, 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().

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

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

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

03457 {
03458    int found = 0;
03459    struct call_queue *q;
03460    struct member *mem;
03461 
03462    /* Special event for when all queues are paused - individual events still generated */
03463    /* XXX In all other cases, we use the membername, but since this affects all queues, we cannot */
03464    if (ast_strlen_zero(queuename))
03465       ast_queue_log("NONE", "NONE", interface, (paused ? "PAUSEALL" : "UNPAUSEALL"), "%s", "");
03466 
03467    AST_LIST_LOCK(&queues);
03468    AST_LIST_TRAVERSE(&queues, q, list) {
03469       ast_mutex_lock(&q->lock);
03470       if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
03471          if ((mem = interface_exists(q, interface))) {
03472             found++;
03473             if (mem->paused == paused)
03474                ast_log(LOG_DEBUG, "%spausing already-%spaused queue member %s:%s\n", (paused ? "" : "un"), (paused ? "" : "un"), q->name, interface);
03475             mem->paused = paused;
03476 
03477             if (queue_persistent_members)
03478                dump_queue_members(q);
03479 
03480             if (mem->realtime)
03481                update_realtime_member_field(mem, q->name, "paused", paused ? "1" : "0");
03482 
03483             ast_queue_log(q->name, "NONE", mem->membername, (paused ? "PAUSE" : "UNPAUSE"), "%s", "");
03484 
03485             manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
03486                "Queue: %s\r\n"
03487                "Location: %s\r\n"
03488                "MemberName: %s\r\n"
03489                "Paused: %d\r\n",
03490                   q->name, mem->interface, mem->membername, paused);
03491             ao2_ref(mem, -1);
03492          }
03493       }
03494       ast_mutex_unlock(&q->lock);
03495    }
03496    AST_LIST_UNLOCK(&queues);
03497 
03498    return found ? RESULT_SUCCESS : RESULT_FAILURE;
03499 }

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

sets the QUEUESTATUS channel variable

Definition at line 483 of file app_queue.c.

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

Referenced by queue_exec().

00484 {
00485    int i;
00486 
00487    for (i = 0; i < sizeof(queue_results) / sizeof(queue_results[0]); i++) {
00488       if (queue_results[i].id == res) {
00489          pbx_builtin_setvar_helper(chan, "QUEUESTATUS", queue_results[i].text);
00490          return;
00491       }
00492    }
00493 }

static struct ast_datastore* setup_transfer_datastore ( struct queue_ent qe,
struct member member,
time_t  starttime,
int  callcompletedinsl 
) [static]

create a datastore for storing relevant info to log attended transfers in the queue_log

Definition at line 2672 of file app_queue.c.

References ast_calloc, ast_channel_datastore_add(), ast_channel_datastore_alloc(), ast_channel_lock, ast_channel_unlock, ast_log(), queue_ent::chan, ast_datastore::data, LOG_WARNING, queue_transfer_ds::member, queue_transfer_ds::qe, and queue_transfer_info.

Referenced by try_calling().

02673 {
02674    struct ast_datastore *ds;
02675    struct queue_transfer_ds *qtds = ast_calloc(1, sizeof(*qtds));
02676 
02677    if (!qtds) {
02678       ast_log(LOG_WARNING, "Memory allocation error!\n");
02679       return NULL;
02680    }
02681 
02682    ast_channel_lock(qe->chan);
02683    if (!(ds = ast_channel_datastore_alloc(&queue_transfer_info, NULL))) {
02684       ast_channel_unlock(qe->chan);
02685       ast_log(LOG_WARNING, "Unable to create transfer datastore. queue_log will not show attended transfer\n");
02686       return NULL;
02687    }
02688 
02689    qtds->qe = qe;
02690    /* This member is refcounted in try_calling, so no need to add it here, too */
02691    qtds->member = member;
02692    qtds->starttime = starttime;
02693    qtds->callcompletedinsl = callcompletedinsl;
02694    ds->data = qtds;
02695    ast_channel_datastore_add(qe->chan, ds);
02696    ast_channel_unlock(qe->chan);
02697    return ds;
02698 }

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

Producer of the statechange queue.

Definition at line 744 of file app_queue.c.

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

Referenced by load_module(), and unload_module().

00745 {
00746    struct statechange *sc;
00747 
00748    if (!(sc = ast_calloc(1, sizeof(*sc) + strlen(dev) + 1)))
00749       return 0;
00750 
00751    sc->state = state;
00752    strcpy(sc->dev, dev);
00753 
00754    ast_mutex_lock(&device_state.lock);
00755    AST_LIST_INSERT_TAIL(&device_state.state_change_q, sc, entry);
00756    ast_cond_signal(&device_state.cond);
00757    ast_mutex_unlock(&device_state.lock);
00758 
00759    return 0;
00760 }

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

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

02009 {
02010    struct callattempt *best = find_best(outgoing);
02011 
02012    if (best) {
02013       /* Ring just the best channel */
02014       if (option_debug)
02015          ast_log(LOG_DEBUG, "Next is '%s' with metric %d\n", best->interface, best->metric);
02016       qe->parent->rrpos = best->metric % 1000;
02017    } else {
02018       /* Just increment rrpos */
02019       if (qe->parent->wrapped) {
02020          /* No more channels, start over */
02021          qe->parent->rrpos = 0;
02022       } else {
02023          /* Prioritize next entry */
02024          qe->parent->rrpos++;
02025       }
02026    }
02027    qe->parent->wrapped = 0;
02028 
02029    return 0;
02030 }

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

Definition at line 507 of file app_queue.c.

References name, and strategies.

Referenced by queue_set_param().

00508 {
00509    int x;
00510 
00511    for (x = 0; x < sizeof(strategies) / sizeof(strategies[0]); x++) {
00512       if (!strcasecmp(strategy, strategies[x].name))
00513          return strategies[x].strategy;
00514    }
00515 
00516    return -1;
00517 }

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 2725 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_DONT_TOUCH, AST_CDR_FLAG_LOCKED, ast_cdr_noanswer(), 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_copy_string(), 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_queue_log(), ast_random(), ast_safe_sleep(), ast_say_number(), ast_set_flag, AST_STATE_UP, ast_strdupa, ast_strlen_zero(), ast_test_flag, attended_transfer_occurred(), 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, ast_channel::language, member::lastcall, leave_queue(), ast_dialed_interface::list, 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, ast_channel::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, queue_transfer_info, recalc_holdtime(), record_abandoned(), call_queue::reportholdtime, ring_one(), member::ringcount, call_queue::ringlimit, call_queue::servicelevel, call_queue::setinterfacevar, setup_transfer_datastore(), call_queue::sound_lessthan, call_queue::sound_minutes, call_queue::sound_reporthold, queue_ent::start, member::status, store_next(), call_queue::strategy, tds, ast_channel::tech, call_queue::timeout, queue_ent::tries, ast_channel_tech::type, ast_cdr::uniqueid, ast_channel::uniqueid, update_queue(), vars2manager(), and wait_for_answer().

Referenced by queue_exec().

02726 {
02727    struct member *cur;
02728    struct callattempt *outgoing = NULL; /* the list of calls we are building */
02729    int to;
02730    char oldexten[AST_MAX_EXTENSION]="";
02731    char oldcontext[AST_MAX_CONTEXT]="";
02732    char queuename[256]="";
02733    struct ast_channel *peer;
02734    struct ast_channel *which;
02735    struct callattempt *lpeer;
02736    struct member *member;
02737    struct ast_app *app;
02738    int res = 0, bridge = 0;
02739    int numbusies = 0;
02740    int x=0;
02741    char *announce = NULL;
02742    char digit = 0;
02743    time_t callstart;
02744    time_t now = time(NULL);
02745    struct ast_bridge_config bridge_config;
02746    char nondataquality = 1;
02747    char *agiexec = NULL;
02748    int ret = 0;
02749    const char *monitorfilename;
02750    const char *monitor_exec;
02751    const char *monitor_options;
02752    char tmpid[256], tmpid2[256];
02753    char meid[1024], meid2[1024];
02754    char mixmonargs[1512];
02755    struct ast_app *mixmonapp = NULL;
02756    char *p;
02757    char vars[2048];
02758    int forwardsallowed = 1;
02759    int callcompletedinsl;
02760    struct ao2_iterator memi;
02761    struct ast_datastore *datastore, *transfer_ds;
02762 
02763    ast_channel_lock(qe->chan);
02764    datastore = ast_channel_datastore_find(qe->chan, &dialed_interface_info, NULL);
02765    ast_channel_unlock(qe->chan);
02766 
02767    memset(&bridge_config, 0, sizeof(bridge_config));
02768    time(&now);
02769 
02770    /* If we've already exceeded our timeout, then just stop
02771     * This should be extremely rare. queue_exec will take care
02772     * of removing the caller and reporting the timeout as the reason.
02773     */
02774    if (qe->expire && now >= qe->expire) {
02775       res = 0;
02776       goto out;
02777    }
02778       
02779    for (; options && *options; options++)
02780       switch (*options) {
02781       case 't':
02782          ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_REDIRECT);
02783          break;
02784       case 'T':
02785          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_REDIRECT);
02786          break;
02787       case 'w':
02788          ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMON);
02789          break;
02790       case 'W':
02791          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMON);
02792          break;
02793       case 'd':
02794          nondataquality = 0;
02795          break;
02796       case 'h':
02797          ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_DISCONNECT);
02798          break;
02799       case 'H':
02800          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT);
02801          break;
02802       case 'n':
02803          if (qe->parent->strategy == QUEUE_STRATEGY_ROUNDROBIN || qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY)
02804             (*tries)++;
02805          else
02806             *tries = qe->parent->membercount;
02807          *noption = 1;
02808          break;
02809       case 'i':
02810          forwardsallowed = 0;
02811          break;
02812       }
02813 
02814    /* Hold the lock while we setup the outgoing calls */
02815    if (use_weight)
02816       AST_LIST_LOCK(&queues);
02817    ast_mutex_lock(&qe->parent->lock);
02818    if (option_debug)
02819       ast_log(LOG_DEBUG, "%s is trying to call a queue member.\n",
02820                      qe->chan->name);
02821    ast_copy_string(queuename, qe->parent->name, sizeof(queuename));
02822    if (!ast_strlen_zero(qe->announce))
02823       announce = qe->announce;
02824    if (!ast_strlen_zero(announceoverride))
02825       announce = announceoverride;
02826 
02827    memi = ao2_iterator_init(qe->parent->members, 0);
02828    while ((cur = ao2_iterator_next(&memi))) {
02829       struct callattempt *tmp = ast_calloc(1, sizeof(*tmp));
02830       struct ast_dialed_interface *di;
02831       AST_LIST_HEAD(, ast_dialed_interface) *dialed_interfaces;
02832       if (!tmp) {
02833          ao2_ref(cur, -1);
02834          ast_mutex_unlock(&qe->parent->lock);
02835          if (use_weight)
02836             AST_LIST_UNLOCK(&queues);
02837          goto out;
02838       }
02839       if (!datastore) {
02840          if (!(datastore = ast_channel_datastore_alloc(&dialed_interface_info, NULL))) {
02841             ao2_ref(cur, -1);
02842             ast_mutex_unlock(&qe->parent->lock);
02843             if (use_weight)
02844                AST_LIST_UNLOCK(&queues);
02845             free(tmp);
02846             goto out;
02847          }
02848          datastore->inheritance = DATASTORE_INHERIT_FOREVER;
02849          if (!(dialed_interfaces = ast_calloc(1, sizeof(*dialed_interfaces)))) {
02850             ao2_ref(cur, -1);
02851             ast_mutex_unlock(&qe->parent->lock);
02852             if (use_weight)
02853                AST_LIST_UNLOCK(&queues);
02854             free(tmp);
02855             goto out;
02856          }
02857          datastore->data = dialed_interfaces;
02858          AST_LIST_HEAD_INIT(dialed_interfaces);
02859 
02860          ast_channel_lock(qe->chan);
02861          ast_channel_datastore_add(qe->chan, datastore);
02862          ast_channel_unlock(qe->chan);
02863       } else
02864          dialed_interfaces = datastore->data;
02865 
02866       AST_LIST_LOCK(dialed_interfaces);
02867       AST_LIST_TRAVERSE(dialed_interfaces, di, list) {
02868          if (!strcasecmp(cur->interface, di->interface)) {
02869             ast_log(LOG_DEBUG, "Skipping dialing interface '%s' since it has already been dialed\n", 
02870                di->interface);
02871             break;
02872          }
02873       }
02874       AST_LIST_UNLOCK(dialed_interfaces);
02875       
02876       if (di) {
02877          free(tmp);
02878          continue;
02879       }
02880 
02881       /* It is always ok to dial a Local interface.  We only keep track of
02882        * which "real" interfaces have been dialed.  The Local channel will
02883        * inherit this list so that if it ends up dialing a real interface,
02884        * it won't call one that has already been called. */
02885       if (strncasecmp(cur->interface, "Local/", 6)) {
02886          if (!(di = ast_calloc(1, sizeof(*di) + strlen(cur->interface)))) {
02887             ao2_ref(cur, -1);
02888             ast_mutex_unlock(&qe->parent->lock);
02889             if (use_weight)
02890                AST_LIST_UNLOCK(&queues);
02891             free(tmp);
02892             goto out;
02893          }
02894          strcpy(di->interface, cur->interface);
02895 
02896          AST_LIST_LOCK(dialed_interfaces);
02897          AST_LIST_INSERT_TAIL(dialed_interfaces, di, list);
02898          AST_LIST_UNLOCK(dialed_interfaces);
02899       }
02900 
02901       tmp->stillgoing = -1;
02902       tmp->member = cur;
02903       tmp->oldstatus = cur->status;
02904       tmp->lastcall = cur->lastcall;
02905       ast_copy_string(tmp->interface, cur->interface, sizeof(tmp->interface));
02906       if (qe->tries == 0 && (cur->ringcount >= qe->parent->ringlimit)) {
02907          cur->ringcount = 0;
02908       }
02909       /* Special case: If we ring everyone, go ahead and ring them, otherwise
02910          just calculate their metric for the appropriate strategy */
02911       if (!calc_metric(qe->parent, cur, x++, qe, tmp)) {
02912          /* Put them in the list of outgoing thingies...  We're ready now.
02913             XXX If we're forcibly removed, these outgoing calls won't get
02914             hung up XXX */
02915          tmp->q_next = outgoing;
02916          outgoing = tmp;      
02917          /* If this line is up, don't try anybody else */
02918          if (outgoing->chan && (outgoing->chan->_state == AST_STATE_UP))
02919             break;
02920       } else {
02921          ao2_ref(cur, -1);
02922          free(tmp);
02923       }
02924    }
02925    if (qe->expire && (!qe->parent->timeout || (qe->expire - now) <= qe->parent->timeout))
02926       to = (qe->expire - now) * 1000;
02927    else
02928       to = (qe->parent->timeout) ? qe->parent->timeout * 1000 : -1;
02929    ++qe->pending;
02930    ++qe->tries;
02931    if (option_debug)
02932       ast_log(LOG_DEBUG, "%s is trying to ring one member from %s. This is try number %d\n",
02933                   qe->chan->name, queuename, qe->tries);
02934    ast_mutex_unlock(&qe->parent->lock);
02935    ring_one(qe, outgoing, &numbusies);
02936    if (use_weight)
02937       AST_LIST_UNLOCK(&queues);
02938    lpeer = wait_for_answer(qe, outgoing, &to, &digit, numbusies, ast_test_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT), forwardsallowed);
02939    /* The ast_channel_datastore_remove() function could fail here if the
02940     * datastore was moved to another channel during a masquerade. If this is
02941     * the case, don't free the datastore here because later, when the channel
02942     * to which the datastore was moved hangs up, it will attempt to free this
02943     * datastore again, causing a crash
02944     */
02945    ast_channel_lock(qe->chan);
02946    if (datastore && !ast_channel_datastore_remove(qe->chan, datastore)) {
02947       ast_channel_datastore_free(datastore);
02948    }
02949    ast_channel_unlock(qe->chan);
02950    ast_mutex_lock(&qe->parent->lock);
02951    if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY) {
02952       store_next(qe, outgoing);
02953    }
02954    ast_mutex_unlock(&qe->parent->lock);
02955    peer = lpeer ? lpeer->chan : NULL;
02956    if (!peer) {
02957       qe->pending = 0;
02958       if (to) {
02959          /* Must gotten hung up */
02960          res = -1;
02961       } else {
02962          /* User exited by pressing a digit */
02963          res = digit;
02964       }
02965       if (res == -1) {
02966          /* Post this CDR, and mark call as NOANSWER */
02967          ast_set_flag(qe->chan->cdr, AST_CDR_FLAG_DONT_TOUCH);
02968          ast_cdr_noanswer(qe->chan->cdr);
02969          if (queue_debug)
02970             ast_log(LOG_NOTICE, "%s: Nobody answered.\n", qe->chan->name);
02971       }
02972       if (qe->parent->eventwhencalled) {
02973          manager_event(EVENT_FLAG_AGENT, "AgentTimeout",
02974                               "Queue: %s\r\n"
02975                               "ChannelCalling: %s\r\n"
02976                               "Uniqueid: %s\r\n"
02977                               "Tries: %d\r\n"
02978                               "Holdtime: %ld\r\n",
02979                               queuename, qe->chan->name, qe->chan->uniqueid, qe->tries,
02980                               (long)time(NULL) - qe->start);
02981       }
02982    } else { /* peer is valid */
02983       /* Ah ha!  Someone answered within the desired timeframe.  Of course after this
02984          we will always return with -1 so that it is hung up properly after the
02985          conversation.  */
02986       if (!strcmp(qe->chan->tech->type, "Zap"))
02987          ast_channel_setoption(qe->chan, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
02988       if (!strcmp(peer->tech->type, "Zap"))
02989          ast_channel_setoption(peer, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
02990       /* Update parameters for the queue */
02991       time(&now);
02992       recalc_holdtime(qe, (now - qe->start));
02993       ast_mutex_lock(&qe->parent->lock);
02994       callcompletedinsl = ((now - qe->start) <= qe->parent->servicelevel);
02995       ast_mutex_unlock(&qe->parent->lock);
02996       member = lpeer->member;
02997       /* Increment the refcount for this member, since we're going to be using it for awhile in here. */
02998       ao2_ref(member, 1);
02999       hangupcalls(outgoing, peer);
03000       outgoing = NULL;
03001       if (announce || qe->parent->reportholdtime || qe->parent->memberdelay) {
03002          int res2;
03003 
03004          res2 = ast_autoservice_start(qe->chan);
03005          if (!res2) {
03006             if (qe->parent->memberdelay) {
03007                ast_log(LOG_NOTICE, "Delaying member connect for %d seconds\n", qe->parent->memberdelay);
03008                res2 |= ast_safe_sleep(peer, qe->parent->memberdelay * 1000);
03009             }
03010             if (!res2 && announce) {
03011                play_file(peer, announce);
03012             }
03013             if (!res2 && qe->parent->reportholdtime) {
03014                if (!play_file(peer, qe->parent->sound_reporthold)) {
03015                   int holdtime;
03016 
03017                   time(&now);
03018                   holdtime = abs((now - qe->start) / 60);
03019                   if (holdtime < 2) {
03020                      play_file(peer, qe->parent->sound_lessthan);
03021                      ast_say_number(peer, 2, AST_DIGIT_ANY, peer->language, NULL);
03022                   } else
03023                      ast_say_number(peer, holdtime, AST_DIGIT_ANY, peer->language, NULL);
03024                   play_file(peer, qe->parent->sound_minutes);
03025                }
03026             }
03027          }
03028          res2 |= ast_autoservice_stop(qe->chan);
03029          if (peer->_softhangup) {
03030             /* Agent must have hung up */
03031             ast_log(LOG_WARNING, "Agent on %s hungup on the customer.\n", peer->name);
03032             ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "AGENTDUMP", "%s", "");
03033             if (qe->parent->eventwhencalled)
03034                manager_event(EVENT_FLAG_AGENT, "AgentDump",
03035                      "Queue: %s\r\n"
03036                      "Uniqueid: %s\r\n"
03037                      "Channel: %s\r\n"
03038                      "Member: %s\r\n"
03039                      "MemberName: %s\r\n"
03040                      "%s",
03041                      queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
03042                      qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
03043             ast_hangup(peer);
03044             ao2_ref(member, -1);
03045             goto out;
03046          } else if (res2) {
03047             /* Caller must have hung up just before being connected*/
03048             ast_log(LOG_NOTICE, "Caller was about to talk to agent on %s but the caller hungup.\n", peer->name);
03049             ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "ABANDON", "%d|%d|%ld", qe->pos, qe->opos, (long)time(NULL) - qe->start);
03050             record_abandoned(qe);
03051             ast_hangup(peer);
03052             ao2_ref(member, -1);
03053             return -1;
03054          }
03055       }
03056       /* Stop music on hold */
03057       ast_moh_stop(qe->chan);
03058       /* If appropriate, log that we have a destination channel */
03059       if (qe->chan->cdr)
03060          ast_cdr_setdestchan(qe->chan->cdr, peer->name);
03061       /* Make sure channels are compatible */
03062       res = ast_channel_make_compatible(qe->chan, peer);
03063       if (res < 0) {
03064          ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "SYSCOMPAT", "%s", "");
03065          ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", qe->chan->name, peer->name);
03066          record_abandoned(qe);
03067          ast_hangup(peer);
03068          ao2_ref(member, -1);
03069          return -1;
03070       }
03071 
03072       if (qe->parent->setinterfacevar)
03073             pbx_builtin_setvar_helper(qe->chan, "MEMBERINTERFACE", member->interface);
03074 
03075       /* Begin Monitoring */
03076       if (qe->parent->monfmt && *qe->parent->monfmt) {
03077          if (!qe->parent->montype) {
03078             if (option_debug)
03079                ast_log(LOG_DEBUG, "Starting Monitor as requested.\n");
03080             monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME");
03081             if (pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC") || pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC_ARGS"))
03082                which = qe->chan;
03083             else
03084                which = peer;
03085             if (monitorfilename)
03086                ast_monitor_start(which, qe->parent->monfmt, monitorfilename, 1 );
03087             else if (qe->chan->cdr)
03088                ast_monitor_start(which, qe->parent->monfmt, qe->chan->cdr->uniqueid, 1 );
03089             else {
03090                /* Last ditch effort -- no CDR, make up something */
03091                snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random());
03092                ast_monitor_start(which, qe->parent->monfmt, tmpid, 1 );
03093             }
03094             if (qe->parent->monjoin)
03095                ast_monitor_setjoinfiles(which, 1);
03096          } else {
03097             if (option_debug)
03098                ast_log(LOG_DEBUG, "Starting MixMonitor as requested.\n");
03099             monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME");
03100             if (!monitorfilename) {
03101                if (qe->chan->cdr)
03102                   ast_copy_string(tmpid, qe->chan->cdr->uniqueid, sizeof(tmpid)-1);
03103                else
03104                   snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random());
03105             } else {
03106                ast_copy_string(tmpid2, monitorfilename, sizeof(tmpid2)-1);
03107                for (p = tmpid2; *p ; p++) {
03108                   if (*p == '^' && *(p+1) == '{') {
03109                      *p = '$';
03110                   }
03111                }
03112 
03113                memset(tmpid, 0, sizeof(tmpid));
03114                pbx_substitute_variables_helper(qe->chan, tmpid2, tmpid, sizeof(tmpid) - 1);
03115             }
03116 
03117             monitor_exec = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC");
03118             monitor_options = pbx_builtin_getvar_helper(qe->chan, "MONITOR_OPTIONS");
03119 
03120             if (monitor_exec) {
03121                ast_copy_string(meid2, monitor_exec, sizeof(meid2)-1);
03122                for (p = meid2; *p ; p++) {
03123                   if (*p == '^' && *(p+1) == '{') {
03124                      *p = '$';
03125                   }
03126                }
03127 
03128                memset(meid, 0, sizeof(meid));
03129                pbx_substitute_variables_helper(qe->chan, meid2, meid, sizeof(meid) - 1);
03130             }
03131    
03132             snprintf(tmpid2, sizeof(tmpid2)-1, "%s.%s", tmpid, qe->parent->monfmt);
03133 
03134             mixmonapp = pbx_findapp("MixMonitor");
03135 
03136             if (strchr(tmpid2, '|')) {
03137                ast_log(LOG_WARNING, "monitor-format (in queues.conf) and MONITOR_FILENAME cannot contain a '|'! Not recording.\n");
03138                mixmonapp = NULL;
03139             }
03140 
03141             if (!monitor_options)
03142                monitor_options = "";
03143             
03144             if (strchr(monitor_options, '|')) {
03145                ast_log(LOG_WARNING, "MONITOR_OPTIONS cannot contain a '|'! Not recording.\n");
03146                mixmonapp = NULL;
03147             }
03148 
03149             if (mixmonapp) {
03150                if (!ast_strlen_zero(monitor_exec))
03151                   snprintf(mixmonargs, sizeof(mixmonargs)-1, "%s|b%s|%s", tmpid2, monitor_options, monitor_exec);
03152                else
03153                   snprintf(mixmonargs, sizeof(mixmonargs)-1, "%s|b%s", tmpid2, monitor_options);
03154                   
03155                if (option_debug)
03156                   ast_log(LOG_DEBUG, "Arguments being passed to MixMonitor: %s\n", mixmonargs);
03157                /* We purposely lock the CDR so that pbx_exec does not update the application data */
03158                if (qe->chan->cdr)
03159                   ast_set_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED);
03160                ret = pbx_exec(qe->chan, mixmonapp, mixmonargs);
03161                if (qe->chan->cdr)
03162                   ast_clear_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED);
03163 
03164             } else
03165                ast_log(LOG_WARNING, "Asked to run MixMonitor on this call, but cannot find the MixMonitor app!\n");
03166 
03167          }
03168       }
03169       /* Drop out of the queue at this point, to prepare for next caller */
03170       leave_queue(qe);        
03171       if (!ast_strlen_zero(url) && ast_channel_supports_html(peer)) {
03172          if (option_debug)
03173             ast_log(LOG_DEBUG, "app_queue: sendurl=%s.\n", url);
03174          ast_channel_sendurl(peer, url);
03175       }
03176       if (!ast_strlen_zero(agi)) {
03177          if (option_debug)
03178             ast_log(LOG_DEBUG, "app_queue: agi=%s.\n", agi);
03179          app = pbx_findapp("agi");
03180          if (app) {
03181             agiexec = ast_strdupa(agi);
03182             ret = pbx_exec(qe->chan, app, agiexec);
03183          } else
03184             ast_log(LOG_WARNING, "Asked to execute an AGI on this channel, but could not find application (agi)!\n");
03185       }
03186       qe->handled++;
03187       ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "CONNECT", "%ld|%s", (long)time(NULL) - qe->start, peer->uniqueid);
03188       if (qe->parent->eventwhencalled)
03189          manager_event(EVENT_FLAG_AGENT, "AgentConnect",
03190                "Queue: %s\r\n"
03191                "Uniqueid: %s\r\n"
03192                "Channel: %s\r\n"
03193                "Member: %s\r\n"
03194                "MemberName: %s\r\n"
03195                "Holdtime: %ld\r\n"
03196                "BridgedChannel: %s\r\n"
03197                "%s",
03198                queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
03199                (long)time(NULL) - qe->start, peer->uniqueid,
03200                qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
03201       ast_copy_string(oldcontext, qe->chan->context, sizeof(oldcontext));
03202       ast_copy_string(oldexten, qe->chan->exten, sizeof(oldexten));
03203       time(&callstart);
03204 
03205       if (member->status == AST_DEVICE_NOT_INUSE)
03206          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);
03207          
03208       transfer_ds = setup_transfer_datastore(qe, member, callstart, callcompletedinsl);
03209       bridge = ast_bridge_call(qe->chan,peer, &bridge_config);
03210 
03211       ast_channel_lock(qe->chan);
03212       if (!attended_transfer_occurred(qe->chan)) {
03213          struct ast_datastore *tds;
03214          if (strcasecmp(oldcontext, qe->chan->context) || strcasecmp(oldexten, qe->chan->exten)) {
03215             ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld",
03216                qe->chan->exten, qe->chan->context, (long) (callstart - qe->start),
03217                (long) (time(NULL) - callstart));
03218          } else if (qe->chan->_softhangup) {
03219             ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETECALLER", "%ld|%ld|%d",
03220                (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
03221             if (qe->parent->eventwhencalled)
03222                manager_event(EVENT_FLAG_AGENT, "AgentComplete",
03223                      "Queue: %s\r\n"
03224                      "Uniqueid: %s\r\n"
03225                      "Channel: %s\r\n"
03226                      "Member: %s\r\n"
03227                      "MemberName: %s\r\n"
03228                      "HoldTime: %ld\r\n"
03229                      "TalkTime: %ld\r\n"
03230                      "Reason: caller\r\n"
03231                      "%s",
03232                      queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
03233                      (long)(callstart - qe->start), (long)(time(NULL) - callstart),
03234                      qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
03235          } else {
03236             ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETEAGENT", "%ld|%ld|%d",
03237                (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
03238             if (qe->parent->eventwhencalled)
03239                manager_event(EVENT_FLAG_AGENT, "AgentComplete",
03240                      "Queue: %s\r\n"
03241                      "Uniqueid: %s\r\n"
03242                      "Channel: %s\r\n"
03243                      "MemberName: %s\r\n"
03244                      "HoldTime: %ld\r\n"
03245                      "TalkTime: %ld\r\n"
03246                      "Reason: agent\r\n"
03247                      "%s",
03248                      queuename, qe->chan->uniqueid, peer->name, member->membername, (long)(callstart - qe->start),
03249                      (long)(time(NULL) - callstart),
03250                      qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
03251          }
03252          if ((tds = ast_channel_datastore_find(qe->chan, &queue_transfer_info, NULL))) {  
03253             ast_channel_datastore_remove(qe->chan, tds);
03254          }
03255          update_queue(qe->parent, member, callcompletedinsl);
03256       }
03257 
03258       if (transfer_ds) {
03259          ast_channel_datastore_free(transfer_ds);
03260       }
03261       ast_channel_unlock(qe->chan);
03262       ast_hangup(peer);
03263       res = bridge ? bridge : 1;
03264       ao2_ref(member, -1);
03265    }
03266 out:
03267    hangupcalls(outgoing, NULL);
03268 
03269    return res;
03270 }

static int unload_module ( void   )  [static]

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

05183 {
05184    int res;
05185 
05186    if (device_state.thread != AST_PTHREADT_NULL) {
05187       device_state.stop = 1;
05188       ast_mutex_lock(&device_state.lock);
05189       ast_cond_signal(&device_state.cond);
05190       ast_mutex_unlock(&device_state.lock);
05191       pthread_join(device_state.thread, NULL);
05192    }
05193 
05194    ast_cli_unregister_multiple(cli_queue, sizeof(cli_queue) / sizeof(struct ast_cli_entry));
05195    res = ast_manager_unregister("QueueStatus");
05196    res |= ast_manager_unregister("Queues");
05197    res |= ast_manager_unregister("QueueAdd");
05198    res |= ast_manager_unregister("QueueRemove");
05199    res |= ast_manager_unregister("QueuePause");
05200    res |= ast_unregister_application(app_aqm);
05201    res |= ast_unregister_application(app_rqm);
05202    res |= ast_unregister_application(app_pqm);
05203    res |= ast_unregister_application(app_upqm);
05204    res |= ast_unregister_application(app_ql);
05205    res |= ast_unregister_application(app);
05206    res |= ast_custom_function_unregister(&queueagentcount_function);
05207    res |= ast_custom_function_unregister(&queuemembercount_function);
05208    res |= ast_custom_function_unregister(&queuememberlist_function);
05209    res |= ast_custom_function_unregister(&queuewaitingcount_function);
05210    ast_devstate_del(statechange_queue, NULL);
05211 
05212    ast_module_user_hangup_all();
05213 
05214    clear_and_free_interfaces();
05215 
05216    return res;
05217 }

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

Definition at line 2525 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 queue_transfer_fixup(), and try_calling().

02526 {
02527    ast_mutex_lock(&q->lock);
02528    time(&member->lastcall);
02529    member->calls++;
02530    q->callscompleted++;
02531    if (callcompletedinsl)
02532       q->callscompletedinsl++;
02533    ast_mutex_unlock(&q->lock);
02534    return 0;
02535 }

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

Definition at line 1315 of file app_queue.c.

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

Referenced by set_member_paused().

01316 {
01317    struct ast_variable *var, *save;
01318    int ret = -1;
01319 
01320    if (!(var = ast_load_realtime("queue_members", "interface", mem->interface, "queue_name", queue_name, NULL))) 
01321       return ret;
01322    save = var;
01323    while (var) {
01324       if (!strcmp(var->name, "uniqueid"))
01325          break;
01326       var = var->next;
01327    }
01328    if (var && !ast_strlen_zero(var->value)) {
01329       if ((ast_update_realtime("queue_members", "uniqueid", var->value, field, value, NULL)) > -1)
01330          ret = 0;
01331    }
01332    ast_variables_destroy(save);
01333    return ret;
01334 }

static void update_realtime_members ( struct call_queue q  )  [static]

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

01337 {
01338    struct ast_config *member_config = NULL;
01339    struct member *m;
01340    char *interface = NULL;
01341    struct ao2_iterator mem_iter;
01342 
01343    if (!(member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", q->name , NULL))) {
01344       /*This queue doesn't have realtime members*/
01345       if (option_debug > 2)
01346          ast_log(LOG_DEBUG, "Queue %s has no realtime members defined. No need for update\n", q->name);
01347       return;
01348    }
01349 
01350    ast_mutex_lock(&q->lock);
01351    
01352    /* Temporarily set realtime  members dead so we can detect deleted ones.*/ 
01353    mem_iter = ao2_iterator_init(q->members, 0);
01354    while ((m = ao2_iterator_next(&mem_iter))) {
01355       if (m->realtime)
01356          m->dead = 1;
01357       ao2_ref(m, -1);
01358    }
01359 
01360    while ((interface = ast_category_browse(member_config, interface))) {
01361       rt_handle_member_record(q, interface,
01362          S_OR(ast_variable_retrieve(member_config, interface, "membername"), interface),
01363          ast_variable_retrieve(member_config, interface, "penalty"),
01364          ast_variable_retrieve(member_config, interface, "paused"));
01365    }
01366 
01367    /* Delete all realtime members that have been deleted in DB. */
01368    mem_iter = ao2_iterator_init(q->members, 0);
01369    while ((m = ao2_iterator_next(&mem_iter))) {
01370       if (m->dead) {
01371          ao2_unlink(q->members, m);
01372          ast_mutex_unlock(&q->lock);
01373          remove_from_interfaces(m->interface);
01374          ast_mutex_lock(&q->lock);
01375          q->membercount--;
01376       }
01377       ao2_ref(m, -1);
01378    }
01379    ast_mutex_unlock(&q->lock);
01380    ast_config_destroy(member_config);
01381 }

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

Definition at line 596 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, member_interface::list, 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().

00597 {
00598    struct member *cur;
00599    struct ao2_iterator mem_iter;
00600    struct call_queue *q;
00601 
00602    AST_LIST_LOCK(&queues);
00603    AST_LIST_TRAVERSE(&queues, q, list) {
00604       ast_mutex_lock(&q->lock);
00605       mem_iter = ao2_iterator_init(q->members, 0);
00606       while ((cur = ao2_iterator_next(&mem_iter))) {
00607          char *tmp_interface;
00608          char *slash_pos;
00609          tmp_interface = ast_strdupa(cur->interface);
00610          if ((slash_pos = strchr(tmp_interface, '/')))
00611             if ((slash_pos = strchr(slash_pos + 1, '/')))
00612                *slash_pos = '\0';
00613 
00614          if (strcasecmp(interface, tmp_interface)) {
00615             ao2_ref(cur, -1);
00616             continue;
00617          }
00618 
00619          if (cur->status != status) {
00620             cur->status = status;
00621             if (q->maskmemberstatus) {
00622                ao2_ref(cur, -1);
00623                continue;
00624             }
00625 
00626             manager_event(EVENT_FLAG_AGENT, "QueueMemberStatus",
00627                "Queue: %s\r\n"
00628                "Location: %s\r\n"
00629                "MemberName: %s\r\n"
00630                "Membership: %s\r\n"
00631                "Penalty: %d\r\n"
00632                "CallsTaken: %d\r\n"
00633                "LastCall: %d\r\n"
00634                "Status: %d\r\n"
00635                "Paused: %d\r\n",
00636                q->name, cur->interface, cur->membername, cur->dynamic ? "dynamic" : cur->realtime ? "realtime" : "static",
00637                cur->penalty, cur->calls, (int)cur->lastcall, cur->status, cur->paused);
00638          }
00639          ao2_ref(cur, -1);
00640       }
00641       ast_mutex_unlock(&q->lock);
00642    }
00643    AST_LIST_UNLOCK(&queues);
00644 
00645    return 0;
00646 }

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

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

Referenced by load_module().

03656 {
03657    struct ast_module_user *lu;
03658    char *parse;
03659    int priority_jump = 0;
03660    int ignore_fail = 0;
03661    AST_DECLARE_APP_ARGS(args,
03662       AST_APP_ARG(queuename);
03663       AST_APP_ARG(interface);
03664       AST_APP_ARG(options);
03665    );
03666 
03667    if (ast_strlen_zero(data)) {
03668       ast_log(LOG_WARNING, "UnpauseQueueMember requires an argument ([queuename]|interface[|options])\n");
03669       return -1;
03670    }
03671 
03672    parse = ast_strdupa(data);
03673 
03674    AST_STANDARD_APP_ARGS(args, parse);
03675 
03676    lu = ast_module_user_add(chan);
03677 
03678    if (args.options) {
03679       if (strchr(args.options, 'j'))
03680          priority_jump = 1;
03681       if (strchr(args.options, 'i'))
03682          ignore_fail = 1;
03683    }
03684 
03685    if (ast_strlen_zero(args.interface)) {
03686       ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename]|interface[|options])\n");
03687       ast_module_user_remove(lu);
03688       return -1;
03689    }
03690 
03691    if (set_member_paused(args.queuename, args.interface, 0)) {
03692       ast_log(LOG_WARNING, "Attempt to unpause interface %s, not found\n", args.interface);
03693       pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND");
03694       if (priority_jump || ast_opt_priority_jumping) {
03695          if (!ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101)) {
03696             ast_module_user_remove(lu);
03697             return 0;
03698          }
03699       }
03700       ast_module_user_remove(lu);
03701       if (ignore_fail) {
03702          return 0;
03703       } else {
03704          return -1;
03705       }
03706    }
03707 
03708    ast_module_user_remove(lu);
03709    pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "UNPAUSED");
03710    return 0;
03711 }

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

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

01514 {
01515    int digitlen = strlen(qe->digits);
01516 
01517    /* Prevent possible buffer overflow */
01518    if (digitlen < sizeof(qe->digits) - 2) {
01519       qe->digits[digitlen] = digit;
01520       qe->digits[digitlen + 1] = '\0';
01521    } else {
01522       qe->digits[0] = '\0';
01523       return 0;
01524    }
01525 
01526    /* If there's no context to goto, short-circuit */
01527    if (ast_strlen_zero(qe->context))
01528       return 0;
01529 
01530    /* If the extension is bad, then reset the digits to blank */
01531    if (!ast_canmatch_extension(qe->chan, qe->context, qe->digits, 1, qe->chan->cid.cid_num)) {
01532       qe->digits[0] = '\0';
01533       return 0;
01534    }
01535 
01536    /* We have an exact match */
01537    if (!ast_goto_if_exists(qe->chan, qe->context, qe->digits, 1)) {
01538       qe->valid_digits = 1;
01539       /* Return 1 on a successful goto */
01540       return 1;
01541    }
01542 
01543    return 0;
01544 }

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

Definition at line 1769 of file app_queue.c.

References ast_copy_string(), and pbx_builtin_serialize_variables().

Referenced by ring_entry(), and try_calling().

01770 {
01771    char *tmp = alloca(len);
01772 
01773    if (pbx_builtin_serialize_variables(chan, tmp, len)) {
01774       int i, j;
01775 
01776       /* convert "\n" to "\nVariable: " */
01777       strcpy(vars, "Variable: ");
01778 
01779       for (i = 0, j = 10; (i < len - 1) && (j < len - 1); i++, j++) {
01780          vars[j] = tmp[i];
01781 
01782          if (tmp[i + 1] == '\0')
01783             break;
01784          if (tmp[i] == '\n') {
01785             vars[j++] = '\r';
01786             vars[j++] = '\n';
01787 
01788             ast_copy_string(&(vars[j]), "Variable: ", len - j);
01789             j += 9;
01790          }
01791       }
01792       if (j > len - 3)
01793          j = len - 3;
01794       vars[j++] = '\r';
01795       vars[j++] = '\n';
01796       vars[j] = '\0';
01797    } else {
01798       /* there are no channel variables; leave it blank */
01799       *vars = '\0';
01800    }
01801    return vars;
01802 }

static int wait_a_bit ( struct queue_ent qe  )  [static]

Definition at line 3272 of file app_queue.c.

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

Referenced by queue_exec().

03273 {
03274    /* Don't need to hold the lock while we setup the outgoing calls */
03275    int retrywait = qe->parent->retry * 1000;
03276 
03277    int res = ast_waitfordigit(qe->chan, retrywait);
03278    if (res > 0 && !valid_exit(qe, res))
03279       res = 0;
03280 
03281    return res;
03282 }

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

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

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

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

Referenced by queue_exec().

02451 {
02452    int res = 0;
02453 
02454    /* This is the holding pen for callers 2 through maxlen */
02455    for (;;) {
02456       enum queue_member_status stat;
02457 
02458       if (is_our_turn(qe))
02459          break;
02460 
02461       /* If we have timed out, break out */
02462       if (qe->expire && (time(NULL) >= qe->expire)) {
02463          *reason = QUEUE_TIMEOUT;
02464          break;
02465       }
02466 
02467       stat = get_member_status(qe->parent, qe->max_penalty);
02468 
02469       /* leave the queue if no agents, if enabled */
02470       if (qe->parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) {
02471          *reason = QUEUE_LEAVEEMPTY;
02472          ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long)time(NULL) - qe->start);
02473          leave_queue(qe);
02474          break;
02475       }
02476 
02477       /* leave the queue if no reachable agents, if enabled */
02478       if ((qe->parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) {
02479          *reason = QUEUE_LEAVEUNAVAIL;
02480          ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long)time(NULL) - qe->start);
02481          leave_queue(qe);
02482          break;
02483       }
02484 
02485       /* Make a position announcement, if enabled */
02486       if (qe->parent->announcefrequency && !ringing &&
02487          (res = say_position(qe)))
02488          break;
02489 
02490       /* If we have timed out, break out */
02491       if (qe->expire && (time(NULL) >= qe->expire)) {
02492          *reason = QUEUE_TIMEOUT;
02493          break;
02494       }
02495 
02496       /* Make a periodic announcement, if enabled */
02497       if (qe->parent->periodicannouncefrequency && !ringing &&
02498          (res = say_periodic_announcement(qe)))
02499          break;
02500 
02501       /* If we have timed out, break out */
02502       if (qe->expire && (time(NULL) >= qe->expire)) {
02503          *reason = QUEUE_TIMEOUT;
02504          break;
02505       }
02506       
02507       /* Wait a second before checking again */
02508       if ((res = ast_waitfordigit(qe->chan, RECHECK * 1000))) {
02509          if (res > 0 && !valid_exit(qe, res))
02510             res = 0;
02511          else
02512             break;
02513       }
02514       
02515       /* If we have timed out, break out */
02516       if (qe->expire && (time(NULL) >= qe->expire)) {
02517          *reason = QUEUE_TIMEOUT;
02518          break;
02519       }
02520    }
02521 
02522    return res;
02523 }


Variable Documentation

struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT | AST_MODFLAG_BUILDSUM, .description = "True Call Queueing" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "f450f61f60e761b3aa089ebed76ca8a5" , .load = load_module, .unload = unload_module, .reload = reload, } [static]

Definition at line 5264 of file app_queue.c.

char* app = "Queue" [static]

Definition at line 148 of file app_queue.c.

char* app_aqm = "AddQueueMember" [static]

Definition at line 192 of file app_queue.c.

char* app_aqm_descrip [static]

Definition at line 194 of file app_queue.c.

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

Definition at line 193 of file app_queue.c.

char* app_pqm = "PauseQueueMember" [static]

Definition at line 224 of file app_queue.c.

char* app_pqm_descrip [static]

Definition at line 226 of file app_queue.c.

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

Definition at line 225 of file app_queue.c.

char* app_ql = "QueueLog" [static]

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

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

Definition at line 264 of file app_queue.c.

char* app_rqm = "RemoveQueueMember" [static]

Definition at line 208 of file app_queue.c.

char* app_rqm_descrip [static]

Definition at line 210 of file app_queue.c.

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

Definition at line 209 of file app_queue.c.

char* app_upqm = "UnpauseQueueMember" [static]

Definition at line 247 of file app_queue.c.

char* app_upqm_descrip [static]

Definition at line 249 of file app_queue.c.

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

Definition at line 248 of file app_queue.c.

const struct ast_module_info* ast_module_info = &__mod_info [static]

Definition at line 5264 of file app_queue.c.

int autofill_default = 0 [static]

queues.conf [general] option

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

struct ast_cli_entry cli_queue[] [static]

Definition at line 5163 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 5158 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 5148 of file app_queue.c.

ast_cond_t cond

Condition for the state change queue

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

struct statechange* first

Definition at line 704 of file app_queue.c.

enum queue_result id

Definition at line 301 of file app_queue.c.

Referenced by _sip_show_peers().

struct statechange* last

Definition at line 704 of file app_queue.c.

ast_mutex_t lock

Lock for the state change queue

Definition at line 700 of file app_queue.c.

int montype_default = 0 [static]

queues.conf [general] option

Definition at line 288 of file app_queue.c.

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

Persistent Members astdb family.

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

char qrm_cmd_usage[] [static]

Initial value:

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

Definition at line 5145 of file app_queue.c.

int queue_debug = 0 [static]

queues.conf [general] extra debug option

Definition at line 276 of file app_queue.c.

int queue_persistent_members = 0 [static]

queues.conf [general] option

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

struct ast_datastore_info queue_transfer_info [static]

Initial value:

 {
   .type = "queue_transfer",
   .chan_fixup = queue_transfer_fixup,
   .destroy = queue_transfer_destroy,
}
a datastore used to help correctly log attended transfers of queue callers

Definition at line 2619 of file app_queue.c.

Referenced by attended_transfer_occurred(), queue_transfer_fixup(), setup_transfer_datastore(), and try_calling().

struct ast_custom_function queueagentcount_function [static]

Definition at line 4358 of file app_queue.c.

Referenced by load_module(), and unload_module().

struct ast_custom_function queuemembercount_function [static]

Definition at line 4368 of file app_queue.c.

Referenced by load_module(), and unload_module().

struct ast_custom_function queuememberlist_function [static]

Definition at line 4392 of file app_queue.c.

Referenced by load_module(), and unload_module().

struct ast_custom_function queuewaitingcount_function [static]

Definition at line 4383 of file app_queue.c.

Referenced by load_module(), and unload_module().

struct { ... } state_change_q

Queue of state changes

unsigned int stop

Set to 1 to stop the thread

Definition at line 696 of file app_queue.c.

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

int use_weight = 0 [static]

queues.conf per-queue weight option

Definition at line 282 of file app_queue.c.


Generated on Wed Feb 11 12:00:04 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7