Fri Sep 11 13:45:07 2009

Asterisk developer's documentation


app_queue.c File Reference

True call queues with optional send URL on answer. More...

#include "asterisk.h"
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <sys/time.h>
#include <sys/signal.h>
#include <netinet/in.h>
#include "asterisk/lock.h"
#include "asterisk/file.h"
#include "asterisk/logger.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/options.h"
#include "asterisk/app.h"
#include "asterisk/linkedlists.h"
#include "asterisk/module.h"
#include "asterisk/translate.h"
#include "asterisk/say.h"
#include "asterisk/features.h"
#include "asterisk/musiconhold.h"
#include "asterisk/cli.h"
#include "asterisk/manager.h"
#include "asterisk/config.h"
#include "asterisk/monitor.h"
#include "asterisk/utils.h"
#include "asterisk/causes.h"
#include "asterisk/astdb.h"
#include "asterisk/devicestate.h"
#include "asterisk/stringfields.h"
#include "asterisk/astobj2.h"
#include "asterisk/global_datastores.h"

Go to the source code of this file.

Data Structures

struct  call_queue
struct  callattempt
 We define a custom "local user" structure because we use it not only for keeping track of what is in use but also for keeping track of who we're dialing. More...
struct  interfaces
struct  member
struct  member_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
}
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 (struct call_queue *q)
static void * device_state_thread (void *data)
 Consumer of the statechange queue.
static void do_hang (struct callattempt *o)
 common hangup actions
static void dump_queue_members (struct call_queue *pm_queue)
static struct callattemptfind_best (struct callattempt *outgoing)
 find the entry with the best metric, or NULL
static struct call_queuefind_queue_by_name_rt (const char *queuename, struct ast_variable *queue_vars, struct ast_config *member_config)
 Reload a single queue via realtime.
static void free_members (struct call_queue *q, int all)
static enum queue_member_status get_member_status (struct call_queue *q, int max_penalty)
 Check if members are available.
static int handle_queue_add_member (int fd, int argc, char *argv[])
static int handle_queue_remove_member (int fd, int argc, char *argv[])
static void * handle_statechange (struct statechange *sc)
 set a member's status based on device state of that member's interface
static void hangupcalls (struct callattempt *outgoing, struct ast_channel *exception)
static void init_queue (struct call_queue *q)
static void insert_entry (struct call_queue *q, struct queue_ent *prev, struct queue_ent *new, int *pos)
 Insert the 'new' entry after the 'prev' entry of queue 'q'.
static char * int2strat (int strategy)
static struct memberinterface_exists (struct call_queue *q, const char *interface)
static int interface_exists_global (const char *interface)
static int is_our_turn (struct queue_ent *qe)
 Check if we should start attempting to call queue members.
static int join_queue (char *queuename, struct queue_ent *qe, enum queue_result *reason)
static void leave_queue (struct queue_ent *qe)
static int load_module (void)
static struct call_queueload_realtime_queue (const char *queuename)
static int manager_add_queue_member (struct mansession *s, const struct message *m)
static int manager_pause_queue_member (struct mansession *s, const struct message *m)
static int manager_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 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 = "f450f61f60e761b3aa089ebed76ca8a5" , .load = load_module, .unload = unload_module, .reload = reload, }
static char * app = "Queue"
static char * app_aqm = "AddQueueMember"
static char * app_aqm_descrip
static char * app_aqm_synopsis = "Dynamically adds queue members"
static char * app_pqm = "PauseQueueMember"
static char * app_pqm_descrip
static char * app_pqm_synopsis = "Pauses a queue member"
static char * app_ql = "QueueLog"
static char * app_ql_descrip
static char * app_ql_synopsis = "Writes to the queue_log"
static char * app_rqm = "RemoveQueueMember"
static char * app_rqm_descrip
static char * app_rqm_synopsis = "Dynamically removes queue members"
static char * app_upqm = "UnpauseQueueMember"
static char * app_upqm_descrip
static char * app_upqm_synopsis = "Unpauses a queue member"
static const struct ast_module_infoast_module_info = &__mod_info
static int autofill_default = 0
 queues.conf [general] option
static struct ast_cli_entry cli_add_queue_member_deprecated
static struct ast_cli_entry cli_queue []
static struct ast_cli_entry cli_remove_queue_member_deprecated
static struct ast_cli_entry cli_show_queue_deprecated
static char * descrip
struct {
   ast_cond_t   cond
   ast_mutex_t   lock
   struct {
      statechange *   first
      statechange *   last
   }   state_change_q
   unsigned int   stop:1
   pthread_t   thread
device_state
 Data used by the device state thread.
static int montype_default = 0
 queues.conf [general] option
static const char * pm_family = "Queue/PersistentMembers"
 Persistent Members astdb family.
static char qam_cmd_usage []
static char 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>:

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

Referenced by queue_set_param().

#define ANNOUNCEHOLDTIME_ONCE   2

Definition at line 391 of file app_queue.c.

Referenced by queue_set_param(), and say_position().

#define AST_MAX_WATCHERS   256

Definition at line 2194 of file app_queue.c.

#define DEFAULT_RETRY   5

Definition at line 137 of file app_queue.c.

Referenced by init_queue(), and queue_set_param().

#define DEFAULT_TIMEOUT   15

Definition at line 138 of file app_queue.c.

Referenced by queue_set_param().

#define MAX_PERIODIC_ANNOUNCEMENTS   10

Definition at line 140 of file app_queue.c.

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

#define PM_MAX_LEN   8192

Definition at line 276 of file app_queue.c.

Referenced by dump_queue_members(), and reload_queue_members().

#define QUEUE_EMPTY_NORMAL   1

Definition at line 388 of file app_queue.c.

Referenced by queue_set_param().

#define QUEUE_EMPTY_STRICT   2

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

Referenced by queue_set_param(), and ring_entry().

#define RECHECK   1

Definition at line 139 of file app_queue.c.

Referenced by wait_our_turn().

#define RES_EXISTS   (-1)

Definition at line 143 of file app_queue.c.

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

#define RES_NOSUCHQUEUE   (-3)

Definition at line 145 of file app_queue.c.

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

#define RES_NOT_DYNAMIC   (-4)

Definition at line 146 of file app_queue.c.

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

#define RES_OKAY   0

Definition at line 142 of file app_queue.c.

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

#define RES_OUTOFMEMORY   (-2)

Definition at line 144 of file app_queue.c.

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


Enumeration Type Documentation

anonymous enum

Enumerator:
QUEUE_STRATEGY_RINGALL 
QUEUE_STRATEGY_ROUNDROBIN 
QUEUE_STRATEGY_LEASTRECENT 
QUEUE_STRATEGY_FEWESTCALLS 
QUEUE_STRATEGY_RANDOM 
QUEUE_STRATEGY_RRMEMORY 

Definition at line 116 of file app_queue.c.

enum qmc_status

Enumerator:
QMC_VALID 
QMC_PAUSED 
QMC_ACTIVE 
QMC_FREE 
QMC_ALL 

Definition at line 4243 of file app_queue.c.

04243                 {
04244    QMC_VALID = 0, /* Count valid members */
04245    QMC_PAUSED,    /* Count paused members */
04246    QMC_ACTIVE,    /* Count active members */
04247    QMC_FREE,      /* Count free members */
04248    QMC_ALL        /* Count all queue members */
04249 };

enum queue_member_status

Enumerator:
QUEUE_NO_MEMBERS 
QUEUE_NO_REACHABLE_MEMBERS 
QUEUE_NORMAL 

Definition at line 543 of file app_queue.c.

00543                          {
00544    QUEUE_NO_MEMBERS,
00545    QUEUE_NO_REACHABLE_MEMBERS,
00546    QUEUE_NORMAL
00547 };

enum queue_result

Enumerator:
QUEUE_UNKNOWN 
QUEUE_TIMEOUT 
QUEUE_JOINEMPTY 
QUEUE_LEAVEEMPTY 
QUEUE_JOINUNAVAIL 
QUEUE_LEAVEUNAVAIL 
QUEUE_FULL 

Definition at line 293 of file app_queue.c.

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


Function Documentation

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

Definition at line 4676 of file app_queue.c.

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

Referenced by manager_queues_show(), and queue_show().

04677 {
04678    struct call_queue *q;
04679    struct queue_ent *qe;
04680    struct member *mem;
04681    int pos, queue_show;
04682    time_t now;
04683    char max_buf[150];
04684    char *max;
04685    size_t max_left;
04686    float sl = 0;
04687    char *term = manager ? "\r\n" : "\n";
04688    struct ao2_iterator mem_iter;
04689 
04690    time(&now);
04691    if (argc == 2)
04692       queue_show = 0;
04693    else if (argc == 3)
04694       queue_show = 1;
04695    else
04696       return RESULT_SHOWUSAGE;
04697 
04698    /* We only want to load realtime queues when a specific queue is asked for. */
04699    if (queue_show) {
04700       load_realtime_queue(argv[2]);
04701    } else if (ast_check_realtime("queues")) {
04702       struct ast_config *cfg = ast_load_realtime_multientry("queues", "name LIKE", "%", (char *) NULL);
04703       char *queuename;
04704       if (cfg) {
04705          for (queuename = ast_category_browse(cfg, NULL); !ast_strlen_zero(queuename); queuename = ast_category_browse(cfg, queuename)) {
04706             load_realtime_queue(queuename);
04707          }
04708          ast_config_destroy(cfg);
04709       }
04710    }
04711 
04712    AST_LIST_LOCK(&queues);
04713    if (AST_LIST_EMPTY(&queues)) {
04714       AST_LIST_UNLOCK(&queues);
04715       if (queue_show) {
04716          if (s)
04717             astman_append(s, "No such queue: %s.%s",argv[2], term);
04718          else
04719             ast_cli(fd, "No such queue: %s.%s",argv[2], term);
04720       } else {
04721          if (s)
04722             astman_append(s, "No queues.%s", term);
04723          else
04724             ast_cli(fd, "No queues.%s", term);
04725       }
04726       return RESULT_SUCCESS;
04727    }
04728    AST_LIST_TRAVERSE(&queues, q, list) {
04729       ast_mutex_lock(&q->lock);
04730       if (queue_show) {
04731          if (strcasecmp(q->name, argv[2]) != 0) {
04732             ast_mutex_unlock(&q->lock);
04733             if (!AST_LIST_NEXT(q, list)) {
04734                ast_cli(fd, "No such queue: %s.%s",argv[2], term);
04735                break;
04736             }
04737             continue;
04738          }
04739       }
04740       max_buf[0] = '\0';
04741       max = max_buf;
04742       max_left = sizeof(max_buf);
04743       if (q->maxlen)
04744          ast_build_string(&max, &max_left, "%d", q->maxlen);
04745       else
04746          ast_build_string(&max, &max_left, "unlimited");
04747       sl = 0;
04748       if (q->callscompleted > 0)
04749          sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
04750       if (s)
04751          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",
04752                               q->name, q->count, max_buf, int2strat(q->strategy), q->holdtime, q->ringlimit,
04753                               q->weight, q->callscompleted, q->callsabandoned, sl, q->servicelevel, term);
04754       else
04755          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",
04756                      q->name, q->count, max_buf, int2strat(q->strategy), q->holdtime, q->ringlimit,
04757                      q->weight, q->callscompleted, q->callsabandoned, sl, q->servicelevel, term);
04758       if (ao2_container_count(q->members)) {
04759          if (s)
04760             astman_append(s, "   Members: %s", term);
04761          else
04762             ast_cli(fd, "   Members: %s", term);
04763          mem_iter = ao2_iterator_init(q->members, 0);
04764          while ((mem = ao2_iterator_next(&mem_iter))) {
04765             max_buf[0] = '\0';
04766             max = max_buf;
04767             max_left = sizeof(max_buf);
04768             if (strcasecmp(mem->membername, mem->interface)) {
04769                ast_build_string(&max, &max_left, " (%s)", mem->interface);
04770             }
04771             if (mem->penalty)
04772                ast_build_string(&max, &max_left, " with penalty %d", mem->penalty);
04773             if (mem->dynamic)
04774                ast_build_string(&max, &max_left, " (dynamic)");
04775             if (mem->realtime)
04776                ast_build_string(&max, &max_left, " (realtime)");
04777             if (mem->paused)
04778                ast_build_string(&max, &max_left, " (paused)");
04779             ast_build_string(&max, &max_left, " (%s)", devstate2str(mem->status));
04780             if (mem->calls) {
04781                ast_build_string(&max, &max_left, " has taken %d calls (last was %ld secs ago)",
04782                   mem->calls, (long) (time(NULL) - mem->lastcall));
04783             } else
04784                ast_build_string(&max, &max_left, " has taken no calls yet");
04785             if (s)
04786                astman_append(s, "      %s%s%s", mem->membername, max_buf, term);
04787             else
04788                ast_cli(fd, "      %s%s%s", mem->membername, max_buf, term);
04789             ao2_ref(mem, -1);
04790          }
04791       } else if (s)
04792          astman_append(s, "   No Members%s", term);
04793       else  
04794          ast_cli(fd, "   No Members%s", term);
04795       if (q->head) {
04796          pos = 1;
04797          if (s)
04798             astman_append(s, "   Callers: %s", term);
04799          else
04800             ast_cli(fd, "   Callers: %s", term);
04801          for (qe = q->head; qe; qe = qe->next) {
04802             if (s)
04803                astman_append(s, "      %d. %s (wait: %ld:%2.2ld, prio: %d)%s",
04804                   pos++, qe->chan->name, (long) (now - qe->start) / 60,
04805                   (long) (now - qe->start) % 60, qe->prio, term);
04806             else
04807                ast_cli(fd, "      %d. %s (wait: %ld:%2.2ld, prio: %d)%s", pos++,
04808                   qe->chan->name, (long) (now - qe->start) / 60,
04809                   (long) (now - qe->start) % 60, qe->prio, term);
04810          }
04811       } else if (s)
04812          astman_append(s, "   No Callers%s", term);
04813       else
04814          ast_cli(fd, "   No Callers%s", term);
04815       if (s)
04816          astman_append(s, "%s", term);
04817       else
04818          ast_cli(fd, "%s", term);
04819       ast_mutex_unlock(&q->lock);
04820       if (queue_show)
04821          break;
04822    }
04823    AST_LIST_UNLOCK(&queues);
04824    return RESULT_SUCCESS;
04825 }

static void __reg_module ( void   )  [static]

Definition at line 5500 of file app_queue.c.

static void __unreg_module ( void   )  [static]

Definition at line 5500 of file app_queue.c.

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

Definition at line 890 of file app_queue.c.

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

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

00891 {
00892    struct member_interface *curint;
00893 
00894    AST_LIST_LOCK(&interfaces);
00895    AST_LIST_TRAVERSE(&interfaces, curint, list) {
00896       if (!strcasecmp(curint->interface, interface))
00897          break;
00898    }
00899 
00900    if (curint) {
00901       AST_LIST_UNLOCK(&interfaces);
00902       return 0;
00903    }
00904 
00905    if (option_debug)
00906       ast_log(LOG_DEBUG, "Adding %s to the list of interfaces that make up all of our queue members.\n", interface);
00907    
00908    if ((curint = ast_calloc(1, sizeof(*curint)))) {
00909       ast_copy_string(curint->interface, interface, sizeof(curint->interface));
00910       AST_LIST_INSERT_HEAD(&interfaces, curint, list);
00911    }
00912    AST_LIST_UNLOCK(&interfaces);
00913 
00914    return 0;
00915 }

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

References add_to_interfaces(), ao2_ref(), AST_LIST_LOCK, AST_LIST_UNLOCK, ast_mutex_lock(), ast_mutex_unlock(), member::calls, create_queue_member(), dump_queue_members(), member::dynamic, EVENT_FLAG_AGENT, member::interface, interface_exists(), member::lastcall, load_realtime_queue(), call_queue::lock, manager_event(), call_queue::membercount, member::membername, call_queue::members, call_queue::name, member::paused, member::penalty, RES_EXISTS, RES_NOSUCHQUEUE, RES_OKAY, RES_OUTOFMEMORY, member::state_interface, and member::status.

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

03468 {
03469    struct call_queue *q;
03470    struct member *new_member, *old_member;
03471    int res = RES_NOSUCHQUEUE;
03472 
03473    /* \note Ensure the appropriate realtime queue is loaded.  Note that this
03474     * short-circuits if the queue is already in memory. */
03475    if (!(q = load_realtime_queue(queuename)))
03476       return res;
03477 
03478    AST_LIST_LOCK(&queues);
03479 
03480    ast_mutex_lock(&q->lock);
03481    if ((old_member = interface_exists(q, interface)) == NULL) {
03482       if ((new_member = create_queue_member(interface, membername, penalty, paused, state_interface))) {
03483          add_to_interfaces(new_member->state_interface);
03484          new_member->dynamic = 1;
03485          ao2_link(q->members, new_member);
03486          q->membercount++;
03487          manager_event(EVENT_FLAG_AGENT, "QueueMemberAdded",
03488             "Queue: %s\r\n"
03489             "Location: %s\r\n"
03490             "MemberName: %s\r\n"
03491             "Membership: %s\r\n"
03492             "Penalty: %d\r\n"
03493             "CallsTaken: %d\r\n"
03494             "LastCall: %d\r\n"
03495             "Status: %d\r\n"
03496             "Paused: %d\r\n",
03497             q->name, new_member->interface, new_member->membername,
03498             "dynamic",
03499             new_member->penalty, new_member->calls, (int) new_member->lastcall,
03500             new_member->status, new_member->paused);
03501          
03502          ao2_ref(new_member, -1);
03503          new_member = NULL;
03504 
03505          if (dump)
03506             dump_queue_members(q);
03507          
03508          res = RES_OKAY;
03509       } else {
03510          res = RES_OUTOFMEMORY;
03511       }
03512    } else {
03513       ao2_ref(old_member, -1);
03514       res = RES_EXISTS;
03515    }
03516    ast_mutex_unlock(&q->lock);
03517    AST_LIST_UNLOCK(&queues);
03518 
03519    return res;
03520 }

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

Definition at line 791 of file app_queue.c.

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

Referenced by find_queue_by_name_rt(), and reload_queues().

00792 {
00793    struct call_queue *q;
00794 
00795    if ((q = ast_calloc(1, sizeof(*q)))) {
00796       ast_mutex_init(&q->lock);
00797       ast_copy_string(q->name, queuename, sizeof(q->name));
00798    }
00799    return q;
00800 }

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

Definition at line 3848 of file app_queue.c.

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

Referenced by load_module().

03849 {
03850    int res=-1;
03851    struct ast_module_user *lu;
03852    char *parse, *temppos = NULL;
03853    int priority_jump = 0;
03854    AST_DECLARE_APP_ARGS(args,
03855       AST_APP_ARG(queuename);
03856       AST_APP_ARG(interface);
03857       AST_APP_ARG(penalty);
03858       AST_APP_ARG(options);
03859       AST_APP_ARG(membername);
03860       AST_APP_ARG(state_interface);
03861    );
03862    int penalty = 0;
03863 
03864    if (ast_strlen_zero(data)) {
03865       ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename[|interface[|penalty[|options[|membername[|state_interface]]]]])\n");
03866       return -1;
03867    }
03868 
03869    parse = ast_strdupa(data);
03870 
03871    AST_STANDARD_APP_ARGS(args, parse);
03872 
03873    lu = ast_module_user_add(chan);
03874 
03875    if (ast_strlen_zero(args.interface)) {
03876       args.interface = ast_strdupa(chan->name);
03877       temppos = strrchr(args.interface, '-');
03878       if (temppos)
03879          *temppos = '\0';
03880    }
03881 
03882    if (!ast_strlen_zero(args.penalty)) {
03883       if ((sscanf(args.penalty, "%30d", &penalty) != 1) || penalty < 0) {
03884          ast_log(LOG_WARNING, "Penalty '%s' is invalid, must be an integer >= 0\n", args.penalty);
03885          penalty = 0;
03886       }
03887    }
03888    
03889    if (args.options) {
03890       if (strchr(args.options, 'j'))
03891          priority_jump = 1;
03892    }
03893 
03894    switch (add_to_queue(args.queuename, args.interface, args.membername, penalty, 0, queue_persistent_members, args.state_interface)) {
03895    case RES_OKAY:
03896       ast_queue_log(args.queuename, chan->uniqueid, args.interface, "ADDMEMBER", "%s", "");
03897       ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", args.interface, args.queuename);
03898       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "ADDED");
03899       res = 0;
03900       break;
03901    case RES_EXISTS:
03902       ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': Already there\n", args.interface, args.queuename);
03903       if (priority_jump || ast_opt_priority_jumping)
03904          ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
03905       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "MEMBERALREADY");
03906       res = 0;
03907       break;
03908    case RES_NOSUCHQUEUE:
03909       ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", args.queuename);
03910       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "NOSUCHQUEUE");
03911       res = 0;
03912       break;
03913    case RES_OUTOFMEMORY:
03914       ast_log(LOG_ERROR, "Out of memory adding member %s to queue %s\n", args.interface, args.queuename);
03915       break;
03916    }
03917 
03918    ast_module_user_remove(lu);
03919 
03920    return res;
03921 }

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

References ast_channel_datastore_find(), and queue_transfer_info.

02718 {
02719    return ast_channel_datastore_find(chan, &queue_transfer_info, NULL) ? 0 : 1;
02720 }

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

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

Referenced by try_calling().

02596 {
02597    if (qe->max_penalty && (mem->penalty > qe->max_penalty))
02598       return -1;
02599 
02600    switch (q->strategy) {
02601    case QUEUE_STRATEGY_RINGALL:
02602       /* Everyone equal, except for penalty */
02603       tmp->metric = mem->penalty * 1000000;
02604       break;
02605    case QUEUE_STRATEGY_ROUNDROBIN:
02606       if (!pos) {
02607          if (!q->wrapped) {
02608             /* No more channels, start over */
02609             q->rrpos = 0;
02610          } else {
02611             /* Prioritize next entry */
02612             q->rrpos++;
02613          }
02614          q->wrapped = 0;
02615       }
02616       /* Fall through */
02617    case QUEUE_STRATEGY_RRMEMORY:
02618       if (pos < q->rrpos) {
02619          tmp->metric = 1000 + pos;
02620       } else {
02621          if (pos > q->rrpos)
02622             /* Indicate there is another priority */
02623             q->wrapped = 1;
02624          tmp->metric = pos;
02625       }
02626       tmp->metric += mem->penalty * 1000000;
02627       break;
02628    case QUEUE_STRATEGY_RANDOM:
02629       tmp->metric = ast_random() % 1000;
02630       tmp->metric += mem->penalty * 1000000;
02631       break;
02632    case QUEUE_STRATEGY_FEWESTCALLS:
02633       tmp->metric = mem->calls;
02634       tmp->metric += mem->penalty * 1000000;
02635       break;
02636    case QUEUE_STRATEGY_LEASTRECENT:
02637       if (!mem->lastcall)
02638          tmp->metric = 0;
02639       else
02640          tmp->metric = 1000000 - (time(NULL) - mem->lastcall);
02641       tmp->metric += mem->penalty * 1000000;
02642       break;
02643    default:
02644       ast_log(LOG_WARNING, "Can't calculate metric for unknown strategy %d\n", q->strategy);
02645       break;
02646    }
02647    if (q->ringlimit && (mem->ringcount >= q->ringlimit)) {
02648       tmp->metric += (mem->ringcount / q->ringlimit) * 10000000;
02649    }
02650    if (option_debug)
02651       ast_log(LOG_DEBUG, "New metric %d for member %s with %d rings (limit %d)\n", 
02652                   tmp->metric, mem->interface, mem->ringcount, q->ringlimit);
02653    return 0;
02654 }

static void clear_and_free_interfaces ( void   )  [static]

Definition at line 968 of file app_queue.c.

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

Referenced by unload_module().

00969 {
00970    struct member_interface *curint;
00971 
00972    AST_LIST_LOCK(&interfaces);
00973    while ((curint = AST_LIST_REMOVE_HEAD(&interfaces, list)))
00974       free(curint);
00975    AST_LIST_UNLOCK(&interfaces);
00976 }

static void clear_queue ( struct call_queue q  )  [static]

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

00882 {
00883    q->holdtime = 0;
00884    q->callscompleted = 0;
00885    q->callsabandoned = 0;
00886    q->callscompletedinsl = 0;
00887    q->wrapuptime = 0;
00888 }

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

Definition at line 5327 of file app_queue.c.

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

05328 {
05329    char buffer[256] = "";
05330    char *queuename;
05331    
05332    if (argc != 4) {
05333       return RESULT_SHOWUSAGE;
05334    }
05335    queuename = argv[3];
05336 
05337    if (qmc_handler(queuename, buffer, sizeof(buffer)) == RESULT_SUCCESS) {
05338          ast_cli(fd, 
05339                      "Member count for queue '%s'\n"
05340                      "%s\n",
05341                      queuename, buffer); 
05342          return RESULT_SUCCESS;
05343    } else {
05344          ast_cli(fd, "No such queue: '%s'\n", queuename);
05345          return RESULT_FAILURE;
05346    }
05347 }

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

Definition at line 1804 of file app_queue.c.

References ao2_find(), ao2_ref(), AST_LIST_TRAVERSE, ast_log(), ast_mutex_lock(), ast_mutex_unlock(), call_queue::count, member::interface, member_interface::list, call_queue::lock, LOG_DEBUG, call_queue::members, call_queue::name, num_available_members(), and call_queue::weight.

Referenced by ring_entry().

01805 {
01806    struct call_queue *q;
01807    struct member *mem;
01808    int found = 0;
01809    
01810    /* &qlock and &rq->lock already set by try_calling()
01811     * to solve deadlock */
01812    AST_LIST_TRAVERSE(&queues, q, list) {
01813       if (q == rq) /* don't check myself, could deadlock */
01814          continue;
01815       ast_mutex_lock(&q->lock);
01816       if (q->count && q->members) {
01817          if ((mem = ao2_find(q->members, member, OBJ_POINTER))) {
01818             ast_log(LOG_DEBUG, "Found matching member %s in queue '%s'\n", mem->interface, q->name);
01819             if (q->weight > rq->weight && q->count >= num_available_members(q)) {
01820                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);
01821                found = 1;
01822             }
01823             ao2_ref(mem, -1);
01824          }
01825       }
01826       ast_mutex_unlock(&q->lock);
01827       if (found)
01828          break;
01829    }
01830    return found;
01831 }

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

Definition at line 4832 of file app_queue.c.

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

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

04833 {
04834    struct call_queue *q;
04835    char *ret = NULL;
04836    int which = 0;
04837    int wordlen = strlen(word);
04838    
04839    AST_LIST_LOCK(&queues);
04840    AST_LIST_TRAVERSE(&queues, q, list) {
04841       if (!strncasecmp(word, q->name, wordlen) && ++which > state) {
04842          ret = ast_strdup(q->name); 
04843          break;
04844       }
04845    }
04846    AST_LIST_UNLOCK(&queues);
04847 
04848    return ret;
04849 }

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

Definition at line 5134 of file app_queue.c.

References ast_malloc, ast_strdup, and complete_queue().

05135 {
05136    /* 0 - queue; 1 - add; 2 - member; 3 - <interface>; 4 - to; 5 - <queue>; 6 - penalty; 7 - <penalty>; 8 - as; 9 - <membername> */
05137    switch (pos) {
05138    case 3:  /* Don't attempt to complete name of interface (infinite possibilities) */
05139       return NULL;
05140    case 4:  /* only one possible match, "to" */
05141       return state == 0 ? ast_strdup("to") : NULL;
05142    case 5:  /* <queue> */
05143       return complete_queue(line, word, pos, state);
05144    case 6: /* only one possible match, "penalty" */
05145       return state == 0 ? ast_strdup("penalty") : NULL;
05146    case 7:
05147       if (state < 100) {   /* 0-99 */
05148          char *num;
05149          if ((num = ast_malloc(3))) {
05150             sprintf(num, "%d", state);
05151          }
05152          return num;
05153       } else {
05154          return NULL;
05155       }
05156    case 8: /* only one possible match, "as" */
05157       return state == 0 ? ast_strdup("as") : NULL;
05158    case 9:  /* Don't attempt to complete name of member (infinite possibilities) */
05159       return NULL;
05160    case 10:
05161       return state == 0 ? ast_strdup("state_interface") : NULL;
05162    default:
05163       return NULL;
05164    }
05165 }

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

Definition at line 5354 of file app_queue.c.

References complete_queue().

05355 {
05356       /* 0 - queue; 1 - member; 2 - count; 3 - <queue> */
05357       switch (pos) {
05358       case 3:  /* <queue> */
05359             return complete_queue(line, word, pos, state);
05360       default:
05361          return NULL;
05362       }
05363 }

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

Definition at line 5202 of file app_queue.c.

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

05203 {
05204    int which = 0;
05205    struct call_queue *q;
05206    struct member *m;
05207    struct ao2_iterator mem_iter;
05208 
05209    /* 0 - queue; 1 - remove; 2 - member; 3 - <member>; 4 - from; 5 - <queue> */
05210    if (pos > 5 || pos < 3)
05211       return NULL;
05212    if (pos == 4)  /* only one possible match, 'from' */
05213       return state == 0 ? ast_strdup("from") : NULL;
05214 
05215    if (pos == 5)  /* No need to duplicate code */
05216       return complete_queue(line, word, pos, state);
05217 
05218    /* here is the case for 3, <member> */
05219    if (!AST_LIST_EMPTY(&queues)) { /* XXX unnecessary ? the traverse does that for us */
05220       AST_LIST_TRAVERSE(&queues, q, list) {
05221          ast_mutex_lock(&q->lock);
05222          mem_iter = ao2_iterator_init(q->members, 0);
05223          while ((m = ao2_iterator_next(&mem_iter))) {
05224             if (++which > state) {
05225                char *tmp;
05226                ast_mutex_unlock(&q->lock);
05227                tmp = ast_strdup(m->interface);
05228                ao2_ref(m, -1);
05229                return tmp;
05230             }
05231             ao2_ref(m, -1);
05232          }
05233          ast_mutex_unlock(&q->lock);
05234       }
05235    }
05236 
05237    return NULL;
05238 }

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

Definition at line 4851 of file app_queue.c.

References complete_queue().

04852 {
04853    if (pos == 2)
04854       return complete_queue(line, word, pos, state);
04855    return NULL;
04856 }

static int compress_char ( const char  c  )  [static]

Definition at line 802 of file app_queue.c.

00803 {
00804    if (c < 32)
00805       return 0;
00806    else if (c > 96)
00807       return c - 64;
00808    else
00809       return c - 32;
00810 }

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

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

Definition at line 766 of file app_queue.c.

References ao2_alloc(), ast_copy_string(), ast_device_state(), ast_log(), ast_strlen_zero(), LOG_WARNING, and member::penalty.

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

00767 {
00768    struct member *cur;
00769    
00770    if ((cur = ao2_alloc(sizeof(*cur), NULL))) {
00771       cur->penalty = penalty;
00772       cur->paused = paused;
00773       ast_copy_string(cur->interface, interface, sizeof(cur->interface));
00774       if (!ast_strlen_zero(state_interface)) {
00775          ast_copy_string(cur->state_interface, state_interface, sizeof(cur->state_interface));
00776       } else {
00777          ast_copy_string(cur->state_interface, interface, sizeof(cur->state_interface));
00778       }
00779       if (!ast_strlen_zero(membername))
00780          ast_copy_string(cur->membername, membername, sizeof(cur->membername));
00781       else
00782          ast_copy_string(cur->membername, interface, sizeof(cur->membername));
00783       if (!strchr(cur->interface, '/'))
00784          ast_log(LOG_WARNING, "No location at interface '%s'\n", interface);
00785       cur->status = ast_device_state(cur->state_interface);
00786    }
00787 
00788    return cur;
00789 }

static void destroy_queue ( struct call_queue q  )  [static]

Definition at line 1200 of file app_queue.c.

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

Referenced by find_queue_by_name_rt(), and leave_queue().

01201 {
01202    free_members(q, 1);
01203    ast_mutex_destroy(&q->lock);
01204    ao2_ref(q->members, -1);
01205    free(q);
01206 }

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

Consumer of the statechange queue.

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

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

static void do_hang ( struct callattempt o  )  [static]

common hangup actions

Definition at line 1834 of file app_queue.c.

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

Referenced by ring_entry().

01835 {
01836    o->stillgoing = 0;
01837    ast_hangup(o->chan);
01838    o->chan = NULL;
01839 }

static void dump_queue_members ( struct call_queue pm_queue  )  [static]

Definition at line 3376 of file app_queue.c.

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

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

03377 {
03378    struct member *cur_member;
03379    char value[PM_MAX_LEN];
03380    int value_len = 0;
03381    int res;
03382    struct ao2_iterator mem_iter;
03383 
03384    memset(value, 0, sizeof(value));
03385 
03386    if (!pm_queue)
03387       return;
03388 
03389    mem_iter = ao2_iterator_init(pm_queue->members, 0);
03390    while ((cur_member = ao2_iterator_next(&mem_iter))) {
03391       if (!cur_member->dynamic) {
03392          ao2_ref(cur_member, -1);
03393          continue;
03394       }
03395 
03396       res = snprintf(value + value_len, sizeof(value) - value_len, "%s%s;%d;%d;%s;%s",
03397          value_len ? "|" : "", cur_member->interface, cur_member->penalty, cur_member->paused, cur_member->membername, cur_member->state_interface);
03398 
03399       ao2_ref(cur_member, -1);
03400 
03401       if (res != strlen(value + value_len)) {
03402          ast_log(LOG_WARNING, "Could not create persistent member string, out of space\n");
03403          break;
03404       }
03405       value_len += res;
03406    }
03407    
03408    if (value_len && !cur_member) {
03409       if (ast_db_put(pm_family, pm_queue->name, value))
03410          ast_log(LOG_WARNING, "failed to create persistent dynamic entry!\n");
03411    } else
03412       /* Delete the entry if the queue is empty or there is an error */
03413       ast_db_del(pm_family, pm_queue->name);
03414 }

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

find the entry with the best metric, or NULL

Definition at line 2040 of file app_queue.c.

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

02041 {
02042    struct callattempt *best = NULL, *cur;
02043 
02044    for (cur = outgoing; cur; cur = cur->q_next) {
02045       if (cur->stillgoing &&              /* Not already done */
02046          !cur->chan &&              /* Isn't already going */
02047          (!best || cur->metric < best->metric)) {     /* We haven't found one yet, or it's better */
02048          best = cur;
02049       }
02050    }
02051 
02052    return best;
02053 }

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

Reload a single queue via realtime.

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

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

Definition at line 1211 of file app_queue.c.

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

Referenced by load_realtime_queue().

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

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

Definition at line 1184 of file app_queue.c.

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

01185 {
01186    /* Free non-dynamic members */
01187    struct member *cur;
01188    struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
01189 
01190    while ((cur = ao2_iterator_next(&mem_iter))) {
01191       if (all || !cur->dynamic) {
01192          ao2_unlink(q->members, cur);
01193          remove_from_interfaces(cur->state_interface);
01194          q->membercount--;
01195       }
01196       ao2_ref(cur, -1);
01197    }
01198 }

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

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

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

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

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

Definition at line 5074 of file app_queue.c.

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

05075 {
05076    char *queuename, *interface, *membername = NULL, *state_interface = NULL;
05077    int penalty;
05078 
05079    if ((argc != 6) && (argc != 8) && (argc != 10) && (argc != 12)) {
05080       return RESULT_SHOWUSAGE;
05081    } else if (strcmp(argv[4], "to")) {
05082       return RESULT_SHOWUSAGE;
05083    } else if ((argc == 8) && strcmp(argv[6], "penalty")) {
05084       return RESULT_SHOWUSAGE;
05085    } else if ((argc == 10) && strcmp(argv[8], "as")) {
05086       return RESULT_SHOWUSAGE;
05087    } else if ((argc == 12) && strcmp(argv[10], "state_interface")) {
05088       return RESULT_SHOWUSAGE;
05089    }
05090 
05091    queuename = argv[5];
05092    interface = argv[3];
05093    if (argc >= 8) {
05094       if (sscanf(argv[7], "%30d", &penalty) == 1) {
05095          if (penalty < 0) {
05096             ast_cli(fd, "Penalty must be >= 0\n");
05097             penalty = 0;
05098          }
05099       } else {
05100          ast_cli(fd, "Penalty must be an integer >= 0\n");
05101          penalty = 0;
05102       }
05103    } else {
05104       penalty = 0;
05105    }
05106 
05107    if (argc >= 10) {
05108       membername = argv[9];
05109    }
05110 
05111    if (argc >= 12) {
05112       state_interface = argv[11];
05113    }
05114 
05115    switch (add_to_queue(queuename, interface, membername, penalty, 0, queue_persistent_members, state_interface)) {
05116    case RES_OKAY:
05117       ast_queue_log(queuename, "CLI", interface, "ADDMEMBER", "%s", "");
05118       ast_cli(fd, "Added interface '%s' to queue '%s'\n", interface, queuename);
05119       return RESULT_SUCCESS;
05120    case RES_EXISTS:
05121       ast_cli(fd, "Unable to add interface '%s' to queue '%s': Already there\n", interface, queuename);
05122       return RESULT_FAILURE;
05123    case RES_NOSUCHQUEUE:
05124       ast_cli(fd, "Unable to add interface to queue '%s': No such queue\n", queuename);
05125       return RESULT_FAILURE;
05126    case RES_OUTOFMEMORY:
05127       ast_cli(fd, "Out of memory\n");
05128       return RESULT_FAILURE;
05129    default:
05130       return RESULT_FAILURE;
05131    }
05132 }

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

Definition at line 5167 of file app_queue.c.

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

05168 {
05169    char *queuename, *interface;
05170 
05171    if (argc != 6) {
05172       return RESULT_SHOWUSAGE;
05173    } else if (strcmp(argv[4], "from")) {
05174       return RESULT_SHOWUSAGE;
05175    }
05176 
05177    queuename = argv[5];
05178    interface = argv[3];
05179 
05180    switch (remove_from_queue(queuename, interface)) {
05181    case RES_OKAY:
05182       ast_queue_log(queuename, "CLI", interface, "REMOVEMEMBER", "%s", "");
05183       ast_cli(fd, "Removed interface '%s' from queue '%s'\n", interface, queuename);
05184       return RESULT_SUCCESS;
05185    case RES_EXISTS:
05186       ast_cli(fd, "Unable to remove interface '%s' from queue '%s': Not there\n", interface, queuename);
05187       return RESULT_FAILURE;
05188    case RES_NOSUCHQUEUE:
05189       ast_cli(fd, "Unable to remove interface from queue '%s': No such queue\n", queuename);
05190       return RESULT_FAILURE;
05191    case RES_OUTOFMEMORY:
05192       ast_cli(fd, "Out of memory\n");
05193       return RESULT_FAILURE;
05194    case RES_NOT_DYNAMIC:
05195       ast_cli(fd, "Member not dynamic\n");
05196       return RESULT_FAILURE;
05197    default:
05198       return RESULT_FAILURE;
05199    }
05200 }

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 653 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, member_interface::list, LOG_DEBUG, option_debug, statechange::state, and update_status().

Referenced by device_state_thread().

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

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

Definition at line 1738 of file app_queue.c.

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

01739 {
01740    struct callattempt *oo;
01741 
01742    while (outgoing) {
01743       /* Hangup any existing lines we have open */
01744       if (outgoing->chan && (outgoing->chan != exception))
01745          ast_hangup(outgoing->chan);
01746       oo = outgoing;
01747       outgoing = outgoing->q_next;
01748       if (oo->member)
01749          ao2_ref(oo->member, -1);
01750       free(oo);
01751    }
01752 }

static void init_queue ( struct call_queue q  )  [static]

Definition at line 830 of file app_queue.c.

References call_queue::announce, call_queue::announcefrequency, call_queue::announceholdtime, ao2_container_alloc(), ast_copy_string(), call_queue::autofill, call_queue::context, call_queue::dead, DEFAULT_RETRY, call_queue::eventwhencalled, call_queue::found, call_queue::joinempty, call_queue::leavewhenempty, call_queue::maskmemberstatus, MAX_PERIODIC_ANNOUNCEMENTS, call_queue::maxlen, member_cmp_fn(), member_hash_fn(), call_queue::membercount, call_queue::memberdelay, call_queue::members, call_queue::moh, call_queue::monfmt, call_queue::monjoin, call_queue::montype, call_queue::periodicannouncefrequency, call_queue::reportholdtime, call_queue::retry, call_queue::ringinuse, call_queue::ringlimit, call_queue::roundingseconds, call_queue::servicelevel, call_queue::setinterfacevar, call_queue::sound_calls, call_queue::sound_holdtime, call_queue::sound_lessthan, call_queue::sound_minutes, call_queue::sound_next, call_queue::sound_periodicannounce, call_queue::sound_reporthold, call_queue::sound_seconds, call_queue::sound_thanks, call_queue::sound_thereare, call_queue::timeout, call_queue::timeoutrestart, call_queue::weight, and call_queue::wrapuptime.

Referenced by find_queue_by_name_rt(), and reload_queues().

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

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

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

Referenced by join_queue().

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

static char* int2strat ( int  strategy  )  [static]

Definition at line 499 of file app_queue.c.

References name, and strategies.

Referenced by __queues_show().

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

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

Definition at line 3352 of file app_queue.c.

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

Referenced by add_to_queue(), and set_member_paused().

03353 {
03354    struct member *mem;
03355    struct ao2_iterator mem_iter;
03356 
03357    if (!q)
03358       return NULL;
03359 
03360    mem_iter = ao2_iterator_init(q->members, 0);
03361    while ((mem = ao2_iterator_next(&mem_iter))) {
03362       if (!strcasecmp(interface, mem->interface))
03363          return mem;
03364       ao2_ref(mem, -1);
03365    }
03366 
03367    return NULL;
03368 }

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

Definition at line 917 of file app_queue.c.

References ao2_iterator_init(), ao2_iterator_next(), ao2_ref(), AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_mutex_lock(), ast_mutex_unlock(), member_interface::list, call_queue::lock, call_queue::members, and member::state_interface.

Referenced by remove_from_interfaces().

00918 {
00919    struct call_queue *q;
00920    struct member *mem;
00921    struct ao2_iterator mem_iter;
00922    int ret = 0;
00923 
00924    AST_LIST_LOCK(&queues);
00925    AST_LIST_TRAVERSE(&queues, q, list) {
00926       ast_mutex_lock(&q->lock);
00927       mem_iter = ao2_iterator_init(q->members, 0);
00928       while ((mem = ao2_iterator_next(&mem_iter))) {
00929          if (!strcasecmp(mem->state_interface, interface)) {
00930             ao2_ref(mem, -1);
00931             ret = 1;
00932             break;
00933          }
00934          ao2_ref(mem, -1);
00935       }
00936       ast_mutex_unlock(&q->lock);
00937       if (ret)
00938          break;
00939    }
00940    AST_LIST_UNLOCK(&queues);
00941 
00942    return ret;
00943 }

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

References ast_log(), ast_mutex_lock(), ast_mutex_unlock(), queue_ent::chan, call_queue::head, call_queue::lock, LOG_DEBUG, ast_channel::name, queue_ent::next, num_available_members(), option_debug, queue_ent::parent, and queue_ent::pending.

Referenced by queue_exec(), and wait_our_turn().

02455 {
02456    struct queue_ent *ch;
02457    int res;
02458    int avl;
02459    int idx = 0;
02460    /* This needs a lock. How many members are available to be served? */
02461    ast_mutex_lock(&qe->parent->lock);
02462 
02463    avl = num_available_members(qe->parent);
02464 
02465    ch = qe->parent->head;
02466 
02467    if (option_debug) {
02468       ast_log(LOG_DEBUG, "There %s %d available %s.\n", avl != 1 ? "are" : "is", avl, avl != 1 ? "members" : "member");
02469    }
02470 
02471    while ((idx < avl) && (ch) && (ch != qe)) {
02472       if (!ch->pending)
02473          idx++;
02474       ch = ch->next;       
02475    }
02476 
02477    ast_mutex_unlock(&qe->parent->lock);
02478 
02479    /* If the queue entry is within avl [the number of available members] calls from the top ... */
02480    if (ch && idx < avl) {
02481       if (option_debug)
02482          ast_log(LOG_DEBUG, "It's our turn (%s).\n", qe->chan->name);
02483       res = 1;
02484    } else {
02485       if (option_debug)
02486          ast_log(LOG_DEBUG, "It's not our turn (%s).\n", qe->chan->name);
02487       res = 0;
02488    }
02489 
02490    return res;
02491 }

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

Definition at line 1453 of file app_queue.c.

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

Referenced by queue_exec().

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

static void leave_queue ( struct queue_ent qe  )  [static]

Definition at line 1694 of file app_queue.c.

References AST_LIST_LOCK, AST_LIST_REMOVE, AST_LIST_UNLOCK, ast_log(), ast_mutex_lock(), ast_mutex_unlock(), queue_ent::chan, call_queue::count, call_queue::dead, destroy_queue(), EVENT_FLAG_CALL, call_queue::head, member_interface::list, call_queue::lock, LOG_DEBUG, manager_event(), call_queue::name, ast_channel::name, queue_ent::next, option_debug, queue_ent::parent, queue_ent::pos, and ast_channel::uniqueid.

Referenced by queue_exec(), and wait_our_turn().

01695 {
01696    struct call_queue *q;
01697    struct queue_ent *cur, *prev = NULL;
01698    int pos = 0;
01699 
01700    if (!(q = qe->parent))
01701       return;
01702    ast_mutex_lock(&q->lock);
01703 
01704    prev = NULL;
01705    for (cur = q->head; cur; cur = cur->next) {
01706       if (cur == qe) {
01707          q->count--;
01708 
01709          /* Take us out of the queue */
01710          manager_event(EVENT_FLAG_CALL, "Leave",
01711             "Channel: %s\r\nQueue: %s\r\nCount: %d\r\nUniqueid: %s\r\n",
01712             qe->chan->name, q->name,  q->count, qe->chan->uniqueid);
01713          if (option_debug)
01714             ast_log(LOG_DEBUG, "Queue '%s' Leave, Channel '%s'\n", q->name, qe->chan->name );
01715          /* Take us out of the queue */
01716          if (prev)
01717             prev->next = cur->next;
01718          else
01719             q->head = cur->next;
01720       } else {
01721          /* Renumber the people after us in the queue based on a new count */
01722          cur->pos = ++pos;
01723          prev = cur;
01724       }
01725    }
01726    ast_mutex_unlock(&q->lock);
01727 
01728    if (q->dead && !q->count) {   
01729       /* It's dead and nobody is in it, so kill it */
01730       AST_LIST_LOCK(&queues);
01731       AST_LIST_REMOVE(&queues, q, list);
01732       AST_LIST_UNLOCK(&queues);
01733       destroy_queue(q);
01734    }
01735 }

static int load_module ( void   )  [static]

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

05455 {
05456    int res;
05457 
05458    if (!reload_queues())
05459       return AST_MODULE_LOAD_DECLINE;
05460 
05461    if (queue_persistent_members)
05462       reload_queue_members();
05463 
05464    ast_mutex_init(&device_state.lock);
05465    ast_cond_init(&device_state.cond, NULL);
05466    ast_pthread_create(&device_state.thread, NULL, device_state_thread, NULL);
05467 
05468    ast_cli_register_multiple(cli_queue, sizeof(cli_queue) / sizeof(struct ast_cli_entry));
05469    res = ast_register_application(app, queue_exec, synopsis, descrip);
05470    res |= ast_register_application(app_aqm, aqm_exec, app_aqm_synopsis, app_aqm_descrip);
05471    res |= ast_register_application(app_rqm, rqm_exec, app_rqm_synopsis, app_rqm_descrip);
05472    res |= ast_register_application(app_pqm, pqm_exec, app_pqm_synopsis, app_pqm_descrip);
05473    res |= ast_register_application(app_upqm, upqm_exec, app_upqm_synopsis, app_upqm_descrip);
05474    res |= ast_register_application(app_ql, ql_exec, app_ql_synopsis, app_ql_descrip);
05475    res |= ast_manager_register("Queues", 0, manager_queues_show, "Queues");
05476    res |= ast_manager_register("QueueStatus", 0, manager_queues_status, "Queue Status");
05477    res |= ast_manager_register("QueueMemberCount", 0, manager_queue_member_count, "Queue Member Count");
05478    res |= ast_manager_register("QueueAdd", EVENT_FLAG_AGENT, manager_add_queue_member, "Add interface to queue.");
05479    res |= ast_manager_register("QueueRemove", EVENT_FLAG_AGENT, manager_remove_queue_member, "Remove interface from queue.");
05480    res |= ast_manager_register("QueuePause", EVENT_FLAG_AGENT, manager_pause_queue_member, "Makes a queue member temporarily unavailable");
05481    res |= ast_custom_function_register(&queueagentcount_function);
05482    res |= ast_custom_function_register(&queuemembercount_function);
05483    res |= ast_custom_function_register(&queuememberlist_function);
05484    res |= ast_custom_function_register(&queuewaitingcount_function);
05485    res |= ast_devstate_add(statechange_queue, NULL);
05486 
05487    return res;
05488 }

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

Definition at line 1403 of file app_queue.c.

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

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

01404 {
01405    struct ast_variable *queue_vars;
01406    struct ast_config *member_config = NULL;
01407    struct call_queue *q;
01408 
01409    /* Find the queue in the in-core list first. */
01410    AST_LIST_LOCK(&queues);
01411    AST_LIST_TRAVERSE(&queues, q, list) {
01412       if (!strcasecmp(q->name, queuename)) {
01413          break;
01414       }
01415    }
01416    AST_LIST_UNLOCK(&queues);
01417 
01418    if (!q || q->realtime) {
01419       /*! \note Load from realtime before taking the global qlock, to avoid blocking all
01420          queue operations while waiting for the DB.
01421 
01422          This will be two separate database transactions, so we might
01423          see queue parameters as they were before another process
01424          changed the queue and member list as it was after the change.
01425          Thus we might see an empty member list when a queue is
01426          deleted. In practise, this is unlikely to cause a problem. */
01427 
01428       queue_vars = ast_load_realtime("queues", "name", queuename, NULL);
01429       if (queue_vars) {
01430          member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", queuename, NULL);
01431          if (!member_config) {
01432             ast_log(LOG_ERROR, "no queue_members defined in your config (extconfig.conf).\n");
01433             ast_variables_destroy(queue_vars);
01434             return NULL;
01435          }
01436       }
01437 
01438       AST_LIST_LOCK(&queues);
01439 
01440       q = find_queue_by_name_rt(queuename, queue_vars, member_config);
01441       if (member_config)
01442          ast_config_destroy(member_config);
01443       if (queue_vars)
01444          ast_variables_destroy(queue_vars);
01445 
01446       AST_LIST_UNLOCK(&queues);
01447    } else { 
01448       update_realtime_members(q);
01449    }
01450    return q;
01451 }

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

Definition at line 4966 of file app_queue.c.

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

Referenced by load_module().

04967 {
04968    const char *queuename, *interface, *penalty_s, *paused_s, *membername, *state_interface;
04969    int paused, penalty = 0;
04970 
04971    queuename = astman_get_header(m, "Queue");
04972    interface = astman_get_header(m, "Interface");
04973    penalty_s = astman_get_header(m, "Penalty");
04974    paused_s = astman_get_header(m, "Paused");
04975    membername = astman_get_header(m, "MemberName");
04976    state_interface = astman_get_header(m, "StateInterface");
04977 
04978    if (ast_strlen_zero(queuename)) {
04979       astman_send_error(s, m, "'Queue' not specified.");
04980       return 0;
04981    }
04982 
04983    if (ast_strlen_zero(interface)) {
04984       astman_send_error(s, m, "'Interface' not specified.");
04985       return 0;
04986    }
04987 
04988    if (ast_strlen_zero(penalty_s))
04989       penalty = 0;
04990    else if (sscanf(penalty_s, "%30d", &penalty) != 1 || penalty < 0)
04991       penalty = 0;
04992 
04993    if (ast_strlen_zero(paused_s))
04994       paused = 0;
04995    else
04996       paused = abs(ast_true(paused_s));
04997 
04998    switch (add_to_queue(queuename, interface, membername, penalty, paused, queue_persistent_members, state_interface)) {
04999    case RES_OKAY:
05000       ast_queue_log(queuename, "MANAGER", interface, "ADDMEMBER", "%s", "");
05001       astman_send_ack(s, m, "Added interface to queue");
05002       break;
05003    case RES_EXISTS:
05004       astman_send_error(s, m, "Unable to add interface: Already there");
05005       break;
05006    case RES_NOSUCHQUEUE:
05007       astman_send_error(s, m, "Unable to add interface to queue: No such queue");
05008       break;
05009    case RES_OUTOFMEMORY:
05010       astman_send_error(s, m, "Out of memory");
05011       break;
05012    }
05013 
05014    return 0;
05015 }

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

Definition at line 5051 of file app_queue.c.

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

Referenced by load_module().

05052 {
05053    const char *queuename, *interface, *paused_s;
05054    int paused;
05055 
05056    interface = astman_get_header(m, "Interface");
05057    paused_s = astman_get_header(m, "Paused");
05058    queuename = astman_get_header(m, "Queue");   /* Optional - if not supplied, pause the given Interface in all queues */
05059 
05060    if (ast_strlen_zero(interface) || ast_strlen_zero(paused_s)) {
05061       astman_send_error(s, m, "Need 'Interface' and 'Paused' parameters.");
05062       return 0;
05063    }
05064 
05065    paused = abs(ast_true(paused_s));
05066 
05067    if (set_member_paused(queuename, interface, paused))
05068       astman_send_error(s, m, "Interface not found");
05069    else
05070       astman_send_ack(s, m, paused ? "Interface paused successfully" : "Interface unpaused successfully");
05071    return 0;
05072 }

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

Definition at line 5309 of file app_queue.c.

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

Referenced by load_module().

05310 {
05311    char buffer[256] = "";
05312    const char *queuename = astman_get_header(m,"Queue");
05313 
05314    if (ast_strlen_zero(queuename)) {
05315          astman_send_error(s, m, "'Queue' not specified.");
05316          return 0;
05317    }
05318    if (qmc_handler(queuename, buffer, sizeof(buffer)) == RESULT_SUCCESS) {
05319          astman_send_ack(s, m, buffer);
05320          return RESULT_SUCCESS;
05321    } else {
05322          astman_send_error(s, m, "Queue not found.");
05323          return 0;
05324    }
05325 }

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

Definition at line 4861 of file app_queue.c.

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

Referenced by load_module().

04862 {
04863    char *a[] = { "queue", "show" };
04864 
04865    __queues_show(s, 1, -1, 2, a);
04866    astman_append(s, "\r\n\r\n"); /* Properly terminate Manager output */
04867 
04868    return RESULT_SUCCESS;
04869 }

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

Definition at line 4872 of file app_queue.c.

References ao2_iterator_init(), ao2_iterator_next(), ao2_ref(), AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_mutex_lock(), ast_mutex_unlock(), ast_strlen_zero(), astman_append(), astman_get_header(), astman_send_ack(), member::calls, call_queue::callsabandoned, call_queue::callscompleted, call_queue::callscompletedinsl, queue_ent::chan, ast_channel::cid, ast_callerid::cid_name, ast_callerid::cid_num, call_queue::count, member::dynamic, call_queue::head, call_queue::holdtime, member::interface, member::lastcall, call_queue::lock, call_queue::maxlen, member::membername, call_queue::members, ast_channel::name, call_queue::name, queue_ent::next, member::paused, member::penalty, RESULT_SUCCESS, call_queue::ringlimit, s, S_OR, call_queue::servicelevel, queue_ent::start, member::status, and call_queue::weight.

Referenced by load_module().

04873 {
04874    time_t now;
04875    int pos;
04876    const char *id = astman_get_header(m,"ActionID");
04877    const char *queuefilter = astman_get_header(m,"Queue");
04878    const char *memberfilter = astman_get_header(m,"Member");
04879    char idText[256] = "";
04880    struct call_queue *q;
04881    struct queue_ent *qe;
04882    float sl = 0;
04883    struct member *mem;
04884    struct ao2_iterator mem_iter;
04885 
04886    astman_send_ack(s, m, "Queue status will follow");
04887    time(&now);
04888    AST_LIST_LOCK(&queues);
04889    if (!ast_strlen_zero(id))
04890       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
04891 
04892    AST_LIST_TRAVERSE(&queues, q, list) {
04893       ast_mutex_lock(&q->lock);
04894 
04895       /* List queue properties */
04896       if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) {
04897          sl = ((q->callscompleted > 0) ? 100 * ((float)q->callscompletedinsl / (float)q->callscompleted) : 0);
04898          astman_append(s, "Event: QueueParams\r\n"
04899             "Queue: %s\r\n"
04900             "Max: %d\r\n"
04901             "Calls: %d\r\n"
04902             "Holdtime: %d\r\n"
04903             "Completed: %d\r\n"
04904             "Abandoned: %d\r\n"
04905             "ServiceLevel: %d\r\n"
04906             "ServicelevelPerf: %2.1f\r\n"
04907             "RingLimit: %d\r\n"
04908             "Weight: %d\r\n"
04909             "%s"
04910             "\r\n",
04911             q->name, q->maxlen, q->count, q->holdtime, q->callscompleted,
04912             q->callsabandoned, q->servicelevel, sl,  q->ringlimit, q->weight, idText);
04913          /* List Queue Members */
04914          mem_iter = ao2_iterator_init(q->members, 0);
04915          while ((mem = ao2_iterator_next(&mem_iter))) {
04916             if (ast_strlen_zero(memberfilter) || !strcmp(mem->interface, memberfilter)) {
04917                astman_append(s, "Event: QueueMember\r\n"
04918                   "Queue: %s\r\n"
04919                   "Name: %s\r\n"
04920                   "Location: %s\r\n"
04921                   "Membership: %s\r\n"
04922                   "Penalty: %d\r\n"
04923                   "CallsTaken: %d\r\n"
04924                   "LastCall: %d\r\n"
04925                   "Status: %d\r\n"
04926                   "Paused: %d\r\n"
04927                   "%s"
04928                   "\r\n",
04929                   q->name, mem->membername, mem->interface, mem->dynamic ? "dynamic" : "static",
04930                   mem->penalty, mem->calls, (int)mem->lastcall, mem->status, mem->paused, idText);
04931             }
04932             ao2_ref(mem, -1);
04933          }
04934          /* List Queue Entries */
04935          pos = 1;
04936          for (qe = q->head; qe; qe = qe->next) {
04937             astman_append(s, "Event: QueueEntry\r\n"
04938                "Queue: %s\r\n"
04939                "Position: %d\r\n"
04940                "Channel: %s\r\n"
04941                "CallerID: %s\r\n"
04942                "CallerIDName: %s\r\n"
04943                "Wait: %ld\r\n"
04944                "%s"
04945                "\r\n",
04946                q->name, pos++, qe->chan->name,
04947                S_OR(qe->chan->cid.cid_num, "unknown"),
04948                S_OR(qe->chan->cid.cid_name, "unknown"),
04949                (long) (now - qe->start), idText);
04950          }
04951       }
04952       ast_mutex_unlock(&q->lock);
04953    }
04954 
04955    astman_append(s,
04956       "Event: QueueStatusComplete\r\n"
04957       "%s"
04958       "\r\n",idText);
04959 
04960    AST_LIST_UNLOCK(&queues);
04961 
04962 
04963    return RESULT_SUCCESS;
04964 }

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

Definition at line 5017 of file app_queue.c.

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

Referenced by load_module().

05018 {
05019    const char *queuename, *interface;
05020 
05021    queuename = astman_get_header(m, "Queue");
05022    interface = astman_get_header(m, "Interface");
05023 
05024    if (ast_strlen_zero(queuename) || ast_strlen_zero(interface)) {
05025       astman_send_error(s, m, "Need 'Queue' and 'Interface' parameters.");
05026       return 0;
05027    }
05028 
05029    switch (remove_from_queue(queuename, interface)) {
05030    case RES_OKAY:
05031       ast_queue_log(queuename, "MANAGER", interface, "REMOVEMEMBER", "%s", "");
05032       astman_send_ack(s, m, "Removed interface from queue");
05033       break;
05034    case RES_EXISTS:
05035       astman_send_error(s, m, "Unable to remove interface: Not there");
05036       break;
05037    case RES_NOSUCHQUEUE:
05038       astman_send_error(s, m, "Unable to remove interface from queue: No such queue");
05039       break;
05040    case RES_OUTOFMEMORY:
05041       astman_send_error(s, m, "Out of memory");
05042       break;
05043    case RES_NOT_DYNAMIC:
05044       astman_send_error(s, m, "Member not dynamic");
05045       break;
05046    }
05047 
05048    return 0;
05049 }

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

Definition at line 824 of file app_queue.c.

References member::interface.

Referenced by init_queue().

00825 {
00826    struct member *mem1 = obj1, *mem2 = obj2;
00827    return strcmp(mem1->interface, mem2->interface) ? 0 : CMP_MATCH | CMP_STOP;
00828 }

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

Definition at line 812 of file app_queue.c.

References compress_char(), and member::interface.

Referenced by init_queue().

00813 {
00814    const struct member *mem = obj;
00815    const char *chname = strchr(mem->interface, '/');
00816    int ret = 0, i;
00817    if (!chname)
00818       chname = mem->interface;
00819    for (i = 0; i < 5 && chname[i]; i++)
00820       ret += compress_char(chname[i]) << (i * 6);
00821    return ret;
00822 }

static void monjoin_dep_warning ( void   )  [static]

Definition at line 478 of file app_queue.c.

References ast_log(), and LOG_NOTICE.

Referenced by queue_set_param().

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

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

References ao2_iterator_init(), ao2_iterator_next(), ao2_ref(), AST_DEVICE_INUSE, AST_DEVICE_NOT_INUSE, 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().

01763 {
01764    struct member *mem;
01765    int avl = 0;
01766    struct ao2_iterator mem_iter;
01767 
01768    mem_iter = ao2_iterator_init(q->members, 0);
01769    while ((mem = ao2_iterator_next(&mem_iter))) {
01770       switch (mem->status) {
01771       case AST_DEVICE_INUSE:
01772          if (!q->ringinuse)
01773             break;
01774          /* else fall through */
01775       case AST_DEVICE_NOT_INUSE:
01776       case AST_DEVICE_UNKNOWN:
01777          if (!mem->paused) {
01778             avl++;
01779          }
01780          break;
01781       }
01782       ao2_ref(mem, -1);
01783 
01784       /* If autofill is not enabled or if the queue's strategy is ringall, then
01785        * we really don't care about the number of available members so much as we
01786        * do that there is at least one available.
01787        *
01788        * In fact, we purposely will return from this function stating that only
01789        * one member is available if either of those conditions hold. That way,
01790        * functions which determine what action to take based on the number of available
01791        * members will operate properly. The reasoning is that even if multiple
01792        * members are available, only the head caller can actually be serviced.
01793        */
01794       if ((!q->autofill || q->strategy == QUEUE_STRATEGY_RINGALL) && avl) {
01795          break;
01796       }
01797    }
01798 
01799    return avl;
01800 }

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

Definition at line 1518 of file app_queue.c.

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

Referenced by say_periodic_announcement(), and say_position().

01519 {
01520    int res;
01521 
01522    if (ast_strlen_zero(filename)) {
01523       return 0;
01524    }
01525 
01526    ast_stopstream(chan);
01527 
01528    res = ast_streamfile(chan, filename, chan->language);
01529    if (!res)
01530       res = ast_waitstream(chan, AST_DIGIT_ANY);
01531 
01532    ast_stopstream(chan);
01533 
01534    return res;
01535 }

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

Definition at line 3665 of file app_queue.c.

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

Referenced by load_module().

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

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

Definition at line 3923 of file app_queue.c.

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

Referenced by load_module().

03924 {
03925    struct ast_module_user *u;
03926    char *parse;
03927 
03928    AST_DECLARE_APP_ARGS(args,
03929       AST_APP_ARG(queuename);
03930       AST_APP_ARG(uniqueid);
03931       AST_APP_ARG(membername);
03932       AST_APP_ARG(event);
03933       AST_APP_ARG(params);
03934    );
03935 
03936    if (ast_strlen_zero(data)) {
03937       ast_log(LOG_WARNING, "QueueLog requires arguments (queuename|uniqueid|membername|event[|additionalinfo]\n");
03938       return -1;
03939    }
03940 
03941    u = ast_module_user_add(chan);
03942 
03943    parse = ast_strdupa(data);
03944 
03945    AST_STANDARD_APP_ARGS(args, parse);
03946 
03947    if (ast_strlen_zero(args.queuename) || ast_strlen_zero(args.uniqueid)
03948        || ast_strlen_zero(args.membername) || ast_strlen_zero(args.event)) {
03949       ast_log(LOG_WARNING, "QueueLog requires arguments (queuename|uniqueid|membername|event[|additionalinfo])\n");
03950       ast_module_user_remove(u);
03951       return -1;
03952    }
03953 
03954    ast_queue_log(args.queuename, args.uniqueid, args.membername, args.event, 
03955       "%s", args.params ? args.params : "");
03956 
03957    ast_module_user_remove(u);
03958 
03959    return 0;
03960 }

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

Definition at line 5294 of file app_queue.c.

References queue_member_count(), RESULT_FAILURE, and RESULT_SUCCESS.

Referenced by cli_queue_member_count(), and manager_queue_member_count().

05295 {
05296       struct member_count qmc;
05297       memset(&qmc, 0, sizeof(qmc));
05298       if (queue_member_count(queuename, &qmc) != 0) {
05299             return RESULT_FAILURE; 
05300       } else {
05301             snprintf(buffer, len, 
05302                          "valid:%d inuse:%d paused:%d active:%d free:%d all:%d",
05303                          qmc.valid, qmc.inuse, qmc.paused, qmc.active, qmc.free, qmc.all);   
05304             return RESULT_SUCCESS;
05305       }
05306 }

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

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

Referenced by load_module().

03975 {
03976    int res=-1;
03977    int ringing=0;
03978    struct ast_module_user *lu;
03979    const char *user_priority;
03980    const char *max_penalty_str;
03981    int prio;
03982    int max_penalty;
03983    enum queue_result reason = QUEUE_UNKNOWN;
03984    /* whether to exit Queue application after the timeout hits */
03985    int tries = 0;
03986    int noption = 0;
03987    char *parse;
03988    AST_DECLARE_APP_ARGS(args,
03989       AST_APP_ARG(queuename);
03990       AST_APP_ARG(options);
03991       AST_APP_ARG(url);
03992       AST_APP_ARG(announceoverride);
03993       AST_APP_ARG(queuetimeoutstr);
03994       AST_APP_ARG(agi);
03995    );
03996    /* Our queue entry */
03997    struct queue_ent qe;
03998    
03999    if (ast_strlen_zero(data)) {
04000       ast_log(LOG_WARNING, "Queue requires an argument: queuename[|options[|URL[|announceoverride[|timeout[|agi]]]]]\n");
04001       return -1;
04002    }
04003    
04004    parse = ast_strdupa(data);
04005    AST_STANDARD_APP_ARGS(args, parse);
04006 
04007    lu = ast_module_user_add(chan);
04008 
04009    /* Setup our queue entry */
04010    memset(&qe, 0, sizeof(qe));
04011    qe.start = time(NULL);
04012 
04013    /* set the expire time based on the supplied timeout; */
04014    if (!ast_strlen_zero(args.queuetimeoutstr))
04015       qe.expire = qe.start + atoi(args.queuetimeoutstr);
04016    else
04017       qe.expire = 0;
04018 
04019    /* Get the priority from the variable ${QUEUE_PRIO} */
04020    user_priority = pbx_builtin_getvar_helper(chan, "QUEUE_PRIO");
04021    if (user_priority) {
04022       if (sscanf(user_priority, "%30d", &prio) == 1) {
04023          if (option_debug)
04024             ast_log(LOG_DEBUG, "%s: Got priority %d from ${QUEUE_PRIO}.\n",
04025                chan->name, prio);
04026       } else {
04027          ast_log(LOG_WARNING, "${QUEUE_PRIO}: Invalid value (%s), channel %s.\n",
04028             user_priority, chan->name);
04029          prio = 0;
04030       }
04031    } else {
04032       if (option_debug > 2)
04033          ast_log(LOG_DEBUG, "NO QUEUE_PRIO variable found. Using default.\n");
04034       prio = 0;
04035    }
04036 
04037    /* Get the maximum penalty from the variable ${QUEUE_MAX_PENALTY} */
04038    if ((max_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MAX_PENALTY"))) {
04039       if (sscanf(max_penalty_str, "%30d", &max_penalty) == 1) {
04040          if (option_debug)
04041             ast_log(LOG_DEBUG, "%s: Got max penalty %d from ${QUEUE_MAX_PENALTY}.\n",
04042                chan->name, max_penalty);
04043       } else {
04044          ast_log(LOG_WARNING, "${QUEUE_MAX_PENALTY}: Invalid value (%s), channel %s.\n",
04045             max_penalty_str, chan->name);
04046          max_penalty = 0;
04047       }
04048    } else {
04049       max_penalty = 0;
04050    }
04051 
04052    if (args.options && (strchr(args.options, 'r')))
04053       ringing = 1;
04054 
04055    if (option_debug)
04056       ast_log(LOG_DEBUG, "queue: %s, options: %s, url: %s, announce: %s, expires: %ld, priority: %d\n",
04057          args.queuename, args.options, args.url, args.announceoverride, (long)qe.expire, prio);
04058 
04059    qe.chan = chan;
04060    qe.prio = prio;
04061    qe.max_penalty = max_penalty;
04062    qe.last_pos_said = 0;
04063    qe.last_pos = 0;
04064    qe.last_periodic_announce_time = time(NULL);
04065    qe.last_periodic_announce_sound = 0;
04066    qe.valid_digits = 0;
04067    if (!join_queue(args.queuename, &qe, &reason)) {
04068       int makeannouncement = 0;
04069 
04070       ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ENTERQUEUE", "%s|%s", S_OR(args.url, ""),
04071          S_OR(chan->cid.cid_num, ""));
04072 check_turns:
04073       if (ringing) {
04074          ast_indicate(chan, AST_CONTROL_RINGING);
04075       } else {
04076          ast_moh_start(chan, qe.moh, NULL);
04077       }
04078 
04079       /* This is the wait loop for callers 2 through maxlen */
04080       res = wait_our_turn(&qe, ringing, &reason);
04081       if (res)
04082          goto stop;
04083 
04084       for (;;) {
04085          /* This is the wait loop for the head caller*/
04086          /* To exit, they may get their call answered; */
04087          /* they may dial a digit from the queue context; */
04088          /* or, they may timeout. */
04089 
04090          enum queue_member_status stat;
04091 
04092          /* Leave if we have exceeded our queuetimeout */
04093          if (qe.expire && (time(NULL) >= qe.expire)) {
04094             record_abandoned(&qe);
04095             ast_cdr_noanswer(qe.chan->cdr);
04096             reason = QUEUE_TIMEOUT;
04097             res = 0;
04098             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
04099             break;
04100          }
04101 
04102          if (makeannouncement) {
04103             /* Make a position announcement, if enabled */
04104             if (qe.parent->announcefrequency && !ringing)
04105                if ((res = say_position(&qe)))
04106                   goto stop;
04107 
04108          }
04109          makeannouncement = 1;
04110 
04111          /* Leave if we have exceeded our queuetimeout */
04112          if (qe.expire && (time(NULL) >= qe.expire)) {
04113             record_abandoned(&qe);
04114             ast_cdr_noanswer(qe.chan->cdr);
04115             reason = QUEUE_TIMEOUT;
04116             res = 0;
04117             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
04118             break;
04119          }
04120          /* Make a periodic announcement, if enabled */
04121          if (qe.parent->periodicannouncefrequency && !ringing)
04122             if ((res = say_periodic_announcement(&qe)))
04123                goto stop;
04124 
04125          /* Leave if we have exceeded our queuetimeout */
04126          if (qe.expire && (time(NULL) >= qe.expire)) {
04127             record_abandoned(&qe);
04128             ast_cdr_noanswer(qe.chan->cdr);
04129             reason = QUEUE_TIMEOUT;
04130             res = 0;
04131             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
04132             break;
04133          }
04134          /* Try calling all queue members for 'timeout' seconds */
04135          res = try_calling(&qe, args.options, args.announceoverride, args.url, &tries, &noption, args.agi);
04136          if (res)
04137             goto stop;
04138 
04139          stat = get_member_status(qe.parent, qe.max_penalty);
04140 
04141          /* exit after 'timeout' cycle if 'n' option enabled */
04142          if (noption && tries >= qe.parent->membercount) {
04143             if (option_verbose > 2)
04144                ast_verbose(VERBOSE_PREFIX_3 "Exiting on time-out cycle\n");
04145             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
04146             record_abandoned(&qe);
04147             ast_cdr_noanswer(qe.chan->cdr);
04148             reason = QUEUE_TIMEOUT;
04149             res = 0;
04150             break;
04151          }
04152 
04153          /* leave the queue if no agents, if enabled */
04154          if (qe.parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) {
04155             record_abandoned(&qe);
04156             ast_cdr_noanswer(qe.chan->cdr);
04157             reason = QUEUE_LEAVEEMPTY;
04158             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
04159             res = 0;
04160             break;
04161          }
04162 
04163          /* leave the queue if no reachable agents, if enabled */
04164          if ((qe.parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) {
04165             record_abandoned(&qe);
04166             ast_cdr_noanswer(qe.chan->cdr);
04167             reason = QUEUE_LEAVEUNAVAIL;
04168             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
04169             res = 0;
04170             break;
04171          }
04172 
04173          /* Leave if we have exceeded our queuetimeout */
04174          if (qe.expire && (time(NULL) >= qe.expire)) {
04175             record_abandoned(&qe);
04176             ast_cdr_noanswer(qe.chan->cdr);
04177             reason = QUEUE_TIMEOUT;
04178             res = 0;
04179             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
04180             break;
04181          }
04182 
04183          /* If using dynamic realtime members, we should regenerate the member list for this queue */
04184          update_realtime_members(qe.parent);
04185 
04186          /* OK, we didn't get anybody; wait for 'retry' seconds; may get a digit to exit with */
04187          res = wait_a_bit(&qe);
04188          if (res)
04189             goto stop;
04190 
04191          /* Since this is a priority queue and
04192           * it is not sure that we are still at the head
04193           * of the queue, go and check for our turn again.
04194           */
04195          if (!is_our_turn(&qe)) {
04196             if (option_debug)
04197                ast_log(LOG_DEBUG, "Darn priorities, going back in queue (%s)!\n",
04198                   qe.chan->name);
04199             goto check_turns;
04200          }
04201       }
04202 
04203 stop:
04204       if (res) {
04205          if (res < 0) {
04206             if (!qe.handled) {
04207                record_abandoned(&qe);
04208                ast_cdr_noanswer(qe.chan->cdr);
04209                ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ABANDON",
04210                   "%d|%d|%ld", qe.pos, qe.opos,
04211                   (long) time(NULL) - qe.start);
04212             }
04213             res = -1;
04214          } else if (qe.valid_digits) {
04215             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHKEY",
04216                "%s|%d", qe.digits, qe.pos);
04217          }
04218       }
04219 
04220       /* Don't allow return code > 0 */
04221       if (res >= 0) {
04222          res = 0; 
04223          if (ringing) {
04224             ast_indicate(chan, -1);
04225          } else {
04226             ast_moh_stop(chan);
04227          }        
04228          ast_stopstream(chan);
04229       }
04230       leave_queue(&qe);
04231       if (reason != QUEUE_UNKNOWN)
04232          set_queue_result(chan, reason);
04233    } else {
04234       ast_log(LOG_WARNING, "Unable to join queue '%s'\n", args.queuename);
04235       set_queue_result(chan, reason);
04236       res = 0;
04237    }
04238    ast_module_user_remove(lu);
04239 
04240    return res;
04241 }

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

Definition at line 4251 of file app_queue.c.

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

04252 {
04253    int count = 0;
04254    struct call_queue *q;
04255    struct ast_module_user *lu;
04256    struct member *m;
04257    struct ao2_iterator mem_iter;
04258    char *name, *item;
04259    enum qmc_status mode = QMC_VALID;
04260 
04261    buf[0] = '\0';
04262    
04263    if (ast_strlen_zero(data)) {
04264       ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
04265       return -1;
04266    }
04267 
04268    name = ast_strdupa(data);
04269  
04270    lu = ast_module_user_add(chan);
04271 
04272    if ((item = strchr(name, ':'))) {
04273       *item = '\0';
04274       item++;
04275    } else {
04276       item = "";
04277    }
04278 
04279    if (!strcasecmp(item, "valid")) {
04280       mode = QMC_VALID;
04281    } else  if (!strcasecmp(item, "paused")) {
04282       mode = QMC_PAUSED;
04283    } else  if (!strcasecmp(item, "active")) {
04284       mode = QMC_ACTIVE;
04285    } else  if (!strcasecmp(item, "free")) {
04286       mode = QMC_FREE;
04287    } else  if (!strcasecmp(item, "all")) {
04288       mode = QMC_ALL;
04289    }
04290 
04291    if ((q = load_realtime_queue(name))) {
04292       ast_mutex_lock(&q->lock);
04293       mem_iter = ao2_iterator_init(q->members, 0);
04294       while ((m = ao2_iterator_next(&mem_iter))) {
04295          switch (mode) {
04296          case QMC_VALID:
04297             /* Count the queue members who are logged in and presently answering calls */
04298             if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
04299                count++;
04300             }
04301             break;
04302          case QMC_PAUSED:
04303             /* Count paused members */
04304             if (m->paused) {
04305                count++;
04306             }
04307             break;
04308          case QMC_ACTIVE:
04309             /* Count not paused members who are logged in and presently answering calls */
04310             if (!m->paused && (m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
04311                count++;
04312             }
04313             break;
04314          case QMC_FREE:
04315             /* Count free members in the queue */
04316             if (!m->paused && ((m->status == AST_DEVICE_UNKNOWN) || (m->status == AST_DEVICE_NOT_INUSE))) {
04317                count++;
04318             }
04319             break;
04320          default:
04321             count++;
04322             break;
04323          }
04324          ao2_ref(m, -1);
04325       }
04326       ast_mutex_unlock(&q->lock);
04327    } else
04328       ast_log(LOG_WARNING, "queue %s was not found\n", name);
04329 
04330    snprintf(buf, len, "%d", count);
04331    ast_module_user_remove(lu);
04332 
04333    return 0;
04334 }

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

Definition at line 4379 of file app_queue.c.

References ao2_iterator_init(), ao2_iterator_next(), ao2_ref(), AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), ast_module_user_add, ast_module_user_remove, ast_mutex_lock(), ast_mutex_unlock(), ast_strlen_zero(), call_queue::lock, LOG_ERROR, LOG_WARNING, member::membername, call_queue::members, and call_queue::name.

04380 {
04381    struct ast_module_user *u;
04382    struct call_queue *q;
04383    struct member *m;
04384 
04385    /* Ensure an otherwise empty list doesn't return garbage */
04386    buf[0] = '\0';
04387 
04388    if (ast_strlen_zero(data)) {
04389       ast_log(LOG_ERROR, "QUEUE_MEMBER_LIST requires an argument: queuename\n");
04390       return -1;
04391    }
04392    
04393    u = ast_module_user_add(chan);
04394 
04395    AST_LIST_LOCK(&queues);
04396    AST_LIST_TRAVERSE(&queues, q, list) {
04397       if (!strcasecmp(q->name, data)) {
04398          ast_mutex_lock(&q->lock);
04399          break;
04400       }
04401    }
04402    AST_LIST_UNLOCK(&queues);
04403 
04404    if (q) {
04405       int buflen = 0, count = 0;
04406       struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
04407 
04408       while ((m = ao2_iterator_next(&mem_iter))) {
04409          /* strcat() is always faster than printf() */
04410          if (count++) {
04411             strncat(buf + buflen, ",", len - buflen - 1);
04412             buflen++;
04413          }
04414          strncat(buf + buflen, m->membername, len - buflen - 1);
04415          buflen += strlen(m->membername);
04416          /* Safeguard against overflow (negative length) */
04417          if (buflen >= len - 2) {
04418             ao2_ref(m, -1);
04419             ast_log(LOG_WARNING, "Truncating list\n");
04420             break;
04421          }
04422          ao2_ref(m, -1);
04423       }
04424       ast_mutex_unlock(&q->lock);
04425    } else
04426       ast_log(LOG_WARNING, "queue %s was not found\n", data);
04427 
04428    /* We should already be terminated, but let's make sure. */
04429    buf[len - 1] = '\0';
04430    ast_module_user_remove(u);
04431 
04432    return 0;
04433 }

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

Definition at line 4336 of file app_queue.c.

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

04337 {
04338    int count = 0;
04339    struct call_queue *q;
04340    struct ast_module_user *lu;
04341    struct ast_variable *var = NULL;
04342 
04343    buf[0] = '\0';
04344    
04345    if (ast_strlen_zero(data)) {
04346       ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
04347       return -1;
04348    }
04349 
04350    lu = ast_module_user_add(chan);
04351    
04352    AST_LIST_LOCK(&queues);
04353    AST_LIST_TRAVERSE(&queues, q, list) {
04354       if (!strcasecmp(q->name, data)) {
04355          ast_mutex_lock(&q->lock);
04356          break;
04357       }
04358    }
04359    AST_LIST_UNLOCK(&queues);
04360 
04361    if (q) {
04362       count = q->count;
04363       ast_mutex_unlock(&q->lock);
04364    } else if ((var = ast_load_realtime("queues", "name", data, NULL))) {
04365       /* if the queue is realtime but was not found in memory, this
04366        * means that the queue had been deleted from memory since it was 
04367        * "dead." This means it has a 0 waiting count
04368        */
04369       count = 0;
04370       ast_variables_destroy(var);
04371    } else
04372       ast_log(LOG_WARNING, "queue %s was not found\n", data);
04373 
04374    snprintf(buf, len, "%d", count);
04375    ast_module_user_remove(lu);
04376    return 0;
04377 }

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

Definition at line 5252 of file app_queue.c.

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

Referenced by qmc_handler().

05253 {
05254       int res = 0;
05255       struct call_queue *q;
05256       struct member *m;
05257       struct ao2_iterator mem_iter;
05258       
05259       if ((q = load_realtime_queue(qname))) {
05260             ast_mutex_lock(&q->lock);
05261             mem_iter = ao2_iterator_init(q->members, 0);
05262             while ((m = ao2_iterator_next(&mem_iter))) {
05263                   /* Count the queue members in use */
05264                   if (m->status == AST_DEVICE_INUSE) {
05265                         qmc->inuse++;
05266                   }
05267                   /* Count the queue members who are logged in and presently answering calls */
05268                   if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
05269                         qmc->valid++;
05270                   }
05271                   /* Count paused members */
05272                   if (m->paused) {
05273                         qmc->paused++;
05274                   }
05275                   /* Count not paused members who are logged in and presently answering calls */
05276                   if (!m->paused && (m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
05277                         qmc->active++;
05278                   }
05279                   /* Count free members in the queue */
05280                   if (!m->paused && ((m->status == AST_DEVICE_UNKNOWN) || (m->status == AST_DEVICE_NOT_INUSE))) {
05281                         qmc->free++;
05282                   }
05283                   qmc->all++;
05284                   ao2_ref(m, -1);
05285             }
05286             ast_mutex_unlock(&q->lock);
05287       } else {
05288             ast_log(LOG_WARNING, "Queue %s was not found\n", qname);
05289             res = -1;
05290       }
05291       return res;
05292 }

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

References call_queue::announce, call_queue::announcefrequency, call_queue::announceholdtime, ANNOUNCEHOLDTIME_ALWAYS, ANNOUNCEHOLDTIME_ONCE, ast_copy_string(), ast_log(), ast_strdupa, ast_true(), call_queue::autofill, call_queue::autopause, call_queue::context, DEFAULT_RETRY, DEFAULT_TIMEOUT, call_queue::eventwhencalled, call_queue::joinempty, call_queue::leavewhenempty, LOG_WARNING, call_queue::maskmemberstatus, MAX_PERIODIC_ANNOUNCEMENTS, call_queue::maxlen, call_queue::memberdelay, call_queue::moh, call_queue::monfmt, call_queue::monjoin, monjoin_dep_warning(), call_queue::montype, call_queue::name, call_queue::periodicannouncefrequency, QUEUE_EMPTY_NORMAL, QUEUE_EMPTY_STRICT, QUEUE_EVENT_VARIABLES, QUEUE_STRATEGY_RINGALL, call_queue::reportholdtime, call_queue::retry, call_queue::ringinuse, call_queue::ringlimit, call_queue::roundingseconds, s, call_queue::servicelevel, call_queue::setinterfacevar, call_queue::sound_calls, call_queue::sound_holdtime, call_queue::sound_lessthan, call_queue::sound_minutes, call_queue::sound_next, call_queue::sound_periodicannounce, call_queue::sound_reporthold, call_queue::sound_seconds, call_queue::sound_thanks, call_queue::sound_thereare, strat2int(), call_queue::strategy, call_queue::timeout, call_queue::timeoutrestart, call_queue::weight, and call_queue::wrapuptime.

Referenced by find_queue_by_name_rt(), and reload_queues().

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

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

Definition at line 4827 of file app_queue.c.

References __queues_show().

Referenced by __queues_show().

04828 {
04829    return __queues_show(NULL, 0, fd, argc, argv);
04830 }

static void queue_transfer_destroy ( void *  data  )  [static]

Definition at line 2663 of file app_queue.c.

References ast_free.

02664 {
02665    struct queue_transfer_ds *qtds = data;
02666    ast_free(qtds);
02667 }

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

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

02687 {
02688    struct queue_transfer_ds *qtds = data;
02689    struct queue_ent *qe = qtds->qe;
02690    struct member *member = qtds->member;
02691    time_t callstart = qtds->starttime;
02692    int callcompletedinsl = qtds->callcompletedinsl;
02693    struct ast_datastore *datastore;
02694 
02695    ast_queue_log(qe->parent->name, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld",
02696             new_chan->exten, new_chan->context, (long) (callstart - qe->start),
02697             (long) (time(NULL) - callstart));
02698 
02699    update_queue(qe->parent, member, callcompletedinsl);
02700    
02701    /* No need to lock the channels because they are already locked in ast_do_masquerade */
02702    if ((datastore = ast_channel_datastore_find(old_chan, &queue_transfer_info, NULL))) {
02703       ast_channel_datastore_remove(old_chan, datastore);
02704    } else {
02705       ast_log(LOG_WARNING, "Can't find the queue_transfer datastore.\n");
02706    }
02707 }

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

Definition at line 1679 of file app_queue.c.

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

01680 {
01681    int oldvalue;
01682 
01683    /* Calculate holdtime using an exponential average */
01684    /* Thanks to SRT for this contribution */
01685    /* 2^2 (4) is the filter coefficient; a higher exponent would give old entries more weight */
01686 
01687    ast_mutex_lock(&qe->parent->lock);
01688    oldvalue = qe->parent->holdtime;
01689    qe->parent->holdtime = (((oldvalue << 2) - oldvalue) + newholdtime) >> 2;
01690    ast_mutex_unlock(&qe->parent->lock);
01691 }

static void record_abandoned ( struct queue_ent qe  )  [static]

Definition at line 2161 of file app_queue.c.

References ast_mutex_lock(), ast_mutex_unlock(), call_queue::callsabandoned, queue_ent::chan, EVENT_FLAG_AGENT, call_queue::lock, manager_event(), call_queue::name, queue_ent::opos, queue_ent::parent, queue_ent::pos, queue_ent::start, and ast_channel::uniqueid.

Referenced by queue_exec().

02162 {
02163    ast_mutex_lock(&qe->parent->lock);
02164    manager_event(EVENT_FLAG_AGENT, "QueueCallerAbandon",
02165       "Queue: %s\r\n"
02166       "Uniqueid: %s\r\n"
02167       "Position: %d\r\n"
02168       "OriginalPosition: %d\r\n"
02169       "HoldTime: %d\r\n",
02170       qe->parent->name, qe->chan->uniqueid, qe->pos, qe->opos, (int)(time(NULL) - qe->start));
02171 
02172    qe->parent->callsabandoned++;
02173    ast_mutex_unlock(&qe->parent->lock);
02174 }

static int reload ( void   )  [static]

Definition at line 5490 of file app_queue.c.

References reload_queues().

05491 {
05492    reload_queues();
05493    return 0;
05494 }

static void reload_queue_members ( void   )  [static]

Definition at line 3568 of file app_queue.c.

References add_to_queue(), ast_db_del(), ast_db_freetree(), ast_db_get(), ast_db_gettree(), AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), ast_mutex_lock(), ast_mutex_unlock(), ast_strlen_zero(), errno, member::interface, ast_db_entry::key, call_queue::list, load_realtime_queue(), call_queue::lock, LOG_DEBUG, LOG_ERROR, LOG_NOTICE, LOG_WARNING, call_queue::name, ast_db_entry::next, member::paused, member::penalty, PM_MAX_LEN, RES_OUTOFMEMORY, and member::state_interface.

Referenced by load_module().

03569 {
03570    char *cur_ptr;
03571    char *queue_name;
03572    char *member;
03573    char *interface;
03574    char *membername = NULL;
03575    char *state_interface;
03576    char *penalty_tok;
03577    int penalty = 0;
03578    char *paused_tok;
03579    int paused = 0;
03580    struct ast_db_entry *db_tree;
03581    struct ast_db_entry *entry;
03582    struct call_queue *cur_queue;
03583    char queue_data[PM_MAX_LEN];
03584 
03585    AST_LIST_LOCK(&queues);
03586 
03587    /* Each key in 'pm_family' is the name of a queue */
03588    db_tree = ast_db_gettree(pm_family, NULL);
03589    for (entry = db_tree; entry; entry = entry->next) {
03590 
03591       queue_name = entry->key + strlen(pm_family) + 2;
03592 
03593       AST_LIST_TRAVERSE(&queues, cur_queue, list) {
03594          ast_mutex_lock(&cur_queue->lock);
03595          if (!strcmp(queue_name, cur_queue->name))
03596             break;
03597          ast_mutex_unlock(&cur_queue->lock);
03598       }
03599       
03600       if (!cur_queue)
03601          cur_queue = load_realtime_queue(queue_name);
03602 
03603       if (!cur_queue) {
03604          /* If the queue no longer exists, remove it from the
03605           * database */
03606          ast_log(LOG_WARNING, "Error loading persistent queue: '%s': it does not exist\n", queue_name);
03607          ast_db_del(pm_family, queue_name);
03608          continue;
03609       } else
03610          ast_mutex_unlock(&cur_queue->lock);
03611 
03612       if (ast_db_get(pm_family, queue_name, queue_data, PM_MAX_LEN))
03613          continue;
03614 
03615       cur_ptr = queue_data;
03616       while ((member = strsep(&cur_ptr, "|"))) {
03617          if (ast_strlen_zero(member))
03618             continue;
03619 
03620          interface = strsep(&member, ";");
03621          penalty_tok = strsep(&member, ";");
03622          paused_tok = strsep(&member, ";");
03623          membername = strsep(&member, ";");
03624          state_interface = strsep(&member,";");
03625 
03626          if (!penalty_tok) {
03627             ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (penalty)\n", queue_name);
03628             break;
03629          }
03630          penalty = strtol(penalty_tok, NULL, 10);
03631          if (errno == ERANGE) {
03632             ast_log(LOG_WARNING, "Error converting penalty: %s: Out of range.\n", penalty_tok);
03633             break;
03634          }
03635          
03636          if (!paused_tok) {
03637             ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (paused)\n", queue_name);
03638             break;
03639          }
03640          paused = strtol(paused_tok, NULL, 10);
03641          if ((errno == ERANGE) || paused < 0 || paused > 1) {
03642             ast_log(LOG_WARNING, "Error converting paused: %s: Expected 0 or 1.\n", paused_tok);
03643             break;
03644          }
03645          if (ast_strlen_zero(membername))
03646             membername = interface;
03647 
03648          if (option_debug)
03649             ast_log(LOG_DEBUG, "Reload Members: Queue: %s  Member: %s  Name: %s  Penalty: %d  Paused: %d\n", queue_name, interface, membername, penalty, paused);
03650          
03651          if (add_to_queue(queue_name, interface, membername, penalty, paused, 0, state_interface) == RES_OUTOFMEMORY) {
03652             ast_log(LOG_ERROR, "Out of Memory when reloading persistent queue member\n");
03653             break;
03654          }
03655       }
03656    }
03657 
03658    AST_LIST_UNLOCK(&queues);
03659    if (db_tree) {
03660       ast_log(LOG_NOTICE, "Queue members successfully reloaded from database.\n");
03661       ast_db_freetree(db_tree);
03662    }
03663 }

static int reload_queues ( void   )  [static]

Definition at line 4478 of file app_queue.c.

References add_to_interfaces(), alloc_queue(), ao2_find(), ao2_iterator_init(), ao2_iterator_next(), ao2_ref(), ao2_unlink(), AST_APP_ARG, ast_category_browse(), ast_config_load(), ast_copy_string(), AST_DECLARE_APP_ARGS, AST_LIST_INSERT_HEAD, AST_LIST_LOCK, AST_LIST_TRAVERSE, ast_log(), ast_mutex_lock(), ast_mutex_unlock(), AST_NONSTANDARD_APP_ARGS, ast_skip_blanks(), 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(), call_queue::lock, LOG_NOTICE, LOG_WARNING, call_queue::membercount, call_queue::members, call_queue::name, parse(), member::paused, queue_set_param(), QUEUE_STRATEGY_ROUNDROBIN, call_queue::realtime, remove_from_interfaces(), rr_dep_warning(), member::state_interface, call_queue::strategy, and var.

Referenced by load_module(), and reload().

04479 {
04480    struct call_queue *q;
04481    struct ast_config *cfg;
04482    char *cat, *tmp;
04483    struct ast_variable *var;
04484    struct member *cur, *newm;
04485    struct ao2_iterator mem_iter;
04486    int new;
04487    const char *general_val = NULL;
04488    char parse[80];
04489    char *interface, *state_interface;
04490    char *membername = NULL;
04491    int penalty;
04492    AST_DECLARE_APP_ARGS(args,
04493       AST_APP_ARG(interface);
04494       AST_APP_ARG(penalty);
04495       AST_APP_ARG(membername);
04496       AST_APP_ARG(state_interface);
04497    );
04498    
04499    if (!(cfg = ast_config_load("queues.conf"))) {
04500       ast_log(LOG_NOTICE, "No call queueing config file (queues.conf), so no call queues\n");
04501       return 0;
04502    }
04503    AST_LIST_LOCK(&queues);
04504    use_weight=0;
04505    /* Mark all non-realtime queues as dead for the moment */
04506    AST_LIST_TRAVERSE(&queues, q, list) {
04507       if (!q->realtime) {
04508          q->dead = 1;
04509          q->found = 0;
04510       }
04511    }
04512 
04513    /* Chug through config file */
04514    cat = NULL;
04515    while ((cat = ast_category_browse(cfg, cat)) ) {
04516       if (!strcasecmp(cat, "general")) {  
04517          /* Initialize global settings */
04518          queue_debug = 0;
04519          if ((general_val = ast_variable_retrieve(cfg, "general", "debug")))
04520             queue_debug = ast_true(general_val);
04521          queue_persistent_members = 0;
04522          if ((general_val = ast_variable_retrieve(cfg, "general", "persistentmembers")))
04523             queue_persistent_members = ast_true(general_val);
04524          autofill_default = 0;
04525          if ((general_val = ast_variable_retrieve(cfg, "general", "autofill")))
04526             autofill_default = ast_true(general_val);
04527          montype_default = 0;
04528          if ((general_val = ast_variable_retrieve(cfg, "general", "monitor-type")))
04529             if (!strcasecmp(general_val, "mixmonitor"))
04530                montype_default = 1;
04531       } else { /* Define queue */
04532          /* Look for an existing one */
04533          AST_LIST_TRAVERSE(&queues, q, list) {
04534             if (!strcmp(q->name, cat))
04535                break;
04536          }
04537          if (!q) {
04538             /* Make one then */
04539             if (!(q = alloc_queue(cat))) {
04540                /* TODO: Handle memory allocation failure */
04541             }
04542             new = 1;
04543          } else
04544             new = 0;
04545          if (q) {
04546             if (!new)
04547                ast_mutex_lock(&q->lock);
04548             /* Check if a queue with this name already exists */
04549             if (q->found) {
04550                ast_log(LOG_WARNING, "Queue '%s' already defined! Skipping!\n", cat);
04551                if (!new)
04552                   ast_mutex_unlock(&q->lock);
04553                continue;
04554             }
04555             /* Re-initialize the queue, and clear statistics */
04556             init_queue(q);
04557             clear_queue(q);
04558             mem_iter = ao2_iterator_init(q->members, 0);
04559             while ((cur = ao2_iterator_next(&mem_iter))) {
04560                if (!cur->dynamic) {
04561                   cur->delme = 1;
04562                }
04563                ao2_ref(cur, -1);
04564             }
04565             for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
04566                if (!strcasecmp(var->name, "member")) {
04567                   struct member tmpmem;
04568                   membername = NULL;
04569 
04570                   if (ast_strlen_zero(var->value)) {
04571                      ast_log(LOG_WARNING, "Empty queue member definition at line %d. Moving on!\n", var->lineno);
04572                      continue;
04573                   }
04574 
04575                   /* Add a new member */
04576                   ast_copy_string(parse, var->value, sizeof(parse));
04577                   
04578                   AST_NONSTANDARD_APP_ARGS(args, parse, ',');
04579 
04580                   interface = args.interface;
04581                   if (!ast_strlen_zero(args.penalty)) {
04582                      tmp = ast_skip_blanks(args.penalty);
04583                      penalty = atoi(tmp);
04584                      if (penalty < 0) {
04585                         penalty = 0;
04586                      }
04587                   } else
04588                      penalty = 0;
04589 
04590                   if (!ast_strlen_zero(args.membername)) {
04591                      membername = ast_skip_blanks(args.membername);
04592                   }
04593 
04594                   if (!ast_strlen_zero(args.state_interface)) {
04595                      state_interface = ast_skip_blanks(args.state_interface);
04596                   } else {
04597                      state_interface = interface;
04598                   }
04599 
04600                   /* Find the old position in the list */
04601                   ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
04602                   cur = ao2_find(q->members, &tmpmem, OBJ_POINTER | OBJ_UNLINK);
04603 
04604                   /* Only attempt removing from interfaces list if the new state_interface is different than the old one */
04605                   if (cur && strcasecmp(cur->state_interface, state_interface)) {
04606                      remove_from_interfaces(cur->state_interface);
04607                   }
04608 
04609                   newm = create_queue_member(interface, membername, penalty, cur ? cur->paused : 0, state_interface);
04610                   if (!cur || (cur && strcasecmp(cur->state_interface, state_interface))) {
04611                      add_to_interfaces(state_interface);
04612                   }
04613                   ao2_link(q->members, newm);
04614                   ao2_ref(newm, -1);
04615                   newm = NULL;
04616 
04617                   if (cur)
04618                      ao2_ref(cur, -1);
04619                   else {
04620                      q->membercount++;
04621                   }
04622                } else {
04623                   queue_set_param(q, var->name, var->value, var->lineno, 1);
04624                }
04625             }
04626 
04627             /* Free remaining members marked as delme */
04628             mem_iter = ao2_iterator_init(q->members, 0);
04629             while ((cur = ao2_iterator_next(&mem_iter))) {
04630                if (! cur->delme) {
04631                   ao2_ref(cur, -1);
04632                   continue;
04633                }
04634 
04635                q->membercount--;
04636                ao2_unlink(q->members, cur);
04637                remove_from_interfaces(cur->state_interface);
04638                ao2_ref(cur, -1);
04639             }
04640 
04641             if (q->strategy == QUEUE_STRATEGY_ROUNDROBIN)
04642                rr_dep_warning();
04643 
04644             if (new) {
04645                AST_LIST_INSERT_HEAD(&queues, q, list);
04646             } else
04647                ast_mutex_unlock(&q->lock);
04648          }
04649       }
04650    }
04651    ast_config_destroy(cfg);
04652    AST_LIST_TRAVERSE_SAFE_BEGIN(&queues, q, list) {
04653       if (q->dead) {
04654          AST_LIST_REMOVE_CURRENT(&queues, list);
04655          if (!q->count)
04656             destroy_queue(q);
04657          else
04658             ast_log(LOG_DEBUG, "XXX Leaking a little memory :( XXX\n");
04659       } else {
04660          ast_mutex_lock(&q->lock);
04661          mem_iter = ao2_iterator_init(q->members, 0);
04662          while ((cur = ao2_iterator_next(&mem_iter))) {
04663             if (cur->dynamic)
04664                q->membercount++;
04665             cur->status = ast_device_state(cur->state_interface);
04666             ao2_ref(cur, -1);
04667          }
04668          ast_mutex_unlock(&q->lock);
04669       }
04670    }
04671    AST_LIST_TRAVERSE_SAFE_END;
04672    AST_LIST_UNLOCK(&queues);
04673    return 1;
04674 }

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

Definition at line 945 of file app_queue.c.

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

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

00946 {
00947    struct member_interface *curint;
00948 
00949    if (interface_exists_global(interface))
00950       return 0;
00951 
00952    AST_LIST_LOCK(&interfaces);
00953    AST_LIST_TRAVERSE_SAFE_BEGIN(&interfaces, curint, list) {
00954       if (!strcasecmp(curint->interface, interface)) {
00955          if (option_debug)
00956             ast_log(LOG_DEBUG, "Removing %s from the list of interfaces that make up all of our queue members.\n", interface);
00957          AST_LIST_REMOVE_CURRENT(&interfaces, list);
00958          free(curint);
00959          break;
00960       }
00961    }
00962    AST_LIST_TRAVERSE_SAFE_END;
00963    AST_LIST_UNLOCK(&interfaces);
00964 
00965    return 0;
00966 }

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

Definition at line 3416 of file app_queue.c.

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

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

03417 {
03418    struct call_queue *q;
03419    struct member *mem, tmpmem;
03420    int res = RES_NOSUCHQUEUE;
03421 
03422    ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
03423 
03424    AST_LIST_LOCK(&queues);
03425    AST_LIST_TRAVERSE(&queues, q, list) {
03426       ast_mutex_lock(&q->lock);
03427       if (strcmp(q->name, queuename)) {
03428          ast_mutex_unlock(&q->lock);
03429          continue;
03430       }
03431 
03432       if ((mem = ao2_find(q->members, &tmpmem, OBJ_POINTER))) {
03433          /* XXX future changes should beware of this assumption!! */
03434          if (!mem->dynamic) {
03435             res = RES_NOT_DYNAMIC;
03436             ao2_ref(mem, -1);
03437             ast_mutex_unlock(&q->lock);
03438             break;
03439          }
03440          q->membercount--;
03441          manager_event(EVENT_FLAG_AGENT, "QueueMemberRemoved",
03442             "Queue: %s\r\n"
03443             "Location: %s\r\n"
03444             "MemberName: %s\r\n",
03445             q->name, mem->interface, mem->membername);
03446          ao2_unlink(q->members, mem);
03447          remove_from_interfaces(mem->state_interface);
03448          ao2_ref(mem, -1);
03449 
03450          if (queue_persistent_members)
03451             dump_queue_members(q);
03452          
03453          res = RES_OKAY;
03454       } else {
03455          res = RES_EXISTS;
03456       }
03457       ast_mutex_unlock(&q->lock);
03458       break;
03459    }
03460 
03461    AST_LIST_UNLOCK(&queues);
03462 
03463    return res;
03464 }

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

References ast_cdr::accountcode, ast_channel::adsicpe, ast_cdr::amaflags, ast_channel::appl, ast_call(), ast_cdr_busy(), ast_cdr_isset_unanswered(), ast_cdr_setdestchan(), ast_channel_inherit_variables(), ast_channel_lock, ast_channel_unlock, ast_copy_string(), AST_DEVICE_NOT_INUSE, ast_device_state(), AST_DEVICE_UNKNOWN, ast_log(), ast_mutex_lock(), ast_mutex_unlock(), ast_request(), ast_strdup, ast_strlen_zero(), ast_verbose(), ast_channel::cdr, callattempt::chan, queue_ent::chan, ast_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, call_queue::lock, LOG_DEBUG, LOG_NOTICE, manager_event(), callattempt::member, member::membername, ast_channel::name, call_queue::name, ast_channel::nativeformats, option_debug, option_verbose, queue_ent::parent, member::paused, pbx_builtin_getvar_helper(), ast_channel::priority, QUEUE_EVENT_VARIABLES, member::ringcount, call_queue::ringinuse, call_queue::rrpos, 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().

01882 {
01883    int res;
01884    int status;
01885    char tech[256];
01886    char *location;
01887    const char *macrocontext, *macroexten;
01888 
01889    /* on entry here, we know that tmp->chan == NULL */
01890    if (qe->parent->wrapuptime && (time(NULL) - tmp->lastcall < qe->parent->wrapuptime)) {
01891       if (queue_debug)
01892          ast_log(LOG_NOTICE, "Wrapuptime not yet expired for %s\n", tmp->interface);
01893       if (qe->chan->cdr)
01894          ast_cdr_busy(qe->chan->cdr);
01895       tmp->stillgoing = 0;
01896       (*busies)++;
01897       return 0;
01898    }
01899 
01900    if (!qe->parent->ringinuse && (tmp->member->status != AST_DEVICE_NOT_INUSE) && (tmp->member->status != AST_DEVICE_UNKNOWN)) {
01901       if (queue_debug)
01902          ast_log(LOG_NOTICE, "%s in use, can't receive call\n", tmp->interface);
01903       if (qe->chan->cdr)
01904          ast_cdr_busy(qe->chan->cdr);
01905       tmp->stillgoing = 0;
01906       return 0;
01907    }
01908 
01909    if (tmp->member->paused) {
01910       if (queue_debug)
01911          ast_log(LOG_NOTICE, "%s paused, can't receive call\n", tmp->interface);
01912       if (qe->chan->cdr)
01913          ast_cdr_busy(qe->chan->cdr);
01914       tmp->stillgoing = 0;
01915       return 0;
01916    }
01917    if (use_weight && compare_weight(qe->parent,tmp->member)) {
01918       if (queue_debug)
01919          ast_log(LOG_NOTICE, "Priority queue delaying call to %s:%s\n", qe->parent->name, tmp->interface);
01920       if (qe->chan->cdr)
01921          ast_cdr_busy(qe->chan->cdr);
01922       tmp->stillgoing = 0;
01923       (*busies)++;
01924       return 0;
01925    }
01926 
01927    ast_copy_string(tech, tmp->interface, sizeof(tech));
01928    if ((location = strchr(tech, '/')))
01929       *location++ = '\0';
01930    else
01931       location = "";
01932 
01933    /* Request the peer */
01934    tmp->chan = ast_request(tech, qe->chan->nativeformats, location, &status);
01935    if (!tmp->chan) {       /* If we can't, just go on to the next call */
01936       if (queue_debug)
01937          ast_log(LOG_NOTICE, "Unable to create channel of type '%s' for Queue\n", tech);
01938       if (qe->chan->cdr)
01939          ast_cdr_busy(qe->chan->cdr);
01940       tmp->stillgoing = 0;
01941 
01942       update_status(tmp->member->state_interface, ast_device_state(tmp->member->state_interface));
01943 
01944       ast_mutex_lock(&qe->parent->lock);
01945       qe->parent->rrpos++;
01946       ast_mutex_unlock(&qe->parent->lock);
01947 
01948       (*busies)++;
01949       return 0;
01950    }
01951    
01952    /* Increment ring count */
01953    tmp->member->ringcount++;
01954    tmp->chan->appl = "AppQueue";
01955    tmp->chan->data = "(Outgoing Line)";
01956    tmp->chan->whentohangup = 0;
01957    if (tmp->chan->cid.cid_num)
01958       free(tmp->chan->cid.cid_num);
01959    tmp->chan->cid.cid_num = ast_strdup(qe->chan->cid.cid_num);
01960    if (tmp->chan->cid.cid_name)
01961       free(tmp->chan->cid.cid_name);
01962    tmp->chan->cid.cid_name = ast_strdup(qe->chan->cid.cid_name);
01963    if (tmp->chan->cid.cid_ani)
01964       free(tmp->chan->cid.cid_ani);
01965    tmp->chan->cid.cid_ani = ast_strdup(qe->chan->cid.cid_ani);
01966 
01967    /* Inherit specially named variables from parent channel */
01968    ast_channel_inherit_variables(qe->chan, tmp->chan);
01969 
01970    /* Presense of ADSI CPE on outgoing channel follows ours */
01971    tmp->chan->adsicpe = qe->chan->adsicpe;
01972 
01973    /* Inherit context and extension */
01974    ast_channel_lock(qe->chan);
01975    macrocontext = pbx_builtin_getvar_helper(qe->chan, "MACRO_CONTEXT");
01976    if (!ast_strlen_zero(macrocontext))
01977       ast_copy_string(tmp->chan->dialcontext, macrocontext, sizeof(tmp->chan->dialcontext));
01978    else
01979       ast_copy_string(tmp->chan->dialcontext, qe->chan->context, sizeof(tmp->chan->dialcontext));
01980    macroexten = pbx_builtin_getvar_helper(qe->chan, "MACRO_EXTEN");
01981    if (!ast_strlen_zero(macroexten))
01982       ast_copy_string(tmp->chan->exten, macroexten, sizeof(tmp->chan->exten));
01983    else
01984       ast_copy_string(tmp->chan->exten, qe->chan->exten, sizeof(tmp->chan->exten));
01985    if (ast_cdr_isset_unanswered()) {
01986       /* they want to see the unanswered dial attempts! */
01987       /* set up the CDR fields on all the CDRs to give sensical information */
01988       ast_cdr_setdestchan(tmp->chan->cdr, tmp->chan->name);
01989       strcpy(tmp->chan->cdr->clid, qe->chan->cdr->clid);
01990       strcpy(tmp->chan->cdr->channel, qe->chan->cdr->channel);
01991       strcpy(tmp->chan->cdr->src, qe->chan->cdr->src);
01992       strcpy(tmp->chan->cdr->dst, qe->chan->exten);
01993       strcpy(tmp->chan->cdr->dcontext, qe->chan->context);
01994       strcpy(tmp->chan->cdr->lastapp, qe->chan->cdr->lastapp);
01995       strcpy(tmp->chan->cdr->lastdata, qe->chan->cdr->lastdata);
01996       tmp->chan->cdr->amaflags = qe->chan->cdr->amaflags;
01997       strcpy(tmp->chan->cdr->accountcode, qe->chan->cdr->accountcode);
01998       strcpy(tmp->chan->cdr->userfield, qe->chan->cdr->userfield);
01999    }
02000    ast_channel_unlock(qe->chan);
02001 
02002    /* Place the call, but don't wait on the answer */
02003    if ((res = ast_call(tmp->chan, location, 0))) {
02004       /* Again, keep going even if there's an error */
02005       if (option_debug)
02006          ast_log(LOG_DEBUG, "ast call on peer returned %d\n", res);
02007       if (option_verbose > 2)
02008          ast_verbose(VERBOSE_PREFIX_3 "Couldn't call %s\n", tmp->interface);
02009       do_hang(tmp);
02010       (*busies)++;
02011       update_status(tmp->member->state_interface, ast_device_state(tmp->member->state_interface));
02012       return 0;
02013    } else if (qe->parent->eventwhencalled) {
02014       char vars[2048];
02015 
02016       manager_event(EVENT_FLAG_AGENT, "AgentCalled",
02017                "AgentCalled: %s\r\n"
02018                "AgentName: %s\r\n"
02019                "ChannelCalling: %s\r\n"
02020                "CallerID: %s\r\n"
02021                "CallerIDName: %s\r\n"
02022                "Context: %s\r\n"
02023                "Extension: %s\r\n"
02024                "Priority: %d\r\n"
02025                "%s",
02026                tmp->interface, tmp->member->membername, qe->chan->name,
02027                tmp->chan->cid.cid_num ? tmp->chan->cid.cid_num : "unknown",
02028                tmp->chan->cid.cid_name ? tmp->chan->cid.cid_name : "unknown",
02029                qe->chan->context, qe->chan->exten, qe->chan->priority,
02030                qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
02031       if (option_verbose > 2)
02032          ast_verbose(VERBOSE_PREFIX_3 "Called %s\n", tmp->interface);
02033    }
02034 
02035    update_status(tmp->member->state_interface, ast_device_state(tmp->member->state_interface));
02036    return 1;
02037 }

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

02064 {
02065    int ret = 0;
02066 
02067    while (ret == 0) {
02068       struct callattempt *best = find_best(outgoing);
02069       if (!best) {
02070          if (option_debug)
02071             ast_log(LOG_DEBUG, "Nobody left to try ringing in queue\n");
02072          break;
02073       }
02074       if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
02075          struct callattempt *cur;
02076          /* Ring everyone who shares this best metric (for ringall) */
02077          for (cur = outgoing; cur; cur = cur->q_next) {
02078             if (cur->stillgoing && !cur->chan && cur->metric <= best->metric) {
02079                if (option_debug)
02080                   ast_log(LOG_DEBUG, "(Parallel) Trying '%s' with metric %d\n", cur->interface, cur->metric);
02081                ret |= ring_entry(qe, cur, busies);
02082             }
02083          }
02084       } else {
02085          /* Ring just the best channel */
02086          if (option_debug)
02087             ast_log(LOG_DEBUG, "Trying '%s' with metric %d\n", best->interface, best->metric);
02088          ret = ring_entry(qe, best, busies);
02089       }
02090    }
02091 
02092    return ret;
02093 }

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

References ast_queue_log(), ast_verbose(), call_queue::autopause, queue_ent::chan, call_queue::name, option_verbose, queue_ent::parent, set_member_paused(), ast_channel::uniqueid, and VERBOSE_PREFIX_3.

02178 {
02179    if (option_verbose > 2)
02180       ast_verbose( VERBOSE_PREFIX_3 "Nobody picked up in %d ms\n", rnatime);
02181    ast_queue_log(qe->parent->name, qe->chan->uniqueid, membername, "RINGNOANSWER", "%d", rnatime);
02182    if (qe->parent->autopause && pause) {
02183       if (!set_member_paused(qe->parent->name, interface, 1)) {
02184          if (option_verbose > 2)
02185             ast_verbose( VERBOSE_PREFIX_3 "Auto-Pausing Queue Member %s in queue %s since they failed to answer.\n", interface, qe->parent->name);
02186       } else {
02187          if (option_verbose > 2)
02188             ast_verbose( VERBOSE_PREFIX_3 "Failed to pause Queue Member %s in queue %s!\n", interface, qe->parent->name);
02189       }
02190    }
02191    return;
02192 }

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

Definition at line 3781 of file app_queue.c.

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

Referenced by load_module().

03782 {
03783    int res=-1;
03784    struct ast_module_user *lu;
03785    char *parse, *temppos = NULL;
03786    int priority_jump = 0;
03787    AST_DECLARE_APP_ARGS(args,
03788       AST_APP_ARG(queuename);
03789       AST_APP_ARG(interface);
03790       AST_APP_ARG(options);
03791    );
03792 
03793 
03794    if (ast_strlen_zero(data)) {
03795       ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename[|interface[|options]])\n");
03796       return -1;
03797    }
03798 
03799    parse = ast_strdupa(data);
03800 
03801    AST_STANDARD_APP_ARGS(args, parse);
03802 
03803    lu = ast_module_user_add(chan);
03804 
03805    if (ast_strlen_zero(args.interface)) {
03806       args.interface = ast_strdupa(chan->name);
03807       temppos = strrchr(args.interface, '-');
03808       if (temppos)
03809          *temppos = '\0';
03810    }
03811 
03812    if (args.options) {
03813       if (strchr(args.options, 'j'))
03814          priority_jump = 1;
03815    }
03816 
03817    switch (remove_from_queue(args.queuename, args.interface)) {
03818    case RES_OKAY:
03819       ast_queue_log(args.queuename, chan->uniqueid, args.interface, "REMOVEMEMBER", "%s", "");
03820       ast_log(LOG_NOTICE, "Removed interface '%s' from queue '%s'\n", args.interface, args.queuename);
03821       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "REMOVED");
03822       res = 0;
03823       break;
03824    case RES_EXISTS:
03825       ast_log(LOG_DEBUG, "Unable to remove interface '%s' from queue '%s': Not there\n", args.interface, args.queuename);
03826       if (priority_jump || ast_opt_priority_jumping)
03827          ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
03828       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTINQUEUE");
03829       res = 0;
03830       break;
03831    case RES_NOSUCHQUEUE:
03832       ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", args.queuename);
03833       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOSUCHQUEUE");
03834       res = 0;
03835       break;
03836    case RES_NOT_DYNAMIC:
03837       ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': '%s' is not a dynamic member\n", args.queuename, args.interface);
03838       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTDYNAMIC");
03839       res = 0;
03840       break;
03841    }
03842 
03843    ast_module_user_remove(lu);
03844 
03845    return res;
03846 }

static void rr_dep_warning ( void   )  [static]

Definition at line 468 of file app_queue.c.

References ast_log(), and LOG_NOTICE.

Referenced by reload_queues().

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

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

01138 {
01139    struct member *m, tmpmem;
01140    int penalty = 0;
01141    int paused  = 0;
01142 
01143    if (penalty_str) {
01144       penalty = atoi(penalty_str);
01145       if (penalty < 0)
01146          penalty = 0;
01147    }
01148 
01149    if (paused_str) {
01150       paused = atoi(paused_str);
01151       if (paused < 0)
01152          paused = 0;
01153    }
01154 
01155    /* Find the member, or the place to put a new one. */
01156    ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
01157    m = ao2_find(q->members, &tmpmem, OBJ_POINTER);
01158 
01159    /* Create a new one if not found, else update penalty */
01160    if (!m) {
01161       if ((m = create_queue_member(interface, membername, penalty, paused, state_interface))) {
01162          m->dead = 0;
01163          m->realtime = 1;
01164          add_to_interfaces(m->state_interface);
01165          ao2_link(q->members, m);
01166          ao2_ref(m, -1);
01167          m = NULL;
01168          q->membercount++;
01169       }
01170    } else {
01171       m->dead = 0;   /* Do not delete this one. */
01172       if (paused_str)
01173          m->paused = paused;
01174       if (strcasecmp(state_interface, m->state_interface)) {
01175          remove_from_interfaces(m->state_interface);
01176          ast_copy_string(m->state_interface, state_interface, sizeof(m->state_interface));
01177          add_to_interfaces(m->state_interface);
01178       }
01179       m->penalty = penalty;
01180       ao2_ref(m, -1);
01181    }
01182 }

static int say_periodic_announcement ( struct queue_ent qe  )  [static]

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

02120 {
02121    int res = 0;
02122    time_t now;
02123 
02124    /* Get the current time */
02125    time(&now);
02126 
02127    /* Check to see if it is time to announce */
02128    if ((now - qe->last_periodic_announce_time) < qe->parent->periodicannouncefrequency)
02129       return 0;
02130 
02131    /* Stop the music on hold so we can play our own file */
02132    ast_moh_stop(qe->chan);
02133 
02134    if (option_verbose > 2)
02135       ast_verbose(VERBOSE_PREFIX_3 "Playing periodic announcement\n");
02136 
02137    /* Check to make sure we have a sound file. If not, reset to the first sound file */
02138    if (qe->last_periodic_announce_sound >= MAX_PERIODIC_ANNOUNCEMENTS || !strlen(qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound])) {
02139       qe->last_periodic_announce_sound = 0;
02140    }
02141    
02142    /* play the announcement */
02143    res = play_file(qe->chan, qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound]);
02144 
02145    if (res > 0 && !valid_exit(qe, res))
02146       res = 0;
02147 
02148    /* Resume Music on Hold if the caller is going to stay in the queue */
02149    if (!res)
02150       ast_moh_start(qe->chan, qe->moh, NULL);
02151 
02152    /* update last_periodic_announce_time */
02153    qe->last_periodic_announce_time = now;
02154 
02155    /* Update the current periodic announcement to the next announcement */
02156    qe->last_periodic_announce_sound++;
02157    
02158    return res;
02159 }

static int say_position ( struct queue_ent qe  )  [static]

Definition at line 1570 of file app_queue.c.

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

Referenced by queue_exec(), and wait_our_turn().

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

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

Definition at line 3522 of file app_queue.c.

References ao2_ref(), AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), ast_mutex_lock(), ast_mutex_unlock(), ast_queue_log(), ast_strlen_zero(), dump_queue_members(), EVENT_FLAG_AGENT, member::interface, interface_exists(), call_queue::lock, LOG_DEBUG, manager_event(), member::membername, call_queue::name, member::paused, member::realtime, RESULT_FAILURE, RESULT_SUCCESS, and update_realtime_member_field().

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

03523 {
03524    int found = 0;
03525    struct call_queue *q;
03526    struct member *mem;
03527 
03528    /* Special event for when all queues are paused - individual events still generated */
03529    /* XXX In all other cases, we use the membername, but since this affects all queues, we cannot */
03530    if (ast_strlen_zero(queuename))
03531       ast_queue_log("NONE", "NONE", interface, (paused ? "PAUSEALL" : "UNPAUSEALL"), "%s", "");
03532 
03533    AST_LIST_LOCK(&queues);
03534    AST_LIST_TRAVERSE(&queues, q, list) {
03535       ast_mutex_lock(&q->lock);
03536       if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
03537          if ((mem = interface_exists(q, interface))) {
03538             found++;
03539             if (mem->paused == paused)
03540                ast_log(LOG_DEBUG, "%spausing already-%spaused queue member %s:%s\n", (paused ? "" : "un"), (paused ? "" : "un"), q->name, interface);
03541             mem->paused = paused;
03542 
03543             if (queue_persistent_members)
03544                dump_queue_members(q);
03545 
03546             if (mem->realtime)
03547                update_realtime_member_field(mem, q->name, "paused", paused ? "1" : "0");
03548 
03549             ast_queue_log(q->name, "NONE", mem->membername, (paused ? "PAUSE" : "UNPAUSE"), "%s", "");
03550 
03551             manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
03552                "Queue: %s\r\n"
03553                "Location: %s\r\n"
03554                "MemberName: %s\r\n"
03555                "Paused: %d\r\n",
03556                   q->name, mem->interface, mem->membername, paused);
03557             ao2_ref(mem, -1);
03558          }
03559       }
03560       ast_mutex_unlock(&q->lock);
03561    }
03562    AST_LIST_UNLOCK(&queues);
03563 
03564    return found ? RESULT_SUCCESS : RESULT_FAILURE;
03565 }

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

sets the QUEUESTATUS channel variable

Definition at line 487 of file app_queue.c.

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

Referenced by queue_exec().

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

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

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

Definition at line 2724 of file app_queue.c.

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

02725 {
02726    struct ast_datastore *ds;
02727    struct queue_transfer_ds *qtds = ast_calloc(1, sizeof(*qtds));
02728 
02729    if (!qtds) {
02730       ast_log(LOG_WARNING, "Memory allocation error!\n");
02731       return NULL;
02732    }
02733 
02734    ast_channel_lock(qe->chan);
02735    if (!(ds = ast_channel_datastore_alloc(&queue_transfer_info, NULL))) {
02736       ast_channel_unlock(qe->chan);
02737       ast_log(LOG_WARNING, "Unable to create transfer datastore. queue_log will not show attended transfer\n");
02738       return NULL;
02739    }
02740 
02741    qtds->qe = qe;
02742    /* This member is refcounted in try_calling, so no need to add it here, too */
02743    qtds->member = member;
02744    qtds->starttime = starttime;
02745    qtds->callcompletedinsl = callcompletedinsl;
02746    ds->data = qtds;
02747    ast_channel_datastore_add(qe->chan, ds);
02748    ast_channel_unlock(qe->chan);
02749    return ds;
02750 }

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

Producer of the statechange queue.

Definition at line 748 of file app_queue.c.

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

Referenced by load_module(), and unload_module().

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

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

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

02096 {
02097    struct callattempt *best = find_best(outgoing);
02098 
02099    if (best) {
02100       /* Ring just the best channel */
02101       if (option_debug)
02102          ast_log(LOG_DEBUG, "Next is '%s' with metric %d\n", best->interface, best->metric);
02103       qe->parent->rrpos = best->metric % 1000;
02104    } else {
02105       /* Just increment rrpos */
02106       if (qe->parent->wrapped) {
02107          /* No more channels, start over */
02108          qe->parent->rrpos = 0;
02109       } else {
02110          /* Prioritize next entry */
02111          qe->parent->rrpos++;
02112       }
02113    }
02114    qe->parent->wrapped = 0;
02115 
02116    return 0;
02117 }

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

Definition at line 511 of file app_queue.c.

References name, and strategies.

Referenced by queue_set_param().

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

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

References ast_channel::_state, queue_ent::announce, ao2_iterator_init(), ao2_iterator_next(), ao2_ref(), ast_calloc, AST_CDR_FLAG_DONT_TOUCH, AST_CDR_FLAG_POST_DISABLED, ast_cdr_isset_unanswered(), ast_cdr_noanswer(), 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_unlock, ast_copy_string(), AST_FEATURE_AUTOMON, AST_FEATURE_DISCONNECT, AST_FEATURE_REDIRECT, 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_mutex_lock(), ast_mutex_unlock(), ast_set_flag, AST_STATE_UP, ast_strlen_zero(), ast_test_flag, calc_metric(), ast_channel::cdr, callattempt::chan, queue_ent::chan, ast_datastore::data, DATASTORE_INHERIT_FOREVER, di, dialed_interface_info, ast_cdr::dstchannel, EVENT_FLAG_AGENT, call_queue::eventwhencalled, queue_ent::expire, free, ast_datastore::inheritance, member::interface, member::lastcall, ast_dialed_interface::list, call_queue::lock, LOG_DEBUG, LOG_NOTICE, manager_event(), call_queue::membercount, call_queue::members, call_queue::name, ast_channel::name, option_debug, queue_ent::parent, queue_ent::pending, callattempt::q_next, QUEUE_STRATEGY_ROUNDROBIN, ring_one(), member::ringcount, call_queue::ringlimit, queue_ent::start, member::status, store_next(), call_queue::strategy, call_queue::timeout, queue_ent::tries, ast_channel::uniqueid, and wait_for_answer().

Referenced by queue_exec().

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

static int unload_module ( void   )  [static]

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

05418 {
05419    int res;
05420 
05421    if (device_state.thread != AST_PTHREADT_NULL) {
05422       device_state.stop = 1;
05423       ast_mutex_lock(&device_state.lock);
05424       ast_cond_signal(&device_state.cond);
05425       ast_mutex_unlock(&device_state.lock);
05426       pthread_join(device_state.thread, NULL);
05427    }
05428 
05429    ast_cli_unregister_multiple(cli_queue, sizeof(cli_queue) / sizeof(struct ast_cli_entry));
05430    res = ast_manager_unregister("QueueStatus");
05431    res |= ast_manager_unregister("Queues");
05432    res |= ast_manager_unregister("QueueAdd");
05433    res |= ast_manager_unregister("QueueRemove");
05434    res |= ast_manager_unregister("QueuePause");
05435    res |= ast_unregister_application(app_aqm);
05436    res |= ast_unregister_application(app_rqm);
05437    res |= ast_unregister_application(app_pqm);
05438    res |= ast_unregister_application(app_upqm);
05439    res |= ast_unregister_application(app_ql);
05440    res |= ast_unregister_application(app);
05441    res |= ast_custom_function_unregister(&queueagentcount_function);
05442    res |= ast_custom_function_unregister(&queuemembercount_function);
05443    res |= ast_custom_function_unregister(&queuememberlist_function);
05444    res |= ast_custom_function_unregister(&queuewaitingcount_function);
05445    ast_devstate_del(statechange_queue, NULL);
05446 
05447    ast_module_user_hangup_all();
05448 
05449    clear_and_free_interfaces();
05450 
05451    return res;
05452 }

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

Definition at line 2577 of file app_queue.c.

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

Referenced by queue_transfer_fixup().

02578 {
02579    ast_mutex_lock(&q->lock);
02580    time(&member->lastcall);
02581    member->calls++;
02582    q->callscompleted++;
02583    if (callcompletedinsl)
02584       q->callscompletedinsl++;
02585    ast_mutex_unlock(&q->lock);
02586    return 0;
02587 }

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

Definition at line 1334 of file app_queue.c.

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

Referenced by set_member_paused().

01335 {
01336    struct ast_variable *var, *save;
01337    int ret = -1;
01338 
01339    if (!(var = ast_load_realtime("queue_members", "interface", mem->interface, "queue_name", queue_name, NULL))) 
01340       return ret;
01341    save = var;
01342    while (var) {
01343       if (!strcmp(var->name, "uniqueid"))
01344          break;
01345       var = var->next;
01346    }
01347    if (var && !ast_strlen_zero(var->value)) {
01348       if ((ast_update_realtime("queue_members", "uniqueid", var->value, field, value, NULL)) > -1)
01349          ret = 0;
01350    }
01351    ast_variables_destroy(save);
01352    return ret;
01353 }

static void update_realtime_members ( struct call_queue q  )  [static]

Definition at line 1355 of file app_queue.c.

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

Referenced by load_realtime_queue(), and queue_exec().

01356 {
01357    struct ast_config *member_config = NULL;
01358    struct member *m;
01359    char *interface = NULL;
01360    struct ao2_iterator mem_iter;
01361 
01362    if (!(member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", q->name , NULL))) {
01363       /*This queue doesn't have realtime members*/
01364       if (option_debug > 2)
01365          ast_log(LOG_DEBUG, "Queue %s has no realtime members defined. No need for update\n", q->name);
01366       return;
01367    }
01368 
01369    ast_mutex_lock(&q->lock);
01370    
01371    /* Temporarily set realtime  members dead so we can detect deleted ones.*/ 
01372    mem_iter = ao2_iterator_init(q->members, 0);
01373    while ((m = ao2_iterator_next(&mem_iter))) {
01374       if (m->realtime)
01375          m->dead = 1;
01376       ao2_ref(m, -1);
01377    }
01378 
01379    while ((interface = ast_category_browse(member_config, interface))) {
01380       rt_handle_member_record(q, interface,
01381          S_OR(ast_variable_retrieve(member_config, interface, "membername"), interface),
01382          ast_variable_retrieve(member_config, interface, "penalty"),
01383          ast_variable_retrieve(member_config, interface, "paused"),
01384          S_OR(ast_variable_retrieve(member_config, interface, "state_interface"), interface));
01385    }
01386 
01387    /* Delete all realtime members that have been deleted in DB. */
01388    mem_iter = ao2_iterator_init(q->members, 0);
01389    while ((m = ao2_iterator_next(&mem_iter))) {
01390       if (m->dead) {
01391          ao2_unlink(q->members, m);
01392          ast_mutex_unlock(&q->lock);
01393          remove_from_interfaces(m->state_interface);
01394          ast_mutex_lock(&q->lock);
01395          q->membercount--;
01396       }
01397       ao2_ref(m, -1);
01398    }
01399    ast_mutex_unlock(&q->lock);
01400    ast_config_destroy(member_config);
01401 }

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

Definition at line 600 of file app_queue.c.

References ao2_iterator_init(), ao2_iterator_next(), ao2_ref(), ast_copy_string(), AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_mutex_lock(), ast_mutex_unlock(), member::calls, member::dynamic, EVENT_FLAG_AGENT, member::interface, member::lastcall, member_interface::list, call_queue::lock, manager_event(), call_queue::maskmemberstatus, member::membername, call_queue::members, call_queue::name, member::paused, member::penalty, member::realtime, member::state_interface, and member::status.

Referenced by handle_statechange(), and ring_entry().

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

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

Definition at line 3723 of file app_queue.c.

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

Referenced by load_module().

03724 {
03725    struct ast_module_user *lu;
03726    char *parse;
03727    int priority_jump = 0;
03728    int ignore_fail = 0;
03729    AST_DECLARE_APP_ARGS(args,
03730       AST_APP_ARG(queuename);
03731       AST_APP_ARG(interface);
03732       AST_APP_ARG(options);
03733    );
03734 
03735    if (ast_strlen_zero(data)) {
03736       ast_log(LOG_WARNING, "UnpauseQueueMember requires an argument ([queuename]|interface[|options])\n");
03737       return -1;
03738    }
03739 
03740    parse = ast_strdupa(data);
03741 
03742    AST_STANDARD_APP_ARGS(args, parse);
03743 
03744    lu = ast_module_user_add(chan);
03745 
03746    if (args.options) {
03747       if (strchr(args.options, 'j'))
03748          priority_jump = 1;
03749       if (strchr(args.options, 'i'))
03750          ignore_fail = 1;
03751    }
03752 
03753    if (ast_strlen_zero(args.interface)) {
03754       ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename]|interface[|options])\n");
03755       ast_module_user_remove(lu);
03756       return -1;
03757    }
03758 
03759    if (set_member_paused(args.queuename, args.interface, 0)) {
03760       ast_log(LOG_WARNING, "Attempt to unpause interface %s, not found\n", args.interface);
03761       pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND");
03762       if (priority_jump || ast_opt_priority_jumping) {
03763          if (!ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101)) {
03764             ast_module_user_remove(lu);
03765             return 0;
03766          }
03767       }
03768       ast_module_user_remove(lu);
03769       if (ignore_fail) {
03770          return 0;
03771       } else {
03772          return -1;
03773       }
03774    }
03775 
03776    ast_module_user_remove(lu);
03777    pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "UNPAUSED");
03778    return 0;
03779 }

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

Definition at line 1537 of file app_queue.c.

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

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

01538 {
01539    int digitlen = strlen(qe->digits);
01540 
01541    /* Prevent possible buffer overflow */
01542    if (digitlen < sizeof(qe->digits) - 2) {
01543       qe->digits[digitlen] = digit;
01544       qe->digits[digitlen + 1] = '\0';
01545    } else {
01546       qe->digits[0] = '\0';
01547       return 0;
01548    }
01549 
01550    /* If there's no context to goto, short-circuit */
01551    if (ast_strlen_zero(qe->context))
01552       return 0;
01553 
01554    /* If the extension is bad, then reset the digits to blank */
01555    if (!ast_canmatch_extension(qe->chan, qe->context, qe->digits, 1, qe->chan->cid.cid_num)) {
01556       qe->digits[0] = '\0';
01557       return 0;
01558    }
01559 
01560    /* We have an exact match */
01561    if (!ast_goto_if_exists(qe->chan, qe->context, qe->digits, 1)) {
01562       qe->valid_digits = 1;
01563       /* Return 1 on a successful goto */
01564       return 1;
01565    }
01566 
01567    return 0;
01568 }

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

Definition at line 1841 of file app_queue.c.

References ast_copy_string(), and pbx_builtin_serialize_variables().

Referenced by ring_entry().

01842 {
01843    char *tmp = alloca(len);
01844 
01845    if (pbx_builtin_serialize_variables(chan, tmp, len)) {
01846       int i, j;
01847 
01848       /* convert "\n" to "\nVariable: " */
01849       strcpy(vars, "Variable: ");
01850 
01851       for (i = 0, j = 10; (i < len - 1) && (j < len - 1); i++, j++) {
01852          vars[j] = tmp[i];
01853 
01854          if (tmp[i + 1] == '\0')
01855             break;
01856          if (tmp[i] == '\n') {
01857             vars[j++] = '\r';
01858             vars[j++] = '\n';
01859 
01860             ast_copy_string(&(vars[j]), "Variable: ", len - j);
01861             j += 9;
01862          }
01863       }
01864       if (j > len - 3)
01865          j = len - 3;
01866       vars[j++] = '\r';
01867       vars[j++] = '\n';
01868       vars[j] = '\0';
01869    } else {
01870       /* there are no channel variables; leave it blank */
01871       *vars = '\0';
01872    }
01873    return vars;
01874 }

static int wait_a_bit ( struct queue_ent qe  )  [static]

Definition at line 3340 of file app_queue.c.

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

Referenced by queue_exec().

03341 {
03342    /* Don't need to hold the lock while we setup the outgoing calls */
03343    int retrywait = qe->parent->retry * 1000;
03344 
03345    int res = ast_waitfordigit(qe->chan, retrywait);
03346    if (res > 0 && !valid_exit(qe, res))
03347       res = 0;
03348 
03349    return res;
03350 }

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

Wait for a member to answer the call.

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

Definition at line 2205 of file app_queue.c.

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

02206 {
02207    char *queue = qe->parent->name;
02208    struct callattempt *o, *start = NULL, *prev = NULL;
02209    int status;
02210    int numbusies = prebusies;
02211    int numnochan = 0;
02212    int stillgoing = 0;
02213    int orig = *to;
02214    struct ast_frame *f;
02215    struct callattempt *peer = NULL;
02216    struct ast_channel *winner;
02217    struct ast_channel *in = qe->chan;
02218    char on[80] = "";
02219    char membername[80] = "";
02220    long starttime = 0;
02221    long endtime = 0; 
02222 
02223    starttime = (long) time(NULL);
02224    
02225    while (*to && !peer) {
02226       int numlines, retry, pos = 1;
02227       struct ast_channel *watchers[AST_MAX_WATCHERS];
02228       watchers[0] = in;
02229       start = NULL;
02230 
02231       for (retry = 0; retry < 2; retry++) {
02232          numlines = 0;
02233          for (o = outgoing; o; o = o->q_next) { /* Keep track of important channels */
02234             if (o->stillgoing) { /* Keep track of important channels */
02235                stillgoing = 1;
02236                if (o->chan) {
02237                   watchers[pos++] = o->chan;
02238                   if (!start)
02239                      start = o;
02240                   else
02241                      prev->call_next = o;
02242                   prev = o;
02243                }
02244             }
02245             numlines++;
02246          }
02247          if (pos > 1 /* found */ || !stillgoing /* nobody listening */ ||
02248             (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) /* ring would not be delivered */)
02249             break;
02250          /* On "ringall" strategy we only move to the next penalty level
02251             when *all* ringing phones are done in the current penalty level */
02252          ring_one(qe, outgoing, &numbusies);
02253          /* and retry... */
02254       }
02255       if (pos == 1 /* not found */) {
02256          if (numlines == (numbusies + numnochan)) {
02257             ast_log(LOG_DEBUG, "Everyone is busy at this time\n");
02258          } else {
02259             ast_log(LOG_NOTICE, "No one is answering queue '%s' (%d/%d/%d)\n", queue, numlines, numbusies, numnochan);
02260          }
02261          *to = 0;
02262          return NULL;
02263       }
02264       winner = ast_waitfor_n(watchers, pos, to);
02265       for (o = start; o; o = o->call_next) {
02266          if (o->stillgoing && (o->chan) &&  (o->chan->_state == AST_STATE_UP)) {
02267             if (!peer) {
02268                if (option_verbose > 2)
02269                   ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
02270                peer = o;
02271             }
02272          } else if (o->chan && (o->chan == winner)) {
02273 
02274             ast_copy_string(on, o->member->interface, sizeof(on));
02275             ast_copy_string(membername, o->member->membername, sizeof(membername));
02276 
02277             if (!ast_strlen_zero(o->chan->call_forward) && !forwardsallowed) {
02278                if (option_verbose > 2)
02279                   ast_verbose(VERBOSE_PREFIX_3 "Forwarding %s to '%s' prevented.\n", in->name, o->chan->call_forward);
02280                numnochan++;
02281                do_hang(o);
02282                winner = NULL;
02283                continue;
02284             } else if (!ast_strlen_zero(o->chan->call_forward)) {
02285                char tmpchan[256];
02286                char *stuff;
02287                char *tech;
02288 
02289                ast_copy_string(tmpchan, o->chan->call_forward, sizeof(tmpchan));
02290                if ((stuff = strchr(tmpchan, '/'))) {
02291                   *stuff++ = '\0';
02292                   tech = tmpchan;
02293                } else {
02294                   snprintf(tmpchan, sizeof(tmpchan), "%s@%s", o->chan->call_forward, o->chan->context);
02295                   stuff = tmpchan;
02296                   tech = "Local";
02297                }
02298                /* Before processing channel, go ahead and check for forwarding */
02299                if (option_verbose > 2)
02300                   ast_verbose(VERBOSE_PREFIX_3 "Now forwarding %s to '%s/%s' (thanks to %s)\n", in->name, tech, stuff, o->chan->name);
02301                /* Setup parameters */
02302                o->chan = ast_request(tech, in->nativeformats, stuff, &status);
02303                if (!o->chan) {
02304                   ast_log(LOG_NOTICE, "Unable to create local channel for call forward to '%s/%s'\n", tech, stuff);
02305                   o->stillgoing = 0;
02306                   numnochan++;
02307                } else {
02308                   ast_channel_inherit_variables(in, o->chan);
02309                   ast_channel_datastore_inherit(in, o->chan);
02310                   if (o->chan->cid.cid_num)
02311                      free(o->chan->cid.cid_num);
02312                   o->chan->cid.cid_num = ast_strdup(in->cid.cid_num);
02313 
02314                   if (o->chan->cid.cid_name)
02315                      free(o->chan->cid.cid_name);
02316                   o->chan->cid.cid_name = ast_strdup(in->cid.cid_name);
02317 
02318                   ast_string_field_set(o->chan, accountcode, in->accountcode);
02319                   o->chan->cdrflags = in->cdrflags;
02320 
02321                   if (in->cid.cid_ani) {
02322                      if (o->chan->cid.cid_ani)
02323                         free(o->chan->cid.cid_ani);
02324                      o->chan->cid.cid_ani = ast_strdup(in->cid.cid_ani);
02325                   }
02326                   if (o->chan->cid.cid_rdnis)
02327                      free(o->chan->cid.cid_rdnis);
02328                   o->chan->cid.cid_rdnis = ast_strdup(S_OR(in->macroexten, in->exten));
02329                   if (ast_call(o->chan, tmpchan, 0)) {
02330                      ast_log(LOG_NOTICE, "Failed to dial on local channel for call forward to '%s'\n", tmpchan);
02331                      do_hang(o);
02332                      numnochan++;
02333                   }
02334                }
02335                /* Hangup the original channel now, in case we needed it */
02336                ast_hangup(winner);
02337                continue;
02338             }
02339             f = ast_read(winner);
02340             if (f) {
02341                if (f->frametype == AST_FRAME_CONTROL) {
02342                   switch (f->subclass) {
02343                   case AST_CONTROL_ANSWER:
02344                      /* This is our guy if someone answered. */
02345                      if (!peer) {
02346                         if (option_verbose > 2)
02347                            ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
02348                         peer = o;
02349                      }
02350                      break;
02351                   case AST_CONTROL_BUSY:
02352                      if (option_verbose > 2)
02353                         ast_verbose( VERBOSE_PREFIX_3 "%s is busy\n", o->chan->name);
02354                      if (in->cdr)
02355                         ast_cdr_busy(in->cdr);
02356                      do_hang(o);
02357                      endtime = (long)time(NULL);
02358                      endtime -= starttime;
02359                      rna(endtime * 1000, qe, on, membername, 0);
02360                      if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
02361                         if (qe->parent->timeoutrestart)
02362                            *to = orig;
02363                         ring_one(qe, outgoing, &numbusies);
02364                      }
02365                      numbusies++;
02366                      break;
02367                   case AST_CONTROL_CONGESTION:
02368                      if (option_verbose > 2)
02369                         ast_verbose( VERBOSE_PREFIX_3 "%s is circuit-busy\n", o->chan->name);
02370                      if (in->cdr)
02371                         ast_cdr_busy(in->cdr);
02372                      endtime = (long)time(NULL);
02373                      endtime -= starttime;
02374                      rna(endtime * 1000, qe, on, membername, 0);
02375                      do_hang(o);
02376                      if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
02377                         if (qe->parent->timeoutrestart)
02378                            *to = orig;
02379                         ring_one(qe, outgoing, &numbusies);
02380                      }
02381                      numbusies++;
02382                      break;
02383                   case AST_CONTROL_RINGING:
02384                      if (option_verbose > 2)
02385                         ast_verbose( VERBOSE_PREFIX_3 "%s is ringing\n", o->chan->name);
02386                      break;
02387                   case AST_CONTROL_OFFHOOK:
02388                      /* Ignore going off hook */
02389                      break;
02390                   default:
02391                      ast_log(LOG_DEBUG, "Dunno what to do with control type %d\n", f->subclass);
02392                   }
02393                }
02394                ast_frfree(f);
02395             } else {
02396                endtime = (long) time(NULL) - starttime;
02397                rna(endtime * 1000, qe, on, membername, 1);
02398                do_hang(o);
02399                if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
02400                   if (qe->parent->timeoutrestart)
02401                      *to = orig;
02402                   ring_one(qe, outgoing, &numbusies);
02403                }
02404             }
02405          }
02406       }
02407       if (winner == in) {
02408          f = ast_read(in);
02409          if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) {
02410             /* Got hung up */
02411             *to = -1;
02412             if (f)
02413                ast_frfree(f);
02414             return NULL;
02415          }
02416          /* First check if DTMF digit is a valid exit */
02417          if ((f->frametype == AST_FRAME_DTMF) && valid_exit(qe, f->subclass)) {
02418             if (option_verbose > 3)
02419                ast_verbose(VERBOSE_PREFIX_3 "User pressed digit: %c\n", f->subclass);
02420             *to = 0;
02421             *digit = f->subclass;
02422             ast_frfree(f);
02423             return NULL;
02424          }
02425          /* Else check if DTMF should be interpreted as caller disconnect */
02426          if ((f->frametype == AST_FRAME_DTMF) && caller_disconnect && (f->subclass == '*')) {
02427             if (option_verbose > 3)
02428                ast_verbose(VERBOSE_PREFIX_3 "User hit %c to disconnect call.\n", f->subclass);
02429             *to = 0;
02430             ast_frfree(f);
02431             return NULL;
02432          }
02433          ast_frfree(f);
02434       }
02435       if (!*to) {
02436          for (o = start; o; o = o->call_next)
02437             rna(orig, qe, o->interface, o->member->membername, 1);
02438       }
02439    }
02440 
02441    return peer;
02442 }

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

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

Referenced by queue_exec().

02503 {
02504    int res = 0;
02505 
02506    /* This is the holding pen for callers 2 through maxlen */
02507    for (;;) {
02508       enum queue_member_status stat;
02509 
02510       if (is_our_turn(qe))
02511          break;
02512 
02513       /* If we have timed out, break out */
02514       if (qe->expire && (time(NULL) >= qe->expire)) {
02515          *reason = QUEUE_TIMEOUT;
02516          break;
02517       }
02518 
02519       stat = get_member_status(qe->parent, qe->max_penalty);
02520 
02521       /* leave the queue if no agents, if enabled */
02522       if (qe->parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) {
02523          *reason = QUEUE_LEAVEEMPTY;
02524          ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long)time(NULL) - qe->start);
02525          leave_queue(qe);
02526          break;
02527       }
02528 
02529       /* leave the queue if no reachable agents, if enabled */
02530       if ((qe->parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) {
02531          *reason = QUEUE_LEAVEUNAVAIL;
02532          ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long)time(NULL) - qe->start);
02533          leave_queue(qe);
02534          break;
02535       }
02536 
02537       /* Make a position announcement, if enabled */
02538       if (qe->parent->announcefrequency && !ringing &&
02539          (res = say_position(qe)))
02540          break;
02541 
02542       /* If we have timed out, break out */
02543       if (qe->expire && (time(NULL) >= qe->expire)) {
02544          *reason = QUEUE_TIMEOUT;
02545          break;
02546       }
02547 
02548       /* Make a periodic announcement, if enabled */
02549       if (qe->parent->periodicannouncefrequency && !ringing &&
02550          (res = say_periodic_announcement(qe)))
02551          break;
02552 
02553       /* If we have timed out, break out */
02554       if (qe->expire && (time(NULL) >= qe->expire)) {
02555          *reason = QUEUE_TIMEOUT;
02556          break;
02557       }
02558       
02559       /* Wait a second before checking again */
02560       if ((res = ast_waitfordigit(qe->chan, RECHECK * 1000))) {
02561          if (res > 0 && !valid_exit(qe, res))
02562             res = 0;
02563          else
02564             break;
02565       }
02566       
02567       /* If we have timed out, break out */
02568       if (qe->expire && (time(NULL) >= qe->expire)) {
02569          *reason = QUEUE_TIMEOUT;
02570          break;
02571       }
02572    }
02573 
02574    return res;
02575 }


Variable Documentation

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

Definition at line 5500 of file app_queue.c.

char* app = "Queue" [static]

Definition at line 148 of file app_queue.c.

char* app_aqm = "AddQueueMember" [static]

Definition at line 192 of file app_queue.c.

char* app_aqm_descrip [static]

Definition at line 194 of file app_queue.c.

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

Definition at line 193 of file app_queue.c.

char* app_pqm = "PauseQueueMember" [static]

Definition at line 227 of file app_queue.c.

char* app_pqm_descrip [static]

Definition at line 229 of file app_queue.c.

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

Definition at line 228 of file app_queue.c.

char* app_ql = "QueueLog" [static]

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

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

Definition at line 267 of file app_queue.c.

char* app_rqm = "RemoveQueueMember" [static]

Definition at line 211 of file app_queue.c.

char* app_rqm_descrip [static]

Definition at line 213 of file app_queue.c.

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

Definition at line 212 of file app_queue.c.

char* app_upqm = "UnpauseQueueMember" [static]

Definition at line 250 of file app_queue.c.

char* app_upqm_descrip [static]

Definition at line 252 of file app_queue.c.

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

Definition at line 251 of file app_queue.c.

const struct ast_module_info* ast_module_info = &__mod_info [static]

Definition at line 5500 of file app_queue.c.

int autofill_default = 0 [static]

queues.conf [general] option

Definition at line 288 of file app_queue.c.

struct ast_cli_entry cli_add_queue_member_deprecated [static]

Initial value:

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

Definition at line 5383 of file app_queue.c.

struct ast_cli_entry cli_queue[] [static]

Definition at line 5393 of file app_queue.c.

Referenced by load_module(), and unload_module().

struct ast_cli_entry cli_remove_queue_member_deprecated [static]

Initial value:

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

Definition at line 5388 of file app_queue.c.

struct ast_cli_entry cli_show_queue_deprecated [static]

Initial value:

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

Definition at line 5378 of file app_queue.c.

ast_cond_t cond

Condition for the state change queue

Definition at line 706 of file app_queue.c.

char* descrip [static]

Definition at line 152 of file app_queue.c.

struct { ... } device_state [static]

Data used by the device state thread.

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

struct statechange* first

Definition at line 708 of file app_queue.c.

enum queue_result id

Definition at line 304 of file app_queue.c.

Referenced by _sip_show_peers().

struct statechange* last

Definition at line 708 of file app_queue.c.

ast_mutex_t lock

Lock for the state change queue

Definition at line 704 of file app_queue.c.

int montype_default = 0 [static]

queues.conf [general] option

Definition at line 291 of file app_queue.c.

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

Persistent Members astdb family.

Definition at line 274 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 5372 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 5349 of file app_queue.c.

char qrm_cmd_usage[] [static]

Initial value:

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

Definition at line 5375 of file app_queue.c.

int queue_debug = 0 [static]

queues.conf [general] extra debug option

Definition at line 279 of file app_queue.c.

int queue_persistent_members = 0 [static]

queues.conf [general] option

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

struct ast_datastore_info queue_transfer_info [static]

Initial value:

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

Definition at line 2671 of file app_queue.c.

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

struct ast_custom_function queueagentcount_function [static]

Definition at line 4435 of file app_queue.c.

Referenced by load_module(), and unload_module().

struct ast_custom_function queuemembercount_function [static]

Definition at line 4445 of file app_queue.c.

Referenced by load_module(), and unload_module().

struct ast_custom_function queuememberlist_function [static]

Definition at line 4469 of file app_queue.c.

Referenced by load_module(), and unload_module().

struct ast_custom_function queuewaitingcount_function [static]

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

struct strategy strategies[] [static]

Referenced by int2strat(), and strat2int().

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

Definition at line 150 of file app_queue.c.

char* text

Definition at line 305 of file app_queue.c.

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

pthread_t thread

The device state monitoring thread

Definition at line 702 of file app_queue.c.

int use_weight = 0 [static]

queues.conf per-queue weight option

Definition at line 285 of file app_queue.c.


Generated on Fri Sep 11 13:45:08 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7