Fri Apr 27 14:27:14 2012

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_queuemembercount (struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
static int queue_function_queuememberlist (struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
static int queue_function_queuewaitingcount (struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
static 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 queuemembercount_function
static struct ast_custom_function queuememberlist_function
static struct ast_custom_function queuewaitingcount_function
static struct strategy strategies []
static char * synopsis = "Queue a call for a call queue"
static int use_weight = 0
 queues.conf per-queue weight option

Detailed Description

True call queues with optional send URL on answer.

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

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

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

Patch Version 1.07 2003-12-24 01

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

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

Definition in file app_queue.c.


Define Documentation

#define ANNOUNCEHOLDTIME_ALWAYS   1

Definition at line 394 of file app_queue.c.

Referenced by queue_set_param().

#define ANNOUNCEHOLDTIME_ONCE   2

Definition at line 395 of file app_queue.c.

Referenced by queue_set_param(), and say_position().

#define AST_MAX_WATCHERS   256

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

Referenced by queue_set_param().

#define QUEUE_EMPTY_STRICT   2

Definition at line 393 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 396 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 4404 of file app_queue.c.

04404                 {
04405    QMC_VALID = 0, /* Count valid members */
04406    QMC_PAUSED,    /* Count paused members */
04407    QMC_ACTIVE,    /* Count active members */
04408    QMC_FREE,      /* Count free members */
04409    QMC_ALL        /* Count all queue members */
04410 };

Enumerator:
QUEUE_NO_MEMBERS 
QUEUE_NO_REACHABLE_MEMBERS 
QUEUE_NORMAL 

Definition at line 572 of file app_queue.c.

00572                          {
00573    QUEUE_NO_MEMBERS,
00574    QUEUE_NO_REACHABLE_MEMBERS,
00575    QUEUE_NORMAL
00576 };

Enumerator:
QUEUE_UNKNOWN 
QUEUE_TIMEOUT 
QUEUE_JOINEMPTY 
QUEUE_LEAVEEMPTY 
QUEUE_JOINUNAVAIL 
QUEUE_LEAVEUNAVAIL 
QUEUE_FULL 

Definition at line 296 of file app_queue.c.

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


Function Documentation

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

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

04860 {
04861    struct call_queue *q;
04862    struct queue_ent *qe;
04863    struct member *mem;
04864    int pos, queue_show;
04865    time_t now;
04866    char max_buf[150];
04867    char *max;
04868    size_t max_left;
04869    float sl = 0;
04870    char *term = manager ? "\r\n" : "\n";
04871    struct ao2_iterator mem_iter;
04872 
04873    time(&now);
04874    if (argc == 2)
04875       queue_show = 0;
04876    else if (argc == 3)
04877       queue_show = 1;
04878    else
04879       return RESULT_SHOWUSAGE;
04880 
04881    /* We only want to load realtime queues when a specific queue is asked for. */
04882    if (queue_show) {
04883       load_realtime_queue(argv[2]);
04884    } else if (ast_check_realtime("queues")) {
04885       struct ast_config *cfg = ast_load_realtime_multientry("queues", "name LIKE", "%", (char *) NULL);
04886       char *queuename;
04887       if (cfg) {
04888          for (queuename = ast_category_browse(cfg, NULL); !ast_strlen_zero(queuename); queuename = ast_category_browse(cfg, queuename)) {
04889             load_realtime_queue(queuename);
04890          }
04891          ast_config_destroy(cfg);
04892       }
04893    }
04894 
04895    AST_LIST_LOCK(&queues);
04896    if (AST_LIST_EMPTY(&queues)) {
04897       AST_LIST_UNLOCK(&queues);
04898       if (queue_show) {
04899          if (s)
04900             astman_append(s, "No such queue: %s.%s",argv[2], term);
04901          else
04902             ast_cli(fd, "No such queue: %s.%s",argv[2], term);
04903       } else {
04904          if (s)
04905             astman_append(s, "No queues.%s", term);
04906          else
04907             ast_cli(fd, "No queues.%s", term);
04908       }
04909       return RESULT_SUCCESS;
04910    }
04911    AST_LIST_TRAVERSE(&queues, q, list) {
04912       ao2_lock(q);
04913       if (queue_show) {
04914          if (strcasecmp(q->name, argv[2]) != 0) {
04915             ao2_unlock(q);
04916             if (!AST_LIST_NEXT(q, list)) {
04917                ast_cli(fd, "No such queue: %s.%s",argv[2], term);
04918                break;
04919             }
04920             continue;
04921          }
04922       }
04923       max_buf[0] = '\0';
04924       max = max_buf;
04925       max_left = sizeof(max_buf);
04926       if (q->maxlen)
04927          ast_build_string(&max, &max_left, "%d", q->maxlen);
04928       else
04929          ast_build_string(&max, &max_left, "unlimited");
04930       sl = 0;
04931       if (q->callscompleted > 0)
04932          sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
04933       if (s)
04934          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",
04935                               q->name, q->count, max_buf, int2strat(q->strategy), q->holdtime, q->ringlimit,
04936                               q->weight, q->callscompleted, q->callsabandoned, sl, q->servicelevel, term);
04937       else
04938          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",
04939                      q->name, q->count, max_buf, int2strat(q->strategy), q->holdtime, q->ringlimit,
04940                      q->weight, q->callscompleted, q->callsabandoned, sl, q->servicelevel, term);
04941       if (ao2_container_count(q->members)) {
04942          if (s)
04943             astman_append(s, "   Members: %s", term);
04944          else
04945             ast_cli(fd, "   Members: %s", term);
04946          mem_iter = ao2_iterator_init(q->members, 0);
04947          while ((mem = ao2_iterator_next(&mem_iter))) {
04948             max_buf[0] = '\0';
04949             max = max_buf;
04950             max_left = sizeof(max_buf);
04951             if (strcasecmp(mem->membername, mem->interface)) {
04952                ast_build_string(&max, &max_left, " (%s)", mem->interface);
04953             }
04954             if (mem->penalty)
04955                ast_build_string(&max, &max_left, " with penalty %d", mem->penalty);
04956             if (mem->dynamic)
04957                ast_build_string(&max, &max_left, " (dynamic)");
04958             if (mem->realtime)
04959                ast_build_string(&max, &max_left, " (realtime)");
04960             if (mem->paused)
04961                ast_build_string(&max, &max_left, " (paused)");
04962             ast_build_string(&max, &max_left, " (%s)", devstate2str(mem->status));
04963             if (mem->calls) {
04964                ast_build_string(&max, &max_left, " has taken %d calls (last was %ld secs ago)",
04965                   mem->calls, (long) (time(NULL) - mem->lastcall));
04966             } else
04967                ast_build_string(&max, &max_left, " has taken no calls yet");
04968             if (s)
04969                astman_append(s, "      %s%s%s", mem->membername, max_buf, term);
04970             else
04971                ast_cli(fd, "      %s%s%s", mem->membername, max_buf, term);
04972             ao2_ref(mem, -1);
04973          }
04974          ao2_iterator_destroy(&mem_iter);
04975       } else if (s)
04976          astman_append(s, "   No Members%s", term);
04977       else  
04978          ast_cli(fd, "   No Members%s", term);
04979       if (q->head) {
04980          pos = 1;
04981          if (s)
04982             astman_append(s, "   Callers: %s", term);
04983          else
04984             ast_cli(fd, "   Callers: %s", term);
04985          for (qe = q->head; qe; qe = qe->next) {
04986             if (s)
04987                astman_append(s, "      %d. %s (wait: %ld:%2.2ld, prio: %d)%s",
04988                   pos++, qe->chan->name, (long) (now - qe->start) / 60,
04989                   (long) (now - qe->start) % 60, qe->prio, term);
04990             else
04991                ast_cli(fd, "      %d. %s (wait: %ld:%2.2ld, prio: %d)%s", pos++,
04992                   qe->chan->name, (long) (now - qe->start) / 60,
04993                   (long) (now - qe->start) % 60, qe->prio, term);
04994          }
04995       } else if (s)
04996          astman_append(s, "   No Callers%s", term);
04997       else
04998          ast_cli(fd, "   No Callers%s", term);
04999       if (s)
05000          astman_append(s, "%s", term);
05001       else
05002          ast_cli(fd, "%s", term);
05003       ao2_unlock(q);
05004       if (queue_show)
05005          break;
05006    }
05007    AST_LIST_UNLOCK(&queues);
05008    return RESULT_SUCCESS;
05009 }

static void __reg_module ( void   )  [static]

Definition at line 5687 of file app_queue.c.

static void __unreg_module ( void   )  [static]

Definition at line 5687 of file app_queue.c.

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

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

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

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

03628 {
03629    struct call_queue *q;
03630    struct member *new_member, *old_member;
03631    int res = RES_NOSUCHQUEUE;
03632 
03633    /* \note Ensure the appropriate realtime queue is loaded.  Note that this
03634     * short-circuits if the queue is already in memory. */
03635    if (!(q = load_realtime_queue(queuename)))
03636       return res;
03637 
03638    AST_LIST_LOCK(&queues);
03639 
03640    ao2_lock(q);
03641    if ((old_member = interface_exists(q, interface)) == NULL) {
03642       if ((new_member = create_queue_member(interface, membername, penalty, paused, state_interface))) {
03643          add_to_interfaces(new_member->state_interface);
03644          new_member->dynamic = 1;
03645          ao2_link(q->members, new_member);
03646          q->membercount++;
03647          manager_event(EVENT_FLAG_AGENT, "QueueMemberAdded",
03648             "Queue: %s\r\n"
03649             "Location: %s\r\n"
03650             "MemberName: %s\r\n"
03651             "Membership: %s\r\n"
03652             "Penalty: %d\r\n"
03653             "CallsTaken: %d\r\n"
03654             "LastCall: %d\r\n"
03655             "Status: %d\r\n"
03656             "Paused: %d\r\n",
03657             q->name, new_member->interface, new_member->membername,
03658             "dynamic",
03659             new_member->penalty, new_member->calls, (int) new_member->lastcall,
03660             new_member->status, new_member->paused);
03661          
03662          ao2_ref(new_member, -1);
03663          new_member = NULL;
03664 
03665          if (dump)
03666             dump_queue_members(q);
03667          
03668          res = RES_OKAY;
03669       } else {
03670          res = RES_OUTOFMEMORY;
03671       }
03672    } else {
03673       ao2_ref(old_member, -1);
03674       res = RES_EXISTS;
03675    }
03676    ao2_unlock(q);
03677    AST_LIST_UNLOCK(&queues);
03678 
03679    return res;
03680 }

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

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

00831 {
00832    struct call_queue *q;
00833 
00834    if ((q = ao2_alloc(sizeof(*q), destroy_queue))) {
00835       ast_copy_string(q->name, queuename, sizeof(q->name));
00836    }
00837    return q;
00838 }

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

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

04009 {
04010    int res=-1;
04011    struct ast_module_user *lu;
04012    char *parse, *temppos = NULL;
04013    int priority_jump = 0;
04014    AST_DECLARE_APP_ARGS(args,
04015       AST_APP_ARG(queuename);
04016       AST_APP_ARG(interface);
04017       AST_APP_ARG(penalty);
04018       AST_APP_ARG(options);
04019       AST_APP_ARG(membername);
04020       AST_APP_ARG(state_interface);
04021    );
04022    int penalty = 0;
04023 
04024    if (ast_strlen_zero(data)) {
04025       ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename[|interface[|penalty[|options[|membername[|state_interface]]]]])\n");
04026       return -1;
04027    }
04028 
04029    parse = ast_strdupa(data);
04030 
04031    AST_STANDARD_APP_ARGS(args, parse);
04032 
04033    lu = ast_module_user_add(chan);
04034 
04035    if (ast_strlen_zero(args.interface)) {
04036       args.interface = ast_strdupa(chan->name);
04037       temppos = strrchr(args.interface, '-');
04038       if (temppos)
04039          *temppos = '\0';
04040    }
04041 
04042    if (!ast_strlen_zero(args.penalty)) {
04043       if ((sscanf(args.penalty, "%30d", &penalty) != 1) || penalty < 0) {
04044          ast_log(LOG_WARNING, "Penalty '%s' is invalid, must be an integer >= 0\n", args.penalty);
04045          penalty = 0;
04046       }
04047    }
04048    
04049    if (args.options) {
04050       if (strchr(args.options, 'j'))
04051          priority_jump = 1;
04052    }
04053 
04054    switch (add_to_queue(args.queuename, args.interface, args.membername, penalty, 0, queue_persistent_members, args.state_interface)) {
04055    case RES_OKAY:
04056       ast_queue_log(args.queuename, chan->uniqueid, args.interface, "ADDMEMBER", "%s", "");
04057       ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", args.interface, args.queuename);
04058       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "ADDED");
04059       res = 0;
04060       break;
04061    case RES_EXISTS:
04062       ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': Already there\n", args.interface, args.queuename);
04063       if (priority_jump || ast_opt_priority_jumping)
04064          ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
04065       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "MEMBERALREADY");
04066       res = 0;
04067       break;
04068    case RES_NOSUCHQUEUE:
04069       ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", args.queuename);
04070       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "NOSUCHQUEUE");
04071       res = 0;
04072       break;
04073    case RES_OUTOFMEMORY:
04074       ast_log(LOG_ERROR, "Out of memory adding member %s to queue %s\n", args.interface, args.queuename);
04075       break;
04076    }
04077 
04078    ast_module_user_remove(lu);
04079 
04080    return res;
04081 }

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

References ast_channel_datastore_find(), and queue_transfer_info.

Referenced by try_calling().

02836 {
02837    return ast_channel_datastore_find(chan, &queue_transfer_info, NULL) ? 0 : 1;
02838 }

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

02713 {
02714    if (qe->max_penalty && (mem->penalty > qe->max_penalty))
02715       return -1;
02716 
02717    switch (q->strategy) {
02718    case QUEUE_STRATEGY_RINGALL:
02719       /* Everyone equal, except for penalty */
02720       tmp->metric = mem->penalty * 1000000;
02721       break;
02722    case QUEUE_STRATEGY_ROUNDROBIN:
02723       if (!pos) {
02724          if (!q->wrapped) {
02725             /* No more channels, start over */
02726             q->rrpos = 0;
02727          } else {
02728             /* Prioritize next entry */
02729             q->rrpos++;
02730          }
02731          q->wrapped = 0;
02732       }
02733       /* Fall through */
02734    case QUEUE_STRATEGY_RRORDERED:
02735    case QUEUE_STRATEGY_RRMEMORY:
02736       if (pos < q->rrpos) {
02737          tmp->metric = 1000 + pos;
02738       } else {
02739          if (pos > q->rrpos)
02740             /* Indicate there is another priority */
02741             q->wrapped = 1;
02742          tmp->metric = pos;
02743       }
02744       tmp->metric += mem->penalty * 1000000;
02745       break;
02746    case QUEUE_STRATEGY_RANDOM:
02747       tmp->metric = ast_random() % 1000;
02748       tmp->metric += mem->penalty * 1000000;
02749       break;
02750    case QUEUE_STRATEGY_FEWESTCALLS:
02751       tmp->metric = mem->calls;
02752       tmp->metric += mem->penalty * 1000000;
02753       break;
02754    case QUEUE_STRATEGY_LEASTRECENT:
02755       if (!mem->lastcall)
02756          tmp->metric = 0;
02757       else
02758          tmp->metric = 1000000 - (time(NULL) - mem->lastcall);
02759       tmp->metric += mem->penalty * 1000000;
02760       break;
02761    default:
02762       ast_log(LOG_WARNING, "Can't calculate metric for unknown strategy %d\n", q->strategy);
02763       break;
02764    }
02765    if (q->ringlimit && (mem->ringcount >= q->ringlimit)) {
02766       tmp->metric += (mem->ringcount / q->ringlimit) * 10000000;
02767    }
02768    if (option_debug)
02769       ast_log(LOG_DEBUG, "New metric %d for member %s with %d rings (limit %d)\n", 
02770                   tmp->metric, mem->interface, mem->ringcount, q->ringlimit);
02771    return 0;
02772 }

static void clear_and_free_interfaces ( void   )  [static]

Definition at line 1012 of file app_queue.c.

References AST_LIST_LOCK, AST_LIST_REMOVE_HEAD, AST_LIST_UNLOCK, and free.

Referenced by unload_module().

01013 {
01014    struct member_interface *curint;
01015 
01016    AST_LIST_LOCK(&interfaces);
01017    while ((curint = AST_LIST_REMOVE_HEAD(&interfaces, list)))
01018       free(curint);
01019    AST_LIST_UNLOCK(&interfaces);
01020 }

static void clear_queue ( struct call_queue q  )  [static]

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

00925 {
00926    q->holdtime = 0;
00927    q->callscompleted = 0;
00928    q->callsabandoned = 0;
00929    q->callscompletedinsl = 0;
00930    q->wrapuptime = 0;
00931 }

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

Definition at line 5514 of file app_queue.c.

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

05515 {
05516    char buffer[256] = "";
05517    char *queuename;
05518    
05519    if (argc != 4) {
05520       return RESULT_SHOWUSAGE;
05521    }
05522    queuename = argv[3];
05523 
05524    if (qmc_handler(queuename, buffer, sizeof(buffer)) == RESULT_SUCCESS) {
05525          ast_cli(fd, 
05526                      "Member count for queue '%s'\n"
05527                      "%s\n",
05528                      queuename, buffer); 
05529          return RESULT_SUCCESS;
05530    } else {
05531          ast_cli(fd, "No such queue: '%s'\n", queuename);
05532          return RESULT_FAILURE;
05533    }
05534 }

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

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

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

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

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

05017 {
05018    struct call_queue *q;
05019    char *ret = NULL;
05020    int which = 0;
05021    int wordlen = strlen(word);
05022    
05023    AST_LIST_LOCK(&queues);
05024    AST_LIST_TRAVERSE(&queues, q, list) {
05025       if (!strncasecmp(word, q->name, wordlen) && ++which > state) {
05026          ret = ast_strdup(q->name); 
05027          break;
05028       }
05029    }
05030    AST_LIST_UNLOCK(&queues);
05031 
05032    return ret;
05033 }

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

Definition at line 5319 of file app_queue.c.

References ast_malloc, ast_strdup, and complete_queue().

05320 {
05321    /* 0 - queue; 1 - add; 2 - member; 3 - <interface>; 4 - to; 5 - <queue>; 6 - penalty; 7 - <penalty>; 8 - as; 9 - <membername> */
05322    switch (pos) {
05323    case 3:  /* Don't attempt to complete name of interface (infinite possibilities) */
05324       return NULL;
05325    case 4:  /* only one possible match, "to" */
05326       return state == 0 ? ast_strdup("to") : NULL;
05327    case 5:  /* <queue> */
05328       return complete_queue(line, word, pos, state);
05329    case 6: /* only one possible match, "penalty" */
05330       return state == 0 ? ast_strdup("penalty") : NULL;
05331    case 7:
05332       if (state < 100) {   /* 0-99 */
05333          char *num;
05334          if ((num = ast_malloc(3))) {
05335             sprintf(num, "%d", state);
05336          }
05337          return num;
05338       } else {
05339          return NULL;
05340       }
05341    case 8: /* only one possible match, "as" */
05342       return state == 0 ? ast_strdup("as") : NULL;
05343    case 9:  /* Don't attempt to complete name of member (infinite possibilities) */
05344       return NULL;
05345    case 10:
05346       return state == 0 ? ast_strdup("state_interface") : NULL;
05347    default:
05348       return NULL;
05349    }
05350 }

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

Definition at line 5541 of file app_queue.c.

References complete_queue().

05542 {
05543       /* 0 - queue; 1 - member; 2 - count; 3 - <queue> */
05544       switch (pos) {
05545       case 3:  /* <queue> */
05546             return complete_queue(line, word, pos, state);
05547       default:
05548          return NULL;
05549       }
05550 }

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

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

05388 {
05389    int which = 0;
05390    struct call_queue *q;
05391    struct member *m;
05392    struct ao2_iterator mem_iter;
05393 
05394    /* 0 - queue; 1 - remove; 2 - member; 3 - <member>; 4 - from; 5 - <queue> */
05395    if (pos > 5 || pos < 3)
05396       return NULL;
05397    if (pos == 4)  /* only one possible match, 'from' */
05398       return state == 0 ? ast_strdup("from") : NULL;
05399 
05400    if (pos == 5)  /* No need to duplicate code */
05401       return complete_queue(line, word, pos, state);
05402 
05403    /* here is the case for 3, <member> */
05404    if (!AST_LIST_EMPTY(&queues)) { /* XXX unnecessary ? the traverse does that for us */
05405       AST_LIST_TRAVERSE(&queues, q, list) {
05406          ao2_lock(q);
05407          mem_iter = ao2_iterator_init(q->members, 0);
05408          while ((m = ao2_iterator_next(&mem_iter))) {
05409             if (++which > state) {
05410                char *tmp;
05411                ao2_iterator_destroy(&mem_iter);
05412                ao2_unlock(q);
05413                tmp = ast_strdup(m->interface);
05414                ao2_ref(m, -1);
05415                return tmp;
05416             }
05417             ao2_ref(m, -1);
05418          }
05419          ao2_iterator_destroy(&mem_iter);
05420          ao2_unlock(q);
05421       }
05422    }
05423 
05424    return NULL;
05425 }

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

Definition at line 5035 of file app_queue.c.

References complete_queue().

05036 {
05037    if (pos == 2)
05038       return complete_queue(line, word, pos, state);
05039    return NULL;
05040 }

static int compress_char ( const char  c  )  [static]

Definition at line 840 of file app_queue.c.

Referenced by member_hash_fn().

00841 {
00842    if (c < 32)
00843       return 0;
00844    else if (c > 96)
00845       return c - 64;
00846    else
00847       return c - 32;
00848 }

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

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

static void destroy_queue ( void *  obj  )  [static]

Definition at line 538 of file app_queue.c.

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

Referenced by alloc_queue().

00539 {
00540    struct call_queue *q = obj;
00541    if (q->members) {
00542       free_members(q, 1);
00543       ao2_ref(q->members, -1);
00544    }
00545 }

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

Consumer of the statechange queue.

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

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

static void do_hang ( struct callattempt o  )  [static]

common hangup actions

Definition at line 1901 of file app_queue.c.

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

Referenced by ring_entry(), and wait_for_answer().

01902 {
01903    o->stillgoing = 0;
01904    ast_hangup(o->chan);
01905    o->chan = NULL;
01906 }

static void dump_queue_members ( struct call_queue pm_queue  )  [static]

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

03536 {
03537    struct member *cur_member;
03538    char value[PM_MAX_LEN];
03539    int value_len = 0;
03540    int res;
03541    struct ao2_iterator mem_iter;
03542 
03543    memset(value, 0, sizeof(value));
03544 
03545    if (!pm_queue)
03546       return;
03547 
03548    mem_iter = ao2_iterator_init(pm_queue->members, 0);
03549    while ((cur_member = ao2_iterator_next(&mem_iter))) {
03550       if (!cur_member->dynamic) {
03551          ao2_ref(cur_member, -1);
03552          continue;
03553       }
03554 
03555       res = snprintf(value + value_len, sizeof(value) - value_len, "%s%s;%d;%d;%s;%s",
03556          value_len ? "|" : "", cur_member->interface, cur_member->penalty, cur_member->paused, cur_member->membername, cur_member->state_interface);
03557 
03558       ao2_ref(cur_member, -1);
03559 
03560       if (res != strlen(value + value_len)) {
03561          ast_log(LOG_WARNING, "Could not create persistent member string, out of space\n");
03562          break;
03563       }
03564       value_len += res;
03565    }
03566    ao2_iterator_destroy(&mem_iter);
03567    
03568    if (value_len && !cur_member) {
03569       if (ast_db_put(pm_family, pm_queue->name, value))
03570          ast_log(LOG_WARNING, "failed to create persistent dynamic entry!\n");
03571    } else
03572       /* Delete the entry if the queue is empty or there is an error */
03573       ast_db_del(pm_family, pm_queue->name);
03574 }

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

find the entry with the best metric, or NULL

Definition at line 2123 of file app_queue.c.

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

Referenced by ring_one(), and store_next().

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

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

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

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

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

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

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

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

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

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

05260 {
05261    char *queuename, *interface, *membername = NULL, *state_interface = NULL;
05262    int penalty;
05263 
05264    if ((argc != 6) && (argc != 8) && (argc != 10) && (argc != 12)) {
05265       return RESULT_SHOWUSAGE;
05266    } else if (strcmp(argv[4], "to")) {
05267       return RESULT_SHOWUSAGE;
05268    } else if ((argc == 8) && strcmp(argv[6], "penalty")) {
05269       return RESULT_SHOWUSAGE;
05270    } else if ((argc == 10) && strcmp(argv[8], "as")) {
05271       return RESULT_SHOWUSAGE;
05272    } else if ((argc == 12) && strcmp(argv[10], "state_interface")) {
05273       return RESULT_SHOWUSAGE;
05274    }
05275 
05276    queuename = argv[5];
05277    interface = argv[3];
05278    if (argc >= 8) {
05279       if (sscanf(argv[7], "%30d", &penalty) == 1) {
05280          if (penalty < 0) {
05281             ast_cli(fd, "Penalty must be >= 0\n");
05282             penalty = 0;
05283          }
05284       } else {
05285          ast_cli(fd, "Penalty must be an integer >= 0\n");
05286          penalty = 0;
05287       }
05288    } else {
05289       penalty = 0;
05290    }
05291 
05292    if (argc >= 10) {
05293       membername = argv[9];
05294    }
05295 
05296    if (argc >= 12) {
05297       state_interface = argv[11];
05298    }
05299 
05300    switch (add_to_queue(queuename, interface, membername, penalty, 0, queue_persistent_members, state_interface)) {
05301    case RES_OKAY:
05302       ast_queue_log(queuename, "CLI", interface, "ADDMEMBER", "%s", "");
05303       ast_cli(fd, "Added interface '%s' to queue '%s'\n", interface, queuename);
05304       return RESULT_SUCCESS;
05305    case RES_EXISTS:
05306       ast_cli(fd, "Unable to add interface '%s' to queue '%s': Already there\n", interface, queuename);
05307       return RESULT_FAILURE;
05308    case RES_NOSUCHQUEUE:
05309       ast_cli(fd, "Unable to add interface to queue '%s': No such queue\n", queuename);
05310       return RESULT_FAILURE;
05311    case RES_OUTOFMEMORY:
05312       ast_cli(fd, "Out of memory\n");
05313       return RESULT_FAILURE;
05314    default:
05315       return RESULT_FAILURE;
05316    }
05317 }

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

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

05353 {
05354    char *queuename, *interface;
05355 
05356    if (argc != 6) {
05357       return RESULT_SHOWUSAGE;
05358    } else if (strcmp(argv[4], "from")) {
05359       return RESULT_SHOWUSAGE;
05360    }
05361 
05362    queuename = argv[5];
05363    interface = argv[3];
05364 
05365    switch (remove_from_queue(queuename, interface)) {
05366    case RES_OKAY:
05367       ast_queue_log(queuename, "CLI", interface, "REMOVEMEMBER", "%s", "");
05368       ast_cli(fd, "Removed interface '%s' from queue '%s'\n", interface, queuename);
05369       return RESULT_SUCCESS;
05370    case RES_EXISTS:
05371       ast_cli(fd, "Unable to remove interface '%s' from queue '%s': Not there\n", interface, queuename);
05372       return RESULT_FAILURE;
05373    case RES_NOSUCHQUEUE:
05374       ast_cli(fd, "Unable to remove interface from queue '%s': No such queue\n", queuename);
05375       return RESULT_FAILURE;
05376    case RES_OUTOFMEMORY:
05377       ast_cli(fd, "Out of memory\n");
05378       return RESULT_FAILURE;
05379    case RES_NOT_DYNAMIC:
05380       ast_cli(fd, "Member not dynamic\n");
05381       return RESULT_FAILURE;
05382    default:
05383       return RESULT_FAILURE;
05384    }
05385 }

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

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

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

Definition at line 1801 of file app_queue.c.

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

Referenced by try_calling().

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

static void init_queue ( struct call_queue q  )  [static]

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

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

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

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

Referenced by join_queue().

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

static char* int2strat ( int  strategy  )  [static]

Definition at line 502 of file app_queue.c.

References strategy::name, and strategies.

Referenced by __queues_show().

00503 {
00504    int x;
00505 
00506    for (x = 0; x < sizeof(strategies) / sizeof(strategies[0]); x++) {
00507       if (strategy == strategies[x].strategy)
00508          return strategies[x].name;
00509    }
00510 
00511    return "<unknown>";
00512 }

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

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

03509 {
03510    struct member *mem;
03511    struct ao2_iterator mem_iter;
03512 
03513    if (!q)
03514       return NULL;
03515 
03516    mem_iter = ao2_iterator_init(q->members, 0);
03517    while ((mem = ao2_iterator_next(&mem_iter))) {
03518       if (!strcasecmp(interface, mem->interface)) {
03519          ao2_iterator_destroy(&mem_iter);
03520          return mem;
03521       }
03522       ao2_ref(mem, -1);
03523    }
03524    ao2_iterator_destroy(&mem_iter);
03525 
03526    return NULL;
03527 }

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

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

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

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

02570 {
02571    struct queue_ent *ch;
02572    int res;
02573    int avl;
02574    int idx = 0;
02575    /* This needs a lock. How many members are available to be served? */
02576    ao2_lock(qe->parent);
02577 
02578    avl = num_available_members(qe->parent);
02579 
02580    ch = qe->parent->head;
02581 
02582    if (option_debug) {
02583       ast_log(LOG_DEBUG, "There %s %d available %s.\n", avl != 1 ? "are" : "is", avl, avl != 1 ? "members" : "member");
02584    }
02585 
02586    while ((idx < avl) && (ch) && (ch != qe)) {
02587       if (!ch->pending)
02588          idx++;
02589       ch = ch->next;       
02590    }
02591 
02592    ao2_unlock(qe->parent);
02593    /* If the queue entry is within avl [the number of available members] calls from the top ... 
02594     * Autofill and position check added to support autofill=no (as only calls
02595     * from the front of the queue are valid when autofill is disabled)
02596     */
02597    if (ch && idx < avl && (qe->parent->autofill || qe->pos == 1)) {
02598       if (option_debug)
02599          ast_log(LOG_DEBUG, "It's our turn (%s).\n", qe->chan->name);
02600       res = 1;
02601    } else {
02602       if (option_debug)
02603          ast_log(LOG_DEBUG, "It's not our turn (%s).\n", qe->chan->name);
02604       res = 0;
02605    }
02606 
02607    return res;
02608 }

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

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

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

static void leave_queue ( struct queue_ent qe  )  [static]

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

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

static int load_module ( void   )  [static]

Definition at line 5641 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, queuemembercount_function, queuememberlist_function, queuewaitingcount_function, reload_queue_members(), reload_queues(), rqm_exec(), statechange_queue(), and upqm_exec().

05642 {
05643    int res;
05644 
05645    if (!reload_queues())
05646       return AST_MODULE_LOAD_DECLINE;
05647 
05648    if (queue_persistent_members)
05649       reload_queue_members();
05650 
05651    ast_mutex_init(&device_state.lock);
05652    ast_cond_init(&device_state.cond, NULL);
05653    ast_pthread_create(&device_state.thread, NULL, device_state_thread, NULL);
05654 
05655    ast_cli_register_multiple(cli_queue, sizeof(cli_queue) / sizeof(struct ast_cli_entry));
05656    res = ast_register_application(app, queue_exec, synopsis, descrip);
05657    res |= ast_register_application(app_aqm, aqm_exec, app_aqm_synopsis, app_aqm_descrip);
05658    res |= ast_register_application(app_rqm, rqm_exec, app_rqm_synopsis, app_rqm_descrip);
05659    res |= ast_register_application(app_pqm, pqm_exec, app_pqm_synopsis, app_pqm_descrip);
05660    res |= ast_register_application(app_upqm, upqm_exec, app_upqm_synopsis, app_upqm_descrip);
05661    res |= ast_register_application(app_ql, ql_exec, app_ql_synopsis, app_ql_descrip);
05662    res |= ast_manager_register("Queues", 0, manager_queues_show, "Queues");
05663    res |= ast_manager_register("QueueStatus", 0, manager_queues_status, "Queue Status");
05664    res |= ast_manager_register("QueueMemberCount", 0, manager_queue_member_count, "Queue Member Count");
05665    res |= ast_manager_register("QueueAdd", EVENT_FLAG_AGENT, manager_add_queue_member, "Add interface to queue.");
05666    res |= ast_manager_register("QueueRemove", EVENT_FLAG_AGENT, manager_remove_queue_member, "Remove interface from queue.");
05667    res |= ast_manager_register("QueuePause", EVENT_FLAG_AGENT, manager_pause_queue_member, "Makes a queue member temporarily unavailable");
05668    res |= ast_custom_function_register(&queueagentcount_function);
05669    res |= ast_custom_function_register(&queuemembercount_function);
05670    res |= ast_custom_function_register(&queuememberlist_function);
05671    res |= ast_custom_function_register(&queuewaitingcount_function);
05672    res |= ast_devstate_add(statechange_queue, NULL);
05673 
05674    return res;
05675 }

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 1465 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_queuemembercount(), queue_member_count(), and reload_queue_members().

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

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

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

05152 {
05153    const char *queuename, *interface, *penalty_s, *paused_s, *membername, *state_interface;
05154    int paused, penalty = 0;
05155 
05156    queuename = astman_get_header(m, "Queue");
05157    interface = astman_get_header(m, "Interface");
05158    penalty_s = astman_get_header(m, "Penalty");
05159    paused_s = astman_get_header(m, "Paused");
05160    membername = astman_get_header(m, "MemberName");
05161    state_interface = astman_get_header(m, "StateInterface");
05162 
05163    if (ast_strlen_zero(queuename)) {
05164       astman_send_error(s, m, "'Queue' not specified.");
05165       return 0;
05166    }
05167 
05168    if (ast_strlen_zero(interface)) {
05169       astman_send_error(s, m, "'Interface' not specified.");
05170       return 0;
05171    }
05172 
05173    if (ast_strlen_zero(penalty_s))
05174       penalty = 0;
05175    else if (sscanf(penalty_s, "%30d", &penalty) != 1 || penalty < 0)
05176       penalty = 0;
05177 
05178    if (ast_strlen_zero(paused_s))
05179       paused = 0;
05180    else
05181       paused = abs(ast_true(paused_s));
05182 
05183    switch (add_to_queue(queuename, interface, membername, penalty, paused, queue_persistent_members, state_interface)) {
05184    case RES_OKAY:
05185       ast_queue_log(queuename, "MANAGER", interface, "ADDMEMBER", "%s", "");
05186       astman_send_ack(s, m, "Added interface to queue");
05187       break;
05188    case RES_EXISTS:
05189       astman_send_error(s, m, "Unable to add interface: Already there");
05190       break;
05191    case RES_NOSUCHQUEUE:
05192       astman_send_error(s, m, "Unable to add interface to queue: No such queue");
05193       break;
05194    case RES_OUTOFMEMORY:
05195       astman_send_error(s, m, "Out of memory");
05196       break;
05197    }
05198 
05199    return 0;
05200 }

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

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

05237 {
05238    const char *queuename, *interface, *paused_s;
05239    int paused;
05240 
05241    interface = astman_get_header(m, "Interface");
05242    paused_s = astman_get_header(m, "Paused");
05243    queuename = astman_get_header(m, "Queue");   /* Optional - if not supplied, pause the given Interface in all queues */
05244 
05245    if (ast_strlen_zero(interface) || ast_strlen_zero(paused_s)) {
05246       astman_send_error(s, m, "Need 'Interface' and 'Paused' parameters.");
05247       return 0;
05248    }
05249 
05250    paused = abs(ast_true(paused_s));
05251 
05252    if (set_member_paused(queuename, interface, paused))
05253       astman_send_error(s, m, "Interface not found");
05254    else
05255       astman_send_ack(s, m, paused ? "Interface paused successfully" : "Interface unpaused successfully");
05256    return 0;
05257 }

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

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

05497 {
05498    char buffer[256] = "";
05499    const char *queuename = astman_get_header(m,"Queue");
05500 
05501    if (ast_strlen_zero(queuename)) {
05502          astman_send_error(s, m, "'Queue' not specified.");
05503          return 0;
05504    }
05505    if (qmc_handler(queuename, buffer, sizeof(buffer)) == RESULT_SUCCESS) {
05506          astman_send_ack(s, m, buffer);
05507          return RESULT_SUCCESS;
05508    } else {
05509          astman_send_error(s, m, "Queue not found.");
05510          return 0;
05511    }
05512 }

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

Definition at line 5045 of file app_queue.c.

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

Referenced by load_module().

05046 {
05047    char *a[] = { "queue", "show" };
05048 
05049    __queues_show(s, 1, -1, 2, a);
05050    astman_append(s, "\r\n\r\n"); /* Properly terminate Manager output */
05051 
05052    return RESULT_SUCCESS;
05053 }

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

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

05057 {
05058    time_t now;
05059    int pos;
05060    const char *id = astman_get_header(m,"ActionID");
05061    const char *queuefilter = astman_get_header(m,"Queue");
05062    const char *memberfilter = astman_get_header(m,"Member");
05063    char idText[256] = "";
05064    struct call_queue *q;
05065    struct queue_ent *qe;
05066    float sl = 0;
05067    struct member *mem;
05068    struct ao2_iterator mem_iter;
05069 
05070    astman_send_ack(s, m, "Queue status will follow");
05071    time(&now);
05072    AST_LIST_LOCK(&queues);
05073    if (!ast_strlen_zero(id))
05074       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
05075 
05076    AST_LIST_TRAVERSE(&queues, q, list) {
05077       ao2_lock(q);
05078 
05079       /* List queue properties */
05080       if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) {
05081          sl = ((q->callscompleted > 0) ? 100 * ((float)q->callscompletedinsl / (float)q->callscompleted) : 0);
05082          astman_append(s, "Event: QueueParams\r\n"
05083             "Queue: %s\r\n"
05084             "Max: %d\r\n"
05085             "Calls: %d\r\n"
05086             "Holdtime: %d\r\n"
05087             "Completed: %d\r\n"
05088             "Abandoned: %d\r\n"
05089             "ServiceLevel: %d\r\n"
05090             "ServicelevelPerf: %2.1f\r\n"
05091             "RingLimit: %d\r\n"
05092             "Weight: %d\r\n"
05093             "%s"
05094             "\r\n",
05095             q->name, q->maxlen, q->count, q->holdtime, q->callscompleted,
05096             q->callsabandoned, q->servicelevel, sl,  q->ringlimit, q->weight, idText);
05097          /* List Queue Members */
05098          mem_iter = ao2_iterator_init(q->members, 0);
05099          while ((mem = ao2_iterator_next(&mem_iter))) {
05100             if (ast_strlen_zero(memberfilter) || !strcmp(mem->interface, memberfilter)) {
05101                astman_append(s, "Event: QueueMember\r\n"
05102                   "Queue: %s\r\n"
05103                   "Name: %s\r\n"
05104                   "Location: %s\r\n"
05105                   "Membership: %s\r\n"
05106                   "Penalty: %d\r\n"
05107                   "CallsTaken: %d\r\n"
05108                   "LastCall: %d\r\n"
05109                   "Status: %d\r\n"
05110                   "Paused: %d\r\n"
05111                   "%s"
05112                   "\r\n",
05113                   q->name, mem->membername, mem->interface, mem->dynamic ? "dynamic" : "static",
05114                   mem->penalty, mem->calls, (int)mem->lastcall, mem->status, mem->paused, idText);
05115             }
05116             ao2_ref(mem, -1);
05117          }
05118          ao2_iterator_destroy(&mem_iter);
05119          /* List Queue Entries */
05120          pos = 1;
05121          for (qe = q->head; qe; qe = qe->next) {
05122             astman_append(s, "Event: QueueEntry\r\n"
05123                "Queue: %s\r\n"
05124                "Position: %d\r\n"
05125                "Channel: %s\r\n"
05126                "CallerID: %s\r\n"
05127                "CallerIDName: %s\r\n"
05128                "Wait: %ld\r\n"
05129                "%s"
05130                "\r\n",
05131                q->name, pos++, qe->chan->name,
05132                S_OR(qe->chan->cid.cid_num, "unknown"),
05133                S_OR(qe->chan->cid.cid_name, "unknown"),
05134                (long) (now - qe->start), idText);
05135          }
05136       }
05137       ao2_unlock(q);
05138    }
05139 
05140    astman_append(s,
05141       "Event: QueueStatusComplete\r\n"
05142       "%s"
05143       "\r\n",idText);
05144 
05145    AST_LIST_UNLOCK(&queues);
05146 
05147 
05148    return RESULT_SUCCESS;
05149 }

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

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

05203 {
05204    const char *queuename, *interface;
05205 
05206    queuename = astman_get_header(m, "Queue");
05207    interface = astman_get_header(m, "Interface");
05208 
05209    if (ast_strlen_zero(queuename) || ast_strlen_zero(interface)) {
05210       astman_send_error(s, m, "Need 'Queue' and 'Interface' parameters.");
05211       return 0;
05212    }
05213 
05214    switch (remove_from_queue(queuename, interface)) {
05215    case RES_OKAY:
05216       ast_queue_log(queuename, "MANAGER", interface, "REMOVEMEMBER", "%s", "");
05217       astman_send_ack(s, m, "Removed interface from queue");
05218       break;
05219    case RES_EXISTS:
05220       astman_send_error(s, m, "Unable to remove interface: Not there");
05221       break;
05222    case RES_NOSUCHQUEUE:
05223       astman_send_error(s, m, "Unable to remove interface from queue: No such queue");
05224       break;
05225    case RES_OUTOFMEMORY:
05226       astman_send_error(s, m, "Out of memory");
05227       break;
05228    case RES_NOT_DYNAMIC:
05229       astman_send_error(s, m, "Member not dynamic");
05230       break;
05231    }
05232 
05233    return 0;
05234 }

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

Definition at line 862 of file app_queue.c.

References member::interface.

Referenced by init_queue().

00863 {
00864    struct member *mem1 = obj1, *mem2 = obj2;
00865    return strcmp(mem1->interface, mem2->interface) ? 0 : CMP_MATCH | CMP_STOP;
00866 }

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

Definition at line 850 of file app_queue.c.

References compress_char(), and member::interface.

Referenced by init_queue().

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

static void monjoin_dep_warning ( void   )  [static]

Definition at line 481 of file app_queue.c.

References ast_log(), and LOG_NOTICE.

Referenced by queue_set_param().

00482 {
00483    static unsigned int warned = 0;
00484    if (!warned) {
00485       ast_log(LOG_NOTICE, "The 'monitor-join' queue option is deprecated. Please use monitor-type=mixmonitor instead.\n");
00486       warned = 1;
00487    }
00488 }

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

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

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

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

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

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

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

03826 {
03827    struct ast_module_user *lu;
03828    char *parse;
03829    int priority_jump = 0;
03830    int ignore_fail = 0;
03831    AST_DECLARE_APP_ARGS(args,
03832       AST_APP_ARG(queuename);
03833       AST_APP_ARG(interface);
03834       AST_APP_ARG(options);
03835    );
03836 
03837    if (ast_strlen_zero(data)) {
03838       ast_log(LOG_WARNING, "PauseQueueMember requires an argument ([queuename]|interface[|options])\n");
03839       return -1;
03840    }
03841 
03842    parse = ast_strdupa(data);
03843 
03844    AST_STANDARD_APP_ARGS(args, parse);
03845 
03846    lu = ast_module_user_add(chan);
03847 
03848    if (args.options) {
03849       if (strchr(args.options, 'j'))
03850          priority_jump = 1;
03851       if (strchr(args.options, 'i'))
03852          ignore_fail = 1;
03853    }
03854 
03855    if (ast_strlen_zero(args.interface)) {
03856       ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename]|interface[|options])\n");
03857       ast_module_user_remove(lu);
03858       return -1;
03859    }
03860 
03861    if (set_member_paused(args.queuename, args.interface, 1)) {
03862       ast_log(LOG_WARNING, "Attempt to pause interface %s, not found\n", args.interface);
03863       pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND");
03864       if (priority_jump || ast_opt_priority_jumping) {
03865          if (!ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101)) {
03866             ast_module_user_remove(lu);
03867             return 0;
03868          }
03869       }
03870       ast_module_user_remove(lu);
03871       if (ignore_fail) {
03872          return 0;
03873       } else {
03874          return -1;
03875       }
03876    }
03877 
03878    ast_module_user_remove(lu);
03879    pbx_builtin_setvar_helper(chan, "PQMSTATUS", "PAUSED");
03880    return 0;
03881 }

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

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

04084 {
04085    struct ast_module_user *u;
04086    char *parse;
04087 
04088    AST_DECLARE_APP_ARGS(args,
04089       AST_APP_ARG(queuename);
04090       AST_APP_ARG(uniqueid);
04091       AST_APP_ARG(membername);
04092       AST_APP_ARG(event);
04093       AST_APP_ARG(params);
04094    );
04095 
04096    if (ast_strlen_zero(data)) {
04097       ast_log(LOG_WARNING, "QueueLog requires arguments (queuename|uniqueid|membername|event[|additionalinfo]\n");
04098       return -1;
04099    }
04100 
04101    u = ast_module_user_add(chan);
04102 
04103    parse = ast_strdupa(data);
04104 
04105    AST_STANDARD_APP_ARGS(args, parse);
04106 
04107    if (ast_strlen_zero(args.queuename) || ast_strlen_zero(args.uniqueid)
04108        || ast_strlen_zero(args.membername) || ast_strlen_zero(args.event)) {
04109       ast_log(LOG_WARNING, "QueueLog requires arguments (queuename|uniqueid|membername|event[|additionalinfo])\n");
04110       ast_module_user_remove(u);
04111       return -1;
04112    }
04113 
04114    ast_queue_log(args.queuename, args.uniqueid, args.membername, args.event, 
04115       "%s", args.params ? args.params : "");
04116 
04117    ast_module_user_remove(u);
04118 
04119    return 0;
04120 }

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

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

05482 {
05483       struct member_count qmc;
05484       memset(&qmc, 0, sizeof(qmc));
05485       if (queue_member_count(queuename, &qmc) != 0) {
05486             return RESULT_FAILURE; 
05487       } else {
05488             snprintf(buffer, len, 
05489                          "valid:%d inuse:%d paused:%d active:%d free:%d all:%d",
05490                          qmc.valid, qmc.inuse, qmc.paused, qmc.active, qmc.free, qmc.all);   
05491             return RESULT_SUCCESS;
05492       }
05493 }

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

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

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

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

04413 {
04414    int count = 0;
04415    struct call_queue *q;
04416    struct ast_module_user *lu;
04417    struct member *m;
04418    struct ao2_iterator mem_iter;
04419    char *name, *item;
04420    enum qmc_status mode = QMC_VALID;
04421 
04422    buf[0] = '\0';
04423    
04424    if (ast_strlen_zero(data)) {
04425       ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
04426       return -1;
04427    }
04428 
04429    name = ast_strdupa(data);
04430 
04431    lu = ast_module_user_add(chan);
04432 
04433    if ((item = strchr(name, ':'))) {
04434       *item = '\0';
04435       item++;
04436    } else {
04437       item = "";
04438    }
04439 
04440    if (!strcasecmp(item, "valid")) {
04441       mode = QMC_VALID;
04442    } else  if (!strcasecmp(item, "paused")) {
04443       mode = QMC_PAUSED;
04444    } else  if (!strcasecmp(item, "active")) {
04445       mode = QMC_ACTIVE;
04446    } else  if (!strcasecmp(item, "free")) {
04447       mode = QMC_FREE;
04448    } else  if (!strcasecmp(item, "all")) {
04449       mode = QMC_ALL;
04450    }
04451 
04452    if ((q = load_realtime_queue(name))) {
04453       ao2_lock(q);
04454       mem_iter = ao2_iterator_init(q->members, 0);
04455       while ((m = ao2_iterator_next(&mem_iter))) {
04456          switch (mode) {
04457          case QMC_VALID:
04458             /* Count the queue members who are logged in and presently answering calls */
04459             if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
04460                count++;
04461             }
04462             break;
04463          case QMC_PAUSED:
04464             /* Count paused members */
04465             if (m->paused) {
04466                count++;
04467             }
04468             break;
04469          case QMC_ACTIVE:
04470             /* Count not paused members who are logged in and presently answering calls */
04471             if (!m->paused && (m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
04472                count++;
04473             }
04474             break;
04475          case QMC_FREE:
04476             /* Count free members in the queue */
04477             if (!m->paused && ((m->status == AST_DEVICE_UNKNOWN) || (m->status == AST_DEVICE_NOT_INUSE))) {
04478                count++;
04479             }
04480             break;
04481          default:
04482             count++;
04483             break;
04484          }
04485          ao2_ref(m, -1);
04486       }
04487       ao2_iterator_destroy(&mem_iter);
04488       ao2_unlock(q);
04489    } else
04490       ast_log(LOG_WARNING, "queue %s was not found\n", name);
04491 
04492    snprintf(buf, len, "%d", count);
04493    ast_module_user_remove(lu);
04494 
04495    return 0;
04496 }

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

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

04542 {
04543    struct ast_module_user *u;
04544    struct call_queue *q;
04545    struct member *m;
04546 
04547    /* Ensure an otherwise empty list doesn't return garbage */
04548    buf[0] = '\0';
04549 
04550    if (ast_strlen_zero(data)) {
04551       ast_log(LOG_ERROR, "QUEUE_MEMBER_LIST requires an argument: queuename\n");
04552       return -1;
04553    }
04554    
04555    u = ast_module_user_add(chan);
04556 
04557    AST_LIST_LOCK(&queues);
04558    AST_LIST_TRAVERSE(&queues, q, list) {
04559       if (!strcasecmp(q->name, data)) {
04560          ao2_lock(q);
04561          break;
04562       }
04563    }
04564    AST_LIST_UNLOCK(&queues);
04565 
04566    if (q) {
04567       int buflen = 0, count = 0;
04568       struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
04569 
04570       while ((m = ao2_iterator_next(&mem_iter))) {
04571          /* strcat() is always faster than printf() */
04572          if (count++) {
04573             strncat(buf + buflen, ",", len - buflen - 1);
04574             buflen++;
04575          }
04576          strncat(buf + buflen, m->interface, len - buflen - 1);
04577          buflen += strlen(m->interface);
04578          /* Safeguard against overflow (negative length) */
04579          if (buflen >= len - 2) {
04580             ao2_ref(m, -1);
04581             ast_log(LOG_WARNING, "Truncating list\n");
04582             break;
04583          }
04584          ao2_ref(m, -1);
04585       }
04586       ao2_iterator_destroy(&mem_iter);
04587       ao2_unlock(q);
04588    } else
04589       ast_log(LOG_WARNING, "queue %s was not found\n", data);
04590 
04591    /* We should already be terminated, but let's make sure. */
04592    buf[len - 1] = '\0';
04593    ast_module_user_remove(u);
04594 
04595    return 0;
04596 }

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

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

04499 {
04500    int count = 0;
04501    struct call_queue *q;
04502    struct ast_module_user *lu;
04503    struct ast_variable *var = NULL;
04504 
04505    buf[0] = '\0';
04506    
04507    if (ast_strlen_zero(data)) {
04508       ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
04509       return -1;
04510    }
04511 
04512    lu = ast_module_user_add(chan);
04513    
04514    AST_LIST_LOCK(&queues);
04515    AST_LIST_TRAVERSE(&queues, q, list) {
04516       if (!strcasecmp(q->name, data)) {
04517          ao2_lock(q);
04518          break;
04519       }
04520    }
04521    AST_LIST_UNLOCK(&queues);
04522 
04523    if (q) {
04524       count = q->count;
04525       ao2_unlock(q);
04526    } else if ((var = ast_load_realtime("queues", "name", data, NULL))) {
04527       /* if the queue is realtime but was not found in memory, this
04528        * means that the queue had been deleted from memory since it was 
04529        * "dead." This means it has a 0 waiting count
04530        */
04531       count = 0;
04532       ast_variables_destroy(var);
04533    } else
04534       ast_log(LOG_WARNING, "queue %s was not found\n", data);
04535 
04536    snprintf(buf, len, "%d", count);
04537    ast_module_user_remove(lu);
04538    return 0;
04539 }

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

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

05440 {
05441       int res = 0;
05442       struct call_queue *q;
05443       struct member *m;
05444       struct ao2_iterator mem_iter;
05445       
05446       if ((q = load_realtime_queue(qname))) {
05447             ao2_lock(q);
05448             mem_iter = ao2_iterator_init(q->members, 0);
05449             while ((m = ao2_iterator_next(&mem_iter))) {
05450                   /* Count the queue members in use */
05451                   if (m->status == AST_DEVICE_INUSE) {
05452                         qmc->inuse++;
05453                   }
05454                   /* Count the queue members who are logged in and presently answering calls */
05455                   if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
05456                         qmc->valid++;
05457                   }
05458                   /* Count paused members */
05459                   if (m->paused) {
05460                         qmc->paused++;
05461                   }
05462                   /* Count not paused members who are logged in and presently answering calls */
05463                   if (!m->paused && (m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
05464                         qmc->active++;
05465                   }
05466                   /* Count free members in the queue */
05467                   if (!m->paused && ((m->status == AST_DEVICE_UNKNOWN) || (m->status == AST_DEVICE_NOT_INUSE))) {
05468                         qmc->free++;
05469                   }
05470                   qmc->all++;
05471                   ao2_ref(m, -1);
05472             }
05473             ao2_unlock(q);
05474       } else {
05475             ast_log(LOG_WARNING, "Queue %s was not found\n", qname);
05476             res = -1;
05477       }
05478       return res;
05479 }

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

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

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

Definition at line 5011 of file app_queue.c.

References __queues_show().

Referenced by __queues_show().

05012 {
05013    return __queues_show(NULL, 0, fd, argc, argv);
05014 }

static void queue_transfer_destroy ( void *  data  )  [static]

Definition at line 2781 of file app_queue.c.

References ast_free.

02782 {
02783    struct queue_transfer_ds *qtds = data;
02784    ast_free(qtds);
02785 }

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

02805 {
02806    struct queue_transfer_ds *qtds = data;
02807    struct queue_ent *qe = qtds->qe;
02808    struct member *member = qtds->member;
02809    time_t callstart = qtds->starttime;
02810    int callcompletedinsl = qtds->callcompletedinsl;
02811    struct ast_datastore *datastore;
02812 
02813    ast_queue_log(qe->parent->name, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld",
02814             new_chan->exten, new_chan->context, (long) (callstart - qe->start),
02815             (long) (time(NULL) - callstart));
02816 
02817    update_queue(qe->parent, member, callcompletedinsl);
02818    
02819    /* No need to lock the channels because they are already locked in ast_do_masquerade */
02820    if ((datastore = ast_channel_datastore_find(old_chan, &queue_transfer_info, NULL))) {
02821       ast_channel_datastore_remove(old_chan, datastore);
02822    } else {
02823       ast_log(LOG_WARNING, "Can't find the queue_transfer datastore.\n");
02824    }
02825 }

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

Definition at line 1745 of file app_queue.c.

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

Referenced by try_calling().

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

static void record_abandoned ( struct queue_ent qe  )  [static]

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

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

static int reload ( void   )  [static]

Definition at line 5677 of file app_queue.c.

References reload_queues().

05678 {
05679    reload_queues();
05680    return 0;
05681 }

static void reload_queue_members ( void   )  [static]

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

03729 {
03730    char *cur_ptr;
03731    char *queue_name;
03732    char *member;
03733    char *interface;
03734    char *membername = NULL;
03735    char *state_interface;
03736    char *penalty_tok;
03737    int penalty = 0;
03738    char *paused_tok;
03739    int paused = 0;
03740    struct ast_db_entry *db_tree;
03741    struct ast_db_entry *entry;
03742    struct call_queue *cur_queue;
03743    char queue_data[PM_MAX_LEN];
03744 
03745    AST_LIST_LOCK(&queues);
03746 
03747    /* Each key in 'pm_family' is the name of a queue */
03748    db_tree = ast_db_gettree(pm_family, NULL);
03749    for (entry = db_tree; entry; entry = entry->next) {
03750 
03751       queue_name = entry->key + strlen(pm_family) + 2;
03752 
03753       AST_LIST_TRAVERSE(&queues, cur_queue, list) {
03754          ao2_lock(cur_queue);
03755          if (!strcmp(queue_name, cur_queue->name))
03756             break;
03757          ao2_unlock(cur_queue);
03758       }
03759       
03760       if (!cur_queue)
03761          cur_queue = load_realtime_queue(queue_name);
03762 
03763       if (!cur_queue) {
03764          /* If the queue no longer exists, remove it from the
03765           * database */
03766          ast_log(LOG_WARNING, "Error loading persistent queue: '%s': it does not exist\n", queue_name);
03767          ast_db_del(pm_family, queue_name);
03768          continue;
03769       } else
03770          ao2_unlock(cur_queue);
03771 
03772       if (ast_db_get(pm_family, queue_name, queue_data, PM_MAX_LEN))
03773          continue;
03774 
03775       cur_ptr = queue_data;
03776       while ((member = strsep(&cur_ptr, "|"))) {
03777          if (ast_strlen_zero(member))
03778             continue;
03779 
03780          interface = strsep(&member, ";");
03781          penalty_tok = strsep(&member, ";");
03782          paused_tok = strsep(&member, ";");
03783          membername = strsep(&member, ";");
03784          state_interface = strsep(&member,";");
03785 
03786          if (!penalty_tok) {
03787             ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (penalty)\n", queue_name);
03788             break;
03789          }
03790          penalty = strtol(penalty_tok, NULL, 10);
03791          if (errno == ERANGE) {
03792             ast_log(LOG_WARNING, "Error converting penalty: %s: Out of range.\n", penalty_tok);
03793             break;
03794          }
03795          
03796          if (!paused_tok) {
03797             ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (paused)\n", queue_name);
03798             break;
03799          }
03800          paused = strtol(paused_tok, NULL, 10);
03801          if ((errno == ERANGE) || paused < 0 || paused > 1) {
03802             ast_log(LOG_WARNING, "Error converting paused: %s: Expected 0 or 1.\n", paused_tok);
03803             break;
03804          }
03805          if (ast_strlen_zero(membername))
03806             membername = interface;
03807 
03808          if (option_debug)
03809             ast_log(LOG_DEBUG, "Reload Members: Queue: %s  Member: %s  Name: %s  Penalty: %d  Paused: %d\n", queue_name, interface, membername, penalty, paused);
03810          
03811          if (add_to_queue(queue_name, interface, membername, penalty, paused, 0, state_interface) == RES_OUTOFMEMORY) {
03812             ast_log(LOG_ERROR, "Out of Memory when reloading persistent queue member\n");
03813             break;
03814          }
03815       }
03816    }
03817 
03818    AST_LIST_UNLOCK(&queues);
03819    if (db_tree) {
03820       ast_log(LOG_NOTICE, "Queue members successfully reloaded from database.\n");
03821       ast_db_freetree(db_tree);
03822    }
03823 }

static int reload_queues ( void   )  [static]

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

04642 {
04643    struct call_queue *q;
04644    struct ast_config *cfg;
04645    char *cat, *tmp;
04646    struct ast_variable *var;
04647    struct member *cur, *newm;
04648    struct ao2_iterator mem_iter;
04649    int new;
04650    const char *general_val = NULL;
04651    char *parse;
04652    char *interface, *state_interface;
04653    char *membername = NULL;
04654    int penalty;
04655    AST_DECLARE_APP_ARGS(args,
04656       AST_APP_ARG(interface);
04657       AST_APP_ARG(penalty);
04658       AST_APP_ARG(membername);
04659       AST_APP_ARG(state_interface);
04660    );
04661    
04662    if (!(cfg = ast_config_load("queues.conf"))) {
04663       ast_log(LOG_NOTICE, "No call queueing config file (queues.conf), so no call queues\n");
04664       return 0;
04665    }
04666    AST_LIST_LOCK(&queues);
04667    use_weight=0;
04668    /* Mark all non-realtime queues as dead for the moment */
04669    AST_LIST_TRAVERSE(&queues, q, list) {
04670       if (!q->realtime) {
04671          q->dead = 1;
04672          q->found = 0;
04673       }
04674    }
04675 
04676    /* Chug through config file */
04677    cat = NULL;
04678    while ((cat = ast_category_browse(cfg, cat)) ) {
04679       if (!strcasecmp(cat, "general")) {  
04680          /* Initialize global settings */
04681          queue_debug = 0;
04682          if ((general_val = ast_variable_retrieve(cfg, "general", "debug")))
04683             queue_debug = ast_true(general_val);
04684          queue_persistent_members = 0;
04685          if ((general_val = ast_variable_retrieve(cfg, "general", "persistentmembers")))
04686             queue_persistent_members = ast_true(general_val);
04687          autofill_default = 0;
04688          if ((general_val = ast_variable_retrieve(cfg, "general", "autofill")))
04689             autofill_default = ast_true(general_val);
04690          montype_default = 0;
04691          if ((general_val = ast_variable_retrieve(cfg, "general", "monitor-type")))
04692             if (!strcasecmp(general_val, "mixmonitor"))
04693                montype_default = 1;
04694       } else { /* Define queue */
04695          /* Look for an existing one */
04696          AST_LIST_TRAVERSE(&queues, q, list) {
04697             if (!strcmp(q->name, cat))
04698                break;
04699          }
04700          if (!q) {
04701             /* Make one then */
04702             if (!(q = alloc_queue(cat))) {
04703                /* TODO: Handle memory allocation failure */
04704             }
04705             new = 1;
04706          } else
04707             new = 0;
04708          if (q) {
04709             const char *tmpvar;
04710             if (!new)
04711                ao2_lock(q);
04712             /* Check if a queue with this name already exists */
04713             if (q->found) {
04714                ast_log(LOG_WARNING, "Queue '%s' already defined! Skipping!\n", cat);
04715                if (!new)
04716                   ao2_unlock(q);
04717                continue;
04718             }
04719 
04720             /* Due to the fact that the "rrordered" strategy will have a different allocation
04721              * scheme for queue members, we must devise the queue's strategy before other initializations.
04722              * To be specific, the rrordered strategy needs to function like a linked list, meaning the ao2
04723              * container used will have only a single bucket instead of the typical number.
04724              */
04725             if ((tmpvar = ast_variable_retrieve(cfg, cat, "strategy"))) {
04726                q->strategy = strat2int(tmpvar);
04727                if (q->strategy < 0) {
04728                   ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n", tmpvar, q->name);
04729                   q->strategy = QUEUE_STRATEGY_RINGALL;
04730                }
04731             } else {
04732                q->strategy = QUEUE_STRATEGY_RINGALL;
04733             }
04734 
04735             /* Re-initialize the queue, and clear statistics */
04736             init_queue(q);
04737             clear_queue(q);
04738             mem_iter = ao2_iterator_init(q->members, 0);
04739             while ((cur = ao2_iterator_next(&mem_iter))) {
04740                if (!cur->dynamic) {
04741                   cur->delme = 1;
04742                }
04743                ao2_ref(cur, -1);
04744             }
04745             ao2_iterator_destroy(&mem_iter);
04746             for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
04747                if (!strcasecmp(var->name, "member")) {
04748                   struct member tmpmem;
04749                   membername = NULL;
04750 
04751                   if (ast_strlen_zero(var->value)) {
04752                      ast_log(LOG_WARNING, "Empty queue member definition at line %d. Moving on!\n", var->lineno);
04753                      continue;
04754                   }
04755 
04756                   /* Add a new member */
04757                   if (!(parse = ast_strdup(var->value))) {
04758                      continue;
04759                   }
04760                   
04761                   AST_NONSTANDARD_APP_ARGS(args, parse, ',');
04762 
04763                   interface = args.interface;
04764                   if (!ast_strlen_zero(args.penalty)) {
04765                      tmp = ast_skip_blanks(args.penalty);
04766                      penalty = atoi(tmp);
04767                      if (penalty < 0) {
04768                         penalty = 0;
04769                      }
04770                   } else
04771                      penalty = 0;
04772 
04773                   if (!ast_strlen_zero(args.membername)) {
04774                      membername = ast_skip_blanks(args.membername);
04775                   }
04776 
04777                   if (!ast_strlen_zero(args.state_interface)) {
04778                      state_interface = ast_skip_blanks(args.state_interface);
04779                   } else {
04780                      state_interface = interface;
04781                   }
04782 
04783                   /* Find the old position in the list */
04784                   ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
04785                   cur = ao2_find(q->members, &tmpmem, OBJ_POINTER | OBJ_UNLINK);
04786 
04787                   /* Only attempt removing from interfaces list if the new state_interface is different than the old one */
04788                   if (cur && strcasecmp(cur->state_interface, state_interface)) {
04789                      remove_from_interfaces(cur->state_interface);
04790                   }
04791 
04792                   newm = create_queue_member(interface, membername, penalty, cur ? cur->paused : 0, state_interface);
04793                   if (!cur || (cur && strcasecmp(cur->state_interface, state_interface))) {
04794                      add_to_interfaces(state_interface);
04795                   }
04796                   ao2_link(q->members, newm);
04797                   ao2_ref(newm, -1);
04798                   newm = NULL;
04799 
04800                   if (cur)
04801                      ao2_ref(cur, -1);
04802                   else {
04803                      q->membercount++;
04804                   }
04805                   ast_free(parse);
04806                } else {
04807                   queue_set_param(q, var->name, var->value, var->lineno, 1);
04808                }
04809             }
04810 
04811             /* Free remaining members marked as delme */
04812             mem_iter = ao2_iterator_init(q->members, 0);
04813             while ((cur = ao2_iterator_next(&mem_iter))) {
04814                if (! cur->delme) {
04815                   ao2_ref(cur, -1);
04816                   continue;
04817                }
04818 
04819                q->membercount--;
04820                ao2_unlink(q->members, cur);
04821                remove_from_interfaces(cur->state_interface);
04822                ao2_ref(cur, -1);
04823             }
04824             ao2_iterator_destroy(&mem_iter);
04825 
04826             if (q->strategy == QUEUE_STRATEGY_ROUNDROBIN)
04827                rr_dep_warning();
04828 
04829             if (new) {
04830                AST_LIST_INSERT_HEAD(&queues, q, list);
04831             } else
04832                ao2_unlock(q);
04833          }
04834       }
04835    }
04836    ast_config_destroy(cfg);
04837    AST_LIST_TRAVERSE_SAFE_BEGIN(&queues, q, list) {
04838       if (q->dead) {
04839          AST_LIST_REMOVE_CURRENT(&queues, list);
04840          ao2_ref(q, -1);
04841       } else {
04842          ao2_lock(q);
04843          mem_iter = ao2_iterator_init(q->members, 0);
04844          while ((cur = ao2_iterator_next(&mem_iter))) {
04845             if (cur->dynamic)
04846                q->membercount++;
04847             cur->status = ast_device_state(cur->state_interface);
04848             ao2_ref(cur, -1);
04849          }
04850          ao2_iterator_destroy(&mem_iter);
04851          ao2_unlock(q);
04852       }
04853    }
04854    AST_LIST_TRAVERSE_SAFE_END;
04855    AST_LIST_UNLOCK(&queues);
04856    return 1;
04857 }

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

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

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

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

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

03577 {
03578    struct call_queue *q;
03579    struct member *mem, tmpmem;
03580    int res = RES_NOSUCHQUEUE;
03581 
03582    ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
03583 
03584    AST_LIST_LOCK(&queues);
03585    AST_LIST_TRAVERSE(&queues, q, list) {
03586       ao2_lock(q);
03587       if (strcmp(q->name, queuename)) {
03588          ao2_unlock(q);
03589          continue;
03590       }
03591 
03592       if ((mem = ao2_find(q->members, &tmpmem, OBJ_POINTER))) {
03593          /* XXX future changes should beware of this assumption!! */
03594          if (!mem->dynamic) {
03595             res = RES_NOT_DYNAMIC;
03596             ao2_ref(mem, -1);
03597             ao2_unlock(q);
03598             break;
03599          }
03600          q->membercount--;
03601          manager_event(EVENT_FLAG_AGENT, "QueueMemberRemoved",
03602             "Queue: %s\r\n"
03603             "Location: %s\r\n"
03604             "MemberName: %s\r\n",
03605             q->name, mem->interface, mem->membername);
03606          ao2_unlink(q->members, mem);
03607          remove_from_interfaces(mem->state_interface);
03608          ao2_ref(mem, -1);
03609 
03610          if (queue_persistent_members)
03611             dump_queue_members(q);
03612          
03613          res = RES_OKAY;
03614       } else {
03615          res = RES_EXISTS;
03616       }
03617       ao2_unlock(q);
03618       break;
03619    }
03620 
03621    AST_LIST_UNLOCK(&queues);
03622 
03623    return res;
03624 }

static void remove_queue ( struct call_queue q  )  [static]

removes a call_queue from the list of call_queues

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

00530 {
00531    AST_LIST_LOCK(&queues);
00532    if (AST_LIST_REMOVE(&queues, q, list)) {
00533       ao2_ref(q, -1);
00534    }
00535    AST_LIST_UNLOCK(&queues);
00536 }

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

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

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

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

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

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

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

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

03942 {
03943    int res=-1;
03944    struct ast_module_user *lu;
03945    char *parse, *temppos = NULL;
03946    int priority_jump = 0;
03947    AST_DECLARE_APP_ARGS(args,
03948       AST_APP_ARG(queuename);
03949       AST_APP_ARG(interface);
03950       AST_APP_ARG(options);
03951    );
03952 
03953 
03954    if (ast_strlen_zero(data)) {
03955       ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename[|interface[|options]])\n");
03956       return -1;
03957    }
03958 
03959    parse = ast_strdupa(data);
03960 
03961    AST_STANDARD_APP_ARGS(args, parse);
03962 
03963    lu = ast_module_user_add(chan);
03964 
03965    if (ast_strlen_zero(args.interface)) {
03966       args.interface = ast_strdupa(chan->name);
03967       temppos = strrchr(args.interface, '-');
03968       if (temppos)
03969          *temppos = '\0';
03970    }
03971 
03972    if (args.options) {
03973       if (strchr(args.options, 'j'))
03974          priority_jump = 1;
03975    }
03976 
03977    switch (remove_from_queue(args.queuename, args.interface)) {
03978    case RES_OKAY:
03979       ast_queue_log(args.queuename, chan->uniqueid, args.interface, "REMOVEMEMBER", "%s", "");
03980       ast_log(LOG_NOTICE, "Removed interface '%s' from queue '%s'\n", args.interface, args.queuename);
03981       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "REMOVED");
03982       res = 0;
03983       break;
03984    case RES_EXISTS:
03985       ast_log(LOG_DEBUG, "Unable to remove interface '%s' from queue '%s': Not there\n", args.interface, args.queuename);
03986       if (priority_jump || ast_opt_priority_jumping)
03987          ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
03988       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTINQUEUE");
03989       res = 0;
03990       break;
03991    case RES_NOSUCHQUEUE:
03992       ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", args.queuename);
03993       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOSUCHQUEUE");
03994       res = 0;
03995       break;
03996    case RES_NOT_DYNAMIC:
03997       ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': '%s' is not a dynamic member\n", args.queuename, args.interface);
03998       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTDYNAMIC");
03999       res = 0;
04000       break;
04001    }
04002 
04003    ast_module_user_remove(lu);
04004 
04005    return res;
04006 }

static void rr_dep_warning ( void   )  [static]

Definition at line 471 of file app_queue.c.

References ast_log(), and LOG_NOTICE.

Referenced by find_queue_by_name_rt(), and reload_queues().

00472 {
00473    static unsigned int warned = 0;
00474 
00475    if (!warned) {
00476       ast_log(LOG_NOTICE, "The 'roundrobin' queue strategy is deprecated. Please use the 'rrmemory' strategy instead.\n");
00477       warned = 1;
00478    }
00479 }

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

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

static int say_periodic_announcement ( struct queue_ent qe  )  [static]

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

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

static int say_position ( struct queue_ent qe  )  [static]

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

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

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

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

03683 {
03684    int found = 0;
03685    struct call_queue *q;
03686    struct member *mem;
03687 
03688    /* Special event for when all queues are paused - individual events still generated */
03689    /* XXX In all other cases, we use the membername, but since this affects all queues, we cannot */
03690    if (ast_strlen_zero(queuename))
03691       ast_queue_log("NONE", "NONE", interface, (paused ? "PAUSEALL" : "UNPAUSEALL"), "%s", "");
03692 
03693    AST_LIST_LOCK(&queues);
03694    AST_LIST_TRAVERSE(&queues, q, list) {
03695       ao2_lock(q);
03696       if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
03697          if ((mem = interface_exists(q, interface))) {
03698             found++;
03699             if (mem->paused == paused)
03700                ast_log(LOG_DEBUG, "%spausing already-%spaused queue member %s:%s\n", (paused ? "" : "un"), (paused ? "" : "un"), q->name, interface);
03701             mem->paused = paused;
03702 
03703             if (queue_persistent_members)
03704                dump_queue_members(q);
03705 
03706             if (mem->realtime)
03707                update_realtime_member_field(mem, q->name, "paused", paused ? "1" : "0");
03708 
03709             ast_queue_log(q->name, "NONE", mem->membername, (paused ? "PAUSE" : "UNPAUSE"), "%s", "");
03710 
03711             manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
03712                "Queue: %s\r\n"
03713                "Location: %s\r\n"
03714                "MemberName: %s\r\n"
03715                "Paused: %d\r\n",
03716                   q->name, mem->interface, mem->membername, paused);
03717             ao2_ref(mem, -1);
03718          }
03719       }
03720       ao2_unlock(q);
03721    }
03722    AST_LIST_UNLOCK(&queues);
03723 
03724    return found ? RESULT_SUCCESS : RESULT_FAILURE;
03725 }

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

sets the QUEUESTATUS channel variable

Definition at line 490 of file app_queue.c.

References pbx_builtin_setvar_helper(), queue_results, and text.

Referenced by queue_exec().

00491 {
00492    int i;
00493 
00494    for (i = 0; i < sizeof(queue_results) / sizeof(queue_results[0]); i++) {
00495       if (queue_results[i].id == res) {
00496          pbx_builtin_setvar_helper(chan, "QUEUESTATUS", queue_results[i].text);
00497          return;
00498       }
00499    }
00500 }

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

02843 {
02844    struct ast_datastore *ds;
02845    struct queue_transfer_ds *qtds = ast_calloc(1, sizeof(*qtds));
02846 
02847    if (!qtds) {
02848       ast_log(LOG_WARNING, "Memory allocation error!\n");
02849       return NULL;
02850    }
02851 
02852    ast_channel_lock(qe->chan);
02853    if (!(ds = ast_channel_datastore_alloc(&queue_transfer_info, NULL))) {
02854       ast_channel_unlock(qe->chan);
02855       ast_log(LOG_WARNING, "Unable to create transfer datastore. queue_log will not show attended transfer\n");
02856       return NULL;
02857    }
02858 
02859    qtds->qe = qe;
02860    /* This member is refcounted in try_calling, so no need to add it here, too */
02861    qtds->member = member;
02862    qtds->starttime = starttime;
02863    qtds->callcompletedinsl = callcompletedinsl;
02864    ds->data = qtds;
02865    ast_channel_datastore_add(qe->chan, ds);
02866    ast_channel_unlock(qe->chan);
02867    return ds;
02868 }

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

Producer of the statechange queue.

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

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

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

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

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

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

Definition at line 514 of file app_queue.c.

References name, and strategies.

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

00515 {
00516    int x;
00517 
00518    for (x = 0; x < sizeof(strategies) / sizeof(strategies[0]); x++) {
00519       if (!strcasecmp(strategy, strategies[x].name))
00520          return strategies[x].strategy;
00521    }
00522 
00523    return -1;
00524 }

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

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

static int unload_module ( void   )  [static]

Definition at line 5604 of file app_queue.c.

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

05605 {
05606    int res;
05607 
05608    if (device_state.thread != AST_PTHREADT_NULL) {
05609       device_state.stop = 1;
05610       ast_mutex_lock(&device_state.lock);
05611       ast_cond_signal(&device_state.cond);
05612       ast_mutex_unlock(&device_state.lock);
05613       pthread_join(device_state.thread, NULL);
05614    }
05615 
05616    ast_cli_unregister_multiple(cli_queue, sizeof(cli_queue) / sizeof(struct ast_cli_entry));
05617    res = ast_manager_unregister("QueueStatus");
05618    res |= ast_manager_unregister("Queues");
05619    res |= ast_manager_unregister("QueueAdd");
05620    res |= ast_manager_unregister("QueueRemove");
05621    res |= ast_manager_unregister("QueuePause");
05622    res |= ast_unregister_application(app_aqm);
05623    res |= ast_unregister_application(app_rqm);
05624    res |= ast_unregister_application(app_pqm);
05625    res |= ast_unregister_application(app_upqm);
05626    res |= ast_unregister_application(app_ql);
05627    res |= ast_unregister_application(app);
05628    res |= ast_custom_function_unregister(&queueagentcount_function);
05629    res |= ast_custom_function_unregister(&queuemembercount_function);
05630    res |= ast_custom_function_unregister(&queuememberlist_function);
05631    res |= ast_custom_function_unregister(&queuewaitingcount_function);
05632    ast_devstate_del(statechange_queue, NULL);
05633 
05634    ast_module_user_hangup_all();
05635 
05636    clear_and_free_interfaces();
05637 
05638    return res;
05639 }

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

Definition at line 2694 of file app_queue.c.

References ao2_lock(), ao2_unlock(), member::calls, call_queue::callscompleted, call_queue::callscompletedinsl, and member::lastcall.

Referenced by queue_transfer_fixup(), and try_calling().

02695 {
02696    ao2_lock(q);
02697    time(&member->lastcall);
02698    member->calls++;
02699    q->callscompleted++;
02700    if (callcompletedinsl)
02701       q->callscompletedinsl++;
02702    ao2_unlock(q);
02703    return 0;
02704 }

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

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

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

static void update_realtime_members ( struct call_queue q  )  [static]

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

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

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

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

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

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

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

03884 {
03885    struct ast_module_user *lu;
03886    char *parse;
03887    int priority_jump = 0;
03888    int ignore_fail = 0;
03889    AST_DECLARE_APP_ARGS(args,
03890       AST_APP_ARG(queuename);
03891       AST_APP_ARG(interface);
03892       AST_APP_ARG(options);
03893    );
03894 
03895    if (ast_strlen_zero(data)) {
03896       ast_log(LOG_WARNING, "UnpauseQueueMember requires an argument ([queuename]|interface[|options])\n");
03897       return -1;
03898    }
03899 
03900    parse = ast_strdupa(data);
03901 
03902    AST_STANDARD_APP_ARGS(args, parse);
03903 
03904    lu = ast_module_user_add(chan);
03905 
03906    if (args.options) {
03907       if (strchr(args.options, 'j'))
03908          priority_jump = 1;
03909       if (strchr(args.options, 'i'))
03910          ignore_fail = 1;
03911    }
03912 
03913    if (ast_strlen_zero(args.interface)) {
03914       ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename]|interface[|options])\n");
03915       ast_module_user_remove(lu);
03916       return -1;
03917    }
03918 
03919    if (set_member_paused(args.queuename, args.interface, 0)) {
03920       ast_log(LOG_WARNING, "Attempt to unpause interface %s, not found\n", args.interface);
03921       pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND");
03922       if (priority_jump || ast_opt_priority_jumping) {
03923          if (!ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101)) {
03924             ast_module_user_remove(lu);
03925             return 0;
03926          }
03927       }
03928       ast_module_user_remove(lu);
03929       if (ignore_fail) {
03930          return 0;
03931       } else {
03932          return -1;
03933       }
03934    }
03935 
03936    ast_module_user_remove(lu);
03937    pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "UNPAUSED");
03938    return 0;
03939 }

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

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

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

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

Definition at line 1908 of file app_queue.c.

References ast_copy_string(), and pbx_builtin_serialize_variables().

Referenced by ring_entry(), and try_calling().

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

static int wait_a_bit ( struct queue_ent qe  )  [static]

Definition at line 3496 of file app_queue.c.

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

Referenced by queue_exec().

03497 {
03498    /* Don't need to hold the lock while we setup the outgoing calls */
03499    int retrywait = qe->parent->retry * 1000;
03500 
03501    int res = ast_waitfordigit(qe->chan, retrywait);
03502    if (res > 0 && !valid_exit(qe, res))
03503       res = 0;
03504 
03505    return res;
03506 }

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

References ast_channel::_state, accountcode, ast_call(), ast_cdr_busy(), ast_channel_datastore_inherit(), ast_channel_inherit_variables(), AST_CONTROL_ANSWER, AST_CONTROL_BUSY, AST_CONTROL_CONGESTION, AST_CONTROL_HANGUP, AST_CONTROL_OFFHOOK, AST_CONTROL_RINGING, ast_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().

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

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

02620 {
02621    int res = 0;
02622 
02623    /* This is the holding pen for callers 2 through maxlen */
02624    for (;;) {
02625       enum queue_member_status stat;
02626 
02627       if (is_our_turn(qe))
02628          break;
02629 
02630       /* If we have timed out, break out */
02631       if (qe->expire && (time(NULL) >= qe->expire)) {
02632          *reason = QUEUE_TIMEOUT;
02633          break;
02634       }
02635 
02636       stat = get_member_status(qe->parent, qe->max_penalty);
02637 
02638       /* leave the queue if no agents, if enabled */
02639       if (qe->parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) {
02640          *reason = QUEUE_LEAVEEMPTY;
02641          ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long)time(NULL) - qe->start);
02642          leave_queue(qe);
02643          break;
02644       }
02645 
02646       /* leave the queue if no reachable agents, if enabled */
02647       if ((qe->parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) {
02648          *reason = QUEUE_LEAVEUNAVAIL;
02649          ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long)time(NULL) - qe->start);
02650          leave_queue(qe);
02651          break;
02652       }
02653 
02654       /* Make a position announcement, if enabled */
02655       if (qe->parent->announcefrequency && !ringing &&
02656          (res = say_position(qe)))
02657          break;
02658 
02659       /* If we have timed out, break out */
02660       if (qe->expire && (time(NULL) >= qe->expire)) {
02661          *reason = QUEUE_TIMEOUT;
02662          break;
02663       }
02664 
02665       /* Make a periodic announcement, if enabled */
02666       if (qe->parent->periodicannouncefrequency && !ringing &&
02667          (res = say_periodic_announcement(qe)))
02668          break;
02669 
02670       /* If we have timed out, break out */
02671       if (qe->expire && (time(NULL) >= qe->expire)) {
02672          *reason = QUEUE_TIMEOUT;
02673          break;
02674       }
02675       
02676       /* Wait a second before checking again */
02677       if ((res = ast_waitfordigit(qe->chan, RECHECK * 1000))) {
02678          if (res > 0 && !valid_exit(qe, res))
02679             res = 0;
02680          else
02681             break;
02682       }
02683       
02684       /* If we have timed out, break out */
02685       if (qe->expire && (time(NULL) >= qe->expire)) {
02686          *reason = QUEUE_TIMEOUT;
02687          break;
02688       }
02689    }
02690 
02691    return res;
02692 }


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

struct ast_cli_entry cli_queue[] [static]

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

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

Definition at line 5565 of file app_queue.c.

Condition for the state change queue

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

Definition at line 307 of file app_queue.c.

Referenced by _sip_show_peers().

struct statechange* last

Definition at line 747 of file app_queue.c.

Lock for the state change queue

Definition at line 743 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 5559 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 5536 of file app_queue.c.

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

Definition at line 5562 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 5555 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 2789 of file app_queue.c.

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

Definition at line 4598 of file app_queue.c.

Referenced by load_module(), and unload_module().

Definition at line 4608 of file app_queue.c.

Referenced by load_module(), and unload_module().

Definition at line 4632 of file app_queue.c.

Referenced by load_module(), and unload_module().

Definition at line 4623 of file app_queue.c.

Referenced by load_module(), and unload_module().

struct { ... } state_change_q

Queue of state changes

unsigned int stop

Set to 1 to stop the thread

Definition at line 739 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 741 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 27 Apr 2012 for Asterisk - the Open Source PBX by  doxygen 1.6.1