Thu Oct 11 06:42:34 2012

Asterisk developer's documentation


app_queue.c File Reference

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

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

Include dependency graph for app_queue.c:

Go to the source code of this file.

Data Structures

struct  call_queue
struct  callattempt
 We define a custom "local user" structure because we use it not only for keeping track of what is in use but also for keeping track of who we're dialing. More...
struct  interfaces
struct  member
struct  member_interface
struct  queue_ent
struct  queue_transfer_ds
struct  queues
struct  statechange
struct  strategy

Defines

#define ANNOUNCEHOLDTIME_ALWAYS   1
#define ANNOUNCEHOLDTIME_ONCE   2
#define AST_MAX_WATCHERS   256
#define DEFAULT_RETRY   5
#define DEFAULT_TIMEOUT   15
#define MAX_PERIODIC_ANNOUNCEMENTS   10
#define PM_MAX_LEN   8192
#define QUEUE_EMPTY_NORMAL   1
#define QUEUE_EMPTY_STRICT   2
#define QUEUE_EVENT_VARIABLES   3
#define RECHECK   1
#define RES_EXISTS   (-1)
#define RES_NOSUCHQUEUE   (-3)
#define RES_NOT_DYNAMIC   (-4)
#define RES_OKAY   0
#define RES_OUTOFMEMORY   (-2)

Enumerations

enum  {
  QUEUE_STRATEGY_RINGALL = 0, QUEUE_STRATEGY_ROUNDROBIN, QUEUE_STRATEGY_LEASTRECENT, QUEUE_STRATEGY_FEWESTCALLS,
  QUEUE_STRATEGY_RANDOM, QUEUE_STRATEGY_RRMEMORY, QUEUE_STRATEGY_RRORDERED
}
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 compare_weight (struct call_queue *rq, struct member *member)
static char * complete_queue (const char *line, const char *word, int pos, int state)
static char * complete_queue_add_member (const char *line, const char *word, int pos, int state)
static char * complete_queue_remove_member (const char *line, const char *word, int pos, int state)
static char * complete_queue_show (const char *line, const char *word, int pos, int state)
static int compress_char (const char c)
static struct membercreate_queue_member (const char *interface, const char *membername, int penalty, int paused, const char *state_interface)
 allocate space for new queue member and set fields based on parameters passed
static void destroy_queue (void *obj)
static void * device_state_thread (void *data)
 Consumer of the statechange queue.
static void do_hang (struct callattempt *o)
 common hangup actions
static void dump_queue_members (struct call_queue *pm_queue)
static struct callattemptfind_best (struct callattempt *outgoing)
 find the entry with the best metric, or NULL
static struct call_queuefind_queue_by_name_rt (const char *queuename, struct ast_variable *queue_vars, struct ast_config *member_config)
 Reload a single queue via realtime.
static void free_members (struct call_queue *q, int all)
static enum queue_member_status get_member_status (struct call_queue *q, int max_penalty)
 Check if members are available.
static int handle_queue_add_member (int fd, int argc, char *argv[])
static int handle_queue_remove_member (int fd, int argc, char *argv[])
static void * handle_statechange (struct statechange *sc)
 set a member's status based on device state of that member's interface
static void hangupcalls (struct callattempt *outgoing, struct ast_channel *exception)
static void init_queue (struct call_queue *q)
static void insert_entry (struct call_queue *q, struct queue_ent *prev, struct queue_ent *new, int *pos)
 Insert the 'new' entry after the 'prev' entry of queue 'q'.
static char * int2strat (int strategy)
static struct memberinterface_exists (struct call_queue *q, const char *interface)
static int interface_exists_global (const char *interface)
static int is_our_turn (struct queue_ent *qe)
 Check if we should start attempting to call queue members.
static int join_queue (char *queuename, struct queue_ent *qe, enum queue_result *reason)
static void leave_queue (struct queue_ent *qe)
static int load_module (void)
static struct call_queueload_realtime_queue (const char *queuename)
static int manager_add_queue_member (struct mansession *s, const struct message *m)
static int manager_pause_queue_member (struct mansession *s, const struct message *m)
static int manager_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 queue_exec (struct ast_channel *chan, void *data)
 The starting point for all queue calls.
static int queue_function_qac (struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
static int queue_function_queuememberlist (struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
static int queue_function_queuewaitingcount (struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
static void queue_set_param (struct call_queue *q, const char *param, const char *val, int linenum, int failunknown)
 Configure a queue parameter.
static int queue_show (int fd, int argc, char **argv)
static void queue_transfer_destroy (void *data)
static void queue_transfer_fixup (void *data, struct ast_channel *old_chan, struct ast_channel *new_chan)
 Log an attended transfer when a queue caller channel is masqueraded.
static void recalc_holdtime (struct queue_ent *qe, int newholdtime)
static void record_abandoned (struct queue_ent *qe)
static int reload (void)
static void reload_queue_members (void)
static int reload_queues (void)
static int remove_from_interfaces (const char *interface)
static int remove_from_queue (const char *queuename, const char *interface)
static void remove_queue (struct call_queue *q)
 removes a call_queue from the list of call_queues
static int ring_entry (struct queue_ent *qe, struct callattempt *tmp, int *busies)
 Part 2 of ring_one.
static int ring_one (struct queue_ent *qe, struct callattempt *outgoing, int *busies)
 Place a call to a queue member.
static void rna (int rnatime, struct queue_ent *qe, char *interface, char *membername, int pause)
 RNA == Ring No Answer. Common code that is executed when we try a queue member and they don't answer.
static int rqm_exec (struct ast_channel *chan, void *data)
static void rr_dep_warning (void)
static void rt_handle_member_record (struct call_queue *q, char *interface, const char *membername, const char *penalty_str, const char *paused_str, const char *state_interface)
static int say_periodic_announcement (struct queue_ent *qe)
static int say_position (struct queue_ent *qe)
static int set_member_paused (const char *queuename, const char *interface, int paused)
static void set_queue_result (struct ast_channel *chan, enum queue_result res)
 sets the QUEUESTATUS channel variable
static struct ast_datastoresetup_transfer_datastore (struct queue_ent *qe, struct member *member, time_t starttime, int callcompletedinsl)
 create a datastore for storing relevant info to log attended transfers in the queue_log
static int statechange_queue (const char *dev, int state, void *ign)
 Producer of the statechange queue.
static int store_next (struct queue_ent *qe, struct callattempt *outgoing)
static int strat2int (const char *strategy)
static int try_calling (struct queue_ent *qe, const char *options, char *announceoverride, const char *url, int *tries, int *noption, const char *agi)
 A large function which calls members, updates statistics, and bridges the caller and a member.
static int unload_module (void)
static int update_queue (struct call_queue *q, struct member *member, int callcompletedinsl)
static int update_realtime_member_field (struct member *mem, const char *queue_name, const char *field, const char *value)
static void update_realtime_members (struct call_queue *q)
static int update_status (const char *interface, const int status)
static int upqm_exec (struct ast_channel *chan, void *data)
static int valid_exit (struct queue_ent *qe, char digit)
static char * vars2manager (struct ast_channel *chan, char *vars, size_t len)
static int wait_a_bit (struct queue_ent *qe)
static struct callattemptwait_for_answer (struct queue_ent *qe, struct callattempt *outgoing, int *to, char *digit, int prebusies, int caller_disconnect, int forwardsallowed)
 Wait for a member to answer the call.
static int wait_our_turn (struct queue_ent *qe, int ringing, enum queue_result *reason)
 The waiting areas for callers who are not actively calling members.

Variables

static struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT | AST_MODFLAG_BUILDSUM, .description = "True Call Queueing" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, .reload = reload, }
static char * app = "Queue"
static char * app_aqm = "AddQueueMember"
static char * app_aqm_descrip
static char * app_aqm_synopsis = "Dynamically adds queue members"
static char * app_pqm = "PauseQueueMember"
static char * app_pqm_descrip
static char * app_pqm_synopsis = "Pauses a queue member"
static char * app_ql = "QueueLog"
static char * app_ql_descrip
static char * app_ql_synopsis = "Writes to the queue_log"
static char * app_rqm = "RemoveQueueMember"
static char * app_rqm_descrip
static char * app_rqm_synopsis = "Dynamically removes queue members"
static char * app_upqm = "UnpauseQueueMember"
static char * app_upqm_descrip
static char * app_upqm_synopsis = "Unpauses a queue member"
static const struct
ast_module_info
ast_module_info = &__mod_info
static int autofill_default = 0
 queues.conf [general] option
static struct ast_cli_entry cli_add_queue_member_deprecated
static struct ast_cli_entry cli_queue []
static struct ast_cli_entry cli_remove_queue_member_deprecated
static struct ast_cli_entry cli_show_queue_deprecated
static char * descrip
struct {
   ast_cond_t   cond
   ast_mutex_t   lock
   struct {
      struct statechange *   first
      struct statechange *   last
   }   state_change_q
   unsigned int   stop:1
   pthread_t   thread
device_state
 Data used by the device state thread.
static int montype_default = 0
 queues.conf [general] option
static const char * pm_family = "Queue/PersistentMembers"
 Persistent Members astdb family.
static char qam_cmd_usage []
static char qrm_cmd_usage []
static int queue_persistent_members = 0
 queues.conf [general] option
struct {
   enum queue_result   id
   char *   text
queue_results []
static char queue_show_usage []
static struct ast_datastore_info queue_transfer_info
 a datastore used to help correctly log attended transfers of queue callers
static struct ast_custom_function queueagentcount_function
static struct ast_custom_function queuemembercount_function
static struct ast_custom_function queuememberlist_function
static struct ast_custom_function queuewaitingcount_function
static struct strategy strategies []
static char * synopsis = "Queue a call for a call queue"
static int use_weight = 0
 queues.conf per-queue weight option


Detailed Description

True call queues with optional send URL on answer.

Author:
Mark Spencer <markster@digium.com>
Development notes
Note:
2004-11-25: Persistent Dynamic Members added by: NetNation Communications (www.netnation.com) Kevin Lindsay <kevinl@netnation.com>
Each dynamic agent in each queue is now stored in the astdb. When asterisk is restarted, each agent will be automatically readded into their recorded queues. This feature can be configured with the 'persistent_members=<1|0>' setting in the '[general]' category in queues.conf. The default is on.

Note:
2004-06-04: Priorities in queues added by inAccess Networks (work funded by Hellas On Line (HOL) www.hol.gr).

These features added by David C. Troy <dave@toad.net>:

  • Per-queue holdtime calculation
  • Estimated holdtime announcement
  • Position announcement
  • Abandoned/completed call counters
  • Failout timer passed as optional app parameter
  • Optional monitoring of calls, started when call is answered
Patch Version 1.07 2003-12-24 01

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

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

Definition in file app_queue.c.


Define Documentation

#define ANNOUNCEHOLDTIME_ALWAYS   1

Definition at line 385 of file app_queue.c.

Referenced by queue_set_param().

#define ANNOUNCEHOLDTIME_ONCE   2

Definition at line 386 of file app_queue.c.

Referenced by queue_set_param(), and say_position().

#define AST_MAX_WATCHERS   256

Definition at line 2239 of file app_queue.c.

#define DEFAULT_RETRY   5

Definition at line 139 of file app_queue.c.

Referenced by init_queue(), and queue_set_param().

#define DEFAULT_TIMEOUT   15

Definition at line 140 of file app_queue.c.

Referenced by queue_set_param().

#define MAX_PERIODIC_ANNOUNCEMENTS   10

Definition at line 142 of file app_queue.c.

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

#define PM_MAX_LEN   8192

Definition at line 276 of file app_queue.c.

Referenced by dump_queue_members(), and reload_queue_members().

#define QUEUE_EMPTY_NORMAL   1

Definition at line 383 of file app_queue.c.

Referenced by queue_set_param().

#define QUEUE_EMPTY_STRICT   2

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

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

#define RECHECK   1

Definition at line 141 of file app_queue.c.

Referenced by wait_our_turn().

#define RES_EXISTS   (-1)

#define RES_NOSUCHQUEUE   (-3)

#define RES_NOT_DYNAMIC   (-4)

#define RES_OKAY   0

#define RES_OUTOFMEMORY   (-2)


Enumeration Type Documentation

anonymous enum

Enumerator:
QUEUE_STRATEGY_RINGALL 
QUEUE_STRATEGY_ROUNDROBIN 
QUEUE_STRATEGY_LEASTRECENT 
QUEUE_STRATEGY_FEWESTCALLS 
QUEUE_STRATEGY_RANDOM 
QUEUE_STRATEGY_RRMEMORY 
QUEUE_STRATEGY_RRORDERED 

Definition at line 116 of file app_queue.c.

Enumerator:
QUEUE_NO_MEMBERS 
QUEUE_NO_REACHABLE_MEMBERS 
QUEUE_NORMAL 

Definition at line 562 of file app_queue.c.

00562                          {
00563    QUEUE_NO_MEMBERS,
00564    QUEUE_NO_REACHABLE_MEMBERS,
00565    QUEUE_NORMAL
00566 };

Enumerator:
QUEUE_UNKNOWN 
QUEUE_TIMEOUT 
QUEUE_JOINEMPTY 
QUEUE_LEAVEEMPTY 
QUEUE_JOINUNAVAIL 
QUEUE_LEAVEUNAVAIL 
QUEUE_FULL 

Definition at line 290 of file app_queue.c.

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


Function Documentation

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

Definition at line 4699 of file app_queue.c.

References ao2_container_count(), ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next(), ao2_lock(), ao2_ref(), ao2_unlock(), ast_build_string(), ast_category_browse(), ast_check_realtime(), ast_cli(), ast_config_destroy(), AST_LIST_EMPTY, AST_LIST_LOCK, AST_LIST_NEXT, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_load_realtime_multientry(), ast_strlen_zero(), astman_append(), member::calls, call_queue::callsabandoned, call_queue::callscompleted, call_queue::callscompletedinsl, queue_ent::chan, call_queue::count, devstate2str(), member::dynamic, call_queue::head, call_queue::holdtime, int2strat(), member::interface, member::lastcall, load_realtime_queue(), call_queue::maxlen, member::membername, call_queue::members, 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::servicelevel, queue_ent::start, member::status, call_queue::strategy, and call_queue::weight.

Referenced by manager_queues_show(), and queue_show().

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

static void __reg_module ( void   )  [static]

Definition at line 5391 of file app_queue.c.

static void __unreg_module ( void   )  [static]

Definition at line 5391 of file app_queue.c.

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

Definition at line 922 of file app_queue.c.

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

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

00923 {
00924    struct member_interface *curint;
00925 
00926    AST_LIST_LOCK(&interfaces);
00927    AST_LIST_TRAVERSE(&interfaces, curint, list) {
00928       if (!strcasecmp(curint->interface, interface))
00929          break;
00930    }
00931 
00932    if (curint) {
00933       AST_LIST_UNLOCK(&interfaces);
00934       return 0;
00935    }
00936 
00937    if (option_debug)
00938       ast_log(LOG_DEBUG, "Adding %s to the list of interfaces that make up all of our queue members.\n", interface);
00939    
00940    if ((curint = ast_calloc(1, sizeof(*curint)))) {
00941       ast_copy_string(curint->interface, interface, sizeof(curint->interface));
00942       AST_LIST_INSERT_HEAD(&interfaces, curint, list);
00943    }
00944    AST_LIST_UNLOCK(&interfaces);
00945 
00946    return 0;
00947 }

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

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

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

03547 {
03548    struct call_queue *q;
03549    struct member *new_member, *old_member;
03550    int res = RES_NOSUCHQUEUE;
03551 
03552    /* \note Ensure the appropriate realtime queue is loaded.  Note that this
03553     * short-circuits if the queue is already in memory. */
03554    if (!(q = load_realtime_queue(queuename)))
03555       return res;
03556 
03557    AST_LIST_LOCK(&queues);
03558 
03559    ao2_lock(q);
03560    if ((old_member = interface_exists(q, interface)) == NULL) {
03561       if ((new_member = create_queue_member(interface, membername, penalty, paused, state_interface))) {
03562          add_to_interfaces(new_member->state_interface);
03563          new_member->dynamic = 1;
03564          ao2_link(q->members, new_member);
03565          q->membercount++;
03566          manager_event(EVENT_FLAG_AGENT, "QueueMemberAdded",
03567             "Queue: %s\r\n"
03568             "Location: %s\r\n"
03569             "MemberName: %s\r\n"
03570             "Membership: %s\r\n"
03571             "Penalty: %d\r\n"
03572             "CallsTaken: %d\r\n"
03573             "LastCall: %d\r\n"
03574             "Status: %d\r\n"
03575             "Paused: %d\r\n",
03576             q->name, new_member->interface, new_member->membername,
03577             "dynamic",
03578             new_member->penalty, new_member->calls, (int) new_member->lastcall,
03579             new_member->status, new_member->paused);
03580          
03581          ao2_ref(new_member, -1);
03582          new_member = NULL;
03583 
03584          if (dump)
03585             dump_queue_members(q);
03586          
03587          res = RES_OKAY;
03588       } else {
03589          res = RES_OUTOFMEMORY;
03590       }
03591    } else {
03592       ao2_ref(old_member, -1);
03593       res = RES_EXISTS;
03594    }
03595    ao2_unlock(q);
03596    AST_LIST_UNLOCK(&queues);
03597 
03598    return res;
03599 }

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

Definition at line 820 of file app_queue.c.

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

Referenced by find_queue_by_name_rt(), and reload_queues().

00821 {
00822    struct call_queue *q;
00823 
00824    if ((q = ao2_alloc(sizeof(*q), destroy_queue))) {
00825       ast_copy_string(q->name, queuename, sizeof(q->name));
00826    }
00827    return q;
00828 }

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

Definition at line 3917 of file app_queue.c.

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

Referenced by load_module().

03918 {
03919    int res=-1;
03920    struct ast_module_user *lu;
03921    char *parse, *temppos = NULL;
03922    int priority_jump = 0;
03923    AST_DECLARE_APP_ARGS(args,
03924       AST_APP_ARG(queuename);
03925       AST_APP_ARG(interface);
03926       AST_APP_ARG(penalty);
03927       AST_APP_ARG(options);
03928       AST_APP_ARG(membername);
03929       AST_APP_ARG(state_interface);
03930    );
03931    int penalty = 0;
03932 
03933    if (ast_strlen_zero(data)) {
03934       ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename[|interface[|penalty[|options[|membername[|state_interface]]]]])\n");
03935       return -1;
03936    }
03937 
03938    parse = ast_strdupa(data);
03939 
03940    AST_STANDARD_APP_ARGS(args, parse);
03941 
03942    lu = ast_module_user_add(chan);
03943 
03944    if (ast_strlen_zero(args.interface)) {
03945       args.interface = ast_strdupa(chan->name);
03946       temppos = strrchr(args.interface, '-');
03947       if (temppos)
03948          *temppos = '\0';
03949    }
03950 
03951    if (!ast_strlen_zero(args.penalty)) {
03952       if ((sscanf(args.penalty, "%30d", &penalty) != 1) || penalty < 0) {
03953          ast_log(LOG_WARNING, "Penalty '%s' is invalid, must be an integer >= 0\n", args.penalty);
03954          penalty = 0;
03955       }
03956    }
03957    
03958    if (args.options) {
03959       if (strchr(args.options, 'j'))
03960          priority_jump = 1;
03961    }
03962 
03963    switch (add_to_queue(args.queuename, args.interface, args.membername, penalty, 0, queue_persistent_members, args.state_interface)) {
03964    case RES_OKAY:
03965       ast_queue_log(args.queuename, chan->uniqueid, args.interface, "ADDMEMBER", "%s", "");
03966       ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", args.interface, args.queuename);
03967       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "ADDED");
03968       res = 0;
03969       break;
03970    case RES_EXISTS:
03971       ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': Already there\n", args.interface, args.queuename);
03972       if (priority_jump || ast_opt_priority_jumping)
03973          ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
03974       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "MEMBERALREADY");
03975       res = 0;
03976       break;
03977    case RES_NOSUCHQUEUE:
03978       ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", args.queuename);
03979       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "NOSUCHQUEUE");
03980       res = 0;
03981       break;
03982    case RES_OUTOFMEMORY:
03983       ast_log(LOG_ERROR, "Out of memory adding member %s to queue %s\n", args.interface, args.queuename);
03984       break;
03985    }
03986 
03987    ast_module_user_remove(lu);
03988 
03989    return res;
03990 }

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

References ast_channel_datastore_find(), and queue_transfer_info.

Referenced by try_calling().

02777 {
02778    return ast_channel_datastore_find(chan, &queue_transfer_info, NULL) ? 0 : 1;
02779 }

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

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

Referenced by try_calling().

02660 {
02661    if (qe->max_penalty && (mem->penalty > qe->max_penalty))
02662       return -1;
02663 
02664    switch (q->strategy) {
02665    case QUEUE_STRATEGY_RINGALL:
02666       /* Everyone equal, except for penalty */
02667       tmp->metric = mem->penalty * 1000000;
02668       break;
02669    case QUEUE_STRATEGY_ROUNDROBIN:
02670       if (!pos) {
02671          if (!q->wrapped) {
02672             /* No more channels, start over */
02673             q->rrpos = 0;
02674          } else {
02675             /* Prioritize next entry */
02676             q->rrpos++;
02677          }
02678          q->wrapped = 0;
02679       }
02680       /* Fall through */
02681    case QUEUE_STRATEGY_RRORDERED:
02682    case QUEUE_STRATEGY_RRMEMORY:
02683       if (pos < q->rrpos) {
02684          tmp->metric = 1000 + pos;
02685       } else {
02686          if (pos > q->rrpos)
02687             /* Indicate there is another priority */
02688             q->wrapped = 1;
02689          tmp->metric = pos;
02690       }
02691       tmp->metric += mem->penalty * 1000000;
02692       break;
02693    case QUEUE_STRATEGY_RANDOM:
02694       tmp->metric = ast_random() % 1000;
02695       tmp->metric += mem->penalty * 1000000;
02696       break;
02697    case QUEUE_STRATEGY_FEWESTCALLS:
02698       tmp->metric = mem->calls;
02699       tmp->metric += mem->penalty * 1000000;
02700       break;
02701    case QUEUE_STRATEGY_LEASTRECENT:
02702       if (!mem->lastcall)
02703          tmp->metric = 0;
02704       else
02705          tmp->metric = 1000000 - (time(NULL) - mem->lastcall);
02706       tmp->metric += mem->penalty * 1000000;
02707       break;
02708    default:
02709       ast_log(LOG_WARNING, "Can't calculate metric for unknown strategy %d\n", q->strategy);
02710       break;
02711    }
02712    return 0;
02713 }

static void clear_and_free_interfaces ( void   )  [static]

Definition at line 1001 of file app_queue.c.

References AST_LIST_LOCK, AST_LIST_REMOVE_HEAD, AST_LIST_UNLOCK, and free.

Referenced by unload_module().

01002 {
01003    struct member_interface *curint;
01004 
01005    AST_LIST_LOCK(&interfaces);
01006    while ((curint = AST_LIST_REMOVE_HEAD(&interfaces, list)))
01007       free(curint);
01008    AST_LIST_UNLOCK(&interfaces);
01009 }

static void clear_queue ( struct call_queue q  )  [static]

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

00914 {
00915    q->holdtime = 0;
00916    q->callscompleted = 0;
00917    q->callsabandoned = 0;
00918    q->callscompletedinsl = 0;
00919    q->wrapuptime = 0;
00920 }

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

Definition at line 1853 of file app_queue.c.

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

Referenced by ring_entry().

01854 {
01855    struct call_queue *q;
01856    struct member *mem;
01857    int found = 0;
01858    
01859    /* &qlock and &rq->lock already set by try_calling()
01860     * to solve deadlock */
01861    AST_LIST_TRAVERSE(&queues, q, list) {
01862       if (q == rq) /* don't check myself, could deadlock */
01863          continue;
01864       ao2_lock(q);
01865       if (q->count && q->members) {
01866          if ((mem = ao2_find(q->members, member, OBJ_POINTER))) {
01867             ast_log(LOG_DEBUG, "Found matching member %s in queue '%s'\n", mem->interface, q->name);
01868             if (q->weight > rq->weight && q->count >= num_available_members(q)) {
01869                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);
01870                found = 1;
01871             }
01872             ao2_ref(mem, -1);
01873          }
01874       }
01875       ao2_unlock(q);
01876       if (found)
01877          break;
01878    }
01879    return found;
01880 }

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

Definition at line 4855 of file app_queue.c.

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

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

04856 {
04857    struct call_queue *q;
04858    char *ret = NULL;
04859    int which = 0;
04860    int wordlen = strlen(word);
04861    
04862    AST_LIST_LOCK(&queues);
04863    AST_LIST_TRAVERSE(&queues, q, list) {
04864       if (!strncasecmp(word, q->name, wordlen) && ++which > state) {
04865          ret = ast_strdup(q->name); 
04866          break;
04867       }
04868    }
04869    AST_LIST_UNLOCK(&queues);
04870 
04871    return ret;
04872 }

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

Definition at line 5157 of file app_queue.c.

References ast_malloc, ast_strdup, and complete_queue().

05158 {
05159    /* 0 - queue; 1 - add; 2 - member; 3 - <interface>; 4 - to; 5 - <queue>; 6 - penalty; 7 - <penalty>; 8 - as; 9 - <membername> */
05160    switch (pos) {
05161    case 3:  /* Don't attempt to complete name of interface (infinite possibilities) */
05162       return NULL;
05163    case 4:  /* only one possible match, "to" */
05164       return state == 0 ? ast_strdup("to") : NULL;
05165    case 5:  /* <queue> */
05166       return complete_queue(line, word, pos, state);
05167    case 6: /* only one possible match, "penalty" */
05168       return state == 0 ? ast_strdup("penalty") : NULL;
05169    case 7:
05170       if (state < 100) {   /* 0-99 */
05171          char *num;
05172          if ((num = ast_malloc(3))) {
05173             sprintf(num, "%d", state);
05174          }
05175          return num;
05176       } else {
05177          return NULL;
05178       }
05179    case 8: /* only one possible match, "as" */
05180       return state == 0 ? ast_strdup("as") : NULL;
05181    case 9:  /* Don't attempt to complete name of member (infinite possibilities) */
05182       return NULL;
05183    case 10:
05184       return state == 0 ? ast_strdup("state_interface") : NULL;
05185    default:
05186       return NULL;
05187    }
05188 }

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

Definition at line 5225 of file app_queue.c.

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

05226 {
05227    int which = 0;
05228    struct call_queue *q;
05229    struct member *m;
05230    struct ao2_iterator mem_iter;
05231 
05232    /* 0 - queue; 1 - remove; 2 - member; 3 - <member>; 4 - from; 5 - <queue> */
05233    if (pos > 5 || pos < 3)
05234       return NULL;
05235    if (pos == 4)  /* only one possible match, 'from' */
05236       return state == 0 ? ast_strdup("from") : NULL;
05237 
05238    if (pos == 5)  /* No need to duplicate code */
05239       return complete_queue(line, word, pos, state);
05240 
05241    /* here is the case for 3, <member> */
05242    if (!AST_LIST_EMPTY(&queues)) { /* XXX unnecessary ? the traverse does that for us */
05243       AST_LIST_TRAVERSE(&queues, q, list) {
05244          ao2_lock(q);
05245          mem_iter = ao2_iterator_init(q->members, 0);
05246          while ((m = ao2_iterator_next(&mem_iter))) {
05247             if (++which > state) {
05248                char *tmp;
05249                ao2_iterator_destroy(&mem_iter);
05250                ao2_unlock(q);
05251                tmp = ast_strdup(m->interface);
05252                ao2_ref(m, -1);
05253                return tmp;
05254             }
05255             ao2_ref(m, -1);
05256          }
05257          ao2_iterator_destroy(&mem_iter);
05258          ao2_unlock(q);
05259       }
05260    }
05261 
05262    return NULL;
05263 }

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

Definition at line 4874 of file app_queue.c.

References complete_queue().

04875 {
04876    if (pos == 2)
04877       return complete_queue(line, word, pos, state);
04878    return NULL;
04879 }

static int compress_char ( const char  c  )  [static]

Definition at line 830 of file app_queue.c.

00831 {
00832    if (c < 32)
00833       return 0;
00834    else if (c > 96)
00835       return c - 64;
00836    else
00837       return c - 32;
00838 }

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

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

Definition at line 795 of file app_queue.c.

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

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

00796 {
00797    struct member *cur;
00798    
00799    if ((cur = ao2_alloc(sizeof(*cur), NULL))) {
00800       cur->penalty = penalty;
00801       cur->paused = paused;
00802       ast_copy_string(cur->interface, interface, sizeof(cur->interface));
00803       if (!ast_strlen_zero(state_interface)) {
00804          ast_copy_string(cur->state_interface, state_interface, sizeof(cur->state_interface));
00805       } else {
00806          ast_copy_string(cur->state_interface, interface, sizeof(cur->state_interface));
00807       }
00808       if (!ast_strlen_zero(membername))
00809          ast_copy_string(cur->membername, membername, sizeof(cur->membername));
00810       else
00811          ast_copy_string(cur->membername, interface, sizeof(cur->membername));
00812       if (!strchr(cur->interface, '/'))
00813          ast_log(LOG_WARNING, "No location at interface '%s'\n", interface);
00814       cur->status = ast_device_state(cur->state_interface);
00815    }
00816 
00817    return cur;
00818 }

static void destroy_queue ( void *  obj  )  [static]

Definition at line 528 of file app_queue.c.

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

Referenced by alloc_queue().

00529 {
00530    struct call_queue *q = obj;
00531    if (q->members) {
00532       free_members(q, 1);
00533       ao2_ref(q->members, -1);
00534    }
00535 }

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

Consumer of the statechange queue.

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

00744 {
00745    struct statechange *sc = NULL;
00746 
00747    while (!device_state.stop) {
00748       ast_mutex_lock(&device_state.lock);
00749       if (!(sc = AST_LIST_REMOVE_HEAD(&device_state.state_change_q, entry))) {
00750          ast_cond_wait(&device_state.cond, &device_state.lock);
00751          sc = AST_LIST_REMOVE_HEAD(&device_state.state_change_q, entry);
00752       }
00753       ast_mutex_unlock(&device_state.lock);
00754 
00755       /* Check to see if we were woken up to see the request to stop */
00756       if (device_state.stop)
00757          break;
00758 
00759       if (!sc)
00760          continue;
00761 
00762       handle_statechange(sc);
00763 
00764       free(sc);
00765       sc = NULL;
00766    }
00767 
00768    if (sc)
00769       free(sc);
00770 
00771    while ((sc = AST_LIST_REMOVE_HEAD(&device_state.state_change_q, entry)))
00772       free(sc);
00773 
00774    return NULL;
00775 }

static void do_hang ( struct callattempt o  )  [static]

common hangup actions

Definition at line 1883 of file app_queue.c.

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

Referenced by ring_entry(), and wait_for_answer().

01884 {
01885    o->stillgoing = 0;
01886    ast_hangup(o->chan);
01887    o->chan = NULL;
01888 }

static void dump_queue_members ( struct call_queue pm_queue  )  [static]

Definition at line 3454 of file app_queue.c.

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

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

03455 {
03456    struct member *cur_member;
03457    char value[PM_MAX_LEN];
03458    int value_len = 0;
03459    int res;
03460    struct ao2_iterator mem_iter;
03461 
03462    memset(value, 0, sizeof(value));
03463 
03464    if (!pm_queue)
03465       return;
03466 
03467    mem_iter = ao2_iterator_init(pm_queue->members, 0);
03468    while ((cur_member = ao2_iterator_next(&mem_iter))) {
03469       if (!cur_member->dynamic) {
03470          ao2_ref(cur_member, -1);
03471          continue;
03472       }
03473 
03474       res = snprintf(value + value_len, sizeof(value) - value_len, "%s%s;%d;%d;%s;%s",
03475          value_len ? "|" : "", cur_member->interface, cur_member->penalty, cur_member->paused, cur_member->membername, cur_member->state_interface);
03476 
03477       ao2_ref(cur_member, -1);
03478 
03479       if (res != strlen(value + value_len)) {
03480          ast_log(LOG_WARNING, "Could not create persistent member string, out of space\n");
03481          break;
03482       }
03483       value_len += res;
03484    }
03485    ao2_iterator_destroy(&mem_iter);
03486    
03487    if (value_len && !cur_member) {
03488       if (ast_db_put(pm_family, pm_queue->name, value))
03489          ast_log(LOG_WARNING, "failed to create persistent dynamic entry!\n");
03490    } else
03491       /* Delete the entry if the queue is empty or there is an error */
03492       ast_db_del(pm_family, pm_queue->name);
03493 }

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

find the entry with the best metric, or NULL

Definition at line 2085 of file app_queue.c.

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

02086 {
02087    struct callattempt *best = NULL, *cur;
02088 
02089    for (cur = outgoing; cur; cur = cur->q_next) {
02090       if (cur->stillgoing &&              /* Not already done */
02091          !cur->chan &&              /* Isn't already going */
02092          (!best || cur->metric < best->metric)) {     /* We haven't found one yet, or it's better */
02093          best = cur;
02094       }
02095    }
02096 
02097    return best;
02098 }

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

Reload a single queue via realtime.

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

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

Definition at line 1233 of file app_queue.c.

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

Referenced by load_realtime_queue().

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

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

Definition at line 1213 of file app_queue.c.

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

Referenced by destroy_queue().

01214 {
01215    /* Free non-dynamic members */
01216    struct member *cur;
01217    struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
01218 
01219    while ((cur = ao2_iterator_next(&mem_iter))) {
01220       if (all || !cur->dynamic) {
01221          ao2_unlink(q->members, cur);
01222          remove_from_interfaces(cur->state_interface);
01223          q->membercount--;
01224       }
01225       ao2_ref(cur, -1);
01226    }
01227    ao2_iterator_destroy(&mem_iter);
01228 }

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

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next(), ao2_lock(), ao2_ref(), ao2_unlock(), AST_DEVICE_INVALID, AST_DEVICE_UNAVAILABLE, call_queue::members, member::paused, member::penalty, QUEUE_NO_MEMBERS, QUEUE_NO_REACHABLE_MEMBERS, QUEUE_NORMAL, and member::status.

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

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

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

Definition at line 5097 of file app_queue.c.

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

05098 {
05099    char *queuename, *interface, *membername = NULL, *state_interface = NULL;
05100    int penalty;
05101 
05102    if ((argc != 6) && (argc != 8) && (argc != 10) && (argc != 12)) {
05103       return RESULT_SHOWUSAGE;
05104    } else if (strcmp(argv[4], "to")) {
05105       return RESULT_SHOWUSAGE;
05106    } else if ((argc == 8) && strcmp(argv[6], "penalty")) {
05107       return RESULT_SHOWUSAGE;
05108    } else if ((argc == 10) && strcmp(argv[8], "as")) {
05109       return RESULT_SHOWUSAGE;
05110    } else if ((argc == 12) && strcmp(argv[10], "state_interface")) {
05111       return RESULT_SHOWUSAGE;
05112    }
05113 
05114    queuename = argv[5];
05115    interface = argv[3];
05116    if (argc >= 8) {
05117       if (sscanf(argv[7], "%30d", &penalty) == 1) {
05118          if (penalty < 0) {
05119             ast_cli(fd, "Penalty must be >= 0\n");
05120             penalty = 0;
05121          }
05122       } else {
05123          ast_cli(fd, "Penalty must be an integer >= 0\n");
05124          penalty = 0;
05125       }
05126    } else {
05127       penalty = 0;
05128    }
05129 
05130    if (argc >= 10) {
05131       membername = argv[9];
05132    }
05133 
05134    if (argc >= 12) {
05135       state_interface = argv[11];
05136    }
05137 
05138    switch (add_to_queue(queuename, interface, membername, penalty, 0, queue_persistent_members, state_interface)) {
05139    case RES_OKAY:
05140       ast_queue_log(queuename, "CLI", interface, "ADDMEMBER", "%s", "");
05141       ast_cli(fd, "Added interface '%s' to queue '%s'\n", interface, queuename);
05142       return RESULT_SUCCESS;
05143    case RES_EXISTS:
05144       ast_cli(fd, "Unable to add interface '%s' to queue '%s': Already there\n", interface, queuename);
05145       return RESULT_FAILURE;
05146    case RES_NOSUCHQUEUE:
05147       ast_cli(fd, "Unable to add interface to queue '%s': No such queue\n", queuename);
05148       return RESULT_FAILURE;
05149    case RES_OUTOFMEMORY:
05150       ast_cli(fd, "Out of memory\n");
05151       return RESULT_FAILURE;
05152    default:
05153       return RESULT_FAILURE;
05154    }
05155 }

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

Definition at line 5190 of file app_queue.c.

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

05191 {
05192    char *queuename, *interface;
05193 
05194    if (argc != 6) {
05195       return RESULT_SHOWUSAGE;
05196    } else if (strcmp(argv[4], "from")) {
05197       return RESULT_SHOWUSAGE;
05198    }
05199 
05200    queuename = argv[5];
05201    interface = argv[3];
05202 
05203    switch (remove_from_queue(queuename, interface)) {
05204    case RES_OKAY:
05205       ast_queue_log(queuename, "CLI", interface, "REMOVEMEMBER", "%s", "");
05206       ast_cli(fd, "Removed interface '%s' from queue '%s'\n", interface, queuename);
05207       return RESULT_SUCCESS;
05208    case RES_EXISTS:
05209       ast_cli(fd, "Unable to remove interface '%s' from queue '%s': Not there\n", interface, queuename);
05210       return RESULT_FAILURE;
05211    case RES_NOSUCHQUEUE:
05212       ast_cli(fd, "Unable to remove interface from queue '%s': No such queue\n", queuename);
05213       return RESULT_FAILURE;
05214    case RES_OUTOFMEMORY:
05215       ast_cli(fd, "Out of memory\n");
05216       return RESULT_FAILURE;
05217    case RES_NOT_DYNAMIC:
05218       ast_cli(fd, "Member not dynamic\n");
05219       return RESULT_FAILURE;
05220    default:
05221       return RESULT_FAILURE;
05222    }
05223 }

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

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

Referenced by device_state_thread().

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

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

Definition at line 1786 of file app_queue.c.

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

Referenced by try_calling().

01787 {
01788    struct callattempt *oo;
01789 
01790    while (outgoing) {
01791       /* Hangup any existing lines we have open */
01792       if (outgoing->chan && (outgoing->chan != exception))
01793          ast_hangup(outgoing->chan);
01794       oo = outgoing;
01795       outgoing = outgoing->q_next;
01796       if (oo->member)
01797          ao2_ref(oo->member, -1);
01798       free(oo);
01799    }
01800 }

static void init_queue ( struct call_queue q  )  [static]

Definition at line 858 of file app_queue.c.

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

Referenced by find_queue_by_name_rt(), and reload_queues().

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

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

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

Referenced by join_queue().

00539 {
00540    struct queue_ent *cur;
00541 
00542    if (!q || !new)
00543       return;
00544    if (prev) {
00545       cur = prev->next;
00546       prev->next = new;
00547    } else {
00548       cur = q->head;
00549       q->head = new;
00550    }
00551    new->next = cur;
00552 
00553    /* every queue_ent must have a reference to it's parent call_queue, this
00554     * reference does not go away until the end of the queue_ent's life, meaning
00555     * that even when the queue_ent leaves the call_queue this ref must remain. */
00556    ao2_ref(q, +1);
00557    new->parent = q;
00558    new->pos = ++(*pos);
00559    new->opos = *pos;
00560 }

static char* int2strat ( int  strategy  )  [static]

Definition at line 492 of file app_queue.c.

References strategy::name, and strategies.

Referenced by __queues_show().

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

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

Definition at line 3427 of file app_queue.c.

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

Referenced by add_to_queue(), and set_member_paused().

03428 {
03429    struct member *mem;
03430    struct ao2_iterator mem_iter;
03431 
03432    if (!q)
03433       return NULL;
03434 
03435    mem_iter = ao2_iterator_init(q->members, 0);
03436    while ((mem = ao2_iterator_next(&mem_iter))) {
03437       if (!strcasecmp(interface, mem->interface)) {
03438          ao2_iterator_destroy(&mem_iter);
03439          return mem;
03440       }
03441       ao2_ref(mem, -1);
03442    }
03443    ao2_iterator_destroy(&mem_iter);
03444 
03445    return NULL;
03446 }

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

Definition at line 949 of file app_queue.c.

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

Referenced by remove_from_interfaces().

00950 {
00951    struct call_queue *q;
00952    struct member *mem;
00953    struct ao2_iterator mem_iter;
00954    int ret = 0;
00955 
00956    AST_LIST_LOCK(&queues);
00957    AST_LIST_TRAVERSE(&queues, q, list) {
00958       ao2_lock(q);
00959       mem_iter = ao2_iterator_init(q->members, 0);
00960       while ((mem = ao2_iterator_next(&mem_iter))) {
00961          if (!strcasecmp(mem->state_interface, interface)) {
00962             ao2_ref(mem, -1);
00963             ret = 1;
00964             break;
00965          }
00966          ao2_ref(mem, -1);
00967       }
00968       ao2_iterator_destroy(&mem_iter);
00969       ao2_unlock(q);
00970       if (ret)
00971          break;
00972    }
00973    AST_LIST_UNLOCK(&queues);
00974 
00975    return ret;
00976 }

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

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

Referenced by queue_exec(), and wait_our_turn().

02517 {
02518    struct queue_ent *ch;
02519    int res;
02520    int avl;
02521    int idx = 0;
02522    /* This needs a lock. How many members are available to be served? */
02523    ao2_lock(qe->parent);
02524 
02525    avl = num_available_members(qe->parent);
02526 
02527    ch = qe->parent->head;
02528 
02529    if (option_debug) {
02530       ast_log(LOG_DEBUG, "There %s %d available %s.\n", avl != 1 ? "are" : "is", avl, avl != 1 ? "members" : "member");
02531    }
02532 
02533    while ((idx < avl) && (ch) && (ch != qe)) {
02534       if (!ch->pending)
02535          idx++;
02536       ch = ch->next;       
02537    }
02538 
02539    ao2_unlock(qe->parent);
02540    /* If the queue entry is within avl [the number of available members] calls from the top ... 
02541     * Autofill and position check added to support autofill=no (as only calls
02542     * from the front of the queue are valid when autofill is disabled)
02543     */
02544    if (ch && idx < avl && (qe->parent->autofill || qe->pos == 1)) {
02545       if (option_debug)
02546          ast_log(LOG_DEBUG, "It's our turn (%s).\n", qe->chan->name);
02547       res = 1;
02548    } else {
02549       if (option_debug)
02550          ast_log(LOG_DEBUG, "It's not our turn (%s).\n", qe->chan->name);
02551       res = 0;
02552    }
02553 
02554    return res;
02555 }

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

Definition at line 1500 of file app_queue.c.

References call_queue::announce, queue_ent::announce, ao2_lock(), ao2_unlock(), ast_copy_string(), AST_LIST_LOCK, AST_LIST_UNLOCK, ast_log(), queue_ent::chan, ast_channel::cid, ast_callerid::cid_name, ast_callerid::cid_num, call_queue::context, queue_ent::context, call_queue::count, EVENT_FLAG_CALL, get_member_status(), call_queue::head, insert_entry(), call_queue::joinempty, load_realtime_queue(), LOG_DEBUG, manager_event(), queue_ent::max_penalty, call_queue::maxlen, call_queue::moh, queue_ent::moh, call_queue::name, 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().

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

static void leave_queue ( struct queue_ent qe  )  [static]

Definition at line 1745 of file app_queue.c.

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

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

01746 {
01747    struct call_queue *q;
01748    struct queue_ent *cur, *prev = NULL;
01749    int pos = 0;
01750 
01751    if (!(q = qe->parent))
01752       return;
01753    ao2_lock(q);
01754 
01755    prev = NULL;
01756    for (cur = q->head; cur; cur = cur->next) {
01757       if (cur == qe) {
01758          q->count--;
01759 
01760          /* Take us out of the queue */
01761          manager_event(EVENT_FLAG_CALL, "Leave",
01762             "Channel: %s\r\nQueue: %s\r\nCount: %d\r\nUniqueid: %s\r\n",
01763             qe->chan->name, q->name,  q->count, qe->chan->uniqueid);
01764          if (option_debug)
01765             ast_log(LOG_DEBUG, "Queue '%s' Leave, Channel '%s'\n", q->name, qe->chan->name );
01766          /* Take us out of the queue */
01767          if (prev)
01768             prev->next = cur->next;
01769          else
01770             q->head = cur->next;
01771       } else {
01772          /* Renumber the people after us in the queue based on a new count */
01773          cur->pos = ++pos;
01774          prev = cur;
01775       }
01776    }
01777    ao2_unlock(q);
01778 
01779    if (q->dead && !q->count) {   
01780       /* It's dead and nobody is in it, so kill it */
01781       remove_queue(q);
01782    }
01783 }

static int load_module ( void   )  [static]

Definition at line 5346 of file app_queue.c.

References aqm_exec(), ast_cli_register_multiple(), ast_cond_init(), ast_custom_function_register(), ast_devstate_add(), ast_manager_register, AST_MODULE_LOAD_DECLINE, ast_mutex_init(), ast_pthread_create, ast_register_application(), cli_queue, device_state, device_state_thread(), EVENT_FLAG_AGENT, manager_add_queue_member(), manager_pause_queue_member(), manager_queues_show(), manager_queues_status(), manager_remove_queue_member(), pqm_exec(), ql_exec(), queue_exec(), queueagentcount_function, queuemembercount_function, queuememberlist_function, queuewaitingcount_function, reload_queue_members(), reload_queues(), rqm_exec(), statechange_queue(), and upqm_exec().

05347 {
05348    int res;
05349 
05350    if (!reload_queues())
05351       return AST_MODULE_LOAD_DECLINE;
05352 
05353    if (queue_persistent_members)
05354       reload_queue_members();
05355 
05356    ast_mutex_init(&device_state.lock);
05357    ast_cond_init(&device_state.cond, NULL);
05358    ast_pthread_create(&device_state.thread, NULL, device_state_thread, NULL);
05359 
05360    ast_cli_register_multiple(cli_queue, sizeof(cli_queue) / sizeof(struct ast_cli_entry));
05361    res = ast_register_application(app, queue_exec, synopsis, descrip);
05362    res |= ast_register_application(app_aqm, aqm_exec, app_aqm_synopsis, app_aqm_descrip);
05363    res |= ast_register_application(app_rqm, rqm_exec, app_rqm_synopsis, app_rqm_descrip);
05364    res |= ast_register_application(app_pqm, pqm_exec, app_pqm_synopsis, app_pqm_descrip);
05365    res |= ast_register_application(app_upqm, upqm_exec, app_upqm_synopsis, app_upqm_descrip);
05366    res |= ast_register_application(app_ql, ql_exec, app_ql_synopsis, app_ql_descrip);
05367    res |= ast_manager_register("Queues", 0, manager_queues_show, "Queues");
05368    res |= ast_manager_register("QueueStatus", 0, manager_queues_status, "Queue Status");
05369    res |= ast_manager_register("QueueAdd", EVENT_FLAG_AGENT, manager_add_queue_member, "Add interface to queue.");
05370    res |= ast_manager_register("QueueRemove", EVENT_FLAG_AGENT, manager_remove_queue_member, "Remove interface from queue.");
05371    res |= ast_manager_register("QueuePause", EVENT_FLAG_AGENT, manager_pause_queue_member, "Makes a queue member temporarily unavailable");
05372    res |= ast_custom_function_register(&queueagentcount_function);
05373    res |= ast_custom_function_register(&queuemembercount_function);
05374    res |= ast_custom_function_register(&queuememberlist_function);
05375    res |= ast_custom_function_register(&queuewaitingcount_function);
05376    res |= ast_devstate_add(statechange_queue, NULL);
05377 
05378    return res;
05379 }

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

Note:
Load from realtime before taking the global qlock, to avoid blocking all queue operations while waiting for the DB.
This will be two separate database transactions, so we might see queue parameters as they were before another process changed the queue and member list as it was after the change. Thus we might see an empty member list when a queue is deleted. In practise, this is unlikely to cause a problem.

Definition at line 1450 of file app_queue.c.

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

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

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

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

Definition at line 4989 of file app_queue.c.

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

Referenced by load_module().

04990 {
04991    const char *queuename, *interface, *penalty_s, *paused_s, *membername, *state_interface;
04992    int paused, penalty = 0;
04993 
04994    queuename = astman_get_header(m, "Queue");
04995    interface = astman_get_header(m, "Interface");
04996    penalty_s = astman_get_header(m, "Penalty");
04997    paused_s = astman_get_header(m, "Paused");
04998    membername = astman_get_header(m, "MemberName");
04999    state_interface = astman_get_header(m, "StateInterface");
05000 
05001    if (ast_strlen_zero(queuename)) {
05002       astman_send_error(s, m, "'Queue' not specified.");
05003       return 0;
05004    }
05005 
05006    if (ast_strlen_zero(interface)) {
05007       astman_send_error(s, m, "'Interface' not specified.");
05008       return 0;
05009    }
05010 
05011    if (ast_strlen_zero(penalty_s))
05012       penalty = 0;
05013    else if (sscanf(penalty_s, "%30d", &penalty) != 1 || penalty < 0)
05014       penalty = 0;
05015 
05016    if (ast_strlen_zero(paused_s))
05017       paused = 0;
05018    else
05019       paused = abs(ast_true(paused_s));
05020 
05021    switch (add_to_queue(queuename, interface, membername, penalty, paused, queue_persistent_members, state_interface)) {
05022    case RES_OKAY:
05023       ast_queue_log(queuename, "MANAGER", interface, "ADDMEMBER", "%s", "");
05024       astman_send_ack(s, m, "Added interface to queue");
05025       break;
05026    case RES_EXISTS:
05027       astman_send_error(s, m, "Unable to add interface: Already there");
05028       break;
05029    case RES_NOSUCHQUEUE:
05030       astman_send_error(s, m, "Unable to add interface to queue: No such queue");
05031       break;
05032    case RES_OUTOFMEMORY:
05033       astman_send_error(s, m, "Out of memory");
05034       break;
05035    }
05036 
05037    return 0;
05038 }

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

Definition at line 5074 of file app_queue.c.

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

Referenced by load_module().

05075 {
05076    const char *queuename, *interface, *paused_s;
05077    int paused;
05078 
05079    interface = astman_get_header(m, "Interface");
05080    paused_s = astman_get_header(m, "Paused");
05081    queuename = astman_get_header(m, "Queue");   /* Optional - if not supplied, pause the given Interface in all queues */
05082 
05083    if (ast_strlen_zero(interface) || ast_strlen_zero(paused_s)) {
05084       astman_send_error(s, m, "Need 'Interface' and 'Paused' parameters.");
05085       return 0;
05086    }
05087 
05088    paused = abs(ast_true(paused_s));
05089 
05090    if (set_member_paused(queuename, interface, paused))
05091       astman_send_error(s, m, "Interface not found");
05092    else
05093       astman_send_ack(s, m, paused ? "Interface paused successfully" : "Interface unpaused successfully");
05094    return 0;
05095 }

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

Definition at line 4884 of file app_queue.c.

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

Referenced by load_module().

04885 {
04886    char *a[] = { "queue", "show" };
04887 
04888    __queues_show(s, 1, -1, 2, a);
04889    astman_append(s, "\r\n\r\n"); /* Properly terminate Manager output */
04890 
04891    return RESULT_SUCCESS;
04892 }

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

Definition at line 4895 of file app_queue.c.

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

Referenced by load_module().

04896 {
04897    time_t now;
04898    int pos;
04899    const char *id = astman_get_header(m,"ActionID");
04900    const char *queuefilter = astman_get_header(m,"Queue");
04901    const char *memberfilter = astman_get_header(m,"Member");
04902    char idText[256] = "";
04903    struct call_queue *q;
04904    struct queue_ent *qe;
04905    float sl = 0;
04906    struct member *mem;
04907    struct ao2_iterator mem_iter;
04908 
04909    astman_send_ack(s, m, "Queue status will follow");
04910    time(&now);
04911    AST_LIST_LOCK(&queues);
04912    if (!ast_strlen_zero(id))
04913       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
04914 
04915    AST_LIST_TRAVERSE(&queues, q, list) {
04916       ao2_lock(q);
04917 
04918       /* List queue properties */
04919       if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) {
04920          sl = ((q->callscompleted > 0) ? 100 * ((float)q->callscompletedinsl / (float)q->callscompleted) : 0);
04921          astman_append(s, "Event: QueueParams\r\n"
04922             "Queue: %s\r\n"
04923             "Max: %d\r\n"
04924             "Calls: %d\r\n"
04925             "Holdtime: %d\r\n"
04926             "Completed: %d\r\n"
04927             "Abandoned: %d\r\n"
04928             "ServiceLevel: %d\r\n"
04929             "ServicelevelPerf: %2.1f\r\n"
04930             "Weight: %d\r\n"
04931             "%s"
04932             "\r\n",
04933             q->name, q->maxlen, q->count, q->holdtime, q->callscompleted,
04934             q->callsabandoned, q->servicelevel, sl, q->weight, idText);
04935          /* List Queue Members */
04936          mem_iter = ao2_iterator_init(q->members, 0);
04937          while ((mem = ao2_iterator_next(&mem_iter))) {
04938             if (ast_strlen_zero(memberfilter) || !strcmp(mem->interface, memberfilter)) {
04939                astman_append(s, "Event: QueueMember\r\n"
04940                   "Queue: %s\r\n"
04941                   "Name: %s\r\n"
04942                   "Location: %s\r\n"
04943                   "Membership: %s\r\n"
04944                   "Penalty: %d\r\n"
04945                   "CallsTaken: %d\r\n"
04946                   "LastCall: %d\r\n"
04947                   "Status: %d\r\n"
04948                   "Paused: %d\r\n"
04949                   "%s"
04950                   "\r\n",
04951                   q->name, mem->membername, mem->interface, mem->dynamic ? "dynamic" : "static",
04952                   mem->penalty, mem->calls, (int)mem->lastcall, mem->status, mem->paused, idText);
04953             }
04954             ao2_ref(mem, -1);
04955          }
04956          ao2_iterator_destroy(&mem_iter);
04957          /* List Queue Entries */
04958          pos = 1;
04959          for (qe = q->head; qe; qe = qe->next) {
04960             astman_append(s, "Event: QueueEntry\r\n"
04961                "Queue: %s\r\n"
04962                "Position: %d\r\n"
04963                "Channel: %s\r\n"
04964                "CallerID: %s\r\n"
04965                "CallerIDName: %s\r\n"
04966                "Wait: %ld\r\n"
04967                "%s"
04968                "\r\n",
04969                q->name, pos++, qe->chan->name,
04970                S_OR(qe->chan->cid.cid_num, "unknown"),
04971                S_OR(qe->chan->cid.cid_name, "unknown"),
04972                (long) (now - qe->start), idText);
04973          }
04974       }
04975       ao2_unlock(q);
04976    }
04977 
04978    astman_append(s,
04979       "Event: QueueStatusComplete\r\n"
04980       "%s"
04981       "\r\n",idText);
04982 
04983    AST_LIST_UNLOCK(&queues);
04984 
04985 
04986    return RESULT_SUCCESS;
04987 }

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

Definition at line 5040 of file app_queue.c.

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

Referenced by load_module().

05041 {
05042    const char *queuename, *interface;
05043 
05044    queuename = astman_get_header(m, "Queue");
05045    interface = astman_get_header(m, "Interface");
05046 
05047    if (ast_strlen_zero(queuename) || ast_strlen_zero(interface)) {
05048       astman_send_error(s, m, "Need 'Queue' and 'Interface' parameters.");
05049       return 0;
05050    }
05051 
05052    switch (remove_from_queue(queuename, interface)) {
05053    case RES_OKAY:
05054       ast_queue_log(queuename, "MANAGER", interface, "REMOVEMEMBER", "%s", "");
05055       astman_send_ack(s, m, "Removed interface from queue");
05056       break;
05057    case RES_EXISTS:
05058       astman_send_error(s, m, "Unable to remove interface: Not there");
05059       break;
05060    case RES_NOSUCHQUEUE:
05061       astman_send_error(s, m, "Unable to remove interface from queue: No such queue");
05062       break;
05063    case RES_OUTOFMEMORY:
05064       astman_send_error(s, m, "Out of memory");
05065       break;
05066    case RES_NOT_DYNAMIC:
05067       astman_send_error(s, m, "Member not dynamic");
05068       break;
05069    }
05070 
05071    return 0;
05072 }

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

Definition at line 852 of file app_queue.c.

References member::interface.

Referenced by init_queue().

00853 {
00854    struct member *mem1 = obj1, *mem2 = obj2;
00855    return strcmp(mem1->interface, mem2->interface) ? 0 : CMP_MATCH | CMP_STOP;
00856 }

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

Definition at line 840 of file app_queue.c.

References compress_char(), and member::interface.

Referenced by init_queue().

00841 {
00842    const struct member *mem = obj;
00843    const char *chname = strchr(mem->interface, '/');
00844    int ret = 0, i;
00845    if (!chname)
00846       chname = mem->interface;
00847    for (i = 0; i < 5 && chname[i]; i++)
00848       ret += compress_char(chname[i]) << (i * 6);
00849    return ret;
00850 }

static void monjoin_dep_warning ( void   )  [static]

Definition at line 471 of file app_queue.c.

References ast_log(), and LOG_NOTICE.

Referenced by queue_set_param().

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

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

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

01811 {
01812    struct member *mem;
01813    int avl = 0;
01814    struct ao2_iterator mem_iter;
01815 
01816    mem_iter = ao2_iterator_init(q->members, 0);
01817    while ((mem = ao2_iterator_next(&mem_iter))) {
01818       switch (mem->status) {
01819       case AST_DEVICE_INUSE:
01820          if (!q->ringinuse)
01821             break;
01822          /* else fall through */
01823       case AST_DEVICE_NOT_INUSE:
01824       case AST_DEVICE_UNKNOWN:
01825          if (!mem->paused) {
01826             avl++;
01827          }
01828          break;
01829       }
01830       ao2_ref(mem, -1);
01831 
01832       /* If autofill is not enabled or if the queue's strategy is ringall, then
01833        * we really don't care about the number of available members so much as we
01834        * do that there is at least one available.
01835        *
01836        * In fact, we purposely will return from this function stating that only
01837        * one member is available if either of those conditions hold. That way,
01838        * functions which determine what action to take based on the number of available
01839        * members will operate properly. The reasoning is that even if multiple
01840        * members are available, only the head caller can actually be serviced.
01841        */
01842       if ((!q->autofill || q->strategy == QUEUE_STRATEGY_RINGALL) && avl) {
01843          break;
01844       }
01845    }
01846    ao2_iterator_destroy(&mem_iter);
01847 
01848    return avl;
01849 }

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

Definition at line 1565 of file app_queue.c.

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

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

01566 {
01567    int res;
01568 
01569    if (ast_strlen_zero(filename)) {
01570       return 0;
01571    }
01572 
01573    if (!ast_fileexists(filename, NULL, chan->language)) {
01574       return 0;
01575    }
01576 
01577    ast_stopstream(chan);
01578 
01579    res = ast_streamfile(chan, filename, chan->language);
01580    if (!res)
01581       res = ast_waitstream(chan, AST_DIGIT_ANY);
01582 
01583    ast_stopstream(chan);
01584 
01585    return res;
01586 }

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

Definition at line 3744 of file app_queue.c.

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

Referenced by load_module().

03745 {
03746    struct ast_module_user *lu;
03747    char *parse;
03748    int priority_jump = 0;
03749    AST_DECLARE_APP_ARGS(args,
03750       AST_APP_ARG(queuename);
03751       AST_APP_ARG(interface);
03752       AST_APP_ARG(options);
03753    );
03754 
03755    if (ast_strlen_zero(data)) {
03756       ast_log(LOG_WARNING, "PauseQueueMember requires an argument ([queuename]|interface[|options])\n");
03757       return -1;
03758    }
03759 
03760    parse = ast_strdupa(data);
03761 
03762    AST_STANDARD_APP_ARGS(args, parse);
03763 
03764    lu = ast_module_user_add(chan);
03765 
03766    if (args.options) {
03767       if (strchr(args.options, 'j'))
03768          priority_jump = 1;
03769    }
03770 
03771    if (ast_strlen_zero(args.interface)) {
03772       ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename]|interface[|options])\n");
03773       ast_module_user_remove(lu);
03774       return -1;
03775    }
03776 
03777    if (set_member_paused(args.queuename, args.interface, 1)) {
03778       ast_log(LOG_WARNING, "Attempt to pause interface %s, not found\n", args.interface);
03779       if (priority_jump || ast_opt_priority_jumping) {
03780          if (ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101)) {
03781             pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND");
03782             ast_module_user_remove(lu);
03783             return 0;
03784          }
03785       }
03786       ast_module_user_remove(lu);
03787       pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND");
03788       return 0;
03789    }
03790 
03791    ast_module_user_remove(lu);
03792    pbx_builtin_setvar_helper(chan, "PQMSTATUS", "PAUSED");
03793 
03794    return 0;
03795 }

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

Definition at line 3992 of file app_queue.c.

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

Referenced by load_module().

03993 {
03994    struct ast_module_user *u;
03995    char *parse;
03996 
03997    AST_DECLARE_APP_ARGS(args,
03998       AST_APP_ARG(queuename);
03999       AST_APP_ARG(uniqueid);
04000       AST_APP_ARG(membername);
04001       AST_APP_ARG(event);
04002       AST_APP_ARG(params);
04003    );
04004 
04005    if (ast_strlen_zero(data)) {
04006       ast_log(LOG_WARNING, "QueueLog requires arguments (queuename|uniqueid|membername|event[|additionalinfo]\n");
04007       return -1;
04008    }
04009 
04010    u = ast_module_user_add(chan);
04011 
04012    parse = ast_strdupa(data);
04013 
04014    AST_STANDARD_APP_ARGS(args, parse);
04015 
04016    if (ast_strlen_zero(args.queuename) || ast_strlen_zero(args.uniqueid)
04017        || ast_strlen_zero(args.membername) || ast_strlen_zero(args.event)) {
04018       ast_log(LOG_WARNING, "QueueLog requires arguments (queuename|uniqueid|membername|event[|additionalinfo])\n");
04019       ast_module_user_remove(u);
04020       return -1;
04021    }
04022 
04023    ast_queue_log(args.queuename, args.uniqueid, args.membername, args.event, 
04024       "%s", args.params ? args.params : "");
04025 
04026    ast_module_user_remove(u);
04027 
04028    return 0;
04029 }

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

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

Referenced by load_module().

04044 {
04045    int res=-1;
04046    int ringing=0;
04047    struct ast_module_user *lu;
04048    const char *user_priority;
04049    const char *max_penalty_str;
04050    int prio;
04051    int max_penalty;
04052    enum queue_result reason = QUEUE_UNKNOWN;
04053    /* whether to exit Queue application after the timeout hits */
04054    int tries = 0;
04055    int noption = 0;
04056    char *parse;
04057    AST_DECLARE_APP_ARGS(args,
04058       AST_APP_ARG(queuename);
04059       AST_APP_ARG(options);
04060       AST_APP_ARG(url);
04061       AST_APP_ARG(announceoverride);
04062       AST_APP_ARG(queuetimeoutstr);
04063       AST_APP_ARG(agi);
04064    );
04065    /* Our queue entry */
04066    struct queue_ent qe = { 0 };
04067    
04068    if (ast_strlen_zero(data)) {
04069       ast_log(LOG_WARNING, "Queue requires an argument: queuename[|options[|URL[|announceoverride[|timeout[|agi]]]]]\n");
04070       return -1;
04071    }
04072    
04073    parse = ast_strdupa(data);
04074    AST_STANDARD_APP_ARGS(args, parse);
04075 
04076    lu = ast_module_user_add(chan);
04077 
04078    /* Setup our queue entry */
04079    qe.start = time(NULL);
04080 
04081    /* set the expire time based on the supplied timeout; */
04082    if (!ast_strlen_zero(args.queuetimeoutstr))
04083       qe.expire = qe.start + atoi(args.queuetimeoutstr);
04084    else
04085       qe.expire = 0;
04086 
04087    /* Get the priority from the variable ${QUEUE_PRIO} */
04088    user_priority = pbx_builtin_getvar_helper(chan, "QUEUE_PRIO");
04089    if (user_priority) {
04090       if (sscanf(user_priority, "%30d", &prio) == 1) {
04091          if (option_debug)
04092             ast_log(LOG_DEBUG, "%s: Got priority %d from ${QUEUE_PRIO}.\n",
04093                chan->name, prio);
04094       } else {
04095          ast_log(LOG_WARNING, "${QUEUE_PRIO}: Invalid value (%s), channel %s.\n",
04096             user_priority, chan->name);
04097          prio = 0;
04098       }
04099    } else {
04100       if (option_debug > 2)
04101          ast_log(LOG_DEBUG, "NO QUEUE_PRIO variable found. Using default.\n");
04102       prio = 0;
04103    }
04104 
04105    /* Get the maximum penalty from the variable ${QUEUE_MAX_PENALTY} */
04106    if ((max_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MAX_PENALTY"))) {
04107       if (sscanf(max_penalty_str, "%30d", &max_penalty) == 1) {
04108          if (option_debug)
04109             ast_log(LOG_DEBUG, "%s: Got max penalty %d from ${QUEUE_MAX_PENALTY}.\n",
04110                chan->name, max_penalty);
04111       } else {
04112          ast_log(LOG_WARNING, "${QUEUE_MAX_PENALTY}: Invalid value (%s), channel %s.\n",
04113             max_penalty_str, chan->name);
04114          max_penalty = 0;
04115       }
04116    } else {
04117       max_penalty = 0;
04118    }
04119 
04120    if (args.options && (strchr(args.options, 'r')))
04121       ringing = 1;
04122 
04123    if (option_debug)
04124       ast_log(LOG_DEBUG, "queue: %s, options: %s, url: %s, announce: %s, expires: %ld, priority: %d\n",
04125          args.queuename, args.options, args.url, args.announceoverride, (long)qe.expire, prio);
04126 
04127    qe.chan = chan;
04128    qe.prio = prio;
04129    qe.max_penalty = max_penalty;
04130    qe.last_pos_said = 0;
04131    qe.last_pos = 0;
04132    qe.last_periodic_announce_time = time(NULL);
04133    qe.last_periodic_announce_sound = 0;
04134    qe.valid_digits = 0;
04135    if (!join_queue(args.queuename, &qe, &reason)) {
04136       int makeannouncement = 0;
04137 
04138       ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ENTERQUEUE", "%s|%s", S_OR(args.url, ""),
04139          S_OR(chan->cid.cid_num, ""));
04140 check_turns:
04141       if (ringing) {
04142          ast_indicate(chan, AST_CONTROL_RINGING);
04143       } else {
04144          ast_moh_start(chan, qe.moh, NULL);
04145       }
04146 
04147       /* This is the wait loop for callers 2 through maxlen */
04148       res = wait_our_turn(&qe, ringing, &reason);
04149       if (res)
04150          goto stop;
04151 
04152       for (;;) {
04153          /* This is the wait loop for the head caller*/
04154          /* To exit, they may get their call answered; */
04155          /* they may dial a digit from the queue context; */
04156          /* or, they may timeout. */
04157 
04158          enum queue_member_status stat;
04159 
04160          /* Leave if we have exceeded our queuetimeout */
04161          if (qe.expire && (time(NULL) >= qe.expire)) {
04162             record_abandoned(&qe);
04163             reason = QUEUE_TIMEOUT;
04164             res = 0;
04165             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
04166             break;
04167          }
04168 
04169          if (makeannouncement) {
04170             /* Make a position announcement, if enabled */
04171             if (qe.parent->announcefrequency && !ringing)
04172                if ((res = say_position(&qe)))
04173                   goto stop;
04174 
04175          }
04176          makeannouncement = 1;
04177 
04178          /* Leave if we have exceeded our queuetimeout */
04179          if (qe.expire && (time(NULL) >= qe.expire)) {
04180             record_abandoned(&qe);
04181             reason = QUEUE_TIMEOUT;
04182             res = 0;
04183             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
04184             break;
04185          }
04186          /* Make a periodic announcement, if enabled */
04187          if (qe.parent->periodicannouncefrequency && !ringing)
04188             if ((res = say_periodic_announcement(&qe)))
04189                goto stop;
04190 
04191          /* Leave if we have exceeded our queuetimeout */
04192          if (qe.expire && (time(NULL) >= qe.expire)) {
04193             record_abandoned(&qe);
04194             reason = QUEUE_TIMEOUT;
04195             res = 0;
04196             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
04197             break;
04198          }
04199          /* Try calling all queue members for 'timeout' seconds */
04200          res = try_calling(&qe, args.options, args.announceoverride, args.url, &tries, &noption, args.agi);
04201          if (res)
04202             goto stop;
04203 
04204          stat = get_member_status(qe.parent, qe.max_penalty);
04205 
04206          /* exit after 'timeout' cycle if 'n' option enabled */
04207          if (noption && tries >= qe.parent->membercount) {
04208             if (option_verbose > 2)
04209                ast_verbose(VERBOSE_PREFIX_3 "Exiting on time-out cycle\n");
04210             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
04211             record_abandoned(&qe);
04212             reason = QUEUE_TIMEOUT;
04213             res = 0;
04214             break;
04215          }
04216 
04217          /* leave the queue if no agents, if enabled */
04218          if (qe.parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) {
04219             record_abandoned(&qe);
04220             reason = QUEUE_LEAVEEMPTY;
04221             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
04222             res = 0;
04223             break;
04224          }
04225 
04226          /* leave the queue if no reachable agents, if enabled */
04227          if ((qe.parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) {
04228             record_abandoned(&qe);
04229             reason = QUEUE_LEAVEUNAVAIL;
04230             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
04231             res = 0;
04232             break;
04233          }
04234 
04235          /* Leave if we have exceeded our queuetimeout */
04236          if (qe.expire && (time(NULL) >= qe.expire)) {
04237             record_abandoned(&qe);
04238             reason = QUEUE_TIMEOUT;
04239             res = 0;
04240             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
04241             break;
04242          }
04243 
04244          /* If using dynamic realtime members, we should regenerate the member list for this queue */
04245          update_realtime_members(qe.parent);
04246 
04247          /* OK, we didn't get anybody; wait for 'retry' seconds; may get a digit to exit with */
04248          res = wait_a_bit(&qe);
04249          if (res)
04250             goto stop;
04251 
04252          /* Since this is a priority queue and
04253           * it is not sure that we are still at the head
04254           * of the queue, go and check for our turn again.
04255           */
04256          if (!is_our_turn(&qe)) {
04257             if (option_debug)
04258                ast_log(LOG_DEBUG, "Darn priorities, going back in queue (%s)!\n",
04259                   qe.chan->name);
04260             goto check_turns;
04261          }
04262       }
04263 
04264 stop:
04265       if (res) {
04266          if (res < 0) {
04267             if (!qe.handled) {
04268                record_abandoned(&qe);
04269                ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ABANDON",
04270                   "%d|%d|%ld", qe.pos, qe.opos,
04271                   (long) time(NULL) - qe.start);
04272             }
04273             res = -1;
04274          } else if (qe.valid_digits) {
04275             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHKEY",
04276                "%s|%d", qe.digits, qe.pos);
04277          }
04278       }
04279 
04280       /* Don't allow return code > 0 */
04281       if (res >= 0) {
04282          res = 0; 
04283          if (ringing) {
04284             ast_indicate(chan, -1);
04285          } else {
04286             ast_moh_stop(chan);
04287          }        
04288          ast_stopstream(chan);
04289       }
04290       leave_queue(&qe);
04291       if (reason != QUEUE_UNKNOWN)
04292          set_queue_result(chan, reason);
04293    } else {
04294       ast_log(LOG_WARNING, "Unable to join queue '%s'\n", args.queuename);
04295       set_queue_result(chan, reason);
04296       res = 0;
04297    }
04298    if (qe.parent) {
04299       /* every queue_ent is given a reference to it's parent call_queue when it joins the queue.
04300        * This ref must be taken away right before the queue_ent is destroyed.  In this case
04301        * the queue_ent is about to be returned on the stack */
04302       ao2_ref(qe.parent, -1);
04303    }
04304    ast_module_user_remove(lu);
04305 
04306    return res;
04307 }

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

Definition at line 4309 of file app_queue.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next(), ao2_lock(), ao2_ref(), ao2_unlock(), AST_DEVICE_INVALID, AST_DEVICE_UNAVAILABLE, ast_log(), ast_module_user_add, ast_module_user_remove, ast_strlen_zero(), load_realtime_queue(), LOG_ERROR, LOG_WARNING, call_queue::members, and member::status.

04310 {
04311    int count = 0;
04312    struct call_queue *q;
04313    struct ast_module_user *lu;
04314    struct member *m;
04315    struct ao2_iterator mem_iter;
04316 
04317    buf[0] = '\0';
04318    
04319    if (ast_strlen_zero(data)) {
04320       ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
04321       return -1;
04322    }
04323 
04324    lu = ast_module_user_add(chan);
04325 
04326    if ((q = load_realtime_queue(data))) {
04327       ao2_lock(q);
04328       mem_iter = ao2_iterator_init(q->members, 0);
04329       while ((m = ao2_iterator_next(&mem_iter))) {
04330          /* Count the agents who are logged in and presently answering calls */
04331          if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
04332             count++;
04333          }
04334          ao2_ref(m, -1);
04335       }
04336       ao2_iterator_destroy(&mem_iter);
04337       ao2_unlock(q);
04338    } else
04339       ast_log(LOG_WARNING, "queue %s was not found\n", data);
04340 
04341    snprintf(buf, len, "%d", count);
04342    ast_module_user_remove(lu);
04343 
04344    return 0;
04345 }

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

Definition at line 4390 of file app_queue.c.

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

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

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

Definition at line 4347 of file app_queue.c.

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

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

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

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

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

Definition at line 4850 of file app_queue.c.

References __queues_show().

Referenced by __queues_show().

04851 {
04852    return __queues_show(NULL, 0, fd, argc, argv);
04853 }

static void queue_transfer_destroy ( void *  data  )  [static]

Definition at line 2722 of file app_queue.c.

References ast_free.

02723 {
02724    struct queue_transfer_ds *qtds = data;
02725    ast_free(qtds);
02726 }

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

02746 {
02747    struct queue_transfer_ds *qtds = data;
02748    struct queue_ent *qe = qtds->qe;
02749    struct member *member = qtds->member;
02750    time_t callstart = qtds->starttime;
02751    int callcompletedinsl = qtds->callcompletedinsl;
02752    struct ast_datastore *datastore;
02753 
02754    ast_queue_log(qe->parent->name, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld",
02755             new_chan->exten, new_chan->context, (long) (callstart - qe->start),
02756             (long) (time(NULL) - callstart));
02757 
02758    update_queue(qe->parent, member, callcompletedinsl);
02759    
02760    /* No need to lock the channels because they are already locked in ast_do_masquerade */
02761    if ((datastore = ast_channel_datastore_find(old_chan, &queue_transfer_info, NULL))) {
02762       ast_channel_datastore_remove(old_chan, datastore);
02763    } else {
02764       ast_log(LOG_WARNING, "Can't find the queue_transfer datastore.\n");
02765    }
02766 }

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

Definition at line 1730 of file app_queue.c.

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

Referenced by try_calling().

01731 {
01732    int oldvalue;
01733 
01734    /* Calculate holdtime using an exponential average */
01735    /* Thanks to SRT for this contribution */
01736    /* 2^2 (4) is the filter coefficient; a higher exponent would give old entries more weight */
01737 
01738    ao2_lock(qe->parent);
01739    oldvalue = qe->parent->holdtime;
01740    qe->parent->holdtime = (((oldvalue << 2) - oldvalue) + newholdtime) >> 2;
01741    ao2_unlock(qe->parent);
01742 }

static void record_abandoned ( struct queue_ent qe  )  [static]

Definition at line 2206 of file app_queue.c.

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

Referenced by queue_exec(), and try_calling().

02207 {
02208    ao2_lock(qe->parent);
02209    manager_event(EVENT_FLAG_AGENT, "QueueCallerAbandon",
02210       "Queue: %s\r\n"
02211       "Uniqueid: %s\r\n"
02212       "Position: %d\r\n"
02213       "OriginalPosition: %d\r\n"
02214       "HoldTime: %d\r\n",
02215       qe->parent->name, qe->chan->uniqueid, qe->pos, qe->opos, (int)(time(NULL) - qe->start));
02216 
02217    qe->parent->callsabandoned++;
02218    ao2_unlock(qe->parent);
02219 }

static int reload ( void   )  [static]

Definition at line 5381 of file app_queue.c.

References reload_queues().

05382 {
05383    reload_queues();
05384    return 0;
05385 }

static void reload_queue_members ( void   )  [static]

Definition at line 3647 of file app_queue.c.

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

Referenced by load_module().

03648 {
03649    char *cur_ptr;
03650    char *queue_name;
03651    char *member;
03652    char *interface;
03653    char *membername = NULL;
03654    char *state_interface;
03655    char *penalty_tok;
03656    int penalty = 0;
03657    char *paused_tok;
03658    int paused = 0;
03659    struct ast_db_entry *db_tree;
03660    struct ast_db_entry *entry;
03661    struct call_queue *cur_queue;
03662    char queue_data[PM_MAX_LEN];
03663 
03664    AST_LIST_LOCK(&queues);
03665 
03666    /* Each key in 'pm_family' is the name of a queue */
03667    db_tree = ast_db_gettree(pm_family, NULL);
03668    for (entry = db_tree; entry; entry = entry->next) {
03669 
03670       queue_name = entry->key + strlen(pm_family) + 2;
03671 
03672       AST_LIST_TRAVERSE(&queues, cur_queue, list) {
03673          ao2_lock(cur_queue);
03674          if (!strcmp(queue_name, cur_queue->name))
03675             break;
03676          ao2_unlock(cur_queue);
03677       }
03678       
03679       if (!cur_queue)
03680          cur_queue = load_realtime_queue(queue_name);
03681 
03682       if (!cur_queue) {
03683          /* If the queue no longer exists, remove it from the
03684           * database */
03685          ast_log(LOG_WARNING, "Error loading persistent queue: '%s': it does not exist\n", queue_name);
03686          ast_db_del(pm_family, queue_name);
03687          continue;
03688       } else
03689          ao2_unlock(cur_queue);
03690 
03691       if (ast_db_get(pm_family, queue_name, queue_data, PM_MAX_LEN))
03692          continue;
03693 
03694       cur_ptr = queue_data;
03695       while ((member = strsep(&cur_ptr, "|"))) {
03696          if (ast_strlen_zero(member))
03697             continue;
03698 
03699          interface = strsep(&member, ";");
03700          penalty_tok = strsep(&member, ";");
03701          paused_tok = strsep(&member, ";");
03702          membername = strsep(&member, ";");
03703          state_interface = strsep(&member,";");
03704 
03705          if (!penalty_tok) {
03706             ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (penalty)\n", queue_name);
03707             break;
03708          }
03709          penalty = strtol(penalty_tok, NULL, 10);
03710          if (errno == ERANGE) {
03711             ast_log(LOG_WARNING, "Error converting penalty: %s: Out of range.\n", penalty_tok);
03712             break;
03713          }
03714          
03715          if (!paused_tok) {
03716             ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (paused)\n", queue_name);
03717             break;
03718          }
03719          paused = strtol(paused_tok, NULL, 10);
03720          if ((errno == ERANGE) || paused < 0 || paused > 1) {
03721             ast_log(LOG_WARNING, "Error converting paused: %s: Expected 0 or 1.\n", paused_tok);
03722             break;
03723          }
03724          if (ast_strlen_zero(membername))
03725             membername = interface;
03726 
03727          if (option_debug)
03728             ast_log(LOG_DEBUG, "Reload Members: Queue: %s  Member: %s  Name: %s  Penalty: %d  Paused: %d\n", queue_name, interface, membername, penalty, paused);
03729          
03730          if (add_to_queue(queue_name, interface, membername, penalty, paused, 0, state_interface) == RES_OUTOFMEMORY) {
03731             ast_log(LOG_ERROR, "Out of Memory when reloading persistent queue member\n");
03732             break;
03733          }
03734       }
03735    }
03736 
03737    AST_LIST_UNLOCK(&queues);
03738    if (db_tree) {
03739       ast_log(LOG_NOTICE, "Queue members successfully reloaded from database.\n");
03740       ast_db_freetree(db_tree);
03741    }
03742 }

static int reload_queues ( void   )  [static]

Definition at line 4484 of file app_queue.c.

References add_to_interfaces(), alloc_queue(), ao2_find(), ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next(), ao2_lock(), ao2_ref(), ao2_unlink(), ao2_unlock(), AST_APP_ARG, ast_category_browse(), ast_config_destroy(), ast_config_load(), ast_copy_string(), AST_DECLARE_APP_ARGS, ast_free, AST_LIST_INSERT_HEAD, AST_LIST_LOCK, AST_LIST_REMOVE_CURRENT, AST_LIST_TRAVERSE, AST_LIST_TRAVERSE_SAFE_BEGIN, AST_LIST_TRAVERSE_SAFE_END, AST_LIST_UNLOCK, ast_log(), AST_NONSTANDARD_APP_ARGS, ast_skip_blanks(), ast_strdup, ast_strlen_zero(), ast_true(), ast_variable_browse(), ast_variable_retrieve(), clear_queue(), create_queue_member(), call_queue::dead, member::delme, member::dynamic, call_queue::found, init_queue(), member::interface, member_interface::interface, ast_variable::lineno, LOG_NOTICE, LOG_WARNING, call_queue::membercount, call_queue::members, ast_variable::name, call_queue::name, ast_variable::next, parse(), member::paused, queue_set_param(), QUEUE_STRATEGY_RINGALL, QUEUE_STRATEGY_ROUNDROBIN, call_queue::realtime, remove_from_interfaces(), rr_dep_warning(), member::state_interface, member::status, strat2int(), call_queue::strategy, ast_variable::value, and var.

Referenced by load_module(), and reload().

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

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

Definition at line 978 of file app_queue.c.

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

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

00979 {
00980    struct member_interface *curint;
00981 
00982    if (interface_exists_global(interface))
00983       return 0;
00984 
00985    AST_LIST_LOCK(&interfaces);
00986    AST_LIST_TRAVERSE_SAFE_BEGIN(&interfaces, curint, list) {
00987       if (!strcasecmp(curint->interface, interface)) {
00988          if (option_debug)
00989             ast_log(LOG_DEBUG, "Removing %s from the list of interfaces that make up all of our queue members.\n", interface);
00990          AST_LIST_REMOVE_CURRENT(&interfaces, list);
00991          free(curint);
00992          break;
00993       }
00994    }
00995    AST_LIST_TRAVERSE_SAFE_END;
00996    AST_LIST_UNLOCK(&interfaces);
00997 
00998    return 0;
00999 }

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

Definition at line 3495 of file app_queue.c.

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

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

03496 {
03497    struct call_queue *q;
03498    struct member *mem, tmpmem;
03499    int res = RES_NOSUCHQUEUE;
03500 
03501    ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
03502 
03503    AST_LIST_LOCK(&queues);
03504    AST_LIST_TRAVERSE(&queues, q, list) {
03505       ao2_lock(q);
03506       if (strcmp(q->name, queuename)) {
03507          ao2_unlock(q);
03508          continue;
03509       }
03510 
03511       if ((mem = ao2_find(q->members, &tmpmem, OBJ_POINTER))) {
03512          /* XXX future changes should beware of this assumption!! */
03513          if (!mem->dynamic) {
03514             res = RES_NOT_DYNAMIC;
03515             ao2_ref(mem, -1);
03516             ao2_unlock(q);
03517             break;
03518          }
03519          q->membercount--;
03520          manager_event(EVENT_FLAG_AGENT, "QueueMemberRemoved",
03521             "Queue: %s\r\n"
03522             "Location: %s\r\n"
03523             "MemberName: %s\r\n",
03524             q->name, mem->interface, mem->membername);
03525          ao2_unlink(q->members, mem);
03526          remove_from_interfaces(mem->state_interface);
03527          ao2_ref(mem, -1);
03528 
03529          if (queue_persistent_members)
03530             dump_queue_members(q);
03531          
03532          res = RES_OKAY;
03533       } else {
03534          res = RES_EXISTS;
03535       }
03536       ao2_unlock(q);
03537       break;
03538    }
03539 
03540    AST_LIST_UNLOCK(&queues);
03541 
03542    return res;
03543 }

static void remove_queue ( struct call_queue q  )  [static]

removes a call_queue from the list of call_queues

Definition at line 519 of file app_queue.c.

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

Referenced by find_queue_by_name_rt(), and leave_queue().

00520 {
00521    AST_LIST_LOCK(&queues);
00522    if (AST_LIST_REMOVE(&queues, q, list)) {
00523       ao2_ref(q, -1);
00524    }
00525    AST_LIST_UNLOCK(&queues);
00526 }

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

References ast_cdr::accountcode, ast_channel::adsicpe, ast_cdr::amaflags, ao2_lock(), ao2_unlock(), ast_channel::appl, ast_call(), ast_cdr_busy(), ast_cdr_isset_unanswered(), ast_cdr_setdestchan(), ast_channel_datastore_inherit(), ast_channel_inherit_variables(), ast_channel_lock, ast_channel_unlock, ast_copy_string(), AST_DEVICE_NOT_INUSE, AST_DEVICE_UNKNOWN, ast_log(), ast_request(), ast_strdup, ast_strlen_zero(), ast_verbose(), ast_channel::cdr, callattempt::chan, queue_ent::chan, ast_cdr::channel, ast_channel::cid, ast_callerid::cid_ani, ast_callerid::cid_name, ast_callerid::cid_num, ast_cdr::clid, compare_weight(), ast_channel::context, ast_channel::data, ast_cdr::dcontext, ast_channel::dialcontext, do_hang(), ast_cdr::dst, EVENT_FLAG_AGENT, call_queue::eventwhencalled, ast_channel::exten, free, callattempt::interface, ast_cdr::lastapp, callattempt::lastcall, ast_cdr::lastdata, LOG_DEBUG, 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, 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().

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

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

02109 {
02110    int ret = 0;
02111 
02112    while (ret == 0) {
02113       struct callattempt *best = find_best(outgoing);
02114       if (!best) {
02115          if (option_debug)
02116             ast_log(LOG_DEBUG, "Nobody left to try ringing in queue\n");
02117          break;
02118       }
02119       if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
02120          struct callattempt *cur;
02121          /* Ring everyone who shares this best metric (for ringall) */
02122          for (cur = outgoing; cur; cur = cur->q_next) {
02123             if (cur->stillgoing && !cur->chan && cur->metric <= best->metric) {
02124                if (option_debug)
02125                   ast_log(LOG_DEBUG, "(Parallel) Trying '%s' with metric %d\n", cur->interface, cur->metric);
02126                ret |= ring_entry(qe, cur, busies);
02127             }
02128          }
02129       } else {
02130          /* Ring just the best channel */
02131          if (option_debug)
02132             ast_log(LOG_DEBUG, "Trying '%s' with metric %d\n", best->interface, best->metric);
02133          ret = ring_entry(qe, best, busies);
02134       }
02135    }
02136 
02137    return ret;
02138 }

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

Referenced by wait_for_answer().

02223 {
02224    if (option_verbose > 2)
02225       ast_verbose( VERBOSE_PREFIX_3 "Nobody picked up in %d ms\n", rnatime);
02226    ast_queue_log(qe->parent->name, qe->chan->uniqueid, membername, "RINGNOANSWER", "%d", rnatime);
02227    if (qe->parent->autopause && pause) {
02228       if (!set_member_paused(qe->parent->name, interface, 1)) {
02229          if (option_verbose > 2)
02230             ast_verbose( VERBOSE_PREFIX_3 "Auto-Pausing Queue Member %s in queue %s since they failed to answer.\n", interface, qe->parent->name);
02231       } else {
02232          if (option_verbose > 2)
02233             ast_verbose( VERBOSE_PREFIX_3 "Failed to pause Queue Member %s in queue %s!\n", interface, qe->parent->name);
02234       }
02235    }
02236    return;
02237 }

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

Definition at line 3850 of file app_queue.c.

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

Referenced by load_module().

03851 {
03852    int res=-1;
03853    struct ast_module_user *lu;
03854    char *parse, *temppos = NULL;
03855    int priority_jump = 0;
03856    AST_DECLARE_APP_ARGS(args,
03857       AST_APP_ARG(queuename);
03858       AST_APP_ARG(interface);
03859       AST_APP_ARG(options);
03860    );
03861 
03862 
03863    if (ast_strlen_zero(data)) {
03864       ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename[|interface[|options]])\n");
03865       return -1;
03866    }
03867 
03868    parse = ast_strdupa(data);
03869 
03870    AST_STANDARD_APP_ARGS(args, parse);
03871 
03872    lu = ast_module_user_add(chan);
03873 
03874    if (ast_strlen_zero(args.interface)) {
03875       args.interface = ast_strdupa(chan->name);
03876       temppos = strrchr(args.interface, '-');
03877       if (temppos)
03878          *temppos = '\0';
03879    }
03880 
03881    if (args.options) {
03882       if (strchr(args.options, 'j'))
03883          priority_jump = 1;
03884    }
03885 
03886    switch (remove_from_queue(args.queuename, args.interface)) {
03887    case RES_OKAY:
03888       ast_queue_log(args.queuename, chan->uniqueid, args.interface, "REMOVEMEMBER", "%s", "");
03889       ast_log(LOG_NOTICE, "Removed interface '%s' from queue '%s'\n", args.interface, args.queuename);
03890       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "REMOVED");
03891       res = 0;
03892       break;
03893    case RES_EXISTS:
03894       ast_log(LOG_DEBUG, "Unable to remove interface '%s' from queue '%s': Not there\n", args.interface, args.queuename);
03895       if (priority_jump || ast_opt_priority_jumping)
03896          ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
03897       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTINQUEUE");
03898       res = 0;
03899       break;
03900    case RES_NOSUCHQUEUE:
03901       ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", args.queuename);
03902       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOSUCHQUEUE");
03903       res = 0;
03904       break;
03905    case RES_NOT_DYNAMIC:
03906       ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': '%s' is not a dynamic member\n", args.queuename, args.interface);
03907       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTDYNAMIC");
03908       res = 0;
03909       break;
03910    }
03911 
03912    ast_module_user_remove(lu);
03913 
03914    return res;
03915 }

static void rr_dep_warning ( void   )  [static]

Definition at line 461 of file app_queue.c.

References ast_log(), and LOG_NOTICE.

Referenced by find_queue_by_name_rt(), and reload_queues().

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

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

References add_to_interfaces(), ao2_find(), ao2_ref(), ast_copy_string(), create_queue_member(), member::dead, member::interface, call_queue::membercount, call_queue::members, member::paused, member::penalty, member::realtime, remove_from_interfaces(), and member::state_interface.

Referenced by find_queue_by_name_rt(), and update_realtime_members().

01167 {
01168    struct member *m, tmpmem;
01169    int penalty = 0;
01170    int paused  = 0;
01171 
01172    if (penalty_str) {
01173       penalty = atoi(penalty_str);
01174       if (penalty < 0)
01175          penalty = 0;
01176    }
01177 
01178    if (paused_str) {
01179       paused = atoi(paused_str);
01180       if (paused < 0)
01181          paused = 0;
01182    }
01183 
01184    /* Find the member, or the place to put a new one. */
01185    ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
01186    m = ao2_find(q->members, &tmpmem, OBJ_POINTER);
01187 
01188    /* Create a new one if not found, else update penalty */
01189    if (!m) {
01190       if ((m = create_queue_member(interface, membername, penalty, paused, state_interface))) {
01191          m->dead = 0;
01192          m->realtime = 1;
01193          add_to_interfaces(m->state_interface);
01194          ao2_link(q->members, m);
01195          ao2_ref(m, -1);
01196          m = NULL;
01197          q->membercount++;
01198       }
01199    } else {
01200       m->dead = 0;   /* Do not delete this one. */
01201       if (paused_str)
01202          m->paused = paused;
01203       if (strcasecmp(state_interface, m->state_interface)) {
01204          remove_from_interfaces(m->state_interface);
01205          ast_copy_string(m->state_interface, state_interface, sizeof(m->state_interface));
01206          add_to_interfaces(m->state_interface);
01207       }
01208       m->penalty = penalty;
01209       ao2_ref(m, -1);
01210    }
01211 }

static int say_periodic_announcement ( struct queue_ent qe  )  [static]

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

02165 {
02166    int res = 0;
02167    time_t now;
02168 
02169    /* Get the current time */
02170    time(&now);
02171 
02172    /* Check to see if it is time to announce */
02173    if ((now - qe->last_periodic_announce_time) < qe->parent->periodicannouncefrequency)
02174       return 0;
02175 
02176    /* Stop the music on hold so we can play our own file */
02177    ast_moh_stop(qe->chan);
02178 
02179    if (option_verbose > 2)
02180       ast_verbose(VERBOSE_PREFIX_3 "Playing periodic announcement\n");
02181 
02182    /* Check to make sure we have a sound file. If not, reset to the first sound file */
02183    if (qe->last_periodic_announce_sound >= MAX_PERIODIC_ANNOUNCEMENTS || !strlen(qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound])) {
02184       qe->last_periodic_announce_sound = 0;
02185    }
02186    
02187    /* play the announcement */
02188    res = play_file(qe->chan, qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound]);
02189 
02190    if (res > 0 && !valid_exit(qe, res))
02191       res = 0;
02192 
02193    /* Resume Music on Hold if the caller is going to stay in the queue */
02194    if (!res)
02195       ast_moh_start(qe->chan, qe->moh, NULL);
02196 
02197    /* update last_periodic_announce_time */
02198    qe->last_periodic_announce_time = now;
02199 
02200    /* Update the current periodic announcement to the next announcement */
02201    qe->last_periodic_announce_sound++;
02202    
02203    return res;
02204 }

static int say_position ( struct queue_ent qe  )  [static]

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

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

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

Definition at line 3601 of file app_queue.c.

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

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

03602 {
03603    int found = 0;
03604    struct call_queue *q;
03605    struct member *mem;
03606 
03607    /* Special event for when all queues are paused - individual events still generated */
03608    /* XXX In all other cases, we use the membername, but since this affects all queues, we cannot */
03609    if (ast_strlen_zero(queuename))
03610       ast_queue_log("NONE", "NONE", interface, (paused ? "PAUSEALL" : "UNPAUSEALL"), "%s", "");
03611 
03612    AST_LIST_LOCK(&queues);
03613    AST_LIST_TRAVERSE(&queues, q, list) {
03614       ao2_lock(q);
03615       if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
03616          if ((mem = interface_exists(q, interface))) {
03617             found++;
03618             if (mem->paused == paused)
03619                ast_log(LOG_DEBUG, "%spausing already-%spaused queue member %s:%s\n", (paused ? "" : "un"), (paused ? "" : "un"), q->name, interface);
03620             mem->paused = paused;
03621 
03622             if (queue_persistent_members)
03623                dump_queue_members(q);
03624 
03625             if (mem->realtime)
03626                update_realtime_member_field(mem, q->name, "paused", paused ? "1" : "0");
03627 
03628             ast_queue_log(q->name, "NONE", mem->membername, (paused ? "PAUSE" : "UNPAUSE"), "%s", "");
03629 
03630             manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
03631                "Queue: %s\r\n"
03632                "Location: %s\r\n"
03633                "MemberName: %s\r\n"
03634                "Paused: %d\r\n",
03635                   q->name, mem->interface, mem->membername, paused);
03636             ao2_ref(mem, -1);
03637          }
03638       }
03639       ao2_unlock(q);
03640    }
03641    AST_LIST_UNLOCK(&queues);
03642 
03643    return found ? RESULT_SUCCESS : RESULT_FAILURE;
03644 }

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

sets the QUEUESTATUS channel variable

Definition at line 480 of file app_queue.c.

References pbx_builtin_setvar_helper(), queue_results, and text.

Referenced by queue_exec().

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

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

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

Definition at line 2783 of file app_queue.c.

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

Referenced by try_calling().

02784 {
02785    struct ast_datastore *ds;
02786    struct queue_transfer_ds *qtds = ast_calloc(1, sizeof(*qtds));
02787 
02788    if (!qtds) {
02789       ast_log(LOG_WARNING, "Memory allocation error!\n");
02790       return NULL;
02791    }
02792 
02793    ast_channel_lock(qe->chan);
02794    if (!(ds = ast_channel_datastore_alloc(&queue_transfer_info, NULL))) {
02795       ast_channel_unlock(qe->chan);
02796       ast_log(LOG_WARNING, "Unable to create transfer datastore. queue_log will not show attended transfer\n");
02797       return NULL;
02798    }
02799 
02800    qtds->qe = qe;
02801    /* This member is refcounted in try_calling, so no need to add it here, too */
02802    qtds->member = member;
02803    qtds->starttime = starttime;
02804    qtds->callcompletedinsl = callcompletedinsl;
02805    ds->data = qtds;
02806    ast_channel_datastore_add(qe->chan, ds);
02807    ast_channel_unlock(qe->chan);
02808    return ds;
02809 }

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

Producer of the statechange queue.

Definition at line 777 of file app_queue.c.

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

Referenced by load_module(), and unload_module().

00778 {
00779    struct statechange *sc;
00780 
00781    if (!(sc = ast_calloc(1, sizeof(*sc) + strlen(dev) + 1)))
00782       return 0;
00783 
00784    sc->state = state;
00785    strcpy(sc->dev, dev);
00786 
00787    ast_mutex_lock(&device_state.lock);
00788    AST_LIST_INSERT_TAIL(&device_state.state_change_q, sc, entry);
00789    ast_cond_signal(&device_state.cond);
00790    ast_mutex_unlock(&device_state.lock);
00791 
00792    return 0;
00793 }

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

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

02141 {
02142    struct callattempt *best = find_best(outgoing);
02143 
02144    if (best) {
02145       /* Ring just the best channel */
02146       if (option_debug)
02147          ast_log(LOG_DEBUG, "Next is '%s' with metric %d\n", best->interface, best->metric);
02148       qe->parent->rrpos = best->metric % 1000;
02149    } else {
02150       /* Just increment rrpos */
02151       if (qe->parent->wrapped) {
02152          /* No more channels, start over */
02153          qe->parent->rrpos = 0;
02154       } else {
02155          /* Prioritize next entry */
02156          qe->parent->rrpos++;
02157       }
02158    }
02159    qe->parent->wrapped = 0;
02160 
02161    return 0;
02162 }

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

Definition at line 504 of file app_queue.c.

References name, and strategies.

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

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

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

References ast_channel::_softhangup, ast_channel::_state, queue_ent::announce, ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next(), ao2_lock(), ao2_ref(), ao2_unlock(), ast_autoservice_start(), ast_autoservice_stop(), ast_bridge_call(), ast_calloc, ast_cdr_failed(), AST_CDR_FLAG_LOCKED, AST_CDR_FLAG_POST_DISABLED, ast_cdr_isset_unanswered(), ast_cdr_setdestchan(), ast_channel_datastore_add(), ast_channel_datastore_alloc(), ast_channel_datastore_find(), ast_channel_datastore_free(), ast_channel_datastore_remove(), ast_channel_lock, ast_channel_make_compatible(), ast_channel_sendurl(), ast_channel_setoption(), ast_channel_supports_html(), ast_channel_unlock, ast_clear_flag, ast_copy_string(), AST_DEVICE_NOT_INUSE, AST_DIGIT_ANY, AST_FEATURE_AUTOMON, AST_FEATURE_DISCONNECT, AST_FEATURE_REDIRECT, ast_hangup(), AST_LIST_HEAD, AST_LIST_HEAD_INIT, AST_LIST_INSERT_TAIL, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), AST_MAX_CONTEXT, AST_MAX_EXTENSION, ast_moh_stop(), ast_monitor_setjoinfiles(), ast_monitor_start(), AST_OPTION_TONE_VERIFY, ast_queue_log(), ast_random(), ast_safe_sleep(), ast_say_number(), ast_set_flag, AST_STATE_UP, ast_strdupa, ast_strlen_zero(), ast_test_flag, attended_transfer_occurred(), calc_metric(), ast_channel::cdr, callattempt::chan, queue_ent::chan, ast_channel::context, ast_datastore::data, DATASTORE_INHERIT_FOREVER, di, dialed_interface_info, ast_cdr::dstchannel, EVENT_FLAG_AGENT, call_queue::eventwhencalled, queue_ent::expire, ast_channel::exten, ast_bridge_config::features_callee, ast_bridge_config::features_caller, free, queue_ent::handled, hangupcalls(), ast_datastore::inheritance, callattempt::interface, ast_dialed_interface::interface, member::interface, ast_channel::language, member::lastcall, callattempt::lastcall, leave_queue(), LOG_DEBUG, LOG_NOTICE, LOG_WARNING, manager_event(), callattempt::member, call_queue::membercount, call_queue::memberdelay, member::membername, call_queue::members, call_queue::monfmt, call_queue::monjoin, call_queue::montype, call_queue::name, ast_channel::name, callattempt::oldstatus, queue_ent::opos, option_debug, queue_ent::parent, pbx_builtin_getvar_helper(), pbx_builtin_setvar_helper(), pbx_exec(), pbx_findapp(), pbx_substitute_variables_helper(), queue_ent::pending, play_file(), queue_ent::pos, callattempt::q_next, QUEUE_EVENT_VARIABLES, QUEUE_STRATEGY_ROUNDROBIN, QUEUE_STRATEGY_RRMEMORY, QUEUE_STRATEGY_RRORDERED, queue_transfer_info, recalc_holdtime(), record_abandoned(), call_queue::reportholdtime, ring_one(), call_queue::servicelevel, call_queue::setinterfacevar, setup_transfer_datastore(), call_queue::sound_lessthan, call_queue::sound_minutes, call_queue::sound_reporthold, queue_ent::start, member::status, callattempt::stillgoing, store_next(), call_queue::strategy, tds, ast_channel::tech, call_queue::timeout, ast_channel_tech::type, ast_cdr::uniqueid, ast_channel::uniqueid, update_queue(), vars2manager(), and wait_for_answer().

Referenced by queue_exec().

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

static int unload_module ( void   )  [static]

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

05310 {
05311    int res;
05312 
05313    if (device_state.thread != AST_PTHREADT_NULL) {
05314       device_state.stop = 1;
05315       ast_mutex_lock(&device_state.lock);
05316       ast_cond_signal(&device_state.cond);
05317       ast_mutex_unlock(&device_state.lock);
05318       pthread_join(device_state.thread, NULL);
05319    }
05320 
05321    ast_cli_unregister_multiple(cli_queue, sizeof(cli_queue) / sizeof(struct ast_cli_entry));
05322    res = ast_manager_unregister("QueueStatus");
05323    res |= ast_manager_unregister("Queues");
05324    res |= ast_manager_unregister("QueueAdd");
05325    res |= ast_manager_unregister("QueueRemove");
05326    res |= ast_manager_unregister("QueuePause");
05327    res |= ast_unregister_application(app_aqm);
05328    res |= ast_unregister_application(app_rqm);
05329    res |= ast_unregister_application(app_pqm);
05330    res |= ast_unregister_application(app_upqm);
05331    res |= ast_unregister_application(app_ql);
05332    res |= ast_unregister_application(app);
05333    res |= ast_custom_function_unregister(&queueagentcount_function);
05334    res |= ast_custom_function_unregister(&queuemembercount_function);
05335    res |= ast_custom_function_unregister(&queuememberlist_function);
05336    res |= ast_custom_function_unregister(&queuewaitingcount_function);
05337    ast_devstate_del(statechange_queue, NULL);
05338 
05339    ast_module_user_hangup_all();
05340 
05341    clear_and_free_interfaces();
05342 
05343    return res;
05344 }

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

Definition at line 2641 of file app_queue.c.

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

Referenced by queue_transfer_fixup(), and try_calling().

02642 {
02643    ao2_lock(q);
02644    time(&member->lastcall);
02645    member->calls++;
02646    q->callscompleted++;
02647    if (callcompletedinsl)
02648       q->callscompletedinsl++;
02649    ao2_unlock(q);
02650    return 0;
02651 }

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

Definition at line 1379 of file app_queue.c.

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

Referenced by set_member_paused().

01380 {
01381    struct ast_variable *var, *save;
01382    int ret = -1;
01383 
01384    if (!(var = ast_load_realtime("queue_members", "interface", mem->interface, "queue_name", queue_name, NULL))) 
01385       return ret;
01386    save = var;
01387    while (var) {
01388       if (!strcmp(var->name, "uniqueid"))
01389          break;
01390       var = var->next;
01391    }
01392    if (var && !ast_strlen_zero(var->value)) {
01393       if ((ast_update_realtime("queue_members", "uniqueid", var->value, field, value, NULL)) > -1)
01394          ret = 0;
01395    }
01396    ast_variables_destroy(save);
01397    return ret;
01398 }

static void update_realtime_members ( struct call_queue q  )  [static]

Definition at line 1400 of file app_queue.c.

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

Referenced by load_realtime_queue(), and queue_exec().

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

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

Definition at line 628 of file app_queue.c.

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

Referenced by handle_statechange(), and ring_entry().

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

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

Definition at line 3797 of file app_queue.c.

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

Referenced by load_module().

03798 {
03799    struct ast_module_user *lu;
03800    char *parse;
03801    int priority_jump = 0;
03802    AST_DECLARE_APP_ARGS(args,
03803       AST_APP_ARG(queuename);
03804       AST_APP_ARG(interface);
03805       AST_APP_ARG(options);
03806    );
03807 
03808    if (ast_strlen_zero(data)) {
03809       ast_log(LOG_WARNING, "UnpauseQueueMember requires an argument ([queuename]|interface[|options])\n");
03810       return -1;
03811    }
03812 
03813    parse = ast_strdupa(data);
03814 
03815    AST_STANDARD_APP_ARGS(args, parse);
03816 
03817    lu = ast_module_user_add(chan);
03818 
03819    if (args.options) {
03820       if (strchr(args.options, 'j'))
03821          priority_jump = 1;
03822    }
03823 
03824    if (ast_strlen_zero(args.interface)) {
03825       ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename]|interface[|options])\n");
03826       ast_module_user_remove(lu);
03827       return -1;
03828    }
03829 
03830    if (set_member_paused(args.queuename, args.interface, 0)) {
03831       ast_log(LOG_WARNING, "Attempt to unpause interface %s, not found\n", args.interface);
03832       if (priority_jump || ast_opt_priority_jumping) {
03833          if (ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101)) {
03834             pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND");
03835             ast_module_user_remove(lu);
03836             return 0;
03837          }
03838       }
03839       ast_module_user_remove(lu);
03840       pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND");
03841       return 0;
03842    }
03843 
03844    ast_module_user_remove(lu);
03845    pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "UNPAUSED");
03846 
03847    return 0;
03848 }

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

Definition at line 1588 of file app_queue.c.

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

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

01589 {
01590    int digitlen = strlen(qe->digits);
01591 
01592    /* Prevent possible buffer overflow */
01593    if (digitlen < sizeof(qe->digits) - 2) {
01594       qe->digits[digitlen] = digit;
01595       qe->digits[digitlen + 1] = '\0';
01596    } else {
01597       qe->digits[0] = '\0';
01598       return 0;
01599    }
01600 
01601    /* If there's no context to goto, short-circuit */
01602    if (ast_strlen_zero(qe->context))
01603       return 0;
01604 
01605    /* If the extension is bad, then reset the digits to blank */
01606    if (!ast_canmatch_extension(qe->chan, qe->context, qe->digits, 1, qe->chan->cid.cid_num)) {
01607       qe->digits[0] = '\0';
01608       return 0;
01609    }
01610 
01611    /* We have an exact match */
01612    if (!ast_goto_if_exists(qe->chan, qe->context, qe->digits, 1)) {
01613       qe->valid_digits = 1;
01614       /* Return 1 on a successful goto */
01615       return 1;
01616    }
01617 
01618    return 0;
01619 }

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

Definition at line 1890 of file app_queue.c.

References ast_copy_string(), and pbx_builtin_serialize_variables().

Referenced by ring_entry(), and try_calling().

01891 {
01892    char *tmp = alloca(len);
01893 
01894    if (pbx_builtin_serialize_variables(chan, tmp, len)) {
01895       int i, j;
01896 
01897       /* convert "\n" to "\nVariable: " */
01898       strcpy(vars, "Variable: ");
01899 
01900       for (i = 0, j = 10; (i < len - 1) && (j < len - 1); i++, j++) {
01901          vars[j] = tmp[i];
01902 
01903          if (tmp[i + 1] == '\0')
01904             break;
01905          if (tmp[i] == '\n') {
01906             vars[j++] = '\r';
01907             vars[j++] = '\n';
01908 
01909             ast_copy_string(&(vars[j]), "Variable: ", len - j);
01910             j += 9;
01911          }
01912       }
01913       if (j > len - 3)
01914          j = len - 3;
01915       vars[j++] = '\r';
01916       vars[j++] = '\n';
01917       vars[j] = '\0';
01918    } else {
01919       /* there are no channel variables; leave it blank */
01920       *vars = '\0';
01921    }
01922    return vars;
01923 }

static int wait_a_bit ( struct queue_ent qe  )  [static]

Definition at line 3415 of file app_queue.c.

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

Referenced by queue_exec().

03416 {
03417    /* Don't need to hold the lock while we setup the outgoing calls */
03418    int retrywait = qe->parent->retry * 1000;
03419 
03420    int res = ast_waitfordigit(qe->chan, retrywait);
03421    if (res > 0 && !valid_exit(qe, res))
03422       res = 0;
03423 
03424    return res;
03425 }

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

Wait for a member to answer the call.

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

Definition at line 2250 of file app_queue.c.

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

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

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

02567 {
02568    int res = 0;
02569 
02570    /* This is the holding pen for callers 2 through maxlen */
02571    for (;;) {
02572       enum queue_member_status stat;
02573 
02574       if (is_our_turn(qe))
02575          break;
02576 
02577       /* If we have timed out, break out */
02578       if (qe->expire && (time(NULL) >= qe->expire)) {
02579          *reason = QUEUE_TIMEOUT;
02580          break;
02581       }
02582 
02583       stat = get_member_status(qe->parent, qe->max_penalty);
02584 
02585       /* leave the queue if no agents, if enabled */
02586       if (qe->parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) {
02587          *reason = QUEUE_LEAVEEMPTY;
02588          ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long)time(NULL) - qe->start);
02589          leave_queue(qe);
02590          break;
02591       }
02592 
02593       /* leave the queue if no reachable agents, if enabled */
02594       if ((qe->parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) {
02595          *reason = QUEUE_LEAVEUNAVAIL</