Fri Jan 31 13:16:26 2014

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_count
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, QUEUE_STRATEGY_RRORDERED
}
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, const char *state_interface)
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 cli_queue_member_count (int fd, int argc, char **argv)
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_member_count (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, const char *state_interface)
 allocate space for new queue member and set fields based on parameters passed
static void destroy_queue (void *obj)
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_queue_member_count (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 num_available_members (struct call_queue *q)
 Get the number of members available to accept a call.
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 qmc_handler (const char *queuename, char *buffer, int len)
static int queue_exec (struct ast_channel *chan, void *data)
 The starting point for all queue calls.
static int queue_function_exists (struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
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_queuememberpaused (struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
static int queue_function_queuememberstatus (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 int queue_member_count (const char *qname, struct member_count *qmc)
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 void remove_queue (struct call_queue *q)
 removes a call_queue from the list of call_queues
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, int pause)
 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, const char *state_interface)
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 = "361d7bb937402d51e4658efb5b4d76e4" , .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_info
ast_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 {
      struct statechange *   first
      struct 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 qmc_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 queueexists_function
static struct ast_custom_function queuemembercount_function
static struct ast_custom_function queuememberlist_function
static struct ast_custom_function queuememberpaused_function
static struct ast_custom_function queuememberstatus_function
static struct ast_custom_function queuewaitingcount_function
static int shared_lastcall = 0
 queues.conf [general] option
static struct strategy strategies []
static char * synopsis = "Queue a call for a call queue"
static int use_weight = 0
 queues.conf per-queue weight option

Detailed Description

True call queues with optional send URL on answer.

Author:
Mark Spencer <markster@digium.com>
Development notes
Note:
2004-11-25: Persistent Dynamic Members added by: NetNation Communications (www.netnation.com) Kevin Lindsay <kevinl@netnation.com>

Each dynamic agent in each queue is now stored in the astdb. When asterisk is restarted, each agent will be automatically readded into their recorded queues. This feature can be configured with the 'persistent_members=<1|0>' setting in the '[general]' category in queues.conf. The default is on.

Note:
2004-06-04: Priorities in queues added by inAccess Networks (work funded by Hellas On Line (HOL) www.hol.gr).
These features added by David C. Troy <dave@toad.net>:
  • Per-queue holdtime calculation
  • Estimated holdtime announcement
  • Position announcement
  • Abandoned/completed call counters
  • Failout timer passed as optional app parameter
  • Optional monitoring of calls, started when call is answered

Patch Version 1.07 2003-12-24 01

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

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

Definition in file app_queue.c.


Define Documentation

#define ANNOUNCEHOLDTIME_ALWAYS   1

Definition at line 399 of file app_queue.c.

Referenced by queue_set_param().

#define ANNOUNCEHOLDTIME_ONCE   2

Definition at line 400 of file app_queue.c.

Referenced by queue_set_param(), and say_position().

#define AST_MAX_WATCHERS   256

Definition at line 2287 of file app_queue.c.

#define DEFAULT_RETRY   5

Definition at line 139 of file app_queue.c.

Referenced by init_queue(), and queue_set_param().

#define DEFAULT_TIMEOUT   15

Definition at line 140 of file app_queue.c.

Referenced by queue_set_param().

#define MAX_PERIODIC_ANNOUNCEMENTS   10

Definition at line 142 of file app_queue.c.

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

#define PM_MAX_LEN   8192

Definition at line 279 of file app_queue.c.

Referenced by dump_queue_members(), and reload_queue_members().

#define QUEUE_EMPTY_NORMAL   1

Definition at line 397 of file app_queue.c.

Referenced by queue_set_param().

#define QUEUE_EMPTY_STRICT   2

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

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

#define RECHECK   1

Definition at line 141 of file app_queue.c.

Referenced by wait_our_turn().

#define RES_EXISTS   (-1)
#define RES_NOSUCHQUEUE   (-3)
#define RES_NOT_DYNAMIC   (-4)
#define RES_OKAY   0
#define RES_OUTOFMEMORY   (-2)

Enumeration Type Documentation

anonymous enum
Enumerator:
QUEUE_STRATEGY_RINGALL 
QUEUE_STRATEGY_ROUNDROBIN 
QUEUE_STRATEGY_LEASTRECENT 
QUEUE_STRATEGY_FEWESTCALLS 
QUEUE_STRATEGY_RANDOM 
QUEUE_STRATEGY_RRMEMORY 
QUEUE_STRATEGY_RRORDERED 

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

04448                 {
04449    QMC_VALID = 0, /* Count valid members */
04450    QMC_PAUSED,    /* Count paused members */
04451    QMC_ACTIVE,    /* Count active members */
04452    QMC_FREE,      /* Count free members */
04453    QMC_ALL        /* Count all queue members */
04454 };

Enumerator:
QUEUE_NO_MEMBERS 
QUEUE_NO_REACHABLE_MEMBERS 
QUEUE_NORMAL 

Definition at line 577 of file app_queue.c.

00577                          {
00578    QUEUE_NO_MEMBERS,
00579    QUEUE_NO_REACHABLE_MEMBERS,
00580    QUEUE_NORMAL
00581 };

Enumerator:
QUEUE_UNKNOWN 
QUEUE_TIMEOUT 
QUEUE_JOINEMPTY 
QUEUE_LEAVEEMPTY 
QUEUE_JOINUNAVAIL 
QUEUE_LEAVEUNAVAIL 
QUEUE_FULL 

Definition at line 299 of file app_queue.c.

00299                   {
00300    QUEUE_UNKNOWN = 0,
00301    QUEUE_TIMEOUT = 1,
00302    QUEUE_JOINEMPTY = 2,
00303    QUEUE_LEAVEEMPTY = 3,
00304    QUEUE_JOINUNAVAIL = 4,
00305    QUEUE_LEAVEUNAVAIL = 5,
00306    QUEUE_FULL = 6,
00307 };


Function Documentation

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

Definition at line 5060 of file app_queue.c.

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

Referenced by manager_queues_show(), and queue_show().

05061 {
05062    struct call_queue *q;
05063    struct queue_ent *qe;
05064    struct member *mem;
05065    int pos, queue_show;
05066    time_t now;
05067    char max_buf[150];
05068    char *max;
05069    size_t max_left;
05070    float sl = 0;
05071    char *term = manager ? "\r\n" : "\n";
05072    struct ao2_iterator mem_iter;
05073 
05074    time(&now);
05075    if (argc == 2)
05076       queue_show = 0;
05077    else if (argc == 3)
05078       queue_show = 1;
05079    else
05080       return RESULT_SHOWUSAGE;
05081 
05082    /* We only want to load realtime queues when a specific queue is asked for. */
05083    if (queue_show) {
05084       load_realtime_queue(argv[2]);
05085    } else if (ast_check_realtime("queues")) {
05086       struct ast_config *cfg = ast_load_realtime_multientry("queues", "name LIKE", "%", (char *) NULL);
05087       char *queuename;
05088       if (cfg) {
05089          for (queuename = ast_category_browse(cfg, NULL); !ast_strlen_zero(queuename); queuename = ast_category_browse(cfg, queuename)) {
05090             load_realtime_queue(queuename);
05091          }
05092          ast_config_destroy(cfg);
05093       }
05094    }
05095 
05096    AST_LIST_LOCK(&queues);
05097    if (AST_LIST_EMPTY(&queues)) {
05098       AST_LIST_UNLOCK(&queues);
05099       if (queue_show) {
05100          if (s)
05101             astman_append(s, "No such queue: %s.%s",argv[2], term);
05102          else
05103             ast_cli(fd, "No such queue: %s.%s",argv[2], term);
05104       } else {
05105          if (s)
05106             astman_append(s, "No queues.%s", term);
05107          else
05108             ast_cli(fd, "No queues.%s", term);
05109       }
05110       return RESULT_SUCCESS;
05111    }
05112    AST_LIST_TRAVERSE(&queues, q, list) {
05113       ao2_lock(q);
05114       if (queue_show) {
05115          if (strcasecmp(q->name, argv[2]) != 0) {
05116             ao2_unlock(q);
05117             if (!AST_LIST_NEXT(q, list)) {
05118                ast_cli(fd, "No such queue: %s.%s",argv[2], term);
05119                break;
05120             }
05121             continue;
05122          }
05123       }
05124       max_buf[0] = '\0';
05125       max = max_buf;
05126       max_left = sizeof(max_buf);
05127       if (q->maxlen)
05128          ast_build_string(&max, &max_left, "%d", q->maxlen);
05129       else
05130          ast_build_string(&max, &max_left, "unlimited");
05131       sl = 0;
05132       if (q->callscompleted > 0)
05133          sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
05134       if (s)
05135          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",
05136                               q->name, q->count, max_buf, int2strat(q->strategy), q->holdtime, q->ringlimit,
05137                               q->weight, q->callscompleted, q->callsabandoned, sl, q->servicelevel, term);
05138       else
05139          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",
05140                      q->name, q->count, max_buf, int2strat(q->strategy), q->holdtime, q->ringlimit,
05141                      q->weight, q->callscompleted, q->callsabandoned, sl, q->servicelevel, term);
05142       if (ao2_container_count(q->members)) {
05143          if (s)
05144             astman_append(s, "   Members: %s", term);
05145          else
05146             ast_cli(fd, "   Members: %s", term);
05147          mem_iter = ao2_iterator_init(q->members, 0);
05148          while ((mem = ao2_iterator_next(&mem_iter))) {
05149             max_buf[0] = '\0';
05150             max = max_buf;
05151             max_left = sizeof(max_buf);
05152             if (strcasecmp(mem->membername, mem->interface)) {
05153                ast_build_string(&max, &max_left, " (%s)", mem->interface);
05154             }
05155             if (mem->penalty)
05156                ast_build_string(&max, &max_left, " with penalty %d", mem->penalty);
05157             if (mem->dynamic)
05158                ast_build_string(&max, &max_left, " (dynamic)");
05159             if (mem->realtime)
05160                ast_build_string(&max, &max_left, " (realtime)");
05161             if (mem->paused)
05162                ast_build_string(&max, &max_left, " (paused)");
05163             ast_build_string(&max, &max_left, " (%s)", devstate2str(mem->status));
05164             if (mem->calls) {
05165                ast_build_string(&max, &max_left, " has taken %d calls (last was %ld secs ago)",
05166                   mem->calls, (long) (time(NULL) - mem->lastcall));
05167             } else
05168                ast_build_string(&max, &max_left, " has taken no calls yet");
05169             if (s)
05170                astman_append(s, "      %s%s%s", mem->membername, max_buf, term);
05171             else
05172                ast_cli(fd, "      %s%s%s", mem->membername, max_buf, term);
05173             ao2_ref(mem, -1);
05174          }
05175          ao2_iterator_destroy(&mem_iter);
05176       } else if (s)
05177          astman_append(s, "   No Members%s", term);
05178       else  
05179          ast_cli(fd, "   No Members%s", term);
05180       if (q->head) {
05181          pos = 1;
05182          if (s)
05183             astman_append(s, "   Callers: %s", term);
05184          else
05185             ast_cli(fd, "   Callers: %s", term);
05186          for (qe = q->head; qe; qe = qe->next) {
05187             if (s)
05188                astman_append(s, "      %d. %s (wait: %ld:%2.2ld, prio: %d)%s",
05189                   pos++, qe->chan->name, (long) (now - qe->start) / 60,
05190                   (long) (now - qe->start) % 60, qe->prio, term);
05191             else
05192                ast_cli(fd, "      %d. %s (wait: %ld:%2.2ld, prio: %d)%s", pos++,
05193                   qe->chan->name, (long) (now - qe->start) / 60,
05194                   (long) (now - qe->start) % 60, qe->prio, term);
05195          }
05196       } else if (s)
05197          astman_append(s, "   No Callers%s", term);
05198       else
05199          ast_cli(fd, "   No Callers%s", term);
05200       if (s)
05201          astman_append(s, "%s", term);
05202       else
05203          ast_cli(fd, "%s", term);
05204       ao2_unlock(q);
05205       if (queue_show)
05206          break;
05207    }
05208    AST_LIST_UNLOCK(&queues);
05209    return RESULT_SUCCESS;
05210 }

static void __reg_module ( void   )  [static]

Definition at line 5894 of file app_queue.c.

static void __unreg_module ( void   )  [static]

Definition at line 5894 of file app_queue.c.

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

Definition at line 938 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, LOG_DEBUG, and option_debug.

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

00939 {
00940    struct member_interface *curint;
00941 
00942    AST_LIST_LOCK(&interfaces);
00943    AST_LIST_TRAVERSE(&interfaces, curint, list) {
00944       if (!strcasecmp(curint->interface, interface))
00945          break;
00946    }
00947 
00948    if (curint) {
00949       AST_LIST_UNLOCK(&interfaces);
00950       return 0;
00951    }
00952 
00953    if (option_debug)
00954       ast_log(LOG_DEBUG, "Adding %s to the list of interfaces that make up all of our queue members.\n", interface);
00955    
00956    if ((curint = ast_calloc(1, sizeof(*curint)))) {
00957       ast_copy_string(curint->interface, interface, sizeof(curint->interface));
00958       AST_LIST_INSERT_HEAD(&interfaces, curint, list);
00959    }
00960    AST_LIST_UNLOCK(&interfaces);
00961 
00962    return 0;
00963 }

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

Definition at line 3671 of file app_queue.c.

References add_to_interfaces(), ao2_lock(), ao2_ref(), ao2_unlock(), AST_LIST_LOCK, AST_LIST_UNLOCK, member::calls, create_queue_member(), dump_queue_members(), member::dynamic, EVENT_FLAG_AGENT, member::interface, interface_exists(), member::lastcall, load_realtime_queue(), 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, member::state_interface, and member::status.

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

03672 {
03673    struct call_queue *q;
03674    struct member *new_member, *old_member;
03675    int res = RES_NOSUCHQUEUE;
03676 
03677    /* \note Ensure the appropriate realtime queue is loaded.  Note that this
03678     * short-circuits if the queue is already in memory. */
03679    if (!(q = load_realtime_queue(queuename)))
03680       return res;
03681 
03682    AST_LIST_LOCK(&queues);
03683 
03684    ao2_lock(q);
03685    if ((old_member = interface_exists(q, interface)) == NULL) {
03686       if ((new_member = create_queue_member(interface, membername, penalty, paused, state_interface))) {
03687          add_to_interfaces(new_member->state_interface);
03688          new_member->dynamic = 1;
03689          ao2_link(q->members, new_member);
03690          q->membercount++;
03691          manager_event(EVENT_FLAG_AGENT, "QueueMemberAdded",
03692             "Queue: %s\r\n"
03693             "Location: %s\r\n"
03694             "MemberName: %s\r\n"
03695             "Membership: %s\r\n"
03696             "Penalty: %d\r\n"
03697             "CallsTaken: %d\r\n"
03698             "LastCall: %d\r\n"
03699             "Status: %d\r\n"
03700             "Paused: %d\r\n",
03701             q->name, new_member->interface, new_member->membername,
03702             "dynamic",
03703             new_member->penalty, new_member->calls, (int) new_member->lastcall,
03704             new_member->status, new_member->paused);
03705          
03706          ao2_ref(new_member, -1);
03707          new_member = NULL;
03708 
03709          if (dump)
03710             dump_queue_members(q);
03711          
03712          res = RES_OKAY;
03713       } else {
03714          res = RES_OUTOFMEMORY;
03715       }
03716    } else {
03717       ao2_ref(old_member, -1);
03718       res = RES_EXISTS;
03719    }
03720    ao2_unlock(q);
03721    AST_LIST_UNLOCK(&queues);
03722 
03723    return res;
03724 }

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

Definition at line 835 of file app_queue.c.

References ao2_alloc(), ast_copy_string(), destroy_queue(), and call_queue::name.

Referenced by find_queue_by_name_rt(), and reload_queues().

00836 {
00837    struct call_queue *q;
00838 
00839    if ((q = ao2_alloc(sizeof(*q), destroy_queue))) {
00840       ast_copy_string(q->name, queuename, sizeof(q->name));
00841    }
00842    return q;
00843 }

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

Definition at line 4052 of file app_queue.c.

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

Referenced by load_module().

04053 {
04054    int res=-1;
04055    struct ast_module_user *lu;
04056    char *parse, *temppos = NULL;
04057    int priority_jump = 0;
04058    AST_DECLARE_APP_ARGS(args,
04059       AST_APP_ARG(queuename);
04060       AST_APP_ARG(interface);
04061       AST_APP_ARG(penalty);
04062       AST_APP_ARG(options);
04063       AST_APP_ARG(membername);
04064       AST_APP_ARG(state_interface);
04065    );
04066    int penalty = 0;
04067 
04068    if (ast_strlen_zero(data)) {
04069       ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename[|interface[|penalty[|options[|membername[|state_interface]]]]])\n");
04070       return -1;
04071    }
04072 
04073    parse = ast_strdupa(data);
04074 
04075    AST_STANDARD_APP_ARGS(args, parse);
04076 
04077    lu = ast_module_user_add(chan);
04078 
04079    if (ast_strlen_zero(args.interface)) {
04080       args.interface = ast_strdupa(chan->name);
04081       temppos = strrchr(args.interface, '-');
04082       if (temppos)
04083          *temppos = '\0';
04084    }
04085 
04086    if (!ast_strlen_zero(args.penalty)) {
04087       if ((sscanf(args.penalty, "%30d", &penalty) != 1) || penalty < 0) {
04088          ast_log(LOG_WARNING, "Penalty '%s' is invalid, must be an integer >= 0\n", args.penalty);
04089          penalty = 0;
04090       }
04091    }
04092    
04093    if (args.options) {
04094       if (strchr(args.options, 'j'))
04095          priority_jump = 1;
04096    }
04097 
04098    switch (add_to_queue(args.queuename, args.interface, args.membername, penalty, 0, queue_persistent_members, args.state_interface)) {
04099    case RES_OKAY:
04100       ast_queue_log(args.queuename, chan->uniqueid, args.interface, "ADDMEMBER", "%s", "");
04101       ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", args.interface, args.queuename);
04102       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "ADDED");
04103       res = 0;
04104       break;
04105    case RES_EXISTS:
04106       ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': Already there\n", args.interface, args.queuename);
04107       if (priority_jump || ast_opt_priority_jumping)
04108          ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
04109       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "MEMBERALREADY");
04110       res = 0;
04111       break;
04112    case RES_NOSUCHQUEUE:
04113       ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", args.queuename);
04114       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "NOSUCHQUEUE");
04115       res = 0;
04116       break;
04117    case RES_OUTOFMEMORY:
04118       ast_log(LOG_ERROR, "Out of memory adding member %s to queue %s\n", args.interface, args.queuename);
04119       break;
04120    }
04121 
04122    ast_module_user_remove(lu);
04123 
04124    return res;
04125 }

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

References ast_channel_datastore_find(), and queue_transfer_info.

Referenced by try_calling().

02879 {
02880    return ast_channel_datastore_find(chan, &queue_transfer_info, NULL) ? 0 : 1;
02881 }

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 2755 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, QUEUE_STRATEGY_RRMEMORY, QUEUE_STRATEGY_RRORDERED, member::ringcount, call_queue::ringlimit, call_queue::rrpos, call_queue::strategy, and call_queue::wrapped.

Referenced by try_calling().

02756 {
02757    if (qe->max_penalty && (mem->penalty > qe->max_penalty))
02758       return -1;
02759 
02760    switch (q->strategy) {
02761    case QUEUE_STRATEGY_RINGALL:
02762       /* Everyone equal, except for penalty */
02763       tmp->metric = mem->penalty * 1000000;
02764       break;
02765    case QUEUE_STRATEGY_ROUNDROBIN:
02766       if (!pos) {
02767          if (!q->wrapped) {
02768             /* No more channels, start over */
02769             q->rrpos = 0;
02770          } else {
02771             /* Prioritize next entry */
02772             q->rrpos++;
02773          }
02774          q->wrapped = 0;
02775       }
02776       /* Fall through */
02777    case QUEUE_STRATEGY_RRORDERED:
02778    case QUEUE_STRATEGY_RRMEMORY:
02779       if (pos < q->rrpos) {
02780          tmp->metric = 1000 + pos;
02781       } else {
02782          if (pos > q->rrpos)
02783             /* Indicate there is another priority */
02784             q->wrapped = 1;
02785          tmp->metric = pos;
02786       }
02787       tmp->metric += mem->penalty * 1000000;
02788       break;
02789    case QUEUE_STRATEGY_RANDOM:
02790       tmp->metric = ast_random() % 1000;
02791       tmp->metric += mem->penalty * 1000000;
02792       break;
02793    case QUEUE_STRATEGY_FEWESTCALLS:
02794       tmp->metric = mem->calls;
02795       tmp->metric += mem->penalty * 1000000;
02796       break;
02797    case QUEUE_STRATEGY_LEASTRECENT:
02798       if (!mem->lastcall)
02799          tmp->metric = 0;
02800       else
02801          tmp->metric = 1000000 - (time(NULL) - mem->lastcall);
02802       tmp->metric += mem->penalty * 1000000;
02803       break;
02804    default:
02805       ast_log(LOG_WARNING, "Can't calculate metric for unknown strategy %d\n", q->strategy);
02806       break;
02807    }
02808    if (q->ringlimit && (mem->ringcount >= q->ringlimit)) {
02809       tmp->metric += (mem->ringcount / q->ringlimit) * 10000000;
02810    }
02811    if (option_debug)
02812       ast_log(LOG_DEBUG, "New metric %d for member %s with %d rings (limit %d)\n", 
02813                   tmp->metric, mem->interface, mem->ringcount, q->ringlimit);
02814    return 0;
02815 }

static void clear_and_free_interfaces ( void   )  [static]

Definition at line 1017 of file app_queue.c.

References AST_LIST_LOCK, AST_LIST_REMOVE_HEAD, AST_LIST_UNLOCK, and free.

Referenced by unload_module().

01018 {
01019    struct member_interface *curint;
01020 
01021    AST_LIST_LOCK(&interfaces);
01022    while ((curint = AST_LIST_REMOVE_HEAD(&interfaces, list)))
01023       free(curint);
01024    AST_LIST_UNLOCK(&interfaces);
01025 }

static void clear_queue ( struct call_queue q  )  [static]

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

00930 {
00931    q->holdtime = 0;
00932    q->callscompleted = 0;
00933    q->callsabandoned = 0;
00934    q->callscompletedinsl = 0;
00935    q->wrapuptime = 0;
00936 }

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

Definition at line 5715 of file app_queue.c.

References ast_cli(), qmc_handler(), RESULT_FAILURE, RESULT_SHOWUSAGE, and RESULT_SUCCESS.

05716 {
05717    char buffer[256] = "";
05718    char *queuename;
05719    
05720    if (argc != 4) {
05721       return RESULT_SHOWUSAGE;
05722    }
05723    queuename = argv[3];
05724 
05725    if (qmc_handler(queuename, buffer, sizeof(buffer)) == RESULT_SUCCESS) {
05726          ast_cli(fd, 
05727                      "Member count for queue '%s'\n"
05728                      "%s\n",
05729                      queuename, buffer); 
05730          return RESULT_SUCCESS;
05731    } else {
05732          ast_cli(fd, "No such queue: '%s'\n", queuename);
05733          return RESULT_FAILURE;
05734    }
05735 }

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

Definition at line 1876 of file app_queue.c.

References ao2_find(), ao2_lock(), ao2_ref(), ao2_unlock(), AST_LIST_TRAVERSE, ast_log(), call_queue::count, member::interface, LOG_DEBUG, call_queue::members, call_queue::name, num_available_members(), and call_queue::weight.

Referenced by ring_entry().

01877 {
01878    struct call_queue *q;
01879    struct member *mem;
01880    int found = 0;
01881    
01882    /* &qlock and &rq->lock already set by try_calling()
01883     * to solve deadlock */
01884    AST_LIST_TRAVERSE(&queues, q, list) {
01885       if (q == rq) /* don't check myself, could deadlock */
01886          continue;
01887       ao2_lock(q);
01888       if (q->count && q->members) {
01889          if ((mem = ao2_find(q->members, member, OBJ_POINTER))) {
01890             ast_log(LOG_DEBUG, "Found matching member %s in queue '%s'\n", mem->interface, q->name);
01891             if (q->weight > rq->weight && q->count >= num_available_members(q)) {
01892                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);
01893                found = 1;
01894             }
01895             ao2_ref(mem, -1);
01896          }
01897       }
01898       ao2_unlock(q);
01899       if (found)
01900          break;
01901    }
01902    return found;
01903 }

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

Definition at line 5217 of file app_queue.c.

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

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

05218 {
05219    struct call_queue *q;
05220    char *ret = NULL;
05221    int which = 0;
05222    int wordlen = strlen(word);
05223    
05224    AST_LIST_LOCK(&queues);
05225    AST_LIST_TRAVERSE(&queues, q, list) {
05226       if (!strncasecmp(word, q->name, wordlen) && ++which > state) {
05227          ret = ast_strdup(q->name); 
05228          break;
05229       }
05230    }
05231    AST_LIST_UNLOCK(&queues);
05232 
05233    return ret;
05234 }

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

Definition at line 5520 of file app_queue.c.

References ast_malloc, ast_strdup, and complete_queue().

05521 {
05522    /* 0 - queue; 1 - add; 2 - member; 3 - <interface>; 4 - to; 5 - <queue>; 6 - penalty; 7 - <penalty>; 8 - as; 9 - <membername> */
05523    switch (pos) {
05524    case 3:  /* Don't attempt to complete name of interface (infinite possibilities) */
05525       return NULL;
05526    case 4:  /* only one possible match, "to" */
05527       return state == 0 ? ast_strdup("to") : NULL;
05528    case 5:  /* <queue> */
05529       return complete_queue(line, word, pos, state);
05530    case 6: /* only one possible match, "penalty" */
05531       return state == 0 ? ast_strdup("penalty") : NULL;
05532    case 7:
05533       if (state < 100) {   /* 0-99 */
05534          char *num;
05535          if ((num = ast_malloc(3))) {
05536             sprintf(num, "%d", state);
05537          }
05538          return num;
05539       } else {
05540          return NULL;
05541       }
05542    case 8: /* only one possible match, "as" */
05543       return state == 0 ? ast_strdup("as") : NULL;
05544    case 9:  /* Don't attempt to complete name of member (infinite possibilities) */
05545       return NULL;
05546    case 10:
05547       return state == 0 ? ast_strdup("state_interface") : NULL;
05548    default:
05549       return NULL;
05550    }
05551 }

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

Definition at line 5742 of file app_queue.c.

References complete_queue().

05743 {
05744       /* 0 - queue; 1 - member; 2 - count; 3 - <queue> */
05745       switch (pos) {
05746       case 3:  /* <queue> */
05747             return complete_queue(line, word, pos, state);
05748       default:
05749          return NULL;
05750       }
05751 }

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

Definition at line 5588 of file app_queue.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next(), ao2_lock(), ao2_ref(), ao2_unlock(), AST_LIST_EMPTY, AST_LIST_TRAVERSE, ast_strdup, complete_queue(), member::interface, and call_queue::members.

05589 {
05590    int which = 0;
05591    struct call_queue *q;
05592    struct member *m;
05593    struct ao2_iterator mem_iter;
05594 
05595    /* 0 - queue; 1 - remove; 2 - member; 3 - <member>; 4 - from; 5 - <queue> */
05596    if (pos > 5 || pos < 3)
05597       return NULL;
05598    if (pos == 4)  /* only one possible match, 'from' */
05599       return state == 0 ? ast_strdup("from") : NULL;
05600 
05601    if (pos == 5)  /* No need to duplicate code */
05602       return complete_queue(line, word, pos, state);
05603 
05604    /* here is the case for 3, <member> */
05605    if (!AST_LIST_EMPTY(&queues)) { /* XXX unnecessary ? the traverse does that for us */
05606       AST_LIST_TRAVERSE(&queues, q, list) {
05607          ao2_lock(q);
05608          mem_iter = ao2_iterator_init(q->members, 0);
05609          while ((m = ao2_iterator_next(&mem_iter))) {
05610             if (++which > state) {
05611                char *tmp;
05612                ao2_iterator_destroy(&mem_iter);
05613                ao2_unlock(q);
05614                tmp = ast_strdup(m->interface);
05615                ao2_ref(m, -1);
05616                return tmp;
05617             }
05618             ao2_ref(m, -1);
05619          }
05620          ao2_iterator_destroy(&mem_iter);
05621          ao2_unlock(q);
05622       }
05623    }
05624 
05625    return NULL;
05626 }

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

Definition at line 5236 of file app_queue.c.

References complete_queue().

05237 {
05238    if (pos == 2)
05239       return complete_queue(line, word, pos, state);
05240    return NULL;
05241 }

static int compress_char ( const char  c  )  [static]

Definition at line 845 of file app_queue.c.

Referenced by member_hash_fn().

00846 {
00847    if (c < 32)
00848       return 0;
00849    else if (c > 96)
00850       return c - 64;
00851    else
00852       return c - 32;
00853 }

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

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

Definition at line 810 of file app_queue.c.

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

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

00811 {
00812    struct member *cur;
00813    
00814    if ((cur = ao2_alloc(sizeof(*cur), NULL))) {
00815       cur->penalty = penalty;
00816       cur->paused = paused;
00817       ast_copy_string(cur->interface, interface, sizeof(cur->interface));
00818       if (!ast_strlen_zero(state_interface)) {
00819          ast_copy_string(cur->state_interface, state_interface, sizeof(cur->state_interface));
00820       } else {
00821          ast_copy_string(cur->state_interface, interface, sizeof(cur->state_interface));
00822       }
00823       if (!ast_strlen_zero(membername))
00824          ast_copy_string(cur->membername, membername, sizeof(cur->membername));
00825       else
00826          ast_copy_string(cur->membername, interface, sizeof(cur->membername));
00827       if (!strchr(cur->interface, '/'))
00828          ast_log(LOG_WARNING, "No location at interface '%s'\n", interface);
00829       cur->status = ast_device_state(cur->state_interface);
00830    }
00831 
00832    return cur;
00833 }

static void destroy_queue ( void *  obj  )  [static]

Definition at line 543 of file app_queue.c.

References ao2_ref(), free_members(), and call_queue::members.

Referenced by alloc_queue().

00544 {
00545    struct call_queue *q = obj;
00546    if (q->members) {
00547       free_members(q, 1);
00548       ao2_ref(q->members, -1);
00549    }
00550 }

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

Consumer of the statechange queue.

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

00759 {
00760    struct statechange *sc = NULL;
00761 
00762    while (!device_state.stop) {
00763       ast_mutex_lock(&device_state.lock);
00764       if (!(sc = AST_LIST_REMOVE_HEAD(&device_state.state_change_q, entry))) {
00765          ast_cond_wait(&device_state.cond, &device_state.lock);
00766          sc = AST_LIST_REMOVE_HEAD(&device_state.state_change_q, entry);
00767       }
00768       ast_mutex_unlock(&device_state.lock);
00769 
00770       /* Check to see if we were woken up to see the request to stop */
00771       if (device_state.stop)
00772          break;
00773 
00774       if (!sc)
00775          continue;
00776 
00777       handle_statechange(sc);
00778 
00779       free(sc);
00780       sc = NULL;
00781    }
00782 
00783    if (sc)
00784       free(sc);
00785 
00786    while ((sc = AST_LIST_REMOVE_HEAD(&device_state.state_change_q, entry)))
00787       free(sc);
00788 
00789    return NULL;
00790 }

static void do_hang ( struct callattempt o  )  [static]

common hangup actions

Definition at line 1906 of file app_queue.c.

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

Referenced by ring_entry(), and wait_for_answer().

01907 {
01908    o->stillgoing = 0;
01909    ast_hangup(o->chan);
01910    o->chan = NULL;
01911 }

static void dump_queue_members ( struct call_queue pm_queue  )  [static]

Definition at line 3579 of file app_queue.c.

References ao2_iterator_destroy(), 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, PM_MAX_LEN, and member::state_interface.

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

03580 {
03581    struct member *cur_member;
03582    char value[PM_MAX_LEN];
03583    int value_len = 0;
03584    int res;
03585    struct ao2_iterator mem_iter;
03586 
03587    memset(value, 0, sizeof(value));
03588 
03589    if (!pm_queue)
03590       return;
03591 
03592    mem_iter = ao2_iterator_init(pm_queue->members, 0);
03593    while ((cur_member = ao2_iterator_next(&mem_iter))) {
03594       if (!cur_member->dynamic) {
03595          ao2_ref(cur_member, -1);
03596          continue;
03597       }
03598 
03599       res = snprintf(value + value_len, sizeof(value) - value_len, "%s%s;%d;%d;%s;%s",
03600          value_len ? "|" : "", cur_member->interface, cur_member->penalty, cur_member->paused, cur_member->membername, cur_member->state_interface);
03601 
03602       ao2_ref(cur_member, -1);
03603 
03604       if (res != strlen(value + value_len)) {
03605          ast_log(LOG_WARNING, "Could not create persistent member string, out of space\n");
03606          break;
03607       }
03608       value_len += res;
03609    }
03610    ao2_iterator_destroy(&mem_iter);
03611    
03612    if (value_len && !cur_member) {
03613       if (ast_db_put(pm_family, pm_queue->name, value))
03614          ast_log(LOG_WARNING, "failed to create persistent dynamic entry!\n");
03615    } else
03616       /* Delete the entry if the queue is empty or there is an error */
03617       ast_db_del(pm_family, pm_queue->name);
03618 }

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

find the entry with the best metric, or NULL

Definition at line 2126 of file app_queue.c.

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

Referenced by ring_one(), and store_next().

02127 {
02128    struct callattempt *best = NULL, *cur;
02129 
02130    for (cur = outgoing; cur; cur = cur->q_next) {
02131       if (cur->stillgoing &&              /* Not already done */
02132          !cur->chan &&              /* Isn't already going */
02133          (!best || cur->metric < best->metric)) {     /* We haven't found one yet, or it's better */
02134          best = cur;
02135       }
02136    }
02137 
02138    return best;
02139 }

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

Reload a single queue via realtime.

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

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

Definition at line 1253 of file app_queue.c.

References alloc_queue(), ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next(), ao2_lock(), ao2_ref(), ao2_unlink(), ao2_unlock(), ast_category_browse(), ast_copy_string(), AST_LIST_INSERT_HEAD, AST_LIST_TRAVERSE, ast_log(), ast_variable_retrieve(), clear_queue(), call_queue::count, member::dead, call_queue::dead, init_queue(), member_interface::interface, LOG_DEBUG, LOG_WARNING, call_queue::membercount, call_queue::members, ast_variable::name, call_queue::name, ast_variable::next, queue_set_param(), QUEUE_STRATEGY_RINGALL, QUEUE_STRATEGY_ROUNDROBIN, member::realtime, call_queue::realtime, remove_from_interfaces(), remove_queue(), rr_dep_warning(), rt_handle_member_record(), S_OR, member::state_interface, strat2int(), call_queue::strategy, and ast_variable::value.

Referenced by load_realtime_queue().

01254 {
01255    struct ast_variable *v;
01256    struct call_queue *q;
01257    struct member *m;
01258    struct ao2_iterator mem_iter;
01259    char *interface = NULL;
01260    char *tmp, *tmp_name;
01261    char tmpbuf[64];  /* Must be longer than the longest queue param name. */
01262 
01263    /* Find the queue in the in-core list (we will create a new one if not found). */
01264    AST_LIST_TRAVERSE(&queues, q, list) {
01265       if (!strcasecmp(q->name, queuename))
01266          break;
01267    }
01268 
01269    /* Static queues override realtime. */
01270    if (q) {
01271       ao2_lock(q);
01272       if (!q->realtime) {
01273          if (q->dead) {
01274             ao2_unlock(q);
01275             return NULL;
01276          } else {
01277             ast_log(LOG_WARNING, "Static queue '%s' already exists. Not loading from realtime\n", q->name);
01278             ao2_unlock(q);
01279             return q;
01280          }
01281       }
01282    } else if (!member_config)
01283       /* Not found in the list, and it's not realtime ... */
01284       return NULL;
01285 
01286    /* Check if queue is defined in realtime. */
01287    if (!queue_vars) {
01288       /* Delete queue from in-core list if it has been deleted in realtime. */
01289       if (q) {
01290          /*! \note Hmm, can't seem to distinguish a DB failure from a not
01291             found condition... So we might delete an in-core queue
01292             in case of DB failure. */
01293          ast_log(LOG_DEBUG, "Queue %s not found in realtime.\n", queuename);
01294 
01295          q->dead = 1;
01296          /* Delete if unused (else will be deleted when last caller leaves). */
01297          if (!q->count) {
01298             /* Delete. */
01299             ao2_unlock(q);
01300             remove_queue(q);
01301          } else
01302             ao2_unlock(q);
01303       }
01304       return NULL;
01305    }
01306 
01307    /* Create a new queue if an in-core entry does not exist yet. */
01308    if (!q) {
01309       struct ast_variable *tmpvar;
01310       if (!(q = alloc_queue(queuename)))
01311          return NULL;
01312       ao2_lock(q);
01313       clear_queue(q);
01314       q->realtime = 1;
01315       AST_LIST_INSERT_HEAD(&queues, q, list);
01316    
01317       /* Due to the fact that the "rrordered" strategy will have a different allocation
01318        * scheme for queue members, we must devise the queue's strategy before other initializations.
01319        * To be specific, the rrordered strategy needs to function like a linked list, meaning the ao2
01320        * container used will have only a single bucket instead of the typical number.
01321        */
01322       for (tmpvar = queue_vars; tmpvar; tmpvar = tmpvar->next) {
01323          if (!strcasecmp(tmpvar->name, "strategy")) {
01324             q->strategy = strat2int(tmpvar->value);
01325             if (q->strategy < 0) {
01326                ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
01327                tmpvar->value, q->name);
01328                q->strategy = QUEUE_STRATEGY_RINGALL;
01329             }
01330             break;
01331          }
01332       }
01333       /* We traversed all variables and didn't find a strategy */
01334       if (!tmpvar) {
01335          q->strategy = QUEUE_STRATEGY_RINGALL;
01336       }
01337    }
01338    init_queue(q);    /* Ensure defaults for all parameters not set explicitly. */
01339 
01340    memset(tmpbuf, 0, sizeof(tmpbuf));
01341    for (v = queue_vars; v; v = v->next) {
01342       /* Convert to dashes `-' from underscores `_' as the latter are more SQL friendly. */
01343       if ((tmp = strchr(v->name, '_'))) {
01344          ast_copy_string(tmpbuf, v->name, sizeof(tmpbuf));
01345          tmp_name = tmpbuf;
01346          tmp = tmp_name;
01347          while ((tmp = strchr(tmp, '_')))
01348             *tmp++ = '-';
01349       } else
01350          tmp_name = v->name;
01351 
01352       /* NULL values don't get returned from realtime; blank values should
01353        * still get set.  If someone doesn't want a value to be set, they
01354        * should set the realtime column to NULL, not blank. */
01355       queue_set_param(q, tmp_name, v->value, -1, 0);
01356    }
01357 
01358    if (q->strategy == QUEUE_STRATEGY_ROUNDROBIN)
01359       rr_dep_warning();
01360 
01361    /* Temporarily set realtime members dead so we can detect deleted ones. 
01362     * Also set the membercount correctly for realtime*/
01363    mem_iter = ao2_iterator_init(q->members, 0);
01364    while ((m = ao2_iterator_next(&mem_iter))) {
01365       q->membercount++;
01366       if (m->realtime)
01367          m->dead = 1;
01368       ao2_ref(m, -1);
01369    }
01370    ao2_iterator_destroy(&mem_iter);
01371 
01372    while ((interface = ast_category_browse(member_config, interface))) {
01373       rt_handle_member_record(q, interface,
01374          ast_variable_retrieve(member_config, interface, "membername"),
01375          ast_variable_retrieve(member_config, interface, "penalty"),
01376          ast_variable_retrieve(member_config, interface, "paused"),
01377          S_OR(ast_variable_retrieve(member_config, interface, "state_interface"),interface));
01378    }
01379 
01380    /* Delete all realtime members that have been deleted in DB. */
01381    mem_iter = ao2_iterator_init(q->members, 0);
01382    while ((m = ao2_iterator_next(&mem_iter))) {
01383       if (m->dead) {
01384          ao2_unlink(q->members, m);
01385          ao2_unlock(q);
01386          remove_from_interfaces(m->state_interface);
01387          ao2_lock(q);
01388          q->membercount--;
01389       }
01390       ao2_ref(m, -1);
01391    }
01392    ao2_iterator_destroy(&mem_iter);
01393 
01394    ao2_unlock(q);
01395 
01396    return q;
01397 }

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

Definition at line 1233 of file app_queue.c.

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

Referenced by destroy_queue().

01234 {
01235    /* Free non-dynamic members */
01236    struct member *cur;
01237    struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
01238 
01239    while ((cur = ao2_iterator_next(&mem_iter))) {
01240       if (all || !cur->dynamic) {
01241          ao2_unlink(q->members, cur);
01242          remove_from_interfaces(cur->state_interface);
01243          q->membercount--;
01244       }
01245       ao2_ref(cur, -1);
01246    }
01247    ao2_iterator_destroy(&mem_iter);
01248 }

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

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next(), ao2_lock(), ao2_ref(), ao2_unlock(), AST_DEVICE_INVALID, AST_DEVICE_UNAVAILABLE, 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().

00590 {
00591    struct member *member;
00592    struct ao2_iterator mem_iter;
00593    enum queue_member_status result = QUEUE_NO_MEMBERS;
00594    int allpaused = 1, empty = 1;
00595 
00596    ao2_lock(q);
00597    mem_iter = ao2_iterator_init(q->members, 0);
00598    while ((member = ao2_iterator_next(&mem_iter))) {
00599       empty = 0;
00600 
00601       if (max_penalty && (member->penalty > max_penalty)) {
00602          ao2_ref(member, -1);
00603          continue;
00604       }
00605 
00606       if (member->paused) {
00607          ao2_ref(member, -1);
00608          continue;
00609       } else {
00610          allpaused = 0;
00611       }
00612 
00613       switch (member->status) {
00614       case AST_DEVICE_INVALID:
00615          /* nothing to do */
00616          ao2_ref(member, -1);
00617          break;
00618       case AST_DEVICE_UNAVAILABLE:
00619          result = QUEUE_NO_REACHABLE_MEMBERS;
00620          ao2_ref(member, -1);
00621          break;
00622       default:
00623          ao2_unlock(q);
00624          ao2_ref(member, -1);
00625          return QUEUE_NORMAL;
00626       }
00627    }
00628    ao2_iterator_destroy(&mem_iter);
00629    ao2_unlock(q);
00630 
00631    if (!empty && allpaused) {
00632       result = QUEUE_NO_REACHABLE_MEMBERS;
00633    }
00634    return result;
00635 }

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

Definition at line 5460 of file app_queue.c.

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

05461 {
05462    char *queuename, *interface, *membername = NULL, *state_interface = NULL;
05463    int penalty;
05464 
05465    if ((argc != 6) && (argc != 8) && (argc != 10) && (argc != 12)) {
05466       return RESULT_SHOWUSAGE;
05467    } else if (strcmp(argv[4], "to")) {
05468       return RESULT_SHOWUSAGE;
05469    } else if ((argc == 8) && strcmp(argv[6], "penalty")) {
05470       return RESULT_SHOWUSAGE;
05471    } else if ((argc == 10) && strcmp(argv[8], "as")) {
05472       return RESULT_SHOWUSAGE;
05473    } else if ((argc == 12) && strcmp(argv[10], "state_interface")) {
05474       return RESULT_SHOWUSAGE;
05475    }
05476 
05477    queuename = argv[5];
05478    interface = argv[3];
05479    if (argc >= 8) {
05480       if (sscanf(argv[7], "%30d", &penalty) == 1) {
05481          if (penalty < 0) {
05482             ast_cli(fd, "Penalty must be >= 0\n");
05483             penalty = 0;
05484          }
05485       } else {
05486          ast_cli(fd, "Penalty must be an integer >= 0\n");
05487          penalty = 0;
05488       }
05489    } else {
05490       penalty = 0;
05491    }
05492 
05493    if (argc >= 10) {
05494       membername = argv[9];
05495    }
05496 
05497    if (argc >= 12) {
05498       state_interface = argv[11];
05499    }
05500 
05501    switch (add_to_queue(queuename, interface, membername, penalty, 0, queue_persistent_members, state_interface)) {
05502    case RES_OKAY:
05503       ast_queue_log(queuename, "CLI", interface, "ADDMEMBER", "%s", "");
05504       ast_cli(fd, "Added interface '%s' to queue '%s'\n", interface, queuename);
05505       return RESULT_SUCCESS;
05506    case RES_EXISTS:
05507       ast_cli(fd, "Unable to add interface '%s' to queue '%s': Already there\n", interface, queuename);
05508       return RESULT_FAILURE;
05509    case RES_NOSUCHQUEUE:
05510       ast_cli(fd, "Unable to add interface to queue '%s': No such queue\n", queuename);
05511       return RESULT_FAILURE;
05512    case RES_OUTOFMEMORY:
05513       ast_cli(fd, "Out of memory\n");
05514       return RESULT_FAILURE;
05515    default:
05516       return RESULT_FAILURE;
05517    }
05518 }

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

Definition at line 5553 of file app_queue.c.

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

05554 {
05555    char *queuename, *interface;
05556 
05557    if (argc != 6) {
05558       return RESULT_SHOWUSAGE;
05559    } else if (strcmp(argv[4], "from")) {
05560       return RESULT_SHOWUSAGE;
05561    }
05562 
05563    queuename = argv[5];
05564    interface = argv[3];
05565 
05566    switch (remove_from_queue(queuename, interface)) {
05567    case RES_OKAY:
05568       ast_queue_log(queuename, "CLI", interface, "REMOVEMEMBER", "%s", "");
05569       ast_cli(fd, "Removed interface '%s' from queue '%s'\n", interface, queuename);
05570       return RESULT_SUCCESS;
05571    case RES_EXISTS:
05572       ast_cli(fd, "Unable to remove interface '%s' from queue '%s': Not there\n", interface, queuename);
05573       return RESULT_FAILURE;
05574    case RES_NOSUCHQUEUE:
05575       ast_cli(fd, "Unable to remove interface from queue '%s': No such queue\n", queuename);
05576       return RESULT_FAILURE;
05577    case RES_OUTOFMEMORY:
05578       ast_cli(fd, "Out of memory\n");
05579       return RESULT_FAILURE;
05580    case RES_NOT_DYNAMIC:
05581       ast_cli(fd, "Member not dynamic\n");
05582       return RESULT_FAILURE;
05583    default:
05584       return RESULT_FAILURE;
05585    }
05586 }

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

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

Referenced by device_state_thread().

00698 {
00699    struct member_interface *curint;
00700    char *loc;
00701    char *technology;
00702    char interface[80];
00703 
00704    technology = ast_strdupa(sc->dev);
00705    loc = strchr(technology, '/');
00706    if (loc) {
00707       *loc++ = '\0';
00708    } else {
00709       return NULL;
00710    }
00711 
00712    AST_LIST_LOCK(&interfaces);
00713    AST_LIST_TRAVERSE(&interfaces, curint, list) {
00714       char *slash_pos;
00715       ast_copy_string(interface, curint->interface, sizeof(interface));
00716       if ((slash_pos = strchr(interface, '/')))
00717          if ((slash_pos = strchr(slash_pos + 1, '/')))
00718             *slash_pos = '\0';
00719 
00720       if (!strcasecmp(interface, sc->dev))
00721          break;
00722    }
00723    AST_LIST_UNLOCK(&interfaces);
00724 
00725    if (!curint) {
00726       if (option_debug > 2)
00727          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));
00728       return NULL;
00729    }
00730 
00731    if (option_debug)
00732       ast_log(LOG_DEBUG, "Device '%s/%s' changed to state '%d' (%s)\n", technology, loc, sc->state, devstate2str(sc->state));
00733 
00734    update_status(sc->dev, sc->state);
00735 
00736    return NULL;
00737 }

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

Definition at line 1806 of file app_queue.c.

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

Referenced by try_calling().

01807 {
01808    struct callattempt *oo;
01809 
01810    while (outgoing) {
01811       /* Hangup any existing lines we have open */
01812       if (outgoing->chan && (outgoing->chan != exception))
01813          ast_hangup(outgoing->chan);
01814       oo = outgoing;
01815       outgoing = outgoing->q_next;
01816       if (oo->member)
01817          ao2_ref(oo->member, -1);
01818       free(oo);
01819    }
01820 }

static void init_queue ( struct call_queue q  )  [static]

Definition at line 873 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, QUEUE_STRATEGY_RRORDERED, 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::strategy, call_queue::timeout, call_queue::timeoutrestart, call_queue::weight, and call_queue::wrapuptime.

Referenced by find_queue_by_name_rt(), and reload_queues().

00874 {
00875    int i;
00876 
00877    q->dead = 0;
00878    q->retry = DEFAULT_RETRY;
00879    q->timeout = -1;
00880    q->maxlen = 0;
00881    q->ringlimit = 0;
00882    q->announcefrequency = 0;
00883    q->announceholdtime = 0;
00884    q->roundingseconds = 0; /* Default - don't announce seconds */
00885    q->servicelevel = 0;
00886    q->ringinuse = 1;
00887    q->setinterfacevar = 0;
00888    q->autofill = autofill_default;
00889    q->montype = montype_default;
00890    q->moh[0] = '\0';
00891    q->announce[0] = '\0';
00892    q->context[0] = '\0';
00893    q->monfmt[0] = '\0';
00894    q->periodicannouncefrequency = 0;
00895    q->reportholdtime = 0;
00896    q->monjoin = 0;
00897    q->wrapuptime = 0;
00898    q->joinempty = 0;
00899    q->leavewhenempty = 0;
00900    q->memberdelay = 0;
00901    q->maskmemberstatus = 0;
00902    q->eventwhencalled = 0;
00903    q->weight = 0;
00904    q->timeoutrestart = 0;
00905    if (!q->members) {
00906       if (q->strategy == QUEUE_STRATEGY_RRORDERED) {
00907          q->members = ao2_container_alloc(1, member_hash_fn, member_cmp_fn);
00908       } else {
00909          q->members = ao2_container_alloc(37, member_hash_fn, member_cmp_fn);
00910       }
00911    }
00912    q->membercount = 0;
00913    q->found = 1;
00914    ast_copy_string(q->sound_next, "queue-youarenext", sizeof(q->sound_next));
00915    ast_copy_string(q->sound_thereare, "queue-thereare", sizeof(q->sound_thereare));
00916    ast_copy_string(q->sound_calls, "queue-callswaiting", sizeof(q->sound_calls));
00917    ast_copy_string(q->sound_holdtime, "queue-holdtime", sizeof(q->sound_holdtime));
00918    ast_copy_string(q->sound_minutes, "queue-minutes", sizeof(q->sound_minutes));
00919    ast_copy_string(q->sound_seconds, "queue-seconds", sizeof(q->sound_seconds));
00920    ast_copy_string(q->sound_thanks, "queue-thankyou", sizeof(q->sound_thanks));
00921    ast_copy_string(q->sound_lessthan, "queue-less-than", sizeof(q->sound_lessthan));
00922    ast_copy_string(q->sound_reporthold, "queue-reporthold", sizeof(q->sound_reporthold));
00923    ast_copy_string(q->sound_periodicannounce[0], "queue-periodic-announce", sizeof(q->sound_periodicannounce[0]));
00924    for (i = 1; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) {
00925       q->sound_periodicannounce[i][0]='\0';
00926    }
00927 }

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

References ao2_ref(), call_queue::head, and queue_ent::next.

Referenced by join_queue().

00554 {
00555    struct queue_ent *cur;
00556 
00557    if (!q || !new)
00558       return;
00559    if (prev) {
00560       cur = prev->next;
00561       prev->next = new;
00562    } else {
00563       cur = q->head;
00564       q->head = new;
00565    }
00566    new->next = cur;
00567 
00568    /* every queue_ent must have a reference to it's parent call_queue, this
00569     * reference does not go away until the end of the queue_ent's life, meaning
00570     * that even when the queue_ent leaves the call_queue this ref must remain. */
00571    ao2_ref(q, +1);
00572    new->parent = q;
00573    new->pos = ++(*pos);
00574    new->opos = *pos;
00575 }

static char* int2strat ( int  strategy  )  [static]

Definition at line 507 of file app_queue.c.

References strategy::name, and strategies.

Referenced by __queues_show().

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

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

Definition at line 3552 of file app_queue.c.

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

Referenced by add_to_queue(), and set_member_paused().

03553 {
03554    struct member *mem;
03555    struct ao2_iterator mem_iter;
03556 
03557    if (!q)
03558       return NULL;
03559 
03560    mem_iter = ao2_iterator_init(q->members, 0);
03561    while ((mem = ao2_iterator_next(&mem_iter))) {
03562       if (!strcasecmp(interface, mem->interface)) {
03563          ao2_iterator_destroy(&mem_iter);
03564          return mem;
03565       }
03566       ao2_ref(mem, -1);
03567    }
03568    ao2_iterator_destroy(&mem_iter);
03569 
03570    return NULL;
03571 }

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

Definition at line 965 of file app_queue.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next(), ao2_lock(), ao2_ref(), ao2_unlock(), AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, call_queue::members, and member::state_interface.

Referenced by remove_from_interfaces().

00966 {
00967    struct call_queue *q;
00968    struct member *mem;
00969    struct ao2_iterator mem_iter;
00970    int ret = 0;
00971 
00972    AST_LIST_LOCK(&queues);
00973    AST_LIST_TRAVERSE(&queues, q, list) {
00974       ao2_lock(q);
00975       mem_iter = ao2_iterator_init(q->members, 0);
00976       while ((mem = ao2_iterator_next(&mem_iter))) {
00977          if (!strcasecmp(mem->state_interface, interface)) {
00978             ao2_ref(mem, -1);
00979             ret = 1;
00980             break;
00981          }
00982          ao2_ref(mem, -1);
00983       }
00984       ao2_iterator_destroy(&mem_iter);
00985       ao2_unlock(q);
00986       if (ret)
00987          break;
00988    }
00989    AST_LIST_UNLOCK(&queues);
00990 
00991    return ret;
00992 }

static int is_our_turn ( struct queue_ent qe  )  [static]

Check if we should start attempting to call queue members.

A simple process, really. Count the number of members who are available to take our call and then see if we are in a position in the queue at which a member could accept our call.

Parameters:
[in] qe The caller who wants to know if it is his turn
Return values:
0 It is not our turn
1 It is our turn

Definition at line 2590 of file app_queue.c.

References ao2_lock(), ao2_unlock(), ast_log(), call_queue::autofill, queue_ent::chan, call_queue::head, LOG_DEBUG, queue_ent::next, num_available_members(), option_debug, queue_ent::parent, queue_ent::pending, and queue_ent::pos.

Referenced by queue_exec(), and wait_our_turn().

02591 {
02592    struct queue_ent *ch;
02593    int res;
02594    int avl;
02595    int idx = 0;
02596    /* This needs a lock. How many members are available to be served? */
02597    ao2_lock(qe->parent);
02598 
02599    avl = num_available_members(qe->parent);
02600 
02601    ch = qe->parent->head;
02602 
02603    if (option_debug) {
02604       ast_log(LOG_DEBUG, "There %s %d available %s.\n", avl != 1 ? "are" : "is", avl, avl != 1 ? "members" : "member");
02605    }
02606 
02607    while ((idx < avl) && (ch) && (ch != qe)) {
02608       if (!ch->pending)
02609          idx++;
02610       ch = ch->next;       
02611    }
02612 
02613    ao2_unlock(qe->parent);
02614    /* If the queue entry is within avl [the number of available members] calls from the top ... 
02615     * Autofill and position check added to support autofill=no (as only calls
02616     * from the front of the queue are valid when autofill is disabled)
02617     */
02618    if (ch && idx < avl && (qe->parent->autofill || qe->pos == 1)) {
02619       if (option_debug)
02620          ast_log(LOG_DEBUG, "It's our turn (%s).\n", qe->chan->name);
02621       res = 1;
02622    } else {
02623       if (option_debug)
02624          ast_log(LOG_DEBUG, "It's not our turn (%s).\n", qe->chan->name);
02625       res = 0;
02626    }
02627 
02628    return res;
02629 }

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

Definition at line 1520 of file app_queue.c.

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

Referenced by queue_exec().

01521 {
01522    struct call_queue *q;
01523    struct queue_ent *cur, *prev = NULL;
01524    int res = -1;
01525    int pos = 0;
01526    int inserted = 0;
01527    enum queue_member_status stat;
01528 
01529    if (!(q = load_realtime_queue(queuename)))
01530       return res;
01531 
01532    AST_LIST_LOCK(&queues);
01533    ao2_lock(q);
01534 
01535    /* This is our one */
01536    stat = get_member_status(q, qe->max_penalty);
01537    if (!q->joinempty && (stat == QUEUE_NO_MEMBERS))
01538       *reason = QUEUE_JOINEMPTY;
01539    else if ((q->joinempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS || stat == QUEUE_NO_MEMBERS))
01540       *reason = QUEUE_JOINUNAVAIL;
01541    else if (q->maxlen && (q->count >= q->maxlen))
01542       *reason = QUEUE_FULL;
01543    else {
01544       /* There's space for us, put us at the right position inside
01545        * the queue.
01546        * Take into account the priority of the calling user */
01547       inserted = 0;
01548       prev = NULL;
01549       cur = q->head;
01550       while (cur) {
01551          /* We have higher priority than the current user, enter
01552           * before him, after all the other users with priority
01553           * higher or equal to our priority. */
01554          if ((!inserted) && (qe->prio > cur->prio)) {
01555             insert_entry(q, prev, qe, &pos);
01556             inserted = 1;
01557          }
01558          cur->pos = ++pos;
01559          prev = cur;
01560          cur = cur->next;
01561       }
01562       /* No luck, join at the end of the queue */
01563       if (!inserted)
01564          insert_entry(q, prev, qe, &pos);
01565       ast_copy_string(qe->moh, q->moh, sizeof(qe->moh));
01566       ast_copy_string(qe->announce, q->announce, sizeof(qe->announce));
01567       ast_copy_string(qe->context, q->context, sizeof(qe->context));
01568       q->count++;
01569       res = 0;
01570       manager_event(EVENT_FLAG_CALL, "Join",
01571          "Channel: %s\r\nCallerID: %s\r\nCallerIDName: %s\r\nQueue: %s\r\nPosition: %d\r\nCount: %d\r\nUniqueid: %s\r\n",
01572          qe->chan->name,
01573          S_OR(qe->chan->cid.cid_num, "unknown"), /* XXX somewhere else it is <unknown> */
01574          S_OR(qe->chan->cid.cid_name, "unknown"),
01575          q->name, qe->pos, q->count, qe->chan->uniqueid );
01576       if (option_debug)
01577          ast_log(LOG_DEBUG, "Queue '%s' Join, Channel '%s', Position '%d'\n", q->name, qe->chan->name, qe->pos );
01578    }
01579    ao2_unlock(q);
01580    AST_LIST_UNLOCK(&queues);
01581 
01582    return res;
01583 }

static void leave_queue ( struct queue_ent qe  )  [static]

Definition at line 1765 of file app_queue.c.

References ao2_lock(), ao2_unlock(), ast_log(), queue_ent::chan, call_queue::count, call_queue::dead, EVENT_FLAG_CALL, call_queue::head, LOG_DEBUG, manager_event(), call_queue::name, queue_ent::next, option_debug, queue_ent::parent, queue_ent::pos, and remove_queue().

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

01766 {
01767    struct call_queue *q;
01768    struct queue_ent *cur, *prev = NULL;
01769    int pos = 0;
01770 
01771    if (!(q = qe->parent))
01772       return;
01773    ao2_lock(q);
01774 
01775    prev = NULL;
01776    for (cur = q->head; cur; cur = cur->next) {
01777       if (cur == qe) {
01778          q->count--;
01779 
01780          /* Take us out of the queue */
01781          manager_event(EVENT_FLAG_CALL, "Leave",
01782             "Channel: %s\r\nQueue: %s\r\nCount: %d\r\nUniqueid: %s\r\n",
01783             qe->chan->name, q->name,  q->count, qe->chan->uniqueid);
01784          if (option_debug)
01785             ast_log(LOG_DEBUG, "Queue '%s' Leave, Channel '%s'\n", q->name, qe->chan->name );
01786          /* Take us out of the queue */
01787          if (prev)
01788             prev->next = cur->next;
01789          else
01790             q->head = cur->next;
01791       } else {
01792          /* Renumber the people after us in the queue based on a new count */
01793          cur->pos = ++pos;
01794          prev = cur;
01795       }
01796    }
01797    ao2_unlock(q);
01798 
01799    if (q->dead && !q->count) {   
01800       /* It's dead and nobody is in it, so kill it */
01801       remove_queue(q);
01802    }
01803 }

static int load_module ( void   )  [static]

Definition at line 5845 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_queue_member_count(), manager_queues_show(), manager_queues_status(), manager_remove_queue_member(), pqm_exec(), ql_exec(), queue_exec(), queueagentcount_function, queueexists_function, queuemembercount_function, queuememberlist_function, queuememberpaused_function, queuememberstatus_function, queuewaitingcount_function, reload_queue_members(), reload_queues(), rqm_exec(), statechange_queue(), and upqm_exec().

05846 {
05847    int res;
05848 
05849    if (!reload_queues())
05850       return AST_MODULE_LOAD_DECLINE;
05851 
05852    if (queue_persistent_members)
05853       reload_queue_members();
05854 
05855    ast_mutex_init(&device_state.lock);
05856    ast_cond_init(&device_state.cond, NULL);
05857    ast_pthread_create(&device_state.thread, NULL, device_state_thread, NULL);
05858 
05859    ast_cli_register_multiple(cli_queue, sizeof(cli_queue) / sizeof(struct ast_cli_entry));
05860    res = ast_register_application(app, queue_exec, synopsis, descrip);
05861    res |= ast_register_application(app_aqm, aqm_exec, app_aqm_synopsis, app_aqm_descrip);
05862    res |= ast_register_application(app_rqm, rqm_exec, app_rqm_synopsis, app_rqm_descrip);
05863    res |= ast_register_application(app_pqm, pqm_exec, app_pqm_synopsis, app_pqm_descrip);
05864    res |= ast_register_application(app_upqm, upqm_exec, app_upqm_synopsis, app_upqm_descrip);
05865    res |= ast_register_application(app_ql, ql_exec, app_ql_synopsis, app_ql_descrip);
05866    res |= ast_manager_register("Queues", 0, manager_queues_show, "Queues");
05867    res |= ast_manager_register("QueueStatus", 0, manager_queues_status, "Queue Status");
05868    res |= ast_manager_register("QueueMemberCount", 0, manager_queue_member_count, "Queue Member Count");
05869    res |= ast_manager_register("QueueAdd", EVENT_FLAG_AGENT, manager_add_queue_member, "Add interface to queue.");
05870    res |= ast_manager_register("QueueRemove", EVENT_FLAG_AGENT, manager_remove_queue_member, "Remove interface from queue.");
05871    res |= ast_manager_register("QueuePause", EVENT_FLAG_AGENT, manager_pause_queue_member, "Makes a queue member temporarily unavailable");
05872    res |= ast_custom_function_register(&queueexists_function);
05873    res |= ast_custom_function_register(&queueagentcount_function);
05874    res |= ast_custom_function_register(&queuemembercount_function);
05875    res |= ast_custom_function_register(&queuememberlist_function);
05876    res |= ast_custom_function_register(&queuememberstatus_function);
05877    res |= ast_custom_function_register(&queuememberpaused_function);
05878    res |= ast_custom_function_register(&queuewaitingcount_function);
05879    res |= ast_devstate_add(statechange_queue, NULL);
05880 
05881    return res;
05882 }

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

Note:
Load from realtime before taking the global qlock, to avoid blocking all queue operations while waiting for the DB.

This will be two separate database transactions, so we might see queue parameters as they were before another process changed the queue and member list as it was after the change. Thus we might see an empty member list when a queue is deleted. In practise, this is unlikely to cause a problem.

Definition at line 1470 of file app_queue.c.

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

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

01471 {
01472    struct ast_variable *queue_vars;
01473    struct ast_config *member_config = NULL;
01474    struct call_queue *q;
01475 
01476    /* Find the queue in the in-core list first. */
01477    AST_LIST_LOCK(&queues);
01478    AST_LIST_TRAVERSE(&queues, q, list) {
01479       if (!strcasecmp(q->name, queuename)) {
01480          break;
01481       }
01482    }
01483    AST_LIST_UNLOCK(&queues);
01484 
01485    if (!q || q->realtime) {
01486       /*! \note Load from realtime before taking the global qlock, to avoid blocking all
01487          queue operations while waiting for the DB.
01488 
01489          This will be two separate database transactions, so we might
01490          see queue parameters as they were before another process
01491          changed the queue and member list as it was after the change.
01492          Thus we might see an empty member list when a queue is
01493          deleted. In practise, this is unlikely to cause a problem. */
01494 
01495       queue_vars = ast_load_realtime("queues", "name", queuename, NULL);
01496       if (queue_vars) {
01497          member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", queuename, NULL);
01498          if (!member_config) {
01499             ast_log(LOG_ERROR, "no queue_members defined in your config (extconfig.conf).\n");
01500             ast_variables_destroy(queue_vars);
01501             return NULL;
01502          }
01503       }
01504 
01505       AST_LIST_LOCK(&queues);
01506 
01507       q = find_queue_by_name_rt(queuename, queue_vars, member_config);
01508       if (member_config)
01509          ast_config_destroy(member_config);
01510       if (queue_vars)
01511          ast_variables_destroy(queue_vars);
01512 
01513       AST_LIST_UNLOCK(&queues);
01514    } else { 
01515       update_realtime_members(q);
01516    }
01517    return q;
01518 }

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

Definition at line 5352 of file app_queue.c.

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

Referenced by load_module().

05353 {
05354    const char *queuename, *interface, *penalty_s, *paused_s, *membername, *state_interface;
05355    int paused, penalty = 0;
05356 
05357    queuename = astman_get_header(m, "Queue");
05358    interface = astman_get_header(m, "Interface");
05359    penalty_s = astman_get_header(m, "Penalty");
05360    paused_s = astman_get_header(m, "Paused");
05361    membername = astman_get_header(m, "MemberName");
05362    state_interface = astman_get_header(m, "StateInterface");
05363 
05364    if (ast_strlen_zero(queuename)) {
05365       astman_send_error(s, m, "'Queue' not specified.");
05366       return 0;
05367    }
05368 
05369    if (ast_strlen_zero(interface)) {
05370       astman_send_error(s, m, "'Interface' not specified.");
05371       return 0;
05372    }
05373 
05374    if (ast_strlen_zero(penalty_s))
05375       penalty = 0;
05376    else if (sscanf(penalty_s, "%30d", &penalty) != 1 || penalty < 0)
05377       penalty = 0;
05378 
05379    if (ast_strlen_zero(paused_s))
05380       paused = 0;
05381    else
05382       paused = abs(ast_true(paused_s));
05383 
05384    switch (add_to_queue(queuename, interface, membername, penalty, paused, queue_persistent_members, state_interface)) {
05385    case RES_OKAY:
05386       ast_queue_log(queuename, "MANAGER", interface, "ADDMEMBER", "%s", "");
05387       astman_send_ack(s, m, "Added interface to queue");
05388       break;
05389    case RES_EXISTS:
05390       astman_send_error(s, m, "Unable to add interface: Already there");
05391       break;
05392    case RES_NOSUCHQUEUE:
05393       astman_send_error(s, m, "Unable to add interface to queue: No such queue");
05394       break;
05395    case RES_OUTOFMEMORY:
05396       astman_send_error(s, m, "Out of memory");
05397       break;
05398    }
05399 
05400    return 0;
05401 }

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

Definition at line 5437 of file app_queue.c.

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

Referenced by load_module().

05438 {
05439    const char *queuename, *interface, *paused_s;
05440    int paused;
05441 
05442    interface = astman_get_header(m, "Interface");
05443    paused_s = astman_get_header(m, "Paused");
05444    queuename = astman_get_header(m, "Queue");   /* Optional - if not supplied, pause the given Interface in all queues */
05445 
05446    if (ast_strlen_zero(interface) || ast_strlen_zero(paused_s)) {
05447       astman_send_error(s, m, "Need 'Interface' and 'Paused' parameters.");
05448       return 0;
05449    }
05450 
05451    paused = abs(ast_true(paused_s));
05452 
05453    if (set_member_paused(queuename, interface, paused))
05454       astman_send_error(s, m, "Interface not found");
05455    else
05456       astman_send_ack(s, m, paused ? "Interface paused successfully" : "Interface unpaused successfully");
05457    return 0;
05458 }

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

Definition at line 5697 of file app_queue.c.

References ast_strlen_zero(), astman_get_header(), astman_send_ack(), astman_send_error(), qmc_handler(), and RESULT_SUCCESS.

Referenced by load_module().

05698 {
05699    char buffer[256] = "";
05700    const char *queuename = astman_get_header(m,"Queue");
05701 
05702    if (ast_strlen_zero(queuename)) {
05703          astman_send_error(s, m, "'Queue' not specified.");
05704          return 0;
05705    }
05706    if (qmc_handler(queuename, buffer, sizeof(buffer)) == RESULT_SUCCESS) {
05707          astman_send_ack(s, m, buffer);
05708          return RESULT_SUCCESS;
05709    } else {
05710          astman_send_error(s, m, "Queue not found.");
05711          return 0;
05712    }
05713 }

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

Definition at line 5246 of file app_queue.c.

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

Referenced by load_module().

05247 {
05248    char *a[] = { "queue", "show" };
05249 
05250    __queues_show(s, 1, -1, 2, a);
05251    astman_append(s, "\r\n\r\n"); /* Properly terminate Manager output */
05252 
05253    return RESULT_SUCCESS;
05254 }

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

Definition at line 5257 of file app_queue.c.

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

Referenced by load_module().

05258 {
05259    time_t now;
05260    int pos;
05261    const char *id = astman_get_header(m,"ActionID");
05262    const char *queuefilter = astman_get_header(m,"Queue");
05263    const char *memberfilter = astman_get_header(m,"Member");
05264    char idText[256] = "";
05265    struct call_queue *q;
05266    struct queue_ent *qe;
05267    float sl = 0;
05268    struct member *mem;
05269    struct ao2_iterator mem_iter;
05270 
05271    astman_send_ack(s, m, "Queue status will follow");
05272    time(&now);
05273    AST_LIST_LOCK(&queues);
05274    if (!ast_strlen_zero(id))
05275       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
05276 
05277    AST_LIST_TRAVERSE(&queues, q, list) {
05278       ao2_lock(q);
05279 
05280       /* List queue properties */
05281       if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) {
05282          sl = ((q->callscompleted > 0) ? 100 * ((float)q->callscompletedinsl / (float)q->callscompleted) : 0);
05283          astman_append(s, "Event: QueueParams\r\n"
05284             "Queue: %s\r\n"
05285             "Max: %d\r\n"
05286             "Calls: %d\r\n"
05287             "Holdtime: %d\r\n"
05288             "Completed: %d\r\n"
05289             "Abandoned: %d\r\n"
05290             "ServiceLevel: %d\r\n"
05291             "ServicelevelPerf: %2.1f\r\n"
05292             "RingLimit: %d\r\n"
05293             "Weight: %d\r\n"
05294             "%s"
05295             "\r\n",
05296             q->name, q->maxlen, q->count, q->holdtime, q->callscompleted,
05297             q->callsabandoned, q->servicelevel, sl,  q->ringlimit, q->weight, idText);
05298          /* List Queue Members */
05299          mem_iter = ao2_iterator_init(q->members, 0);
05300          while ((mem = ao2_iterator_next(&mem_iter))) {
05301             if (ast_strlen_zero(memberfilter) || !strcmp(mem->interface, memberfilter)) {
05302                astman_append(s, "Event: QueueMember\r\n"
05303                   "Queue: %s\r\n"
05304                   "Name: %s\r\n"
05305                   "Location: %s\r\n"
05306                   "Membership: %s\r\n"
05307                   "Penalty: %d\r\n"
05308                   "CallsTaken: %d\r\n"
05309                   "LastCall: %d\r\n"
05310                   "Status: %d\r\n"
05311                   "Paused: %d\r\n"
05312                   "%s"
05313                   "\r\n",
05314                   q->name, mem->membername, mem->interface, mem->dynamic ? "dynamic" : "static",
05315                   mem->penalty, mem->calls, (int)mem->lastcall, mem->status, mem->paused, idText);
05316             }
05317             ao2_ref(mem, -1);
05318          }
05319          ao2_iterator_destroy(&mem_iter);
05320          /* List Queue Entries */
05321          pos = 1;
05322          for (qe = q->head; qe; qe = qe->next) {
05323             astman_append(s, "Event: QueueEntry\r\n"
05324                "Queue: %s\r\n"
05325                "Position: %d\r\n"
05326                "Channel: %s\r\n"
05327                "CallerID: %s\r\n"
05328                "CallerIDName: %s\r\n"
05329                "Wait: %ld\r\n"
05330                "%s"
05331                "\r\n",
05332                q->name, pos++, qe->chan->name,
05333                S_OR(qe->chan->cid.cid_num, "unknown"),
05334                S_OR(qe->chan->cid.cid_name, "unknown"),
05335                (long) (now - qe->start), idText);
05336          }
05337       }
05338       ao2_unlock(q);
05339    }
05340 
05341    astman_append(s,
05342       "Event: QueueStatusComplete\r\n"
05343       "%s"
05344       "\r\n",idText);
05345 
05346    AST_LIST_UNLOCK(&queues);
05347 
05348 
05349    return RESULT_SUCCESS;
05350 }

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

Definition at line 5403 of file app_queue.c.

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

Referenced by load_module().

05404 {
05405    const char *queuename, *interface;
05406 
05407    queuename = astman_get_header(m, "Queue");
05408    interface = astman_get_header(m, "Interface");
05409 
05410    if (ast_strlen_zero(queuename) || ast_strlen_zero(interface)) {
05411       astman_send_error(s, m, "Need 'Queue' and 'Interface' parameters.");
05412       return 0;
05413    }
05414 
05415    switch (remove_from_queue(queuename, interface)) {
05416    case RES_OKAY:
05417       ast_queue_log(queuename, "MANAGER", interface, "REMOVEMEMBER", "%s", "");
05418       astman_send_ack(s, m, "Removed interface from queue");
05419       break;
05420    case RES_EXISTS:
05421       astman_send_error(s, m, "Unable to remove interface: Not there");
05422       break;
05423    case RES_NOSUCHQUEUE:
05424       astman_send_error(s, m, "Unable to remove interface from queue: No such queue");
05425       break;
05426    case RES_OUTOFMEMORY:
05427       astman_send_error(s, m, "Out of memory");
05428       break;
05429    case RES_NOT_DYNAMIC:
05430       astman_send_error(s, m, "Member not dynamic");
05431       break;
05432    }
05433 
05434    return 0;
05435 }

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

Definition at line 867 of file app_queue.c.

References member::interface.

Referenced by init_queue().

00868 {
00869    struct member *mem1 = obj1, *mem2 = obj2;
00870    return strcasecmp(mem1->interface, mem2->interface) ? 0 : CMP_MATCH;
00871 }

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

Definition at line 855 of file app_queue.c.

References compress_char(), and member::interface.

Referenced by init_queue().

00856 {
00857    const struct member *mem = obj;
00858    const char *chname = strchr(mem->interface, '/');
00859    int ret = 0, i;
00860    if (!chname)
00861       chname = mem->interface;
00862    for (i = 0; i < 5 && chname[i]; i++)
00863       ret += compress_char(chname[i]) << (i * 6);
00864    return ret;
00865 }

static void monjoin_dep_warning ( void   )  [static]

Definition at line 486 of file app_queue.c.

References ast_log(), and LOG_NOTICE.

Referenced by queue_set_param().

00487 {
00488    static unsigned int warned = 0;
00489    if (!warned) {
00490       ast_log(LOG_NOTICE, "The 'monitor-join' queue option is deprecated. Please use monitor-type=mixmonitor instead.\n");
00491       warned = 1;
00492    }
00493 }

static int num_available_members ( struct call_queue q  )  [static]

Get the number of members available to accept a call.

Note:
The queue passed in should be locked prior to this function call
Parameters:
[in] q The queue for which we are couting the number of available members
Returns:
Return the number of available members in queue q

Definition at line 1830 of file app_queue.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next(), ao2_ref(), AST_DEVICE_INUSE, AST_DEVICE_NOT_INUSE, AST_DEVICE_ONHOLD, AST_DEVICE_RINGING, AST_DEVICE_RINGINUSE, AST_DEVICE_UNKNOWN, call_queue::autofill, call_queue::members, member::paused, QUEUE_STRATEGY_RINGALL, call_queue::ringinuse, member::status, and call_queue::strategy.

Referenced by compare_weight(), and is_our_turn().

01831 {
01832    struct member *mem;
01833    int avl = 0;
01834    struct ao2_iterator mem_iter;
01835 
01836    mem_iter = ao2_iterator_init(q->members, 0);
01837    while ((mem = ao2_iterator_next(&mem_iter))) {
01838       switch (mem->status) {
01839       case AST_DEVICE_INUSE:
01840          if (!q->ringinuse)
01841             break;
01842          /* else fall through */
01843       case AST_DEVICE_NOT_INUSE:
01844       case AST_DEVICE_ONHOLD:
01845       case AST_DEVICE_RINGINUSE:
01846       case AST_DEVICE_RINGING:
01847       case AST_DEVICE_UNKNOWN:
01848          if (!mem->paused) {
01849             avl++;
01850          }
01851          break;
01852       }
01853       ao2_ref(mem, -1);
01854 
01855       /* If autofill is not enabled or if the queue's strategy is ringall, then
01856        * we really don't care about the number of available members so much as we
01857        * do that there is at least one available.
01858        *
01859        * In fact, we purposely will return from this function stating that only
01860        * one member is available if either of those conditions hold. That way,
01861        * functions which determine what action to take based on the number of available
01862        * members will operate properly. The reasoning is that even if multiple
01863        * members are available, only the head caller can actually be serviced.
01864        */
01865       if ((!q->autofill || q->strategy == QUEUE_STRATEGY_RINGALL) && avl) {
01866          break;
01867       }
01868    }
01869    ao2_iterator_destroy(&mem_iter);
01870 
01871    return avl;
01872 }

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

Definition at line 1585 of file app_queue.c.

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

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

01586 {
01587    int res;
01588 
01589    if (ast_strlen_zero(filename)) {
01590       return 0;
01591    }
01592 
01593    if (!ast_fileexists(filename, NULL, chan->language)) {
01594       return 0;
01595    }
01596 
01597    ast_stopstream(chan);
01598 
01599    res = ast_streamfile(chan, filename, chan->language);
01600    if (!res)
01601       res = ast_waitstream(chan, AST_DIGIT_ANY);
01602 
01603    ast_stopstream(chan);
01604 
01605    return res;
01606 }

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

Definition at line 3869 of file app_queue.c.

References AST_APP_ARG, AST_DECLARE_APP_ARGS, ast_goto_if_exists(), ast_log(), ast_module_user_add, ast_module_user_remove, ast_opt_priority_jumping, AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), ast_channel::context, ast_channel::exten, member_interface::interface, LOG_WARNING, parse(), pbx_builtin_setvar_helper(), ast_channel::priority, and set_member_paused().

Referenced by load_module().

03870 {
03871    struct ast_module_user *lu;
03872    char *parse;
03873    int priority_jump = 0;
03874    int ignore_fail = 0;
03875    AST_DECLARE_APP_ARGS(args,
03876       AST_APP_ARG(queuename);
03877       AST_APP_ARG(interface);
03878       AST_APP_ARG(options);
03879    );
03880 
03881    if (ast_strlen_zero(data)) {
03882       ast_log(LOG_WARNING, "PauseQueueMember requires an argument ([queuename]|interface[|options])\n");
03883       return -1;
03884    }
03885 
03886    parse = ast_strdupa(data);
03887 
03888    AST_STANDARD_APP_ARGS(args, parse);
03889 
03890    lu = ast_module_user_add(chan);
03891 
03892    if (args.options) {
03893       if (strchr(args.options, 'j'))
03894          priority_jump = 1;
03895       if (strchr(args.options, 'i'))
03896          ignore_fail = 1;
03897    }
03898 
03899    if (ast_strlen_zero(args.interface)) {
03900       ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename]|interface[|options])\n");
03901       ast_module_user_remove(lu);
03902       return -1;
03903    }
03904 
03905    if (set_member_paused(args.queuename, args.interface, 1)) {
03906       ast_log(LOG_WARNING, "Attempt to pause interface %s, not found\n", args.interface);
03907       pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND");
03908       if (priority_jump || ast_opt_priority_jumping) {
03909          if (!ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101)) {
03910             ast_module_user_remove(lu);
03911             return 0;
03912          }
03913       }
03914       ast_module_user_remove(lu);
03915       if (ignore_fail) {
03916          return 0;
03917       } else {
03918          return -1;
03919       }
03920    }
03921 
03922    ast_module_user_remove(lu);
03923    pbx_builtin_setvar_helper(chan, "PQMSTATUS", "PAUSED");
03924    return 0;
03925 }

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

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

Referenced by load_module().

04128 {
04129    struct ast_module_user *u;
04130    char *parse;
04131 
04132    AST_DECLARE_APP_ARGS(args,
04133       AST_APP_ARG(queuename);
04134       AST_APP_ARG(uniqueid);
04135       AST_APP_ARG(membername);
04136       AST_APP_ARG(event);
04137       AST_APP_ARG(params);
04138    );
04139 
04140    if (ast_strlen_zero(data)) {
04141       ast_log(LOG_WARNING, "QueueLog requires arguments (queuename|uniqueid|membername|event[|additionalinfo]\n");
04142       return -1;
04143    }
04144 
04145    u = ast_module_user_add(chan);
04146 
04147    parse = ast_strdupa(data);
04148 
04149    AST_STANDARD_APP_ARGS(args, parse);
04150 
04151    if (ast_strlen_zero(args.queuename) || ast_strlen_zero(args.uniqueid)
04152        || ast_strlen_zero(args.membername) || ast_strlen_zero(args.event)) {
04153       ast_log(LOG_WARNING, "QueueLog requires arguments (queuename|uniqueid|membername|event[|additionalinfo])\n");
04154       ast_module_user_remove(u);
04155       return -1;
04156    }
04157 
04158    ast_queue_log(args.queuename, args.uniqueid, args.membername, args.event, 
04159       "%s", args.params ? args.params : "");
04160 
04161    ast_module_user_remove(u);
04162 
04163    return 0;
04164 }

static int qmc_handler ( const char *  queuename,
char *  buffer,
int  len 
) [static]

Definition at line 5682 of file app_queue.c.

References member_count::active, member_count::all, member_count::free, member_count::inuse, member_count::paused, queue_member_count(), RESULT_FAILURE, RESULT_SUCCESS, and member_count::valid.

Referenced by cli_queue_member_count(), and manager_queue_member_count().

05683 {
05684       struct member_count qmc;
05685       memset(&qmc, 0, sizeof(qmc));
05686       if (queue_member_count(queuename, &qmc) != 0) {
05687             return RESULT_FAILURE; 
05688       } else {
05689             snprintf(buffer, len, 
05690                          "valid:%d inuse:%d paused:%d active:%d free:%d all:%d",
05691                          qmc.valid, qmc.inuse, qmc.paused, qmc.active, qmc.free, qmc.all);   
05692             return RESULT_SUCCESS;
05693       }
05694 }

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

References call_queue::announcefrequency, ao2_ref(), AST_APP_ARG, AST_CONTROL_RINGING, AST_DECLARE_APP_ARGS, ast_indicate(), ast_log(), ast_module_user_add, ast_module_user_remove, ast_moh_start(), ast_moh_stop(), ast_queue_log(), AST_STANDARD_APP_ARGS, ast_stopstream(), ast_strdupa, ast_strlen_zero(), ast_verbose(), queue_ent::chan, ast_channel::cid, ast_callerid::cid_num, queue_ent::digits, queue_ent::expire, get_member_status(), queue_ent::handled, is_our_turn(), join_queue(), queue_ent::last_periodic_announce_sound, queue_ent::last_periodic_announce_time, queue_ent::last_pos, queue_ent::last_pos_said, leave_queue(), call_queue::leavewhenempty, LOG_DEBUG, LOG_WARNING, queue_ent::max_penalty, call_queue::membercount, queue_ent::moh, queue_ent::opos, option_debug, option_verbose, queue_ent::parent, parse(), pbx_builtin_getvar_helper(), call_queue::periodicannouncefrequency, queue_ent::pos, queue_ent::prio, QUEUE_EMPTY_STRICT, QUEUE_LEAVEEMPTY, QUEUE_LEAVEUNAVAIL, QUEUE_NO_MEMBERS, QUEUE_NO_REACHABLE_MEMBERS, QUEUE_TIMEOUT, QUEUE_UNKNOWN, record_abandoned(), queue_ent::ring_when_ringing, S_OR, say_periodic_announcement(), say_position(), set_queue_result(), queue_ent::start, stop, try_calling(), update_realtime_members(), queue_ent::valid_digits, VERBOSE_PREFIX_3, wait_a_bit(), and wait_our_turn().

Referenced by load_module().

04179 {
04180    int res=-1;
04181    int ringing=0;
04182    struct ast_module_user *lu;
04183    const char *user_priority;
04184    const char *max_penalty_str;
04185    int prio;
04186    int max_penalty;
04187    enum queue_result reason = QUEUE_UNKNOWN;
04188    /* whether to exit Queue application after the timeout hits */
04189    int tries = 0;
04190    int noption = 0;
04191    char *parse;
04192    AST_DECLARE_APP_ARGS(args,
04193       AST_APP_ARG(queuename);
04194       AST_APP_ARG(options);
04195       AST_APP_ARG(url);
04196       AST_APP_ARG(announceoverride);
04197       AST_APP_ARG(queuetimeoutstr);
04198       AST_APP_ARG(agi);
04199    );
04200    /* Our queue entry */
04201    struct queue_ent qe = { 0 };
04202    
04203    if (ast_strlen_zero(data)) {
04204       ast_log(LOG_WARNING, "Queue requires an argument: queuename[|options[|URL[|announceoverride[|timeout[|agi]]]]]\n");
04205       return -1;
04206    }
04207    
04208    parse = ast_strdupa(data);
04209    AST_STANDARD_APP_ARGS(args, parse);
04210 
04211    lu = ast_module_user_add(chan);
04212 
04213    /* Setup our queue entry */
04214    qe.start = time(NULL);
04215 
04216    /* set the expire time based on the supplied timeout; */
04217    if (!ast_strlen_zero(args.queuetimeoutstr))
04218       qe.expire = qe.start + atoi(args.queuetimeoutstr);
04219    else
04220       qe.expire = 0;
04221 
04222    /* Get the priority from the variable ${QUEUE_PRIO} */
04223    user_priority = pbx_builtin_getvar_helper(chan, "QUEUE_PRIO");
04224    if (user_priority) {
04225       if (sscanf(user_priority, "%30d", &prio) == 1) {
04226          if (option_debug)
04227             ast_log(LOG_DEBUG, "%s: Got priority %d from ${QUEUE_PRIO}.\n",
04228                chan->name, prio);
04229       } else {
04230          ast_log(LOG_WARNING, "${QUEUE_PRIO}: Invalid value (%s), channel %s.\n",
04231             user_priority, chan->name);
04232          prio = 0;
04233       }
04234    } else {
04235       if (option_debug > 2)
04236          ast_log(LOG_DEBUG, "NO QUEUE_PRIO variable found. Using default.\n");
04237       prio = 0;
04238    }
04239 
04240    /* Get the maximum penalty from the variable ${QUEUE_MAX_PENALTY} */
04241    if ((max_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MAX_PENALTY"))) {
04242       if (sscanf(max_penalty_str, "%30d", &max_penalty) == 1) {
04243          if (option_debug)
04244             ast_log(LOG_DEBUG, "%s: Got max penalty %d from ${QUEUE_MAX_PENALTY}.\n",
04245                chan->name, max_penalty);
04246       } else {
04247          ast_log(LOG_WARNING, "${QUEUE_MAX_PENALTY}: Invalid value (%s), channel %s.\n",
04248             max_penalty_str, chan->name);
04249          max_penalty = 0;
04250       }
04251    } else {
04252       max_penalty = 0;
04253    }
04254 
04255    if (args.options && (strchr(args.options, 'r')))
04256       ringing = 1;
04257 
04258    if (ringing != 1 && args.options && (strchr(args.options, 'R'))) {
04259       qe.ring_when_ringing = 1;
04260    }
04261 
04262    if (option_debug)
04263       ast_log(LOG_DEBUG, "queue: %s, options: %s, url: %s, announce: %s, expires: %ld, priority: %d\n",
04264          args.queuename, args.options, args.url, args.announceoverride, (long)qe.expire, prio);
04265 
04266    qe.chan = chan;
04267    qe.prio = prio;
04268    qe.max_penalty = max_penalty;
04269    qe.last_pos_said = 0;
04270    qe.last_pos = 0;
04271    qe.last_periodic_announce_time = time(NULL);
04272    qe.last_periodic_announce_sound = 0;
04273    qe.valid_digits = 0;
04274    if (!join_queue(args.queuename, &qe, &reason)) {
04275       int makeannouncement = 0;
04276 
04277       ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ENTERQUEUE", "%s|%s", S_OR(args.url, ""),
04278          S_OR(chan->cid.cid_num, ""));
04279 check_turns:
04280       if (ringing) {
04281          ast_indicate(chan, AST_CONTROL_RINGING);
04282       } else {
04283          ast_moh_start(chan, qe.moh, NULL);
04284       }
04285 
04286       /* This is the wait loop for callers 2 through maxlen */
04287       res = wait_our_turn(&qe, ringing, &reason);
04288       if (res)
04289          goto stop;
04290 
04291       for (;;) {
04292          /* This is the wait loop for the head caller*/
04293          /* To exit, they may get their call answered; */
04294          /* they may dial a digit from the queue context; */
04295          /* or, they may timeout. */
04296 
04297          enum queue_member_status stat;
04298 
04299          /* Leave if we have exceeded our queuetimeout */
04300          if (qe.expire && (time(NULL) >= qe.expire)) {
04301             record_abandoned(&qe);
04302             reason = QUEUE_TIMEOUT;
04303             res = 0;
04304             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
04305             break;
04306          }
04307 
04308          if (makeannouncement) {
04309             /* Make a position announcement, if enabled */
04310             if (qe.parent->announcefrequency && !ringing)
04311                if ((res = say_position(&qe)))
04312                   goto stop;
04313 
04314          }
04315          makeannouncement = 1;
04316 
04317          /* Leave if we have exceeded our queuetimeout */
04318          if (qe.expire && (time(NULL) >= qe.expire)) {
04319             record_abandoned(&qe);
04320             reason = QUEUE_TIMEOUT;
04321             res = 0;
04322             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
04323             break;
04324          }
04325          /* Make a periodic announcement, if enabled */
04326          if (qe.parent->periodicannouncefrequency && !ringing)
04327             if ((res = say_periodic_announcement(&qe)))
04328                goto stop;
04329 
04330          /* Leave if we have exceeded our queuetimeout */
04331          if (qe.expire && (time(NULL) >= qe.expire)) {
04332             record_abandoned(&qe);
04333             reason = QUEUE_TIMEOUT;
04334             res = 0;
04335             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
04336             break;
04337          }
04338          /* Try calling all queue members for 'timeout' seconds */
04339          res = try_calling(&qe, args.options, args.announceoverride, args.url, &tries, &noption, args.agi);
04340          if (res)
04341             goto stop;
04342 
04343          stat = get_member_status(qe.parent, qe.max_penalty);
04344 
04345          /* exit after 'timeout' cycle if 'n' option enabled */
04346          if (noption && tries >= qe.parent->membercount) {
04347             if (option_verbose > 2)
04348                ast_verbose(VERBOSE_PREFIX_3 "Exiting on time-out cycle\n");
04349             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
04350             record_abandoned(&qe);
04351             reason = QUEUE_TIMEOUT;
04352             res = 0;
04353             break;
04354          }
04355 
04356          /* leave the queue if no agents, if enabled */
04357          if (qe.parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) {
04358             record_abandoned(&qe);
04359             reason = QUEUE_LEAVEEMPTY;
04360             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
04361             res = 0;
04362             break;
04363          }
04364 
04365          /* leave the queue if no reachable agents, if enabled */
04366          if ((qe.parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) {
04367             record_abandoned(&qe);
04368             reason = QUEUE_LEAVEUNAVAIL;
04369             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
04370             res = 0;
04371             break;
04372          }
04373 
04374          /* Leave if we have exceeded our queuetimeout */
04375          if (qe.expire && (time(NULL) >= qe.expire)) {
04376             record_abandoned(&qe);
04377             reason = QUEUE_TIMEOUT;
04378             res = 0;
04379             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
04380             break;
04381          }
04382 
04383          /* If using dynamic realtime members, we should regenerate the member list for this queue */
04384          update_realtime_members(qe.parent);
04385 
04386          /* OK, we didn't get anybody; wait for 'retry' seconds; may get a digit to exit with */
04387          res = wait_a_bit(&qe);
04388          if (res)
04389             goto stop;
04390 
04391          /* Since this is a priority queue and
04392           * it is not sure that we are still at the head
04393           * of the queue, go and check for our turn again.
04394           */
04395          if (!is_our_turn(&qe)) {
04396             if (option_debug)
04397                ast_log(LOG_DEBUG, "Darn priorities, going back in queue (%s)!\n",
04398                   qe.chan->name);
04399             goto check_turns;
04400          }
04401       }
04402 
04403 stop:
04404       if (res) {
04405          if (res < 0) {
04406             if (!qe.handled) {
04407                record_abandoned(&qe);
04408                ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ABANDON",
04409                   "%d|%d|%ld", qe.pos, qe.opos,
04410                   (long) time(NULL) - qe.start);
04411             }
04412             res = -1;
04413          } else if (qe.valid_digits) {
04414             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHKEY",
04415                "%s|%d", qe.digits, qe.pos);
04416          }
04417       }
04418 
04419       /* Don't allow return code > 0 */
04420       if (res >= 0) {
04421          res = 0; 
04422          if (ringing) {
04423             ast_indicate(chan, -1);
04424          } else {
04425             ast_moh_stop(chan);
04426          }        
04427          ast_stopstream(chan);
04428       }
04429       leave_queue(&qe);
04430       if (reason != QUEUE_UNKNOWN)
04431          set_queue_result(chan, reason);
04432    } else {
04433       ast_log(LOG_WARNING, "Unable to join queue '%s'\n", args.queuename);
04434       set_queue_result(chan, reason);
04435       res = 0;
04436    }
04437    if (qe.parent) {
04438       /* every queue_ent is given a reference to it's parent call_queue when it joins the queue.
04439        * This ref must be taken away right before the queue_ent is destroyed.  In this case
04440        * the queue_ent is about to be returned on the stack */
04441       ao2_ref(qe.parent, -1);
04442    }
04443    ast_module_user_remove(lu);
04444 
04445    return res;
04446 }

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

Definition at line 4754 of file app_queue.c.

References ast_log(), ast_module_user_add, ast_module_user_remove, ast_strlen_zero(), load_realtime_queue(), and LOG_ERROR.

04755 {
04756   struct ast_module_user *lu;
04757   buf[0] = '\0';
04758   
04759   if (ast_strlen_zero(data)) {
04760     ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
04761     return -1;
04762   }
04763   lu = ast_module_user_add(chan);
04764   snprintf(buf, len, "%d", load_realtime_queue(data) != NULL? 1 : 0);
04765   ast_module_user_remove(lu);
04766   
04767   return 0;
04768 }

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

Definition at line 4456 of file app_queue.c.

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

04457 {
04458    int count = 0;
04459    struct call_queue *q;
04460    struct ast_module_user *lu;
04461    struct member *m;
04462    struct ao2_iterator mem_iter;
04463    char *name, *item;
04464    enum qmc_status mode = QMC_VALID;
04465 
04466    buf[0] = '\0';
04467    
04468    if (ast_strlen_zero(data)) {
04469       ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
04470       return -1;
04471    }
04472 
04473    name = ast_strdupa(data);
04474 
04475    lu = ast_module_user_add(chan);
04476 
04477    if ((item = strchr(name, ':'))) {
04478       *item = '\0';
04479       item++;
04480    } else {
04481       item = "";
04482    }
04483 
04484    if (!strcasecmp(item, "valid")) {
04485       mode = QMC_VALID;
04486    } else  if (!strcasecmp(item, "paused")) {
04487       mode = QMC_PAUSED;
04488    } else  if (!strcasecmp(item, "active")) {
04489       mode = QMC_ACTIVE;
04490    } else  if (!strcasecmp(item, "free")) {
04491       mode = QMC_FREE;
04492    } else  if (!strcasecmp(item, "all")) {
04493       mode = QMC_ALL;
04494    }
04495 
04496    if ((q = load_realtime_queue(name))) {
04497       ao2_lock(q);
04498       mem_iter = ao2_iterator_init(q->members, 0);
04499       while ((m = ao2_iterator_next(&mem_iter))) {
04500          switch (mode) {
04501          case QMC_VALID:
04502             /* Count the queue members who are logged in and presently answering calls */
04503             if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
04504                count++;
04505             }
04506             break;
04507          case QMC_PAUSED:
04508             /* Count paused members */
04509             if (m->paused) {
04510                count++;
04511             }
04512             break;
04513          case QMC_ACTIVE:
04514             /* Count not paused members who are logged in and presently answering calls */
04515             if (!m->paused && (m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
04516                count++;
04517             }
04518             break;
04519          case QMC_FREE:
04520             /* Count free members in the queue */
04521             if (!m->paused && ((m->status == AST_DEVICE_UNKNOWN) || (m->status == AST_DEVICE_NOT_INUSE))) {
04522                count++;
04523             }
04524             break;
04525          default:
04526             count++;
04527             break;
04528          }
04529          ao2_ref(m, -1);
04530       }
04531       ao2_iterator_destroy(&mem_iter);
04532       ao2_unlock(q);
04533    } else
04534       ast_log(LOG_WARNING, "queue %s was not found\n", name);
04535 
04536    snprintf(buf, len, "%d", count);
04537    ast_module_user_remove(lu);
04538 
04539    return 0;
04540 }

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

Definition at line 4585 of file app_queue.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next(), ao2_lock(), ao2_ref(), ao2_unlock(), AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), ast_module_user_add, ast_module_user_remove, ast_strlen_zero(), member::interface, LOG_ERROR, LOG_WARNING, call_queue::members, and call_queue::name.

04586 {
04587    struct ast_module_user *u;
04588    struct call_queue *q;
04589    struct member *m;
04590 
04591    /* Ensure an otherwise empty list doesn't return garbage */
04592    buf[0] = '\0';
04593 
04594    if (ast_strlen_zero(data)) {
04595       ast_log(LOG_ERROR, "QUEUE_MEMBER_LIST requires an argument: queuename\n");
04596       return -1;
04597    }
04598    
04599    u = ast_module_user_add(chan);
04600 
04601    AST_LIST_LOCK(&queues);
04602    AST_LIST_TRAVERSE(&queues, q, list) {
04603       if (!strcasecmp(q->name, data)) {
04604          ao2_lock(q);
04605          break;
04606       }
04607    }
04608    AST_LIST_UNLOCK(&queues);
04609 
04610    if (q) {
04611       int buflen = 0, count = 0;
04612       struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
04613 
04614       while ((m = ao2_iterator_next(&mem_iter))) {
04615          /* strcat() is always faster than printf() */
04616          if (count++) {
04617             strncat(buf + buflen, ",", len - buflen - 1);
04618             buflen++;
04619          }
04620          strncat(buf + buflen, m->interface, len - buflen - 1);
04621          buflen += strlen(m->interface);
04622          /* Safeguard against overflow (negative length) */
04623          if (buflen >= len - 2) {
04624             ao2_ref(m, -1);
04625             ast_log(LOG_WARNING, "Truncating list\n");
04626             break;
04627          }
04628          ao2_ref(m, -1);
04629       }
04630       ao2_iterator_destroy(&mem_iter);
04631       ao2_unlock(q);
04632    } else
04633       ast_log(LOG_WARNING, "queue %s was not found\n", data);
04634 
04635    /* We should already be terminated, but let's make sure. */
04636    buf[len - 1] = '\0';
04637    ast_module_user_remove(u);
04638 
04639    return 0;
04640 }

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

Definition at line 4698 of file app_queue.c.

References ao2_iterator_init(), ao2_iterator_next(), ao2_lock(), ao2_ref(), ao2_unlock(), AST_APP_ARG, ast_copy_string(), AST_DECLARE_APP_ARGS, ast_free, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), ast_malloc, ast_module_user_add, ast_module_user_remove, AST_STANDARD_APP_ARGS, ast_strlen_zero(), member_interface::interface, LOG_WARNING, member::membername, call_queue::members, call_queue::name, and member::paused.

04699 {
04700    struct ast_module_user *lu;
04701    struct call_queue *q;
04702    struct member *cur;
04703    struct ao2_iterator mem_iter;
04704    char tmp[128] = "";
04705    char *buffer;
04706 
04707    AST_DECLARE_APP_ARGS(args,
04708       AST_APP_ARG(queue);
04709       AST_APP_ARG(interface);
04710    );
04711 
04712    AST_STANDARD_APP_ARGS(args, data);
04713    if (ast_strlen_zero(args.interface)) {
04714       ast_log(LOG_WARNING, "This function requires an interface name.\n");
04715       return -1;
04716    } 
04717    lu = ast_module_user_add(chan);
04718 
04719    buffer = ast_malloc(len);
04720    buffer[0]='\0';
04721 
04722    AST_LIST_LOCK(&queues);
04723    AST_LIST_TRAVERSE(&queues, q, list) {
04724       ao2_lock(q);
04725       if (ast_strlen_zero(args.queue) ||
04726             (!ast_strlen_zero(args.queue) && !strncmp(args.queue, q->name, strlen(args.queue)))
04727             ) {
04728             /* Iterate over queue members */
04729             mem_iter = ao2_iterator_init(q->members, 0);
04730             while ((cur = ao2_iterator_next(&mem_iter))) {
04731                   if (!strncasecmp(args.interface, cur->membername, strlen(args.interface))) {
04732                         if (!ast_strlen_zero(args.queue)) {
04733                               ast_copy_string(buffer, cur->paused?"1":"0", len);
04734                         } else {
04735                               snprintf(tmp, sizeof(tmp), "%s%s:%s", ast_strlen_zero(tmp)?"":",", q->name, cur->paused?"1":"0");
04736                               strncat(buffer, tmp, sizeof(tmp));
04737                         }
04738                         ao2_ref(cur, -1);
04739                         break;
04740                   }
04741                   ao2_ref(cur, -1);
04742             }
04743       }
04744       ao2_unlock(q);
04745    }
04746    AST_LIST_UNLOCK(&queues);
04747    ast_copy_string(buf, buffer, len);
04748    ast_free(buffer);
04749 
04750    ast_module_user_remove(lu);
04751    return 0;
04752 }

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

Definition at line 4642 of file app_queue.c.

References ao2_iterator_init(), ao2_iterator_next(), ao2_lock(), ao2_ref(), ao2_unlock(), AST_APP_ARG, ast_copy_string(), AST_DECLARE_APP_ARGS, ast_free, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), ast_malloc, ast_module_user_add, ast_module_user_remove, AST_STANDARD_APP_ARGS, ast_strlen_zero(), devstate2str(), member_interface::interface, LOG_WARNING, member::membername, call_queue::members, call_queue::name, and member::status.

04643 {
04644    struct ast_module_user *lu;
04645    struct call_queue *q;
04646    struct member *cur;
04647    struct ao2_iterator mem_iter;
04648    char tmp[128] = "";
04649    char *buffer;
04650 
04651    AST_DECLARE_APP_ARGS(args,
04652       AST_APP_ARG(queue);
04653       AST_APP_ARG(interface);
04654    );
04655 
04656    AST_STANDARD_APP_ARGS(args, data);
04657    if (ast_strlen_zero(args.interface)) {
04658       ast_log(LOG_WARNING, "This function requires an interface name.\n");
04659       return -1;
04660    } 
04661    lu = ast_module_user_add(chan);
04662 
04663    buffer = ast_malloc(len);
04664    buffer[0]='\0';
04665 
04666    AST_LIST_LOCK(&queues);
04667    AST_LIST_TRAVERSE(&queues, q, list) {
04668       ao2_lock(q);
04669       if (ast_strlen_zero(args.queue) ||
04670             (!ast_strlen_zero(args.queue) && !strncmp(args.queue, q->name, strlen(args.queue)))
04671             ) {
04672             /* Iterate over queue members */
04673             mem_iter = ao2_iterator_init(q->members, 0);
04674             while ((cur = ao2_iterator_next(&mem_iter))) {
04675                   if (!strncasecmp(args.interface, cur->membername, strlen(args.interface))) {
04676                         if (!ast_strlen_zero(args.queue)) {
04677                               ast_copy_string(buffer, devstate2str(cur->status), len);
04678                         } else {
04679                                         snprintf(tmp, sizeof(tmp), "%s%s:%s", ast_strlen_zero(tmp)?"":",", q->name, devstate2str(cur->status));
04680                               strncat(buffer, tmp, sizeof(tmp));
04681                         }
04682                         ao2_ref(cur, -1);
04683                         continue;
04684                   }
04685                   ao2_ref(cur, -1);
04686             }
04687       }
04688       ao2_unlock(q);
04689    }
04690    AST_LIST_UNLOCK(&queues);
04691    ast_copy_string(buf, buffer, len);
04692    ast_free(buffer);
04693 
04694    ast_module_user_remove(lu);
04695    return 0;
04696 }

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

Definition at line 4542 of file app_queue.c.

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

04543 {
04544    int count = 0;
04545    struct call_queue *q;
04546    struct ast_module_user *lu;
04547    struct ast_variable *var = NULL;
04548 
04549    buf[0] = '\0';
04550    
04551    if (ast_strlen_zero(data)) {
04552       ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
04553       return -1;
04554    }
04555 
04556    lu = ast_module_user_add(chan);
04557    
04558    AST_LIST_LOCK(&queues);
04559    AST_LIST_TRAVERSE(&queues, q, list) {
04560       if (!strcasecmp(q->name, data)) {
04561          ao2_lock(q);
04562          break;
04563       }
04564    }
04565    AST_LIST_UNLOCK(&queues);
04566 
04567    if (q) {
04568       count = q->count;
04569       ao2_unlock(q);
04570    } else if ((var = ast_load_realtime("queues", "name", data, NULL))) {
04571       /* if the queue is realtime but was not found in memory, this
04572        * means that the queue had been deleted from memory since it was 
04573        * "dead." This means it has a 0 waiting count
04574        */
04575       count = 0;
04576       ast_variables_destroy(var);
04577    } else
04578       ast_log(LOG_WARNING, "queue %s was not found\n", data);
04579 
04580    snprintf(buf, len, "%d", count);
04581    ast_module_user_remove(lu);
04582    return 0;
04583 }

static int queue_member_count ( const char *  qname,
struct member_count qmc 
) [static]

Definition at line 5640 of file app_queue.c.

References member_count::active, member_count::all, ao2_iterator_init(), ao2_iterator_next(), ao2_lock(), ao2_ref(), ao2_unlock(), AST_DEVICE_INUSE, AST_DEVICE_INVALID, AST_DEVICE_NOT_INUSE, AST_DEVICE_UNAVAILABLE, AST_DEVICE_UNKNOWN, ast_log(), member_count::free, member_count::inuse, load_realtime_queue(), LOG_WARNING, call_queue::members, member_count::paused, member::paused, member::status, and member_count::valid.

Referenced by qmc_handler().

05641 {
05642       int res = 0;
05643       struct call_queue *q;
05644       struct member *m;
05645       struct ao2_iterator mem_iter;
05646       
05647       if ((q = load_realtime_queue(qname))) {
05648             ao2_lock(q);
05649             mem_iter = ao2_iterator_init(q->members, 0);
05650             while ((m = ao2_iterator_next(&mem_iter))) {
05651                   /* Count the queue members in use */
05652                   if (m->status == AST_DEVICE_INUSE) {
05653                         qmc->inuse++;
05654                   }
05655                   /* Count the queue members who are logged in and presently answering calls */
05656                   if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
05657                         qmc->valid++;
05658                   }
05659                   /* Count paused members */
05660                   if (m->paused) {
05661                         qmc->paused++;
05662                   }
05663                   /* Count not paused members who are logged in and presently answering calls */
05664                   if (!m->paused && (m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
05665                         qmc->active++;
05666                   }
05667                   /* Count free members in the queue */
05668                   if (!m->paused && ((m->status == AST_DEVICE_UNKNOWN) || (m->status == AST_DEVICE_NOT_INUSE))) {
05669                         qmc->free++;
05670                   }
05671                   qmc->all++;
05672                   ao2_ref(m, -1);
05673             }
05674             ao2_unlock(q);
05675       } else {
05676             ast_log(LOG_WARNING, "Queue %s was not found\n", qname);
05677             res = -1;
05678       }
05679       return res;
05680 }

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 1034 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, strsep(), call_queue::timeout, call_queue::timeoutrestart, call_queue::weight, and call_queue::wrapuptime.

Referenced by find_queue_by_name_rt(), and reload_queues().

01035 {
01036    if (!strcasecmp(param, "musicclass") || 
01037       !strcasecmp(param, "music") || !strcasecmp(param, "musiconhold")) {
01038       ast_copy_string(q->moh, val, sizeof(q->moh));
01039    } else if (!strcasecmp(param, "announce")) {
01040       ast_copy_string(q->announce, val, sizeof(q->announce));
01041    } else if (!strcasecmp(param, "context")) {
01042       ast_copy_string(q->context, val, sizeof(q->context));
01043    } else if (!strcasecmp(param, "timeout")) {
01044       q->timeout = atoi(val);
01045       if (q->timeout < 0)
01046          q->timeout = DEFAULT_TIMEOUT;
01047    } else if (!strcasecmp(param, "ringinuse")) {
01048       q->ringinuse = ast_true(val);
01049    } else if (!strcasecmp(param, "setinterfacevar")) {
01050       q->setinterfacevar = ast_true(val);
01051    } else if (!strcasecmp(param, "monitor-join")) {
01052       monjoin_dep_warning();
01053       q->monjoin = ast_true(val);
01054    } else if (!strcasecmp(param, "monitor-format")) {
01055       ast_copy_string(q->monfmt, val, sizeof(q->monfmt));
01056    } else if (!strcasecmp(param, "queue-youarenext")) {
01057       ast_copy_string(q->sound_next, val, sizeof(q->sound_next));
01058    } else if (!strcasecmp(param, "queue-thereare")) {
01059       ast_copy_string(q->sound_thereare, val, sizeof(q->sound_thereare));
01060    } else if (!strcasecmp(param, "queue-callswaiting")) {
01061       ast_copy_string(q->sound_calls, val, sizeof(q->sound_calls));
01062    } else if (!strcasecmp(param, "queue-holdtime")) {
01063       ast_copy_string(q->sound_holdtime, val, sizeof(q->sound_holdtime));
01064    } else if (!strcasecmp(param, "queue-minutes")) {
01065       ast_copy_string(q->sound_minutes, val, sizeof(q->sound_minutes));
01066    } else if (!strcasecmp(param, "queue-seconds")) {
01067       ast_copy_string(q->sound_seconds, val, sizeof(q->sound_seconds));
01068    } else if (!strcasecmp(param, "queue-lessthan")) {
01069       ast_copy_string(q->sound_lessthan, val, sizeof(q->sound_lessthan));
01070    } else if (!strcasecmp(param, "queue-thankyou")) {
01071       ast_copy_string(q->sound_thanks, val, sizeof(q->sound_thanks));
01072    } else if (!strcasecmp(param, "queue-reporthold")) {
01073       ast_copy_string(q->sound_reporthold, val, sizeof(q->sound_reporthold));
01074    } else if (!strcasecmp(param, "announce-frequency")) {
01075       q->announcefrequency = atoi(val);
01076    } else if (!strcasecmp(param, "announce-round-seconds")) {
01077       q->roundingseconds = atoi(val);
01078       if (q->roundingseconds>60 || q->roundingseconds<0) {
01079          if (linenum >= 0) {
01080             ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
01081                "using 0 instead for queue '%s' at line %d of queues.conf\n",
01082                val, param, q->name, linenum);
01083          } else {
01084             ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
01085                "using 0 instead for queue '%s'\n", val, param, q->name);
01086          }
01087          q->roundingseconds=0;
01088       }
01089    } else if (!strcasecmp(param, "announce-holdtime")) {
01090       if (!strcasecmp(val, "once"))
01091          q->announceholdtime = ANNOUNCEHOLDTIME_ONCE;
01092       else if (ast_true(val))
01093          q->announceholdtime = ANNOUNCEHOLDTIME_ALWAYS;
01094       else
01095          q->announceholdtime = 0;
01096    } else if (!strcasecmp(param, "periodic-announce")) {
01097       if (strchr(val, '|')) {
01098          char *s, *buf = ast_strdupa(val);
01099          unsigned int i = 0;
01100 
01101          while ((s = strsep(&buf, "|"))) {
01102             ast_copy_string(q->sound_periodicannounce[i], s, sizeof(q->sound_periodicannounce[i]));
01103             i++;
01104             if (i == MAX_PERIODIC_ANNOUNCEMENTS)
01105                break;
01106          }
01107       } else {
01108          ast_copy_string(q->sound_periodicannounce[0], val, sizeof(q->sound_periodicannounce[0]));
01109       }
01110    } else if (!strcasecmp(param, "periodic-announce-frequency")) {
01111       q->periodicannouncefrequency = atoi(val);
01112    } else if (!strcasecmp(param, "retry")) {
01113       q->retry = atoi(val);
01114       if (q->retry <= 0)
01115          q->retry = DEFAULT_RETRY;
01116    } else if (!strcasecmp(param, "wrapuptime")) {
01117       q->wrapuptime = atoi(val);
01118    } else if (!strcasecmp(param, "autofill")) {
01119       q->autofill = ast_true(val);
01120    } else if (!strcasecmp(param, "monitor-type")) {
01121       if (!strcasecmp(val, "mixmonitor"))
01122          q->montype = 1;
01123    } else if (!strcasecmp(param, "autopause")) {
01124       q->autopause = ast_true(val);
01125    } else if (!strcasecmp(param, "maxlen")) {
01126       q->maxlen = atoi(val);
01127       if (q->maxlen < 0)
01128          q->maxlen = 0;
01129    } else if (!strcasecmp(param, "ringlimit")) {
01130       q->ringlimit = atoi(val);
01131       if (q->ringlimit < 0)
01132          q->ringlimit = 0;
01133    } else if (!strcasecmp(param, "servicelevel")) {
01134       q->servicelevel= atoi(val);
01135    } else if (!strcasecmp(param, "strategy")) {
01136       q->strategy = strat2int(val);
01137       if (q->strategy < 0) {
01138          ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
01139             val, q->name);
01140          q->strategy = QUEUE_STRATEGY_RINGALL;
01141       }
01142    } else if (!strcasecmp(param, "joinempty")) {
01143       if (!strcasecmp(val, "strict"))
01144          q->joinempty = QUEUE_EMPTY_STRICT;
01145       else if (ast_true(val))
01146          q->joinempty = QUEUE_EMPTY_NORMAL;
01147       else
01148          q->joinempty = 0;
01149    } else if (!strcasecmp(param, "leavewhenempty")) {
01150       if (!strcasecmp(val, "strict"))
01151          q->leavewhenempty = QUEUE_EMPTY_STRICT;
01152       else if (ast_true(val))
01153          q->leavewhenempty = QUEUE_EMPTY_NORMAL;
01154       else
01155          q->leavewhenempty = 0;
01156    } else if (!strcasecmp(param, "eventmemberstatus")) {
01157       q->maskmemberstatus = !ast_true(val);
01158    } else if (!strcasecmp(param, "eventwhencalled")) {
01159       if (!strcasecmp(val, "vars")) {
01160          q->eventwhencalled = QUEUE_EVENT_VARIABLES;
01161       } else {
01162          q->eventwhencalled = ast_true(val) ? 1 : 0;
01163       }
01164    } else if (!strcasecmp(param, "reportholdtime")) {
01165       q->reportholdtime = ast_true(val);
01166    } else if (!strcasecmp(param, "memberdelay")) {
01167       q->memberdelay = atoi(val);
01168    } else if (!strcasecmp(param, "weight")) {
01169       q->weight = atoi(val);
01170       if (q->weight)
01171          use_weight++;
01172       /* With Realtime queues, if the last queue using weights is deleted in realtime,
01173          we will not see any effect on use_weight until next reload. */
01174    } else if (!strcasecmp(param, "timeoutrestart")) {
01175       q->timeoutrestart = ast_true(val);
01176    } else if (failunknown) {
01177       if (linenum >= 0) {
01178          ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s at line %d of queues.conf\n",
01179             q->name, param, linenum);
01180       } else {
01181          ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s\n", q->name, param);
01182       }
01183    }
01184 }

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

Definition at line 5212 of file app_queue.c.

References __queues_show().

Referenced by __queues_show().

05213 {
05214    return __queues_show(NULL, 0, fd, argc, argv);
05215 }

static void queue_transfer_destroy ( void *  data  )  [static]

Definition at line 2824 of file app_queue.c.

References ast_free.

02825 {
02826    struct queue_transfer_ds *qtds = data;
02827    ast_free(qtds);
02828 }

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 2847 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, and update_queue().

02848 {
02849    struct queue_transfer_ds *qtds = data;
02850    struct queue_ent *qe = qtds->qe;
02851    struct member *member = qtds->member;
02852    time_t callstart = qtds->starttime;
02853    int callcompletedinsl = qtds->callcompletedinsl;
02854    struct ast_datastore *datastore;
02855 
02856    ast_queue_log(qe->parent->name, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld",
02857             new_chan->exten, new_chan->context, (long) (callstart - qe->start),
02858             (long) (time(NULL) - callstart));
02859 
02860    update_queue(qe->parent, member, callcompletedinsl);
02861    
02862    /* No need to lock the channels because they are already locked in ast_do_masquerade */
02863    if ((datastore = ast_channel_datastore_find(old_chan, &queue_transfer_info, NULL))) {
02864       ast_channel_datastore_remove(old_chan, datastore);
02865    } else {
02866       ast_log(LOG_WARNING, "Can't find the queue_transfer datastore.\n");
02867    }
02868 }

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

Definition at line 1750 of file app_queue.c.

References ao2_lock(), ao2_unlock(), call_queue::holdtime, and queue_ent::parent.

Referenced by try_calling().

01751 {
01752    int oldvalue;
01753 
01754    /* Calculate holdtime using an exponential average */
01755    /* Thanks to SRT for this contribution */
01756    /* 2^2 (4) is the filter coefficient; a higher exponent would give old entries more weight */
01757 
01758    ao2_lock(qe->parent);
01759    oldvalue = qe->parent->holdtime;
01760    qe->parent->holdtime = (((oldvalue << 2) - oldvalue) + newholdtime) >> 2;
01761    ao2_unlock(qe->parent);
01762 }

static void record_abandoned ( struct queue_ent qe  )  [static]

Definition at line 2247 of file app_queue.c.

References ao2_lock(), ao2_unlock(), call_queue::callsabandoned, queue_ent::chan, EVENT_FLAG_AGENT, manager_event(), call_queue::name, queue_ent::opos, queue_ent::parent, queue_ent::pos, and queue_ent::start.

Referenced by queue_exec(), and try_calling().

02248 {
02249    ao2_lock(qe->parent);
02250    manager_event(EVENT_FLAG_AGENT, "QueueCallerAbandon",
02251       "Queue: %s\r\n"
02252       "Uniqueid: %s\r\n"
02253       "Position: %d\r\n"
02254       "OriginalPosition: %d\r\n"
02255       "HoldTime: %d\r\n",
02256       qe->parent->name, qe->chan->uniqueid, qe->pos, qe->opos, (int)(time(NULL) - qe->start));
02257 
02258    qe->parent->callsabandoned++;
02259    ao2_unlock(qe->parent);
02260 }

static int reload ( void   )  [static]

Definition at line 5884 of file app_queue.c.

References reload_queues().

05885 {
05886    reload_queues();
05887    return 0;
05888 }

static void reload_queue_members ( void   )  [static]

Definition at line 3772 of file app_queue.c.

References add_to_queue(), ao2_lock(), ao2_unlock(), ast_db_del(), ast_db_freetree(), ast_db_get(), ast_db_gettree(), AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), ast_strlen_zero(), ERANGE, errno, member_interface::interface, ast_db_entry::key, load_realtime_queue(), LOG_DEBUG, LOG_ERROR, LOG_NOTICE, LOG_WARNING, member::membername, call_queue::name, ast_db_entry::next, option_debug, member::paused, member::penalty, PM_MAX_LEN, RES_OUTOFMEMORY, member::state_interface, and strsep().

Referenced by load_module().

03773 {
03774    char *cur_ptr;
03775    char *queue_name;
03776    char *member;
03777    char *interface;
03778    char *membername = NULL;
03779    char *state_interface;
03780    char *penalty_tok;
03781    int penalty = 0;
03782    char *paused_tok;
03783    int paused = 0;
03784    struct ast_db_entry *db_tree;
03785    struct ast_db_entry *entry;
03786    struct call_queue *cur_queue;
03787    char queue_data[PM_MAX_LEN];
03788 
03789    AST_LIST_LOCK(&queues);
03790 
03791    /* Each key in 'pm_family' is the name of a queue */
03792    db_tree = ast_db_gettree(pm_family, NULL);
03793    for (entry = db_tree; entry; entry = entry->next) {
03794 
03795       queue_name = entry->key + strlen(pm_family) + 2;
03796 
03797       AST_LIST_TRAVERSE(&queues, cur_queue, list) {
03798          ao2_lock(cur_queue);
03799          if (!strcmp(queue_name, cur_queue->name))
03800             break;
03801          ao2_unlock(cur_queue);
03802       }
03803       
03804       if (!cur_queue)
03805          cur_queue = load_realtime_queue(queue_name);
03806 
03807       if (!cur_queue) {
03808          /* If the queue no longer exists, remove it from the
03809           * database */
03810          ast_log(LOG_WARNING, "Error loading persistent queue: '%s': it does not exist\n", queue_name);
03811          ast_db_del(pm_family, queue_name);
03812          continue;
03813       } else
03814          ao2_unlock(cur_queue);
03815 
03816       if (ast_db_get(pm_family, queue_name, queue_data, PM_MAX_LEN))
03817          continue;
03818 
03819       cur_ptr = queue_data;
03820       while ((member = strsep(&cur_ptr, "|"))) {
03821          if (ast_strlen_zero(member))
03822             continue;
03823 
03824          interface = strsep(&member, ";");
03825          penalty_tok = strsep(&member, ";");
03826          paused_tok = strsep(&member, ";");
03827          membername = strsep(&member, ";");
03828          state_interface = strsep(&member,";");
03829 
03830          if (!penalty_tok) {
03831             ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (penalty)\n", queue_name);
03832             break;
03833          }
03834          penalty = strtol(penalty_tok, NULL, 10);
03835          if (errno == ERANGE) {
03836             ast_log(LOG_WARNING, "Error converting penalty: %s: Out of range.\n", penalty_tok);
03837             break;
03838          }
03839          
03840          if (!paused_tok) {
03841             ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (paused)\n", queue_name);
03842             break;
03843          }
03844          paused = strtol(paused_tok, NULL, 10);
03845          if ((errno == ERANGE) || paused < 0 || paused > 1) {
03846             ast_log(LOG_WARNING, "Error converting paused: %s: Expected 0 or 1.\n", paused_tok);
03847             break;
03848          }
03849          if (ast_strlen_zero(membername))
03850             membername = interface;
03851 
03852          if (option_debug)
03853             ast_log(LOG_DEBUG, "Reload Members: Queue: %s  Member: %s  Name: %s  Penalty: %d  Paused: %d\n", queue_name, interface, membername, penalty, paused);
03854          
03855          if (add_to_queue(queue_name, interface, membername, penalty, paused, 0, state_interface) == RES_OUTOFMEMORY) {
03856             ast_log(LOG_ERROR, "Out of Memory when reloading persistent queue member\n");
03857             break;
03858          }
03859       }
03860    }
03861 
03862    AST_LIST_UNLOCK(&queues);
03863    if (db_tree) {
03864       ast_log(LOG_NOTICE, "Queue members successfully reloaded from database.\n");
03865       ast_db_freetree(db_tree);
03866    }
03867 }

static int reload_queues ( void   )  [static]

Definition at line 4839 of file app_queue.c.

References add_to_interfaces(), alloc_queue(), ao2_find(), ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next(), ao2_lock(), ao2_ref(), ao2_unlink(), ao2_unlock(), AST_APP_ARG, ast_category_browse(), ast_config_destroy(), ast_config_load(), ast_copy_string(), AST_DECLARE_APP_ARGS, ast_free, AST_LIST_INSERT_HEAD, AST_LIST_LOCK, AST_LIST_REMOVE_CURRENT, AST_LIST_TRAVERSE, AST_LIST_TRAVERSE_SAFE_BEGIN, AST_LIST_TRAVERSE_SAFE_END, AST_LIST_UNLOCK, ast_log(), AST_NONSTANDARD_APP_ARGS, ast_skip_blanks(), ast_strdup, 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, member_interface::interface, ast_variable::lineno, LOG_NOTICE, LOG_WARNING, call_queue::membercount, call_queue::members, ast_variable::name, call_queue::name, ast_variable::next, parse(), member::paused, queue_set_param(), QUEUE_STRATEGY_RINGALL, QUEUE_STRATEGY_ROUNDROBIN, call_queue::realtime, remove_from_interfaces(), rr_dep_warning(), member::state_interface, member::status, strat2int(), call_queue::strategy, ast_variable::value, and var.

Referenced by load_module(), and reload().

04840 {
04841    struct call_queue *q;
04842    struct ast_config *cfg;
04843    char *cat, *tmp;
04844    struct ast_variable *var;
04845    struct member *cur, *newm;
04846    struct ao2_iterator mem_iter;
04847    int new;
04848    const char *general_val = NULL;
04849    char *parse;
04850    char *interface, *state_interface;
04851    char *membername = NULL;
04852    int penalty;
04853    AST_DECLARE_APP_ARGS(args,
04854       AST_APP_ARG(interface);
04855       AST_APP_ARG(penalty);
04856       AST_APP_ARG(membername);
04857       AST_APP_ARG(state_interface);
04858    );
04859    
04860    if (!(cfg = ast_config_load("queues.conf"))) {
04861       ast_log(LOG_NOTICE, "No call queueing config file (queues.conf), so no call queues\n");
04862       return 0;
04863    }
04864    AST_LIST_LOCK(&queues);
04865    use_weight=0;
04866    /* Mark all non-realtime queues as dead for the moment */
04867    AST_LIST_TRAVERSE(&queues, q, list) {
04868       if (!q->realtime) {
04869          q->dead = 1;
04870          q->found = 0;
04871       }
04872    }
04873 
04874    /* Chug through config file */
04875    cat = NULL;
04876    while ((cat = ast_category_browse(cfg, cat)) ) {
04877       if (!strcasecmp(cat, "general")) {  
04878          /* Initialize global settings */
04879          queue_debug = 0;
04880          if ((general_val = ast_variable_retrieve(cfg, "general", "debug")))
04881             queue_debug = ast_true(general_val);
04882          queue_persistent_members = 0;
04883          if ((general_val = ast_variable_retrieve(cfg, "general", "persistentmembers")))
04884             queue_persistent_members = ast_true(general_val);
04885          autofill_default = 0;
04886          if ((general_val = ast_variable_retrieve(cfg, "general", "autofill")))
04887             autofill_default = ast_true(general_val);
04888          montype_default = 0;
04889          if ((general_val = ast_variable_retrieve(cfg, "general", "monitor-type")))
04890             if (!strcasecmp(general_val, "mixmonitor"))
04891                montype_default = 1;
04892          shared_lastcall = 0;
04893          if ((general_val = ast_variable_retrieve(cfg, "general", "shared_lastcall")))
04894             shared_lastcall = ast_true(general_val);
04895       } else { /* Define queue */
04896          /* Look for an existing one */
04897          AST_LIST_TRAVERSE(&queues, q, list) {
04898             if (!strcmp(q->name, cat))
04899                break;
04900          }
04901          if (!q) {
04902             /* Make one then */
04903             if (!(q = alloc_queue(cat))) {
04904                /* TODO: Handle memory allocation failure */
04905             }
04906             new = 1;
04907          } else
04908             new = 0;
04909          if (q) {
04910             const char *tmpvar;
04911             if (!new)
04912                ao2_lock(q);
04913             /* Check if a queue with this name already exists */
04914             if (q->found) {
04915                ast_log(LOG_WARNING, "Queue '%s' already defined! Skipping!\n", cat);
04916                if (!new)
04917                   ao2_unlock(q);
04918                continue;
04919             }
04920 
04921             /* Due to the fact that the "rrordered" strategy will have a different allocation
04922              * scheme for queue members, we must devise the queue's strategy before other initializations.
04923              * To be specific, the rrordered strategy needs to function like a linked list, meaning the ao2
04924              * container used will have only a single bucket instead of the typical number.
04925              */
04926             if ((tmpvar = ast_variable_retrieve(cfg, cat, "strategy"))) {
04927                q->strategy = strat2int(tmpvar);
04928                if (q->strategy < 0) {
04929                   ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n", tmpvar, q->name);
04930                   q->strategy = QUEUE_STRATEGY_RINGALL;
04931                }
04932             } else {
04933                q->strategy = QUEUE_STRATEGY_RINGALL;
04934             }
04935 
04936             /* Re-initialize the queue, and clear statistics */
04937             init_queue(q);
04938             clear_queue(q);
04939             mem_iter = ao2_iterator_init(q->members, 0);
04940             while ((cur = ao2_iterator_next(&mem_iter))) {
04941                if (!cur->dynamic) {
04942                   cur->delme = 1;
04943                }
04944                ao2_ref(cur, -1);
04945             }
04946             ao2_iterator_destroy(&mem_iter);
04947             for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
04948                if (!strcasecmp(var->name, "member")) {
04949                   struct member tmpmem;
04950                   membername = NULL;
04951 
04952                   if (ast_strlen_zero(var->value)) {
04953                      ast_log(LOG_WARNING, "Empty queue member definition at line %d. Moving on!\n", var->lineno);
04954                      continue;
04955                   }
04956 
04957                   /* Add a new member */
04958                   if (!(parse = ast_strdup(var->value))) {
04959                      continue;
04960                   }
04961                   
04962                   AST_NONSTANDARD_APP_ARGS(args, parse, ',');
04963 
04964                   interface = args.interface;
04965                   if (!ast_strlen_zero(args.penalty)) {
04966                      tmp = ast_skip_blanks(args.penalty);
04967                      penalty = atoi(tmp);
04968                      if (penalty < 0) {
04969                         penalty = 0;
04970                      }
04971                   } else
04972                      penalty = 0;
04973 
04974                   if (!ast_strlen_zero(args.membername)) {
04975                      membername = ast_skip_blanks(args.membername);
04976                   }
04977 
04978                   if (!ast_strlen_zero(args.state_interface)) {
04979                      state_interface = ast_skip_blanks(args.state_interface);
04980                   } else {
04981                      state_interface = interface;
04982                   }
04983 
04984                   /* Find the old position in the list */
04985                   ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
04986                   cur = ao2_find(q->members, &tmpmem, OBJ_POINTER | OBJ_UNLINK);
04987 
04988                   /* Only attempt removing from interfaces list if the new state_interface is different than the old one */
04989                   if (cur && strcasecmp(cur->state_interface, state_interface)) {
04990                      remove_from_interfaces(cur->state_interface);
04991                   }
04992 
04993                   newm = create_queue_member(interface, membername, penalty, cur ? cur->paused : 0, state_interface);
04994                   if (!cur || (cur && strcasecmp(cur->state_interface, state_interface))) {
04995                      add_to_interfaces(state_interface);
04996                   }
04997                   ao2_link(q->members, newm);
04998                   ao2_ref(newm, -1);
04999                   newm = NULL;
05000 
05001                   if (cur)
05002                      ao2_ref(cur, -1);
05003                   else {
05004                      q->membercount++;
05005                   }
05006                   ast_free(parse);
05007                } else {
05008                   queue_set_param(q, var->name, var->value, var->lineno, 1);
05009                }
05010             }
05011 
05012             /* Free remaining members marked as delme */
05013             mem_iter = ao2_iterator_init(q->members, 0);
05014             while ((cur = ao2_iterator_next(&mem_iter))) {
05015                if (! cur->delme) {
05016                   ao2_ref(cur, -1);
05017                   continue;
05018                }
05019 
05020                q->membercount--;
05021                ao2_unlink(q->members, cur);
05022                remove_from_interfaces(cur->state_interface);
05023                ao2_ref(cur, -1);
05024             }
05025             ao2_iterator_destroy(&mem_iter);
05026 
05027             if (q->strategy == QUEUE_STRATEGY_ROUNDROBIN)
05028                rr_dep_warning();
05029 
05030             if (new) {
05031                AST_LIST_INSERT_HEAD(&queues, q, list);
05032             } else
05033                ao2_unlock(q);
05034          }
05035       }
05036    }
05037    ast_config_destroy(cfg);
05038    AST_LIST_TRAVERSE_SAFE_BEGIN(&queues, q, list) {
05039       if (q->dead) {
05040          AST_LIST_REMOVE_CURRENT(&queues, list);
05041          ao2_ref(q, -1);
05042       } else {
05043          ao2_lock(q);
05044          mem_iter = ao2_iterator_init(q->members, 0);
05045          while ((cur = ao2_iterator_next(&mem_iter))) {
05046             if (cur->dynamic)
05047                q->membercount++;
05048             cur->status = ast_device_state(cur->state_interface);
05049             ao2_ref(cur, -1);
05050          }
05051          ao2_iterator_destroy(&mem_iter);
05052          ao2_unlock(q);
05053       }
05054    }
05055    AST_LIST_TRAVERSE_SAFE_END;
05056    AST_LIST_UNLOCK(&queues);
05057    return 1;
05058 }

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

Definition at line 994 of file app_queue.c.

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

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

00995 {
00996    struct member_interface *curint;
00997 
00998    if (interface_exists_global(interface))
00999       return 0;
01000 
01001    AST_LIST_LOCK(&interfaces);
01002    AST_LIST_TRAVERSE_SAFE_BEGIN(&interfaces, curint, list) {
01003       if (!strcasecmp(curint->interface, interface)) {
01004          if (option_debug)
01005             ast_log(LOG_DEBUG, "Removing %s from the list of interfaces that make up all of our queue members.\n", interface);
01006          AST_LIST_REMOVE_CURRENT(&interfaces, list);
01007          free(curint);
01008          break;
01009       }
01010    }
01011    AST_LIST_TRAVERSE_SAFE_END;
01012    AST_LIST_UNLOCK(&interfaces);
01013 
01014    return 0;
01015 }

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

Definition at line 3620 of file app_queue.c.

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

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

03621 {
03622    struct call_queue *q;
03623    struct member *mem, tmpmem;
03624    int res = RES_NOSUCHQUEUE;
03625 
03626    ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
03627 
03628    AST_LIST_LOCK(&queues);
03629    AST_LIST_TRAVERSE(&queues, q, list) {
03630       ao2_lock(q);
03631       if (strcmp(q->name, queuename)) {
03632          ao2_unlock(q);
03633          continue;
03634       }
03635 
03636       if ((mem = ao2_find(q->members, &tmpmem, OBJ_POINTER))) {
03637          /* XXX future changes should beware of this assumption!! */
03638          if (!mem->dynamic) {
03639             res = RES_NOT_DYNAMIC;
03640             ao2_ref(mem, -1);
03641             ao2_unlock(q);
03642             break;
03643          }
03644          q->membercount--;
03645          manager_event(EVENT_FLAG_AGENT, "QueueMemberRemoved",
03646             "Queue: %s\r\n"
03647             "Location: %s\r\n"
03648             "MemberName: %s\r\n",
03649             q->name, mem->interface, mem->membername);
03650          ao2_unlink(q->members, mem);
03651          remove_from_interfaces(mem->state_interface);
03652          ao2_ref(mem, -1);
03653 
03654          if (queue_persistent_members)
03655             dump_queue_members(q);
03656          
03657          res = RES_OKAY;
03658       } else {
03659          res = RES_EXISTS;
03660       }
03661       ao2_unlock(q);
03662       break;
03663    }
03664 
03665    AST_LIST_UNLOCK(&queues);
03666 
03667    return res;
03668 }

static void remove_queue ( struct call_queue q  )  [static]

removes a call_queue from the list of call_queues

Definition at line 534 of file app_queue.c.

References ao2_ref(), AST_LIST_LOCK, AST_LIST_REMOVE, and AST_LIST_UNLOCK.

Referenced by find_queue_by_name_rt(), and leave_queue().

00535 {
00536    AST_LIST_LOCK(&queues);
00537    if (AST_LIST_REMOVE(&queues, q, list)) {
00538       ao2_ref(q, -1);
00539    }
00540    AST_LIST_UNLOCK(&queues);
00541 }

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

References ast_cdr::accountcode, ast_channel::adsicpe, ast_cdr::amaflags, ao2_lock(), ao2_unlock(), ast_channel::appl, ast_call(), ast_cdr_busy(), ast_cdr_isset_unanswered(), ast_cdr_setdestchan(), ast_channel_datastore_inherit(), ast_channel_inherit_variables(), ast_channel_lock, ast_channel_unlock, ast_copy_string(), AST_DEVICE_NOT_INUSE, AST_DEVICE_UNKNOWN, ast_log(), ast_request(), ast_strdup, ast_strlen_zero(), ast_verbose(), ast_channel::cdr, callattempt::chan, queue_ent::chan, ast_cdr::channel, ast_channel::cid, ast_callerid::cid_ani, ast_callerid::cid_name, ast_callerid::cid_num, ast_cdr::clid, compare_weight(), ast_channel::context, ast_channel::data, ast_cdr::dcontext, ast_channel::dialcontext, do_hang(), ast_cdr::dst, EVENT_FLAG_AGENT, call_queue::eventwhencalled, ast_channel::exten, free, callattempt::interface, ast_cdr::lastapp, callattempt::lastcall, ast_cdr::lastdata, callattempt::lastqueue, LOG_DEBUG, LOG_NOTICE, manager_event(), callattempt::member, member::membername, call_queue::name, ast_channel::nativeformats, option_debug, option_verbose, queue_ent::parent, member::paused, pbx_builtin_getvar_helper(), pbx_builtin_setvar_helper(), ast_channel::priority, QUEUE_EVENT_VARIABLES, member::ringcount, call_queue::ringinuse, call_queue::rrpos, ast_cdr::src, member::state_interface, member::status, callattempt::stillgoing, update_status(), ast_cdr::userfield, vars2manager(), VERBOSE_PREFIX_3, ast_channel::whentohangup, and call_queue::wrapuptime.

Referenced by ring_one().

01954 {
01955    int res;
01956    int status;
01957    char tech[256];
01958    char *location, *location2;
01959    const char *macrocontext, *macroexten;
01960    char pickupmark[256], chan[128];
01961 
01962    /* on entry here, we know that tmp->chan == NULL */
01963         if ((tmp->lastqueue && tmp->lastqueue->wrapuptime && (time(NULL) - tmp->lastcall < tmp->lastqueue->wrapuptime)) ||
01964                 (!tmp->lastqueue && qe->parent->wrapuptime && (time(NULL) - tmp->lastcall < qe->parent->wrapuptime))) {
01965                 if (queue_debug)
01966          ast_log(LOG_NOTICE, "Wrapuptime not yet expired on queue %s for %s\n", 
01967             (tmp->lastqueue ? tmp->lastqueue->name : qe->parent->name), tmp->interface);
01968       tmp->stillgoing = 0;
01969       (*busies)++;
01970       return 0;
01971    }
01972 
01973    if (!qe->parent->ringinuse && (tmp->member->status != AST_DEVICE_NOT_INUSE) && (tmp->member->status != AST_DEVICE_UNKNOWN)) {
01974       if (queue_debug)
01975          ast_log(LOG_NOTICE, "%s in use, can't receive call\n", tmp->interface);
01976       tmp->stillgoing = 0;
01977       (*busies)++;
01978       return 0;
01979    }
01980 
01981    if (tmp->member->paused) {
01982       if (queue_debug)
01983          ast_log(LOG_NOTICE, "%s paused, can't receive call\n", tmp->interface);
01984       tmp->stillgoing = 0;
01985       (*busies)++;
01986       return 0;
01987    }
01988    if (use_weight && compare_weight(qe->parent,tmp->member)) {
01989       if (queue_debug)
01990          ast_log(LOG_NOTICE, "Priority queue delaying call to %s:%s\n", qe->parent->name, tmp->interface);
01991       if (qe->chan->cdr)
01992          ast_cdr_busy(qe->chan->cdr);
01993       tmp->stillgoing = 0;
01994       (*busies)++;
01995       return 0;
01996    }
01997 
01998    ast_copy_string(tech, tmp->interface, sizeof(tech));
01999    if ((location = strchr(tech, '/')))
02000       *location++ = '\0';
02001    else
02002       location = "";
02003 
02004    /* Request the peer */
02005    tmp->chan = ast_request(tech, qe->chan->nativeformats, location, &status);
02006    if (!tmp->chan) {       /* If we can't, just go on to the next call */
02007       if (queue_debug)
02008          ast_log(LOG_NOTICE, "Unable to create channel of type '%s' for Queue\n", tech);
02009       if (qe->chan->cdr)
02010          ast_cdr_busy(qe->chan->cdr);
02011       tmp->stillgoing = 0;
02012 
02013       update_status(tmp->member->state_interface, ast_device_state(tmp->member->state_interface));
02014 
02015       ao2_lock(qe->parent);
02016       qe->parent->rrpos++;
02017       ao2_unlock(qe->parent);
02018 
02019       (*busies)++;
02020       return 0;
02021    }
02022    
02023    /* Increment ring count */
02024    tmp->member->ringcount++;
02025    tmp->chan->appl = "AppQueue";
02026    tmp->chan->data = "(Outgoing Line)";
02027    tmp->chan->whentohangup = 0;
02028    if (tmp->chan->cid.cid_num)
02029       free(tmp->chan->cid.cid_num);
02030    tmp->chan->cid.cid_num = ast_strdup(qe->chan->cid.cid_num);
02031    if (tmp->chan->cid.cid_name)
02032       free(tmp->chan->cid.cid_name);
02033    tmp->chan->cid.cid_name = ast_strdup(qe->chan->cid.cid_name);
02034    if (tmp->chan->cid.cid_ani)
02035       free(tmp->chan->cid.cid_ani);
02036    tmp->chan->cid.cid_ani = ast_strdup(qe->chan->cid.cid_ani);
02037 
02038    /* Inherit specially named variables from parent channel */
02039    ast_channel_inherit_variables(qe->chan, tmp->chan);
02040    ast_channel_datastore_inherit(qe->chan, tmp->chan);
02041 
02042    /* Presense of ADSI CPE on outgoing channel follows ours */
02043    tmp->chan->adsicpe = qe->chan->adsicpe;
02044 
02045    /* Inherit context and extension */
02046    ast_channel_lock(qe->chan);
02047    macrocontext = pbx_builtin_getvar_helper(qe->chan, "MACRO_CONTEXT");
02048    if (!ast_strlen_zero(macrocontext))
02049       ast_copy_string(tmp->chan->dialcontext, macrocontext, sizeof(tmp->chan->dialcontext));
02050    else
02051       ast_copy_string(tmp->chan->dialcontext, qe->chan->context, sizeof(tmp->chan->dialcontext));
02052    macroexten = pbx_builtin_getvar_helper(qe->chan, "MACRO_EXTEN");
02053    if (!ast_strlen_zero(macroexten))
02054       ast_copy_string(tmp->chan->exten, macroexten, sizeof(tmp->chan->exten));
02055    else
02056       ast_copy_string(tmp->chan->exten, qe->chan->exten, sizeof(tmp->chan->exten));
02057    if (ast_cdr_isset_unanswered()) {
02058       /* they want to see the unanswered dial attempts! */
02059       /* set up the CDR fields on all the CDRs to give sensical information */
02060       ast_cdr_setdestchan(tmp->chan->cdr, tmp->chan->name);
02061       strcpy(tmp->chan->cdr->clid, qe->chan->cdr->clid);
02062       strcpy(tmp->chan->cdr->channel, qe->chan->cdr->channel);
02063       strcpy(tmp->chan->cdr->src, qe->chan->cdr->src);
02064       strcpy(tmp->chan->cdr->dst, qe->chan->exten);
02065       strcpy(tmp->chan->cdr->dcontext, qe->chan->context);
02066       strcpy(tmp->chan->cdr->lastapp, qe->chan->cdr->lastapp);
02067       strcpy(tmp->chan->cdr->lastdata, qe->chan->cdr->lastdata);
02068       tmp->chan->cdr->amaflags = qe->chan->cdr->amaflags;
02069       strcpy(tmp->chan->cdr->accountcode, qe->chan->cdr->accountcode);
02070       strcpy(tmp->chan->cdr->userfield, qe->chan->cdr->userfield);
02071    }
02072    ast_channel_unlock(qe->chan);
02073 
02074    /* Add a PICKUPMARK variable to ringing interface */
02075    if (option_debug > 2)
02076       ast_log(LOG_DEBUG, "chan %s, tech: %s, part %s\n", tmp->chan->name, tech, location);
02077    /* Delete DAHDI ring pattern in tech like DAHDI/1r2 */
02078    ast_copy_string(chan, location, sizeof(chan));
02079    if (!strncasecmp(tech, "dahdi", 5)) {
02080       if((chan[0] > '0') && (chan[0] <= '9')) {
02081          if ((location2 = strchr(chan, 'r')))
02082             *location2++ = '\0';
02083       }
02084    }
02085    snprintf(pickupmark, sizeof(pickupmark), "%s/%s", tech, chan);
02086    pbx_builtin_setvar_helper(tmp->chan, "PICKUPMARK", pickupmark);
02087 
02088    /* Place the call, but don't wait on the answer */
02089    if ((res = ast_call(tmp->chan, location, 0))) {
02090       /* Again, keep going even if there's an error */
02091       if (option_debug)
02092          ast_log(LOG_DEBUG, "ast call on peer returned %d\n", res);
02093       if (option_verbose > 2)
02094          ast_verbose(VERBOSE_PREFIX_3 "Couldn't call %s\n", tmp->interface);
02095       do_hang(tmp);
02096       (*busies)++;
02097       update_status(tmp->member->state_interface, ast_device_state(tmp->member->state_interface));
02098       return 0;
02099    } else if (qe->parent->eventwhencalled) {
02100       char vars[2048];
02101 
02102       manager_event(EVENT_FLAG_AGENT, "AgentCalled",
02103                "AgentCalled: %s\r\n"
02104                "AgentName: %s\r\n"
02105                "ChannelCalling: %s\r\n"
02106                "CallerID: %s\r\n"
02107                "CallerIDName: %s\r\n"
02108                "Context: %s\r\n"
02109                "Extension: %s\r\n"
02110                "Priority: %d\r\n"
02111                "%s",
02112                tmp->interface, tmp->member->membername, qe->chan->name,
02113                tmp->chan->cid.cid_num ? tmp->chan->cid.cid_num : "unknown",
02114                tmp->chan->cid.cid_name ? tmp->chan->cid.cid_name : "unknown",
02115                qe->chan->context, qe->chan->exten, qe->chan->priority,
02116                qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
02117       if (option_verbose > 2)
02118          ast_verbose(VERBOSE_PREFIX_3 "Called %s\n", tmp->interface);
02119    }
02120 
02121    update_status(tmp->member->state_interface, ast_device_state(tmp->member->state_interface));
02122    return 1;
02123 }

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

02150 {
02151    int ret = 0;
02152 
02153    while (ret == 0) {
02154       struct callattempt *best = find_best(outgoing);
02155       if (!best) {
02156          if (option_debug)
02157             ast_log(LOG_DEBUG, "Nobody left to try ringing in queue\n");
02158          break;
02159       }
02160       if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
02161          struct callattempt *cur;
02162          /* Ring everyone who shares this best metric (for ringall) */
02163          for (cur = outgoing; cur; cur = cur->q_next) {
02164             if (cur->stillgoing && !cur->chan && cur->metric <= best->metric) {
02165                if (option_debug)
02166                   ast_log(LOG_DEBUG, "(Parallel) Trying '%s' with metric %d\n", cur->interface, cur->metric);
02167                ret |= ring_entry(qe, cur, busies);
02168             }
02169          }
02170       } else {
02171          /* Ring just the best channel */
02172          if (option_debug)
02173             ast_log(LOG_DEBUG, "Trying '%s' with metric %d\n", best->interface, best->metric);
02174          ret = ring_entry(qe, best, busies);
02175       }
02176    }
02177 
02178    return ret;
02179 }

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

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

Definition at line 2263 of file app_queue.c.

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

Referenced by wait_for_answer().

02264 {
02265    if (option_verbose > 2)
02266       ast_verbose( VERBOSE_PREFIX_3 "Nobody picked up in %d ms\n", rnatime);
02267 
02268    /* Stop ringing, and resume MOH if specified */
02269    if (qe->ring_when_ringing) {
02270       ast_indicate(qe->chan, -1);
02271       ast_moh_start(qe->chan, qe->moh, NULL);
02272    }
02273 
02274    ast_queue_log(qe->parent->name, qe->chan->uniqueid, membername, "RINGNOANSWER", "%d", rnatime);
02275    if (qe->parent->autopause && pause) {
02276       if (!set_member_paused(qe->parent->name, interface, 1)) {
02277          if (option_verbose > 2)
02278             ast_verbose( VERBOSE_PREFIX_3 "Auto-Pausing Queue Member %s in queue %s since they failed to answer.\n", interface, qe->parent->name);
02279       } else {
02280          if (option_verbose > 2)
02281             ast_verbose( VERBOSE_PREFIX_3 "Failed to pause Queue Member %s in queue %s!\n", interface, qe->parent->name);
02282       }
02283    }
02284    return;
02285 }

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

Definition at line 3985 of file app_queue.c.

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

Referenced by load_module().

03986 {
03987    int res=-1;
03988    struct ast_module_user *lu;
03989    char *parse, *temppos = NULL;
03990    int priority_jump = 0;
03991    AST_DECLARE_APP_ARGS(args,
03992       AST_APP_ARG(queuename);
03993       AST_APP_ARG(interface);
03994       AST_APP_ARG(options);
03995    );
03996 
03997 
03998    if (ast_strlen_zero(data)) {
03999       ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename[|interface[|options]])\n");
04000       return -1;
04001    }
04002 
04003    parse = ast_strdupa(data);
04004 
04005    AST_STANDARD_APP_ARGS(args, parse);
04006 
04007    lu = ast_module_user_add(chan);
04008 
04009    if (ast_strlen_zero(args.interface)) {
04010       args.interface = ast_strdupa(chan->name);
04011       temppos = strrchr(args.interface, '-');
04012       if (temppos)
04013          *temppos = '\0';
04014    }
04015 
04016    if (args.options) {
04017       if (strchr(args.options, 'j'))
04018          priority_jump = 1;
04019    }
04020 
04021    switch (remove_from_queue(args.queuename, args.interface)) {
04022    case RES_OKAY:
04023       ast_queue_log(args.queuename, chan->uniqueid, args.interface, "REMOVEMEMBER", "%s", "");
04024       ast_log(LOG_NOTICE, "Removed interface '%s' from queue '%s'\n", args.interface, args.queuename);
04025       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "REMOVED");
04026       res = 0;
04027       break;
04028    case RES_EXISTS:
04029       ast_log(LOG_DEBUG, "Unable to remove interface '%s' from queue '%s': Not there\n", args.interface, args.queuename);
04030       if (priority_jump || ast_opt_priority_jumping)
04031          ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
04032       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTINQUEUE");
04033       res = 0;
04034       break;
04035    case RES_NOSUCHQUEUE:
04036       ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", args.queuename);
04037       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOSUCHQUEUE");
04038       res = 0;
04039       break;
04040    case RES_NOT_DYNAMIC:
04041       ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': '%s' is not a dynamic member\n", args.queuename, args.interface);
04042       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTDYNAMIC");
04043       res = 0;
04044       break;
04045    }
04046 
04047    ast_module_user_remove(lu);
04048 
04049    return res;
04050 }

static void rr_dep_warning ( void   )  [static]

Definition at line 476 of file app_queue.c.

References ast_log(), and LOG_NOTICE.

Referenced by find_queue_by_name_rt(), and reload_queues().

00477 {
00478    static unsigned int warned = 0;
00479 
00480    if (!warned) {
00481       ast_log(LOG_NOTICE, "The 'roundrobin' queue strategy is deprecated. Please use the 'rrmemory' strategy instead.\n");
00482       warned = 1;
00483    }
00484 }

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

Definition at line 1186 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, member::realtime, remove_from_interfaces(), and member::state_interface.

Referenced by find_queue_by_name_rt(), and update_realtime_members().

01187 {
01188    struct member *m, tmpmem;
01189    int penalty = 0;
01190    int paused  = 0;
01191 
01192    if (penalty_str) {
01193       penalty = atoi(penalty_str);
01194       if (penalty < 0)
01195          penalty = 0;
01196    }
01197 
01198    if (paused_str) {
01199       paused = atoi(paused_str);
01200       if (paused < 0)
01201          paused = 0;
01202    }
01203 
01204    /* Find the member, or the place to put a new one. */
01205    ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
01206    m = ao2_find(q->members, &tmpmem, OBJ_POINTER);
01207 
01208    /* Create a new one if not found, else update penalty */
01209    if (!m) {
01210       if ((m = create_queue_member(interface, membername, penalty, paused, state_interface))) {
01211          m->dead = 0;
01212          m->realtime = 1;
01213          add_to_interfaces(m->state_interface);
01214          ao2_link(q->members, m);
01215          ao2_ref(m, -1);
01216          m = NULL;
01217          q->membercount++;
01218       }
01219    } else {
01220       m->dead = 0;   /* Do not delete this one. */
01221       if (paused_str)
01222          m->paused = paused;
01223       if (strcasecmp(state_interface, m->state_interface)) {
01224          remove_from_interfaces(m->state_interface);
01225          ast_copy_string(m->state_interface, state_interface, sizeof(m->state_interface));
01226          add_to_interfaces(m->state_interface);
01227       }
01228       m->penalty = penalty;
01229       ao2_ref(m, -1);
01230    }
01231 }

static int say_periodic_announcement ( struct queue_ent qe  )  [static]

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

02206 {
02207    int res = 0;
02208    time_t now;
02209 
02210    /* Get the current time */
02211    time(&now);
02212 
02213    /* Check to see if it is time to announce */
02214    if ((now - qe->last_periodic_announce_time) < qe->parent->periodicannouncefrequency)
02215       return 0;
02216 
02217    /* Stop the music on hold so we can play our own file */
02218    ast_moh_stop(qe->chan);
02219 
02220    if (option_verbose > 2)
02221       ast_verbose(VERBOSE_PREFIX_3 "Playing periodic announcement\n");
02222 
02223    /* Check to make sure we have a sound file. If not, reset to the first sound file */
02224    if (qe->last_periodic_announce_sound >= MAX_PERIODIC_ANNOUNCEMENTS || !strlen(qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound])) {
02225       qe->last_periodic_announce_sound = 0;
02226    }
02227    
02228    /* play the announcement */
02229    res = play_file(qe->chan, qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound]);
02230 
02231    if (res > 0 && !valid_exit(qe, res))
02232       res = 0;
02233 
02234    /* Resume Music on Hold if the caller is going to stay in the queue */
02235    if (!res)
02236       ast_moh_start(qe->chan, qe->moh, NULL);
02237 
02238    /* update last_periodic_announce_time */
02239    qe->last_periodic_announce_time = now;
02240 
02241    /* Update the current periodic announcement to the next announcement */
02242    qe->last_periodic_announce_sound++;
02243    
02244    return res;
02245 }

static int say_position ( struct queue_ent qe  )  [static]

Definition at line 1641 of file app_queue.c.

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

Referenced by queue_exec(), and wait_our_turn().

01642 {
01643    int res = 0, avgholdmins, avgholdsecs;
01644    time_t now;
01645 
01646    /* Check to see if this is ludicrous -- if we just announced position, don't do it again*/
01647    time(&now);
01648    if ((now - qe->last_pos) < 15)
01649       return 0;
01650 
01651    /* If either our position has changed, or we are over the freq timer, say position */
01652    if ((qe->last_pos_said == qe->pos) && ((now - qe->last_pos) < qe->parent->announcefrequency))
01653       return 0;
01654 
01655    ast_moh_stop(qe->chan);
01656    /* Say we're next, if we are */
01657    if (qe->pos == 1) {
01658       res = play_file(qe->chan, qe->parent->sound_next);
01659       if (res)
01660          goto playout;
01661       else
01662          goto posout;
01663    } else {
01664       res = play_file(qe->chan, qe->parent->sound_thereare);
01665       if (res)
01666          goto playout;
01667       res = ast_say_number(qe->chan, qe->pos, AST_DIGIT_ANY, qe->chan->language, (char *) NULL); /* Needs gender */
01668       if (res)
01669          goto playout;
01670       res = play_file(qe->chan, qe->parent->sound_calls);
01671       if (res)
01672          goto playout;
01673    }
01674    /* Round hold time to nearest minute */
01675    avgholdmins = abs(((qe->parent->holdtime + 30) - (now - qe->start)) / 60);
01676 
01677    /* If they have specified a rounding then round the seconds as well */
01678    if (qe->parent->roundingseconds) {
01679       avgholdsecs = (abs(((qe->parent->holdtime + 30) - (now - qe->start))) - 60 * avgholdmins) / qe->parent->roundingseconds;
01680       avgholdsecs *= qe->parent->roundingseconds;
01681    } else {
01682       avgholdsecs = 0;
01683    }
01684 
01685    if (option_verbose > 2)
01686       ast_verbose(VERBOSE_PREFIX_3 "Hold time for %s is %d minutes %d seconds\n", qe->parent->name, avgholdmins, avgholdsecs);
01687 
01688    /* If the hold time is >1 min, if it's enabled, and if it's not
01689       supposed to be only once and we have already said it, say it */
01690     if ((avgholdmins+avgholdsecs) > 0 && qe->parent->announceholdtime &&
01691         ((qe->parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE && !qe->last_pos) ||
01692         !(qe->parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE))) {
01693       res = play_file(qe->chan, qe->parent->sound_holdtime);
01694       if (res)
01695          goto playout;
01696 
01697       if (avgholdmins > 0) {
01698          if (avgholdmins < 2) {
01699             res = play_file(qe->chan, qe->parent->sound_lessthan);
01700             if (res)
01701                goto playout;
01702 
01703             res = ast_say_number(qe->chan, 2, AST_DIGIT_ANY, qe->chan->language, NULL);
01704             if (res)
01705                goto playout;
01706          } else {
01707             res = ast_say_number(qe->chan, avgholdmins, AST_DIGIT_ANY, qe->chan->language, NULL);
01708             if (res)
01709                goto playout;
01710          }
01711          
01712          res = play_file(qe->chan, qe->parent->sound_minutes);
01713          if (res)
01714             goto playout;
01715       }
01716       if (avgholdsecs>0) {
01717          res = ast_say_number(qe->chan, avgholdsecs, AST_DIGIT_ANY, qe->chan->language, NULL);
01718          if (res)
01719             goto playout;
01720 
01721          res = play_file(qe->chan, qe->parent->sound_seconds);
01722          if (res)
01723             goto playout;
01724       }
01725 
01726    }
01727 
01728 posout:
01729    if (option_verbose > 2)
01730       ast_verbose(VERBOSE_PREFIX_3 "Told %s in %s their queue position (which was %d)\n",
01731          qe->chan->name, qe->parent->name, qe->pos);
01732    res = play_file(qe->chan, qe->parent->sound_thanks);
01733 
01734 playout:
01735 
01736    if ((res > 0 && !valid_exit(qe, res)))
01737       res = 0;
01738 
01739    /* Set our last_pos indicators */
01740    qe->last_pos = now;
01741    qe->last_pos_said = qe->pos;
01742 
01743    /* Don't restart music on hold if we're about to exit the caller from the queue */
01744    if (!res)
01745       ast_moh_start(qe->chan, qe->moh, NULL);
01746 
01747    return res;
01748 }

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

Definition at line 3726 of file app_queue.c.

References ao2_lock(), ao2_ref(), ao2_unlock(), AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), ast_queue_log(), ast_strlen_zero(), dump_queue_members(), EVENT_FLAG_AGENT, member::interface, interface_exists(), 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().

03727 {
03728    int found = 0;
03729    struct call_queue *q;
03730    struct member *mem;
03731 
03732    /* Special event for when all queues are paused - individual events still generated */
03733    /* XXX In all other cases, we use the membername, but since this affects all queues, we cannot */
03734    if (ast_strlen_zero(queuename))
03735       ast_queue_log("NONE", "NONE", interface, (paused ? "PAUSEALL" : "UNPAUSEALL"), "%s", "");
03736 
03737    AST_LIST_LOCK(&queues);
03738    AST_LIST_TRAVERSE(&queues, q, list) {
03739       ao2_lock(q);
03740       if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
03741          if ((mem = interface_exists(q, interface))) {
03742             found++;
03743             if (mem->paused == paused)
03744                ast_log(LOG_DEBUG, "%spausing already-%spaused queue member %s:%s\n", (paused ? "" : "un"), (paused ? "" : "un"), q->name, interface);
03745             mem->paused = paused;
03746 
03747             if (queue_persistent_members)
03748                dump_queue_members(q);
03749 
03750             if (mem->realtime)
03751                update_realtime_member_field(mem, q->name, "paused", paused ? "1" : "0");
03752 
03753             ast_queue_log(q->name, "NONE", mem->membername, (paused ? "PAUSE" : "UNPAUSE"), "%s", "");
03754 
03755             manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
03756                "Queue: %s\r\n"
03757                "Location: %s\r\n"
03758                "MemberName: %s\r\n"
03759                "Paused: %d\r\n",
03760                   q->name, mem->interface, mem->membername, paused);
03761             ao2_ref(mem, -1);
03762          }
03763       }
03764       ao2_unlock(q);
03765    }
03766    AST_LIST_UNLOCK(&queues);
03767 
03768    return found ? RESULT_SUCCESS : RESULT_FAILURE;
03769 }

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

sets the QUEUESTATUS channel variable

Definition at line 495 of file app_queue.c.

References pbx_builtin_setvar_helper(), queue_results, and text.

Referenced by queue_exec().

00496 {
00497    int i;
00498 
00499    for (i = 0; i < sizeof(queue_results) / sizeof(queue_results[0]); i++) {
00500       if (queue_results[i].id == res) {
00501          pbx_builtin_setvar_helper(chan, "QUEUESTATUS", queue_results[i].text);
00502          return;
00503       }
00504    }
00505 }

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

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

Definition at line 2885 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_transfer_ds::callcompletedinsl, queue_ent::chan, ast_datastore::data, LOG_WARNING, queue_transfer_ds::member, queue_transfer_ds::qe, queue_transfer_info, and queue_transfer_ds::starttime.

Referenced by try_calling().

02886 {
02887    struct ast_datastore *ds;
02888    struct queue_transfer_ds *qtds = ast_calloc(1, sizeof(*qtds));
02889 
02890    if (!qtds) {
02891       ast_log(LOG_WARNING, "Memory allocation error!\n");
02892       return NULL;
02893    }
02894 
02895    ast_channel_lock(qe->chan);
02896    if (!(ds = ast_channel_datastore_alloc(&queue_transfer_info, NULL))) {
02897       ast_channel_unlock(qe->chan);
02898       ast_log(LOG_WARNING, "Unable to create transfer datastore. queue_log will not show attended transfer\n");
02899       return NULL;
02900    }
02901 
02902    qtds->qe = qe;
02903    /* This member is refcounted in try_calling, so no need to add it here, too */
02904    qtds->member = member;
02905    qtds->starttime = starttime;
02906    qtds->callcompletedinsl = callcompletedinsl;
02907    ds->data = qtds;
02908    ast_channel_datastore_add(qe->chan, ds);
02909    ast_channel_unlock(qe->chan);
02910    return ds;
02911 }

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

Producer of the statechange queue.

Definition at line 792 of file app_queue.c.

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

Referenced by load_module(), and unload_module().

00793 {
00794    struct statechange *sc;
00795 
00796    if (!(sc = ast_calloc(1, sizeof(*sc) + strlen(dev) + 1)))
00797       return 0;
00798 
00799    sc->state = state;
00800    strcpy(sc->dev, dev);
00801 
00802    ast_mutex_lock(&device_state.lock);
00803    AST_LIST_INSERT_TAIL(&device_state.state_change_q, sc, entry);
00804    ast_cond_signal(&device_state.cond);
00805    ast_mutex_unlock(&device_state.lock);
00806 
00807    return 0;
00808 }

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

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

02182 {
02183    struct callattempt *best = find_best(outgoing);
02184 
02185    if (best) {
02186       /* Ring just the best channel */
02187       if (option_debug)
02188          ast_log(LOG_DEBUG, "Next is '%s' with metric %d\n", best->interface, best->metric);
02189       qe->parent->rrpos = best->metric % 1000;
02190    } else {
02191       /* Just increment rrpos */
02192       if (qe->parent->wrapped) {
02193          /* No more channels, start over */
02194          qe->parent->rrpos = 0;
02195       } else {
02196          /* Prioritize next entry */
02197          qe->parent->rrpos++;
02198       }
02199    }
02200    qe->parent->wrapped = 0;
02201 
02202    return 0;
02203 }

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

Definition at line 519 of file app_queue.c.

References name, and strategies.

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

00520 {
00521    int x;
00522 
00523    for (x = 0; x < sizeof(strategies) / sizeof(strategies[0]); x++) {
00524       if (!strcasecmp(strategy, strategies[x].name))
00525          return strategies[x].strategy;
00526    }
00527 
00528    return -1;
00529 }

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

References ast_channel::_softhangup, ast_channel::_state, queue_ent::announce, ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next(), ao2_lock(), ao2_ref(), ao2_unlock(), ast_autoservice_start(), ast_autoservice_stop(), ast_bridge_call(), ast_calloc, ast_cdr_failed(), AST_CDR_FLAG_DONT_TOUCH, AST_CDR_FLAG_LOCKED, AST_CDR_FLAG_POST_DISABLED, ast_cdr_isset_unanswered(), 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_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, ast_cdr::dstchannel, EVENT_FLAG_AGENT, call_queue::eventwhencalled, queue_ent::expire, ast_channel::exten, ast_bridge_config::features_callee, ast_bridge_config::features_caller, free, queue_ent::handled, hangupcalls(), ast_datastore::inheritance, callattempt::interface, ast_dialed_interface::interface, member::interface, member::lastcall, callattempt::lastcall, member::lastqueue, callattempt::lastqueue, leave_queue(), LOG_DEBUG, LOG_NOTICE, LOG_WARNING, manager_event(), callattempt::member, call_queue::membercount, call_queue::memberdelay, member::membername, call_queue::members, call_queue::monfmt, call_queue::monjoin, call_queue::montype, call_queue::name, callattempt::oldstatus, queue_ent::opos, option_debug, queue_ent::parent, pbx_builtin_getvar_helper(), pbx_builtin_setvar_helper(), pbx_exec(), pbx_findapp(), pbx_substitute_variables_helper(), queue_ent::pending, play_file(), queue_ent::pos, callattempt::q_next, QUEUE_EVENT_VARIABLES, QUEUE_STRATEGY_ROUNDROBIN, QUEUE_STRATEGY_RRMEMORY, QUEUE_STRATEGY_RRORDERED, 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, callattempt::stillgoing, store_next(), call_queue::strategy, tds, ast_channel::tech, call_queue::timeout, queue_ent::tries, ast_channel_tech::type, ast_cdr::uniqueid, update_queue(), vars2manager(), and wait_for_answer().

Referenced by queue_exec().

02939 {
02940    struct member *cur;
02941    struct callattempt *outgoing = NULL; /* the list of calls we are building */
02942    int to;
02943    char oldexten[AST_MAX_EXTENSION]="";
02944    char oldcontext[AST_MAX_CONTEXT]="";
02945    char queuename[256]="";
02946    struct ast_channel *peer;
02947    struct ast_channel *which;
02948    struct callattempt *lpeer;
02949    struct member *member;
02950    struct ast_app *app;
02951    int res = 0, bridge = 0;
02952    int numbusies = 0;
02953    int x=0;
02954    char *announce = NULL;
02955    char digit = 0;
02956    time_t callstart;
02957    time_t now = time(NULL);
02958    struct ast_bridge_config bridge_config;
02959    char nondataquality = 1;
02960    char *agiexec = NULL;
02961    int ret = 0;
02962    const char *monitorfilename;
02963    const char *monitor_exec;
02964    const char *monitor_options;
02965    char tmpid[256], tmpid2[256];
02966    char meid[1024], meid2[1024];
02967    char mixmonargs[1512];
02968    struct ast_app *mixmonapp = NULL;
02969    char *p;
02970    char vars[2048];
02971    int forwardsallowed = 1;
02972    int callcompletedinsl;
02973    struct ao2_iterator memi;
02974    struct ast_datastore *datastore, *transfer_ds;
02975    const int need_weight = use_weight;
02976 
02977    ast_channel_lock(qe->chan);
02978    datastore = ast_channel_datastore_find(qe->chan, &dialed_interface_info, NULL);
02979    ast_channel_unlock(qe->chan);
02980 
02981    memset(&bridge_config, 0, sizeof(bridge_config));
02982    time(&now);
02983 
02984    /* If we've already exceeded our timeout, then just stop
02985     * This should be extremely rare. queue_exec will take care
02986     * of removing the caller and reporting the timeout as the reason.
02987     */
02988    if (qe->expire && now >= qe->expire) {
02989       res = 0;
02990       goto out;
02991    }
02992       
02993    for (; options && *options; options++)
02994       switch (*options) {
02995       case 't':
02996          ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_REDIRECT);
02997          break;
02998       case 'T':
02999          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_REDIRECT);
03000          break;
03001       case 'w':
03002          ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMON);
03003          break;
03004       case 'W':
03005          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMON);
03006          break;
03007       case 'd':
03008          nondataquality = 0;
03009          break;
03010       case 'h':
03011          ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_DISCONNECT);
03012          break;
03013       case 'H':
03014          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT);
03015          break;
03016       case 'n':
03017          if (qe->parent->strategy == QUEUE_STRATEGY_ROUNDROBIN || qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY || qe->parent->strategy == QUEUE_STRATEGY_RRORDERED)
03018             (*tries)++;
03019          else
03020             *tries = qe->parent->membercount;
03021          *noption = 1;
03022          break;
03023       case 'i':
03024          forwardsallowed = 0;
03025          break;
03026       }
03027 
03028    /* Hold the lock while we setup the outgoing calls */
03029 
03030    if (need_weight)
03031       AST_LIST_LOCK(&queues);
03032    ao2_lock(qe->parent);
03033    if (option_debug)
03034       ast_log(LOG_DEBUG, "%s is trying to call a queue member.\n",
03035                      qe->chan->name);
03036    ast_copy_string(queuename, qe->parent->name, sizeof(queuename));
03037    if (!ast_strlen_zero(qe->announce))
03038       announce = qe->announce;
03039    if (!ast_strlen_zero(announceoverride))
03040       announce = announceoverride;
03041 
03042    memi = ao2_iterator_init(qe->parent->members, 0);
03043    while ((cur = ao2_iterator_next(&memi))) {
03044       struct callattempt *tmp = ast_calloc(1, sizeof(*tmp));
03045       struct ast_dialed_interface *di;
03046       AST_LIST_HEAD(, ast_dialed_interface) *dialed_interfaces;
03047       if (!tmp) {
03048          ao2_iterator_destroy(&memi);
03049          ao2_ref(cur, -1);
03050          ao2_unlock(qe->parent);
03051          if (need_weight)
03052             AST_LIST_UNLOCK(&queues);
03053          goto out;
03054       }
03055       if (!datastore) {
03056          if (!(datastore = ast_channel_datastore_alloc(&dialed_interface_info, NULL))) {
03057             ao2_iterator_destroy(&memi);
03058             ao2_ref(cur, -1);
03059             ao2_unlock(qe->parent);
03060             if (need_weight)
03061                AST_LIST_UNLOCK(&queues);
03062             free(tmp);
03063             goto out;
03064          }
03065          datastore->inheritance = DATASTORE_INHERIT_FOREVER;
03066          if (!(dialed_interfaces = ast_calloc(1, sizeof(*dialed_interfaces)))) {
03067             ao2_iterator_destroy(&memi);
03068             ao2_ref(cur, -1);
03069             ao2_unlock(qe->parent);
03070             if (need_weight)
03071                AST_LIST_UNLOCK(&queues);
03072             free(tmp);
03073             goto out;
03074          }
03075          datastore->data = dialed_interfaces;
03076          AST_LIST_HEAD_INIT(dialed_interfaces);
03077 
03078          ast_channel_lock(qe->chan);
03079          ast_channel_datastore_add(qe->chan, datastore);
03080          ast_channel_unlock(qe->chan);
03081       } else
03082          dialed_interfaces = datastore->data;
03083 
03084       AST_LIST_LOCK(dialed_interfaces);
03085       AST_LIST_TRAVERSE(dialed_interfaces, di, list) {
03086          if (!strcasecmp(cur->interface, di->interface)) {
03087             ast_log(LOG_DEBUG, "Skipping dialing interface '%s' since it has already been dialed\n", 
03088                di->interface);
03089             break;
03090          }
03091       }
03092       AST_LIST_UNLOCK(dialed_interfaces);
03093       
03094       if (di) {
03095          free(tmp);
03096          continue;
03097       }
03098 
03099       /* It is always ok to dial a Local interface.  We only keep track of
03100        * which "real" interfaces have been dialed.  The Local channel will
03101        * inherit this list so that if it ends up dialing a real interface,
03102        * it won't call one that has already been called. */
03103       if (strncasecmp(cur->interface, "Local/", 6)) {
03104          if (!(di = ast_calloc(1, sizeof(*di) + strlen(cur->interface)))) {
03105             ao2_iterator_destroy(&memi);
03106             ao2_ref(cur, -1);
03107             ao2_unlock(qe->parent);
03108             if (need_weight)
03109                AST_LIST_UNLOCK(&queues);
03110             free(tmp);
03111             goto out;
03112          }
03113          strcpy(di->interface, cur->interface);
03114 
03115          AST_LIST_LOCK(dialed_interfaces);
03116          AST_LIST_INSERT_TAIL(dialed_interfaces, di, list);
03117          AST_LIST_UNLOCK(dialed_interfaces);
03118       }
03119 
03120       tmp->stillgoing = -1;
03121       tmp->member = cur;
03122       tmp->oldstatus = cur->status;
03123       tmp->lastcall = cur->lastcall;
03124       tmp->lastqueue = cur->lastqueue;
03125       ast_copy_string(tmp->interface, cur->interface, sizeof(tmp->interface));
03126       if (qe->tries == 0 && (cur->ringcount >= qe->parent->ringlimit)) {
03127          cur->ringcount = 0;
03128       }
03129       /* Special case: If we ring everyone, go ahead and ring them, otherwise
03130          just calculate their metric for the appropriate strategy */
03131       if (!calc_metric(qe->parent, cur, x++, qe, tmp)) {
03132          /* Put them in the list of outgoing thingies...  We're ready now.
03133             XXX If we're forcibly removed, these outgoing calls won't get
03134             hung up XXX */
03135          tmp->q_next = outgoing;
03136          outgoing = tmp;      
03137          /* If this line is up, don't try anybody else */
03138          if (outgoing->chan && (outgoing->chan->_state == AST_STATE_UP))
03139             break;
03140       } else {
03141          ao2_ref(cur, -1);
03142          free(tmp);
03143       }
03144    }
03145    ao2_iterator_destroy(&memi);
03146    if (qe->expire && (!qe->parent->timeout || (qe->expire - now) <= qe->parent->timeout))
03147       to = (qe->expire - now) * 1000;
03148    else
03149       to = (qe->parent->timeout) ? qe->parent->timeout * 1000 : -1;
03150    ++qe->pending;
03151    ++qe->tries;
03152    if (option_debug)
03153       ast_log(LOG_DEBUG, "%s is trying to ring one member from %s. This is try number %d\n",
03154                   qe->chan->name, queuename, qe->tries);
03155    ao2_unlock(qe->parent);
03156    ring_one(qe, outgoing, &numbusies);
03157    if (need_weight)
03158       AST_LIST_UNLOCK(&queues);
03159    lpeer = wait_for_answer(qe, outgoing, &to, &digit, numbusies, ast_test_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT), forwardsallowed);
03160    /* The ast_channel_datastore_remove() function could fail here if the
03161     * datastore was moved to another channel during a masquerade. If this is
03162     * the case, don't free the datastore here because later, when the channel
03163     * to which the datastore was moved hangs up, it will attempt to free this
03164     * datastore again, causing a crash
03165     */
03166    ast_channel_lock(qe->chan);
03167    if (datastore && !ast_channel_datastore_remove(qe->chan, datastore)) {
03168       ast_channel_datastore_free(datastore);
03169    }
03170    ast_channel_unlock(qe->chan);
03171    ao2_lock(qe->parent);
03172    if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY || qe->parent->strategy == QUEUE_STRATEGY_RRORDERED) {
03173       store_next(qe, outgoing);
03174    }
03175    ao2_unlock(qe->parent);
03176    peer = lpeer ? lpeer->chan : NULL;
03177    if (!peer) {
03178       qe->pending = 0;
03179       if (to) {
03180          /* Must gotten hung up */
03181          res = -1;
03182       } else {
03183          /* User exited by pressing a digit */
03184          res = digit;
03185       }
03186       if (res == -1) {
03187          /* Post this CDR, and mark call as NOANSWER */
03188          ast_set_flag(qe->chan->cdr, AST_CDR_FLAG_DONT_TOUCH);
03189          ast_cdr_noanswer(qe->chan->cdr);
03190          if (queue_debug)
03191             ast_log(LOG_NOTICE, "%s: Nobody answered.\n", qe->chan->name);
03192       }
03193       if (qe->parent->eventwhencalled) {
03194          manager_event(EVENT_FLAG_AGENT, "AgentTimeout",
03195                               "Queue: %s\r\n"
03196                               "ChannelCalling: %s\r\n"
03197                               "Uniqueid: %s\r\n"
03198                               "Tries: %d\r\n"
03199                               "Holdtime: %ld\r\n",
03200                               queuename, qe->chan->name, qe->chan->uniqueid, qe->tries,
03201                               (long)time(NULL) - qe->start);
03202       }
03203       if (ast_cdr_isset_unanswered()) {
03204          /* channel contains the name of one of the outgoing channels
03205             in its CDR; zero out this CDR to avoid a dual-posting */
03206          struct callattempt *o;
03207          for (o = outgoing; o; o = o->q_next) {
03208             if (!o->chan) {
03209                continue;
03210             }
03211             if (strcmp(o->chan->cdr->dstchannel, qe->chan->cdr->dstchannel) == 0) {
03212                ast_set_flag(o->chan->cdr, AST_CDR_FLAG_POST_DISABLED);
03213                break;
03214             }
03215          }
03216       }
03217    } else { /* peer is valid */
03218       /* Ah ha!  Someone answered within the desired timeframe.  Of course after this
03219          we will always return with -1 so that it is hung up properly after the
03220          conversation.  */
03221       if (!strcmp(qe->chan->tech->type, "Zap"))
03222          ast_channel_setoption(qe->chan, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
03223       if (!strcmp(peer->tech->type, "Zap"))
03224          ast_channel_setoption(peer, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
03225       /* Update parameters for the queue */
03226       time(&now);
03227       recalc_holdtime(qe, (now - qe->start));
03228       ao2_lock(qe->parent);
03229       callcompletedinsl = ((now - qe->start) <= qe->parent->servicelevel);
03230       ao2_unlock(qe->parent);
03231       member = lpeer->member;
03232       /* Increment the refcount for this member, since we're going to be using it for awhile in here. */
03233       ao2_ref(member, 1);
03234       hangupcalls(outgoing, peer);
03235       outgoing = NULL;
03236       if (announce || qe->parent->reportholdtime || qe->parent->memberdelay) {
03237          int res2;
03238 
03239          res2 = ast_autoservice_start(qe->chan);
03240          if (!res2) {
03241             if (qe->parent->memberdelay) {
03242                ast_log(LOG_NOTICE, "Delaying member connect for %d seconds\n", qe->parent->memberdelay);
03243                res2 |= ast_safe_sleep(peer, qe->parent->memberdelay * 1000);
03244             }
03245             if (!res2 && announce) {
03246                play_file(peer, announce);
03247             }
03248             if (!res2 && qe->parent->reportholdtime) {
03249                if (!play_file(peer, qe->parent->sound_reporthold)) {
03250                   int holdtime;
03251 
03252                   time(&now);
03253                   holdtime = abs((now - qe->start) / 60);
03254                   if (holdtime < 2) {
03255                      play_file(peer, qe->parent->sound_lessthan);
03256                      ast_say_number(peer, 2, AST_DIGIT_ANY, peer->language, NULL);
03257                   } else
03258                      ast_say_number(peer, holdtime, AST_DIGIT_ANY, peer->language, NULL);
03259                   play_file(peer, qe->parent->sound_minutes);
03260                }
03261             }
03262          }
03263          res2 |= ast_autoservice_stop(qe->chan);
03264          if (peer->_softhangup) {
03265             /* Agent must have hung up */
03266             ast_log(LOG_WARNING, "Agent on %s hungup on the customer.\n", peer->name);
03267             ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "AGENTDUMP", "%s", "");
03268             if (qe->parent->eventwhencalled)
03269                manager_event(EVENT_FLAG_AGENT, "AgentDump",
03270                      "Queue: %s\r\n"
03271                      "Uniqueid: %s\r\n"
03272                      "Channel: %s\r\n"
03273                      "Member: %s\r\n"
03274                      "MemberName: %s\r\n"
03275                      "%s",
03276                      queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
03277                      qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
03278             ast_hangup(peer);
03279             ao2_ref(member, -1);
03280             goto out;
03281          } else if (res2) {
03282             /* Caller must have hung up just before being connected*/
03283             ast_log(LOG_NOTICE, "Caller was about to talk to agent on %s but the caller hungup.\n", peer->name);
03284             ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "ABANDON", "%d|%d|%ld", qe->pos, qe->opos, (long)time(NULL) - qe->start);
03285             record_abandoned(qe);
03286             ast_hangup(peer);
03287             ao2_ref(member, -1);
03288             return -1;
03289          }
03290       }
03291       /* Stop music on hold */
03292       ast_moh_stop(qe->chan);
03293       /* If appropriate, log that we have a destination channel */
03294       if (qe->chan->cdr)
03295          ast_cdr_setdestchan(qe->chan->cdr, peer->name);
03296       /* Make sure channels are compatible */
03297       res = ast_channel_make_compatible(qe->chan, peer);
03298       if (res < 0) {
03299          ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "SYSCOMPAT", "%s", "");
03300          ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", qe->chan->name, peer->name);
03301          record_abandoned(qe);
03302          ast_cdr_failed(qe->chan->cdr);
03303          ast_hangup(peer);
03304          ao2_ref(member, -1);
03305          return -1;
03306       }
03307 
03308       if (qe->parent->setinterfacevar)
03309             pbx_builtin_setvar_helper(qe->chan, "MEMBERINTERFACE", member->interface);
03310 
03311       /* Begin Monitoring */
03312       if (qe->parent->monfmt && *qe->parent->monfmt) {
03313          if (!qe->parent->montype) {
03314             if (option_debug)
03315                ast_log(LOG_DEBUG, "Starting Monitor as requested.\n");
03316             monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME");
03317             if (pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC") || pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC_ARGS"))
03318                which = qe->chan;
03319             else
03320                which = peer;
03321             if (monitorfilename)
03322                ast_monitor_start(which, qe->parent->monfmt, monitorfilename, 1 );
03323             else if (qe->chan->cdr)
03324                ast_monitor_start(which, qe->parent->monfmt, qe->chan->cdr->uniqueid, 1 );
03325             else {
03326                /* Last ditch effort -- no CDR, make up something */
03327                snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random());
03328                ast_monitor_start(which, qe->parent->monfmt, tmpid, 1 );
03329             }
03330             if (qe->parent->monjoin)
03331                ast_monitor_setjoinfiles(which, 1);
03332          } else {
03333             if (option_debug)
03334                ast_log(LOG_DEBUG, "Starting MixMonitor as requested.\n");
03335             monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME");
03336             if (!monitorfilename) {
03337                if (qe->chan->cdr)
03338                   ast_copy_string(tmpid, qe->chan->cdr->uniqueid, sizeof(tmpid)-1);
03339                else
03340                   snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random());
03341             } else {
03342                ast_copy_string(tmpid2, monitorfilename, sizeof(tmpid2)-1);
03343                for (p = tmpid2; *p ; p++) {
03344                   if (*p == '^' && *(p+1) == '{') {
03345                      *p = '$';
03346                   }
03347                }
03348 
03349                memset(tmpid, 0, sizeof(tmpid));
03350                pbx_substitute_variables_helper(qe->chan, tmpid2, tmpid, sizeof(tmpid) - 1);
03351             }
03352 
03353             monitor_exec = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC");
03354             monitor_options = pbx_builtin_getvar_helper(qe->chan, "MONITOR_OPTIONS");
03355 
03356             if (monitor_exec) {
03357                ast_copy_string(meid2, monitor_exec, sizeof(meid2)-1);
03358                for (p = meid2; *p ; p++) {
03359                   if (*p == '^' && *(p+1) == '{') {
03360                      *p = '$';
03361                   }
03362                }
03363 
03364                memset(meid, 0, sizeof(meid));
03365                pbx_substitute_variables_helper(qe->chan, meid2, meid, sizeof(meid) - 1);
03366             }
03367    
03368             snprintf(tmpid2, sizeof(tmpid2)-1, "%s.%s", tmpid, qe->parent->monfmt);
03369 
03370             mixmonapp = pbx_findapp("MixMonitor");
03371 
03372             if (strchr(tmpid2, '|')) {
03373                ast_log(LOG_WARNING, "monitor-format (in queues.conf) and MONITOR_FILENAME cannot contain a '|'! Not recording.\n");
03374                mixmonapp = NULL;
03375             }
03376 
03377             if (!monitor_options)
03378                monitor_options = "";
03379             
03380             if (strchr(monitor_options, '|')) {
03381                ast_log(LOG_WARNING, "MONITOR_OPTIONS cannot contain a '|'! Not recording.\n");
03382                mixmonapp = NULL;
03383             }
03384 
03385             if (mixmonapp) {
03386                if (!ast_strlen_zero(monitor_exec))
03387                   snprintf(mixmonargs, sizeof(mixmonargs)-1, "%s|b%s|%s", tmpid2, monitor_options, monitor_exec);
03388                else
03389                   snprintf(mixmonargs, sizeof(mixmonargs)-1, "%s|b%s", tmpid2, monitor_options);
03390                   
03391                if (option_debug)
03392                   ast_log(LOG_DEBUG, "Arguments being passed to MixMonitor: %s\n", mixmonargs);
03393                /* We purposely lock the CDR so that pbx_exec does not update the application data */
03394                if (qe->chan->cdr)
03395                   ast_set_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED);
03396                ret = pbx_exec(qe->chan, mixmonapp, mixmonargs);
03397                if (qe->chan->cdr)
03398                   ast_clear_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED);
03399 
03400             } else
03401                ast_log(LOG_WARNING, "Asked to run MixMonitor on this call, but cannot find the MixMonitor app!\n");
03402 
03403          }
03404       }
03405       /* Drop out of the queue at this point, to prepare for next caller */
03406       leave_queue(qe);        
03407       if (!ast_strlen_zero(url) && ast_channel_supports_html(peer)) {
03408          if (option_debug)
03409             ast_log(LOG_DEBUG, "app_queue: sendurl=%s.\n", url);
03410          ast_channel_sendurl(peer, url);
03411       }
03412       if (!ast_strlen_zero(agi)) {
03413          if (option_debug)
03414             ast_log(LOG_DEBUG, "app_queue: agi=%s.\n", agi);
03415          app = pbx_findapp("agi");
03416          if (app) {
03417             agiexec = ast_strdupa(agi);
03418             ret = pbx_exec(qe->chan, app, agiexec);
03419          } else
03420             ast_log(LOG_WARNING, "Asked to execute an AGI on this channel, but could not find application (agi)!\n");
03421       }
03422       qe->handled++;
03423       ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "CONNECT", "%ld|%s", (long)time(NULL) - qe->start, peer->uniqueid);
03424       if (qe->parent->eventwhencalled)
03425          manager_event(EVENT_FLAG_AGENT, "AgentConnect",
03426                "Queue: %s\r\n"
03427                "Uniqueid: %s\r\n"
03428                "Channel: %s\r\n"
03429                "Member: %s\r\n"
03430                "MemberName: %s\r\n"
03431                "Holdtime: %ld\r\n"
03432                "BridgedChannel: %s\r\n"
03433                "%s",
03434                queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
03435                (long)time(NULL) - qe->start, peer->uniqueid,
03436                qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
03437       ast_copy_string(oldcontext, qe->chan->context, sizeof(oldcontext));
03438       ast_copy_string(oldexten, qe->chan->exten, sizeof(oldexten));
03439       time(&callstart);
03440 
03441       if (member->status == AST_DEVICE_NOT_INUSE)
03442          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);
03443          
03444       transfer_ds = setup_transfer_datastore(qe, member, callstart, callcompletedinsl);
03445       bridge = ast_bridge_call(qe->chan,peer, &bridge_config);
03446 
03447       ast_channel_lock(qe->chan);
03448       if (!attended_transfer_occurred(qe->chan)) {
03449          struct ast_datastore *tds;
03450 
03451          /* detect a blind transfer */
03452          if (!(qe->chan->_softhangup | peer->_softhangup) && (strcasecmp(oldcontext, qe->chan->context) || strcasecmp(oldexten, qe->chan->exten))) {
03453             ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld",
03454                qe->chan->exten, qe->chan->context, (long) (callstart - qe->start),
03455                (long) (time(NULL) - callstart));
03456             if (qe->parent->eventwhencalled)
03457                manager_event(EVENT_FLAG_AGENT, "AgentComplete",
03458                      "Queue: %s\r\n"
03459                      "Uniqueid: %s\r\n"
03460                      "Channel: %s\r\n"
03461                      "Member: %s\r\n"
03462                      "MemberName: %s\r\n"
03463                      "HoldTime: %ld\r\n"
03464                      "TalkTime: %ld\r\n"
03465                      "Reason: transfer\r\n"
03466                      "%s",
03467                      queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
03468                      (long)(callstart - qe->start), (long)(time(NULL) - callstart),
03469                      qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
03470          } else if (qe->chan->_softhangup) {
03471             ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETECALLER", "%ld|%ld|%d",
03472                (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
03473             if (qe->parent->eventwhencalled)
03474                manager_event(EVENT_FLAG_AGENT, "AgentComplete",
03475                      "Queue: %s\r\n"
03476                      "Uniqueid: %s\r\n"
03477                      "Channel: %s\r\n"
03478                      "Member: %s\r\n"
03479                      "MemberName: %s\r\n"
03480                      "HoldTime: %ld\r\n"
03481                      "TalkTime: %ld\r\n"
03482                      "Reason: caller\r\n"
03483                      "%s",
03484                      queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
03485                      (long)(callstart - qe->start), (long)(time(NULL) - callstart),
03486                      qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
03487          } else {
03488             ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETEAGENT", "%ld|%ld|%d",
03489                (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
03490             if (qe->parent->eventwhencalled)
03491                manager_event(EVENT_FLAG_AGENT, "AgentComplete",
03492                      "Queue: %s\r\n"
03493                      "Uniqueid: %s\r\n"
03494                      "Channel: %s\r\n"
03495                      "Member: %s\r\n"
03496                      "MemberName: %s\r\n"
03497                      "HoldTime: %ld\r\n"
03498                      "TalkTime: %ld\r\n"
03499                      "Reason: agent\r\n"
03500                      "%s",
03501                      queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername, (long)(callstart - qe->start),
03502                      (long)(time(NULL) - callstart),
03503                      qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
03504          }
03505          if ((tds = ast_channel_datastore_find(qe->chan, &queue_transfer_info, NULL))) {  
03506             ast_channel_datastore_remove(qe->chan, tds);
03507          }
03508          update_queue(qe->parent, member, callcompletedinsl);
03509       } else {
03510          if (qe->parent->eventwhencalled)
03511             manager_event(EVENT_FLAG_AGENT, "AgentComplete",
03512                   "Queue: %s\r\n"
03513                   "Uniqueid: %s\r\n"
03514                   "Channel: %s\r\n"
03515                   "Member: %s\r\n"
03516                   "MemberName: %s\r\n"
03517                   "HoldTime: %ld\r\n"
03518                   "TalkTime: %ld\r\n"
03519                   "Reason: transfer\r\n"
03520                   "%s",
03521                   queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername, (long)(callstart - qe->start),
03522                   (long)(time(NULL) - callstart),
03523                   qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
03524       }
03525 
03526       if (transfer_ds) {
03527          ast_channel_datastore_free(transfer_ds);
03528       }
03529       ast_channel_unlock(qe->chan);
03530       ast_hangup(peer);
03531       res = bridge ? bridge : 1;
03532       ao2_ref(member, -1);
03533    }
03534 out:
03535    hangupcalls(outgoing, NULL);
03536 
03537    return res;
03538 }

static int unload_module ( void   )  [static]

Definition at line 5805 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, queueexists_function, queuemembercount_function, queuememberlist_function, queuememberpaused_function, queuememberstatus_function, queuewaitingcount_function, and statechange_queue().

05806 {
05807    int res;
05808 
05809    if (device_state.thread != AST_PTHREADT_NULL) {
05810       device_state.stop = 1;
05811       ast_mutex_lock(&device_state.lock);
05812       ast_cond_signal(&device_state.cond);
05813       ast_mutex_unlock(&device_state.lock);
05814       pthread_join(device_state.thread, NULL);
05815    }
05816 
05817    ast_cli_unregister_multiple(cli_queue, sizeof(cli_queue) / sizeof(struct ast_cli_entry));
05818    res = ast_manager_unregister("QueueStatus");
05819    res |= ast_manager_unregister("Queues");
05820    res |= ast_manager_unregister("QueueAdd");
05821    res |= ast_manager_unregister("QueueRemove");
05822    res |= ast_manager_unregister("QueuePause");
05823    res |= ast_unregister_application(app_aqm);
05824    res |= ast_unregister_application(app_rqm);
05825    res |= ast_unregister_application(app_pqm);
05826    res |= ast_unregister_application(app_upqm);
05827    res |= ast_unregister_application(app_ql);
05828    res |= ast_unregister_application(app);
05829    res |= ast_custom_function_unregister(&queueexists_function);
05830    res |= ast_custom_function_unregister(&queueagentcount_function);
05831    res |= ast_custom_function_unregister(&queuemembercount_function);
05832    res |= ast_custom_function_unregister(&queuememberlist_function);
05833    res |= ast_custom_function_unregister(&queuememberstatus_function);
05834    res |= ast_custom_function_unregister(&queuememberpaused_function);
05835    res |= ast_custom_function_unregister(&queuewaitingcount_function);
05836    ast_devstate_del(statechange_queue, NULL);
05837 
05838    ast_module_user_hangup_all();
05839 
05840    clear_and_free_interfaces();
05841 
05842    return res;
05843 }

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

Definition at line 2715 of file app_queue.c.

References ao2_find(), ao2_lock(), ao2_ref(), ao2_unlock(), AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, member::calls, call_queue::callscompleted, call_queue::callscompletedinsl, member::lastcall, member::lastqueue, and call_queue::members.

Referenced by queue_transfer_fixup(), and try_calling().

02716 {
02717    struct member *mem;
02718    struct call_queue *qtmp;
02719 
02720    if (shared_lastcall) {
02721       AST_LIST_LOCK(&queues);
02722       AST_LIST_TRAVERSE(&queues, qtmp, list) {
02723          ao2_lock(qtmp);
02724          if ((mem = ao2_find(qtmp->members, member, OBJ_POINTER))) {
02725             time(&mem->lastcall);
02726             mem->calls++;
02727             mem->lastqueue = q;
02728             ao2_ref(mem, -1);
02729          }
02730          ao2_unlock(qtmp);
02731       }
02732       AST_LIST_UNLOCK(&queues);
02733    } else {
02734       ao2_lock(q);
02735       time(&member->lastcall);
02736       member->calls++;
02737       member->lastqueue = q;
02738       ao2_unlock(q);
02739    }  
02740    ao2_lock(q);
02741    q->callscompleted++;
02742    if (callcompletedinsl)
02743       q->callscompletedinsl++;
02744    ao2_unlock(q);
02745 
02746    return 0;
02747 }

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

Definition at line 1399 of file app_queue.c.

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

Referenced by set_member_paused().

01400 {
01401    struct ast_variable *var, *save;
01402    int ret = -1;
01403 
01404    if (!(var = ast_load_realtime("queue_members", "interface", mem->interface, "queue_name", queue_name, NULL))) 
01405       return ret;
01406    save = var;
01407    while (var) {
01408       if (!strcmp(var->name, "uniqueid"))
01409          break;
01410       var = var->next;
01411    }
01412    if (var && !ast_strlen_zero(var->value)) {
01413       if ((ast_update_realtime("queue_members", "uniqueid", var->value, field, value, NULL)) > -1)
01414          ret = 0;
01415    }
01416    ast_variables_destroy(save);
01417    return ret;
01418 }

static void update_realtime_members ( struct call_queue q  )  [static]

Definition at line 1420 of file app_queue.c.

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

Referenced by load_realtime_queue(), and queue_exec().

01421 {
01422    struct ast_config *member_config = NULL;
01423    struct member *m;
01424    char *interface = NULL;
01425    struct ao2_iterator mem_iter;
01426 
01427    if (!(member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", q->name , NULL))) {
01428       /*This queue doesn't have realtime members*/
01429       if (option_debug > 2)
01430          ast_log(LOG_DEBUG, "Queue %s has no realtime members defined. No need for update\n", q->name);
01431       return;
01432    }
01433 
01434    ao2_lock(q);
01435    
01436    /* Temporarily set realtime  members dead so we can detect deleted ones.*/ 
01437    mem_iter = ao2_iterator_init(q->members, 0);
01438    while ((m = ao2_iterator_next(&mem_iter))) {
01439       if (m->realtime)
01440          m->dead = 1;
01441       ao2_ref(m, -1);
01442    }
01443    ao2_iterator_destroy(&mem_iter);
01444 
01445    while ((interface = ast_category_browse(member_config, interface))) {
01446       rt_handle_member_record(q, interface,
01447          S_OR(ast_variable_retrieve(member_config, interface, "membername"), interface),
01448          ast_variable_retrieve(member_config, interface, "penalty"),
01449          ast_variable_retrieve(member_config, interface, "paused"),
01450          S_OR(ast_variable_retrieve(member_config, interface, "state_interface"), interface));
01451    }
01452 
01453    /* Delete all realtime members that have been deleted in DB. */
01454    mem_iter = ao2_iterator_init(q->members, 0);
01455    while ((m = ao2_iterator_next(&mem_iter))) {
01456       if (m->dead) {
01457          ao2_unlink(q->members, m);
01458          ao2_unlock(q);
01459          remove_from_interfaces(m->state_interface);
01460          ao2_lock(q);
01461          q->membercount--;
01462       }
01463       ao2_ref(m, -1);
01464    }
01465    ao2_iterator_destroy(&mem_iter);
01466    ao2_unlock(q);
01467    ast_config_destroy(member_config);
01468 }

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

Definition at line 643 of file app_queue.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next(), ao2_lock(), ao2_ref(), ao2_unlock(), ast_copy_string(), AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, member::calls, member::dynamic, EVENT_FLAG_AGENT, member::interface, member::lastcall, manager_event(), call_queue::maskmemberstatus, member::membername, call_queue::members, call_queue::name, member::paused, member::penalty, member::realtime, member::state_interface, and member::status.

Referenced by handle_statechange(), and ring_entry().

00644 {
00645    struct member *cur;
00646    struct ao2_iterator mem_iter;
00647    struct call_queue *q;
00648    char tmp_interface[80];
00649 
00650    AST_LIST_LOCK(&queues);
00651    AST_LIST_TRAVERSE(&queues, q, list) {
00652       ao2_lock(q);
00653       mem_iter = ao2_iterator_init(q->members, 0);
00654       while ((cur = ao2_iterator_next(&mem_iter))) {
00655          char *slash_pos;
00656          ast_copy_string(tmp_interface, cur->state_interface, sizeof(tmp_interface));
00657          if ((slash_pos = strchr(tmp_interface, '/')))
00658             if ((slash_pos = strchr(slash_pos + 1, '/')))
00659                *slash_pos = '\0';
00660 
00661          if (strcasecmp(interface, tmp_interface)) {
00662             ao2_ref(cur, -1);
00663             continue;
00664          }
00665 
00666          if (cur->status != status) {
00667             cur->status = status;
00668             if (q->maskmemberstatus) {
00669                ao2_ref(cur, -1);
00670                continue;
00671             }
00672 
00673             manager_event(EVENT_FLAG_AGENT, "QueueMemberStatus",
00674                "Queue: %s\r\n"
00675                "Location: %s\r\n"
00676                "MemberName: %s\r\n"
00677                "Membership: %s\r\n"
00678                "Penalty: %d\r\n"
00679                "CallsTaken: %d\r\n"
00680                "LastCall: %d\r\n"
00681                "Status: %d\r\n"
00682                "Paused: %d\r\n",
00683                q->name, cur->interface, cur->membername, cur->dynamic ? "dynamic" : cur->realtime ? "realtime" : "static",
00684                cur->penalty, cur->calls, (int)cur->lastcall, cur->status, cur->paused);
00685          }
00686          ao2_ref(cur, -1);
00687       }
00688       ao2_iterator_destroy(&mem_iter);
00689       ao2_unlock(q);
00690    }
00691    AST_LIST_UNLOCK(&queues);
00692 
00693    return 0;
00694 }

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

Definition at line 3927 of file app_queue.c.

References AST_APP_ARG, AST_DECLARE_APP_ARGS, ast_goto_if_exists(), ast_log(), ast_module_user_add, ast_module_user_remove, ast_opt_priority_jumping, AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), ast_channel::context, ast_channel::exten, member_interface::interface, LOG_WARNING, parse(), pbx_builtin_setvar_helper(), ast_channel::priority, and set_member_paused().

Referenced by load_module().

03928 {
03929    struct ast_module_user *lu;
03930    char *parse;
03931    int priority_jump = 0;
03932    int ignore_fail = 0;
03933    AST_DECLARE_APP_ARGS(args,
03934       AST_APP_ARG(queuename);
03935       AST_APP_ARG(interface);
03936       AST_APP_ARG(options);
03937    );
03938 
03939    if (ast_strlen_zero(data)) {
03940       ast_log(LOG_WARNING, "UnpauseQueueMember requires an argument ([queuename]|interface[|options])\n");
03941       return -1;
03942    }
03943 
03944    parse = ast_strdupa(data);
03945 
03946    AST_STANDARD_APP_ARGS(args, parse);
03947 
03948    lu = ast_module_user_add(chan);
03949 
03950    if (args.options) {
03951       if (strchr(args.options, 'j'))
03952          priority_jump = 1;
03953       if (strchr(args.options, 'i'))
03954          ignore_fail = 1;
03955    }
03956 
03957    if (ast_strlen_zero(args.interface)) {
03958       ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename]|interface[|options])\n");
03959       ast_module_user_remove(lu);
03960       return -1;
03961    }
03962 
03963    if (set_member_paused(args.queuename, args.interface, 0)) {
03964       ast_log(LOG_WARNING, "Attempt to unpause interface %s, not found\n", args.interface);
03965       pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND");
03966       if (priority_jump || ast_opt_priority_jumping) {
03967          if (!ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101)) {
03968             ast_module_user_remove(lu);
03969             return 0;
03970          }
03971       }
03972       ast_module_user_remove(lu);
03973       if (ignore_fail) {
03974          return 0;
03975       } else {
03976          return -1;
03977       }
03978    }
03979 
03980    ast_module_user_remove(lu);
03981    pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "UNPAUSED");
03982    return 0;
03983 }

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

Definition at line 1608 of file app_queue.c.

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

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

01609 {
01610    int digitlen = strlen(qe->digits);
01611 
01612    /* Prevent possible buffer overflow */
01613    if (digitlen < sizeof(qe->digits) - 2) {
01614       qe->digits[digitlen] = digit;
01615       qe->digits[digitlen + 1] = '\0';
01616    } else {
01617       qe->digits[0] = '\0';
01618       return 0;
01619    }
01620 
01621    /* If there's no context to goto, short-circuit */
01622    if (ast_strlen_zero(qe->context))
01623       return 0;
01624 
01625    /* If the extension is bad, then reset the digits to blank */
01626    if (!ast_canmatch_extension(qe->chan, qe->context, qe->digits, 1, qe->chan->cid.cid_num)) {
01627       qe->digits[0] = '\0';
01628       return 0;
01629    }
01630 
01631    /* We have an exact match */
01632    if (!ast_goto_if_exists(qe->chan, qe->context, qe->digits, 1)) {
01633       qe->valid_digits = 1;
01634       /* Return 1 on a successful goto */
01635       return 1;
01636    }
01637 
01638    return 0;
01639 }

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

Definition at line 1913 of file app_queue.c.

References ast_copy_string(), and pbx_builtin_serialize_variables().

Referenced by ring_entry(), and try_calling().

01914 {
01915    char *tmp = alloca(len);
01916 
01917    if (pbx_builtin_serialize_variables(chan, tmp, len)) {
01918       int i, j;
01919 
01920       /* convert "\n" to "\nVariable: " */
01921       strcpy(vars, "Variable: ");
01922 
01923       for (i = 0, j = 10; (i < len - 1) && (j < len - 1); i++, j++) {
01924          vars[j] = tmp[i];
01925 
01926          if (tmp[i + 1] == '\0')
01927             break;
01928          if (tmp[i] == '\n') {
01929             vars[j++] = '\r';
01930             vars[j++] = '\n';
01931 
01932             ast_copy_string(&(vars[j]), "Variable: ", len - j);
01933             j += 9;
01934          }
01935       }
01936       if (j > len - 3)
01937          j = len - 3;
01938       vars[j++] = '\r';
01939       vars[j++] = '\n';
01940       vars[j] = '\0';
01941    } else {
01942       /* there are no channel variables; leave it blank */
01943       *vars = '\0';
01944    }
01945    return vars;
01946 }

static int wait_a_bit ( struct queue_ent qe  )  [static]

Definition at line 3540 of file app_queue.c.

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

Referenced by queue_exec().

03541 {
03542    /* Don't need to hold the lock while we setup the outgoing calls */
03543    int retrywait = qe->parent->retry * 1000;
03544 
03545    int res = ast_waitfordigit(qe->chan, retrywait);
03546    if (res > 0 && !valid_exit(qe, res))
03547       res = 0;
03548 
03549    return res;
03550 }

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

Wait for a member to answer the call.

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

Definition at line 2298 of file app_queue.c.

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

Referenced by try_calling().

02299 {
02300    char *queue = qe->parent->name;
02301    struct callattempt *o, *start = NULL, *prev = NULL;
02302    int status;
02303    int numbusies = prebusies;
02304    int numnochan = 0;
02305    int stillgoing = 0;
02306    int orig = *to;
02307    struct ast_frame *f;
02308    struct callattempt *peer = NULL;
02309    struct ast_channel *winner;
02310    struct ast_channel *in = qe->chan;
02311    char on[80] = "";
02312    char membername[80] = "";
02313    long starttime = 0;
02314    long endtime = 0; 
02315 
02316    starttime = (long) time(NULL);
02317    
02318    while (*to && !peer) {
02319       int numlines, retry, pos = 1;
02320       struct ast_channel *watchers[AST_MAX_WATCHERS];
02321       watchers[0] = in;
02322       start = NULL;
02323 
02324       for (retry = 0; retry < 2; retry++) {
02325          numlines = 0;
02326          for (o = outgoing; o; o = o->q_next) { /* Keep track of important channels */
02327             if (o->stillgoing) { /* Keep track of important channels */
02328                stillgoing = 1;
02329                if (o->chan) {
02330                   watchers[pos++] = o->chan;
02331                   if (!start)
02332                      start = o;
02333                   else
02334                      prev->call_next = o;
02335                   prev = o;
02336                }
02337             }
02338             numlines++;
02339          }
02340          if (pos > 1 /* found */ || !stillgoing /* nobody listening */ ||
02341             (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) /* ring would not be delivered */)
02342             break;
02343          /* On "ringall" strategy we only move to the next penalty level
02344             when *all* ringing phones are done in the current penalty level */
02345          ring_one(qe, outgoing, &numbusies);
02346          /* and retry... */
02347       }
02348       if (pos == 1 /* not found */) {
02349          if (numlines == (numbusies + numnochan)) {
02350             ast_log(LOG_DEBUG, "Everyone is busy at this time\n");
02351             if (in->cdr && (in->_state != AST_STATE_UP)) {
02352                ast_cdr_busy(in->cdr);
02353             }
02354          } else {
02355             ast_log(LOG_NOTICE, "No one is answering queue '%s' (%d/%d/%d)\n", queue, numlines, numbusies, numnochan);
02356             if (in->cdr && (in->_state != AST_STATE_UP)) {
02357                ast_cdr_failed(in->cdr);
02358             }
02359          }
02360          *to = 0;
02361          return NULL;
02362       }
02363 
02364       /* Poll for events from both the incoming channel as well as any outgoing channels */
02365       winner = ast_waitfor_n(watchers, pos, to);
02366 
02367       /* Service all of the outgoing channels */
02368       for (o = start; o; o = o->call_next) {
02369          if (o->stillgoing && (o->chan) &&  (o->chan->_state == AST_STATE_UP)) {
02370             if (!peer) {
02371                if (option_verbose > 2)
02372                   ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
02373                peer = o;
02374             }
02375          } else if (o->chan && (o->chan == winner)) {
02376 
02377             ast_copy_string(on, o->member->interface, sizeof(on));
02378             ast_copy_string(membername, o->member->membername, sizeof(membername));
02379 
02380             if (!ast_strlen_zero(o->chan->call_forward) && !forwardsallowed) {
02381                if (option_verbose > 2)
02382                   ast_verbose(VERBOSE_PREFIX_3 "Forwarding %s to '%s' prevented.\n", in->name, o->chan->call_forward);
02383                numnochan++;
02384                do_hang(o);
02385                winner = NULL;
02386                continue;
02387             } else if (!ast_strlen_zero(o->chan->call_forward)) {
02388                char tmpchan[256];
02389                char *stuff;
02390                char *tech;
02391 
02392                ast_copy_string(tmpchan, o->chan->call_forward, sizeof(tmpchan));
02393                if ((stuff = strchr(tmpchan, '/'))) {
02394                   *stuff++ = '\0';
02395                   tech = tmpchan;
02396                } else {
02397                   snprintf(tmpchan, sizeof(tmpchan), "%s@%s", o->chan->call_forward, o->chan->context);
02398                   stuff = tmpchan;
02399                   tech = "Local";
02400                }
02401                /* Before processing channel, go ahead and check for forwarding */
02402                if (option_verbose > 2)
02403                   ast_verbose(VERBOSE_PREFIX_3 "Now forwarding %s to '%s/%s' (thanks to %s)\n", in->name, tech, stuff, o->chan->name);
02404                /* Setup parameters */
02405                o->chan = ast_request(tech, in->nativeformats, stuff, &status);
02406                if (!o->chan) {
02407                   ast_log(LOG_NOTICE,
02408                      "Forwarding failed to create channel to dial '%s/%s'\n",
02409                      tech, stuff);
02410                   o->stillgoing = 0;
02411                   numnochan++;
02412                } else {
02413                   ast_channel_inherit_variables(in, o->chan);
02414                   ast_channel_datastore_inherit(in, o->chan);
02415                   if (o->chan->cid.cid_num)
02416                      free(o->chan->cid.cid_num);
02417                   o->chan->cid.cid_num = ast_strdup(in->cid.cid_num);
02418 
02419                   if (o->chan->cid.cid_name)
02420                      free(o->chan->cid.cid_name);
02421                   o->chan->cid.cid_name = ast_strdup(in->cid.cid_name);
02422 
02423                   ast_string_field_set(o->chan, accountcode, in->accountcode);
02424                   o->chan->cdrflags = in->cdrflags;
02425 
02426                   if (in->cid.cid_ani) {
02427                      if (o->chan->cid.cid_ani)
02428                         free(o->chan->cid.cid_ani);
02429                      o->chan->cid.cid_ani = ast_strdup(in->cid.cid_ani);
02430                   }
02431                   if (o->chan->cid.cid_rdnis)
02432                      free(o->chan->cid.cid_rdnis);
02433                   o->chan->cid.cid_rdnis = ast_strdup(S_OR(in->macroexten, in->exten));
02434                   if (ast_call(o->chan, stuff, 0)) {
02435                      ast_log(LOG_NOTICE, "Forwarding failed to dial '%s/%s'\n",
02436                         tech, stuff);
02437                      do_hang(o);
02438                      numnochan++;
02439                   }
02440                }
02441                /* Hangup the original channel now, in case we needed it */
02442                ast_hangup(winner);
02443                continue;
02444             }
02445             f = ast_read(winner);
02446             if (f) {
02447                if (f->frametype == AST_FRAME_CONTROL) {
02448                   switch (f->subclass) {
02449                   case AST_CONTROL_ANSWER:
02450                      /* This is our guy if someone answered. */
02451                      if (!peer) {
02452                         if (option_verbose > 2)
02453                            ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
02454                         peer = o;
02455                      }
02456                      break;
02457                   case AST_CONTROL_BUSY:
02458                      if (option_verbose > 2)
02459                         ast_verbose( VERBOSE_PREFIX_3 "%s is busy\n", o->chan->name);
02460                      if (in->cdr)
02461                         ast_cdr_busy(in->cdr);
02462                      do_hang(o);
02463                      endtime = (long)time(NULL);
02464                      endtime -= starttime;
02465                      rna(endtime * 1000, qe, on, membername, 0);
02466                      if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
02467                         if (qe->parent->timeoutrestart)
02468                            *to = orig;
02469                         /* Have enough time for a queue member to answer? */
02470                         if (*to > 500) {
02471                            ring_one(qe, outgoing, &numbusies);
02472                            starttime = (long) time(NULL);
02473                         }
02474                      }
02475                      numbusies++;
02476                      break;
02477                   case AST_CONTROL_CONGESTION:
02478                      if (option_verbose > 2)
02479                         ast_verbose( VERBOSE_PREFIX_3 "%s is circuit-busy\n", o->chan->name);
02480                      if (in->cdr)
02481                         ast_cdr_failed(in->cdr);
02482                      endtime = (long)time(NULL);
02483                      endtime -= starttime;
02484                      rna(endtime * 1000, qe, on, membername, 0);
02485                      do_hang(o);
02486                      if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
02487                         if (qe->parent->timeoutrestart)
02488                            *to = orig;
02489                         if (*to > 500) {
02490                            ring_one(qe, outgoing, &numbusies);
02491                            starttime = (long) time(NULL);
02492                         }
02493                      }
02494                      numbusies++;
02495                      break;
02496                   case AST_CONTROL_RINGING:
02497                      if (option_verbose > 2)
02498                         ast_verbose( VERBOSE_PREFIX_3 "%s is ringing\n", o->chan->name);
02499 
02500                      /* Start ring indication when the channel is ringing, if specified */
02501                      if (qe->ring_when_ringing) {
02502                         ast_moh_stop(qe->chan);
02503                         ast_indicate(qe->chan, AST_CONTROL_RINGING);
02504                      }
02505                      break;
02506                   case AST_CONTROL_OFFHOOK:
02507                      /* Ignore going off hook */
02508                      break;
02509                   default:
02510                      ast_log(LOG_DEBUG, "Dunno what to do with control type %d\n", f->subclass);
02511                   }
02512                }
02513                ast_frfree(f);
02514             } else { /* ast_read() returned NULL */
02515                endtime = (long) time(NULL) - starttime;
02516                rna(endtime * 1000, qe, on, membername, 1);
02517                do_hang(o);
02518                if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
02519                   if (qe->parent->timeoutrestart)
02520                      *to = orig;
02521                   if (*to > 500) {
02522                      ring_one(qe, outgoing, &numbusies);
02523                      starttime = (long) time(NULL);
02524                   }
02525                }
02526             }
02527          }
02528       }
02529 
02530       /* If we received an event from the caller, deal with it. */
02531       if (winner == in) {
02532          f = ast_read(in);
02533          if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) {
02534             /* Got hung up */
02535             *to = -1;
02536             if (f)
02537                ast_frfree(f);
02538             return NULL;
02539          }
02540          /* First check if DTMF digit is a valid exit */
02541          if ((f->frametype == AST_FRAME_DTMF) && valid_exit(qe, f->subclass)) {
02542             if (option_verbose > 3)
02543                ast_verbose(VERBOSE_PREFIX_3 "User pressed digit: %c\n", f->subclass);
02544             *to = 0;
02545             *digit = f->subclass;
02546             ast_frfree(f);
02547             if (in->cdr && (in->_state != AST_STATE_UP)) {
02548                ast_cdr_noanswer(in->cdr);
02549             }
02550             return NULL;
02551          }
02552          /* Else check if DTMF should be interpreted as caller disconnect */
02553          if ((f->frametype == AST_FRAME_DTMF) && caller_disconnect && (f->subclass == '*')) {
02554             if (option_verbose > 3)
02555                ast_verbose(VERBOSE_PREFIX_3 "User hit %c to disconnect call.\n", f->subclass);
02556             *to = 0;
02557             ast_frfree(f);
02558             if (in->cdr && (in->_state != AST_STATE_UP)) {
02559                ast_cdr_noanswer(in->cdr);
02560             }
02561             return NULL;
02562          }
02563          ast_frfree(f);
02564       }
02565       if (!*to) {
02566          for (o = start; o; o = o->call_next)
02567             rna(orig, qe, o->interface, o->member->membername, 1);
02568       }
02569    }
02570 
02571    if (in->cdr
02572        && in->_state != AST_STATE_UP
02573        && (!*to || ast_check_hangup(in))) {
02574      ast_cdr_noanswer(in->cdr);
02575    }
02576 
02577    return peer;
02578 }

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

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

Referenced by queue_exec().

02641 {
02642    int res = 0;
02643 
02644    /* This is the holding pen for callers 2 through maxlen */
02645    for (;;) {
02646       enum queue_member_status stat;
02647 
02648       if (is_our_turn(qe))
02649          break;
02650 
02651       /* If we have timed out, break out */
02652       if (qe->expire && (time(NULL) >= qe->expire)) {
02653          *reason = QUEUE_TIMEOUT;
02654          break;
02655       }
02656 
02657       stat = get_member_status(qe->parent, qe->max_penalty);
02658 
02659       /* leave the queue if no agents, if enabled */
02660       if (qe->parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) {
02661          *reason = QUEUE_LEAVEEMPTY;
02662          ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long)time(NULL) - qe->start);
02663          leave_queue(qe);
02664          break;
02665       }
02666 
02667       /* leave the queue if no reachable agents, if enabled */
02668       if ((qe->parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) {
02669          *reason = QUEUE_LEAVEUNAVAIL;
02670          ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long)time(NULL) - qe->start);
02671          leave_queue(qe);
02672          break;
02673       }
02674 
02675       /* Make a position announcement, if enabled */
02676       if (qe->parent->announcefrequency && !ringing &&
02677          (res = say_position(qe)))
02678          break;
02679 
02680       /* If we have timed out, break out */
02681       if (qe->expire && (time(NULL) >= qe->expire)) {
02682          *reason = QUEUE_TIMEOUT;
02683          break;
02684       }
02685 
02686       /* Make a periodic announcement, if enabled */
02687       if (qe->parent->periodicannouncefrequency && !ringing &&
02688          (res = say_periodic_announcement(qe)))
02689          break;
02690 
02691       /* If we have timed out, break out */
02692       if (qe->expire && (time(NULL) >= qe->expire)) {
02693          *reason = QUEUE_TIMEOUT;
02694          break;
02695       }
02696       
02697       /* Wait a second before checking again */
02698       if ((res = ast_waitfordigit(qe->chan, RECHECK * 1000))) {
02699          if (res > 0 && !valid_exit(qe, res))
02700             res = 0;
02701          else
02702             break;
02703       }
02704       
02705       /* If we have timed out, break out */
02706       if (qe->expire && (time(NULL) >= qe->expire)) {
02707          *reason = QUEUE_TIMEOUT;
02708          break;
02709       }
02710    }
02711 
02712    return res;
02713 }


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 = "361d7bb937402d51e4658efb5b4d76e4" , .load = load_module, .unload = unload_module, .reload = reload, } [static]

Definition at line 5894 of file app_queue.c.

char* app = "Queue" [static]

Definition at line 150 of file app_queue.c.

char* app_aqm = "AddQueueMember" [static]

Definition at line 195 of file app_queue.c.

char* app_aqm_descrip [static]

Definition at line 197 of file app_queue.c.

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

Definition at line 196 of file app_queue.c.

char* app_pqm = "PauseQueueMember" [static]

Definition at line 230 of file app_queue.c.

char* app_pqm_descrip [static]

Definition at line 232 of file app_queue.c.

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

Definition at line 231 of file app_queue.c.

char* app_ql = "QueueLog" [static]

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

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

Definition at line 270 of file app_queue.c.

char* app_rqm = "RemoveQueueMember" [static]

Definition at line 214 of file app_queue.c.

char* app_rqm_descrip [static]

Definition at line 216 of file app_queue.c.

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

Definition at line 215 of file app_queue.c.

char* app_upqm = "UnpauseQueueMember" [static]

Definition at line 253 of file app_queue.c.

char* app_upqm_descrip [static]

Definition at line 255 of file app_queue.c.

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

Definition at line 254 of file app_queue.c.

const struct ast_module_info* ast_module_info = &__mod_info [static]

Definition at line 5894 of file app_queue.c.

int autofill_default = 0 [static]

queues.conf [general] option

Definition at line 291 of file app_queue.c.

Initial value:
 {
   { "add", "queue", "member", NULL },
   handle_queue_add_member, NULL,
   NULL, complete_queue_add_member }

Definition at line 5771 of file app_queue.c.

struct ast_cli_entry cli_queue[] [static]

Definition at line 5781 of file app_queue.c.

Referenced by load_module(), and unload_module().

Initial value:
 {
   { "remove", "queue", "member", NULL },
   handle_queue_remove_member, NULL,
   NULL, complete_queue_remove_member }

Definition at line 5776 of file app_queue.c.

Initial value:
 {
   { "show", "queue", NULL },
   queue_show, NULL,
   NULL, complete_queue_show }

Definition at line 5766 of file app_queue.c.

Condition for the state change queue

Definition at line 750 of file app_queue.c.

char* descrip [static]

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

Definition at line 310 of file app_queue.c.

Referenced by _sip_show_peers().

struct statechange* last

Definition at line 752 of file app_queue.c.

Lock for the state change queue

Definition at line 748 of file app_queue.c.

int montype_default = 0 [static]

queues.conf [general] option

Definition at line 294 of file app_queue.c.

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

Persistent Members astdb family.

Definition at line 277 of file app_queue.c.

char qam_cmd_usage[] [static]
Initial value:
"Usage: queue add member <channel> to <queue> [penalty <penalty> [as <membername> [state_interface <state_interface>]]]\n"

Definition at line 5760 of file app_queue.c.

char qmc_cmd_usage[] [static]
Initial value:
"Usage: queue member count <queue>\n"
"       Provides member count information on a specified queue.\n"

Definition at line 5737 of file app_queue.c.

char qrm_cmd_usage[] [static]
Initial value:
"Usage: queue remove member <channel> from <queue>\n"

Definition at line 5763 of file app_queue.c.

int queue_debug = 0 [static]

queues.conf [general] extra debug option

Definition at line 282 of file app_queue.c.

int queue_persistent_members = 0 [static]

queues.conf [general] option

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

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

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

Definition at line 4778 of file app_queue.c.

Referenced by load_module(), and unload_module().

Definition at line 4770 of file app_queue.c.

Referenced by load_module(), and unload_module().

Definition at line 4788 of file app_queue.c.

Referenced by load_module(), and unload_module().

Definition at line 4812 of file app_queue.c.

Referenced by load_module(), and unload_module().

Definition at line 4830 of file app_queue.c.

Referenced by load_module(), and unload_module().

Definition at line 4821 of file app_queue.c.

Referenced by load_module(), and unload_module().

Definition at line 4803 of file app_queue.c.

Referenced by load_module(), and unload_module().

int shared_lastcall = 0 [static]

queues.conf [general] option

Definition at line 297 of file app_queue.c.

struct { ... } state_change_q

Queue of state changes

unsigned int stop

Set to 1 to stop the thread

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

char* text
pthread_t thread

The device state monitoring thread

Definition at line 746 of file app_queue.c.

int use_weight = 0 [static]

queues.conf per-queue weight option

Definition at line 288 of file app_queue.c.


Generated on 31 Jan 2014 for Asterisk - the Open Source PBX by  doxygen 1.6.1