Sat Nov 25 00:45:45 2006

Asterisk developer's documentation


app_queue.c File Reference

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

#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.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"

Go to the source code of this file.

Data Structures

struct  call_queue
struct  localuser
 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  member
struct  member_interface
struct  queue_ent
struct  statechange
struct  strategy

Defines

#define ANNOUNCEHOLDTIME_ALWAYS   1
#define ANNOUNCEHOLDTIME_ONCE   2
#define AST_MAX_WATCHERS   256
#define BUILD_WATCHERS
#define DEFAULT_RETRY   5
#define DEFAULT_TIMEOUT   15
#define PM_MAX_LEN   2048
#define QUEUE_EMPTY_NORMAL   1
#define QUEUE_EMPTY_STRICT   2
#define QUEUE_STRATEGY_FEWESTCALLS   3
#define QUEUE_STRATEGY_LEASTRECENT   2
#define QUEUE_STRATEGY_RANDOM   4
#define QUEUE_STRATEGY_RINGALL   0
#define QUEUE_STRATEGY_ROUNDROBIN   1
#define QUEUE_STRATEGY_RRMEMORY   5
#define RECHECK   1
#define RES_EXISTS   (-1)
#define RES_NOSUCHQUEUE   (-3)
#define RES_OKAY   0
#define RES_OUTOFMEMORY   (-2)

Enumerations

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 (int manager, int fd, int argc, char **argv, int queue_show)
static int add_to_interfaces (char *interface)
static int add_to_queue (char *queuename, char *interface, int penalty, int paused, int dump)
static struct call_queuealloc_queue (const char *queuename)
static int aqm_exec (struct ast_channel *chan, void *data)
static AST_LIST_HEAD_STATIC (interfaces, member_interface)
 AST_MUTEX_DEFINE_STATIC (qlock)
static int background_file (struct queue_ent *qe, struct ast_channel *chan, char *filename)
static int calc_metric (struct call_queue *q, struct member *mem, int pos, struct queue_ent *qe, struct localuser *tmp)
static void * changethread (void *data)
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_add_queue_member (char *line, char *word, int pos, int state)
static char * complete_queue (char *line, char *word, int pos, int state)
static char * complete_remove_queue_member (char *line, char *word, int pos, int state)
static struct membercreate_queue_member (char *interface, int penalty, int paused)
char * description (void)
 Provides a description of the module.
static void destroy_queue (struct call_queue *q)
static void dump_queue_members (struct call_queue *pm_queue)
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)
static int handle_add_queue_member (int fd, int argc, char *argv[])
static int handle_remove_queue_member (int fd, int argc, char *argv[])
static void hangupcalls (struct localuser *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, char *interface)
static int interface_exists_global (char *interface)
static int is_our_turn (struct queue_ent *qe)
static int join_queue (char *queuename, struct queue_ent *qe, enum queue_result *reason)
char * key ()
 Returns the ASTERISK_GPL_KEY.
static void leave_queue (struct queue_ent *qe)
int load_module (void)
 Initialize the module.
static struct call_queueload_realtime_queue (char *queuename)
static int manager_add_queue_member (struct mansession *s, struct message *m)
static int manager_pause_queue_member (struct mansession *s, struct message *m)
static int manager_queues_show (struct mansession *s, struct message *m)
static int manager_queues_status (struct mansession *s, struct message *m)
static int manager_remove_queue_member (struct mansession *s, struct message *m)
static int play_file (struct ast_channel *chan, char *filename)
static int pqm_exec (struct ast_channel *chan, void *data)
static int queue_exec (struct ast_channel *chan, void *data)
static char * queue_function_qac (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 int queues_show (int fd, int argc, char **argv)
static void recalc_holdtime (struct queue_ent *qe)
static void record_abandoned (struct queue_ent *qe)
int reload (void)
 Reload stuff.
static void reload_queue_members (void)
static void reload_queues (void)
static int remove_from_interfaces (char *interface)
static int remove_from_queue (char *queuename, char *interface)
static void remove_queue (struct call_queue *q)
static int ring_entry (struct queue_ent *qe, struct localuser *tmp, int *busies)
static int ring_one (struct queue_ent *qe, struct localuser *outgoing, int *busies)
static int rqm_exec (struct ast_channel *chan, void *data)
static void rt_handle_member_record (struct call_queue *q, char *interface, const char *penalty_str)
static int say_periodic_announcement (struct queue_ent *qe)
static int say_position (struct queue_ent *qe)
static int set_member_paused (char *queuename, char *interface, int paused)
static void set_queue_result (struct ast_channel *chan, enum queue_result res)
static int statechange_queue (const char *dev, int state, void *ign)
static int store_next (struct queue_ent *qe, struct localuser *outgoing)
static int strat2int (const char *strategy)
static int try_calling (struct queue_ent *qe, const char *options, char *announceoverride, const char *url, int *go_on)
int unload_module (void)
 Cleanup all module structures, sockets, etc.
static int update_dial_status (struct call_queue *q, struct member *member, int status)
static int update_queue (struct call_queue *q, struct member *member)
static int update_status (struct call_queue *q, struct member *member, int status)
static int upqm_exec (struct ast_channel *chan, void *data)
int usecount (void)
 Provides a usecount.
static int valid_exit (struct queue_ent *qe, char digit)
static int wait_a_bit (struct queue_ent *qe)
static struct localuserwait_for_answer (struct queue_ent *qe, struct localuser *outgoing, int *to, char *digit, int prebusies, int caller_disconnect)
static int wait_our_turn (struct queue_ent *qe, int ringing, enum queue_result *reason)

Variables

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_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 char aqm_cmd_usage []
static struct ast_cli_entry cli_add_queue_member
static struct ast_cli_entry cli_remove_queue_member
static struct ast_cli_entry cli_show_queue
static struct ast_cli_entry cli_show_queues
static char * descrip
 LOCAL_USER_DECL
static const char * pm_family = "/Queue/PersistentMembers"
 Persistent Members astdb family.
static int queue_persistent_members = 0
 queues.conf [general] option
struct {
   int   alarm
   char *   description
   unsigned int   event_log:1
   enum queue_result   id
   char *   name
   char *   name
   char *   name
   rtpPayloadType   payloadType
   unsigned int   queue_log:1
   char *   subtype
   char *   text
   char *   type
   int   val
queue_results []
static struct ast_custom_function queueagentcount_function
static struct call_queuequeues = NULL
static char rqm_cmd_usage []
static char show_queue_usage []
static char show_queues_usage []
static struct strategy strategies []
static char * synopsis = "Queue a call for a call queue"
static char * tdesc = "True Call Queueing"
static int use_weight = 0
 queues.conf per-queue weight option


Detailed Description

True call queues with optional send URL on answer.

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

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

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

Patch Version 1.07 2003-12-24 01

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

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

Definition in file app_queue.c.


Define Documentation

#define ANNOUNCEHOLDTIME_ALWAYS   1
 

Definition at line 316 of file app_queue.c.

Referenced by queue_set_param().

#define ANNOUNCEHOLDTIME_ONCE   2
 

Definition at line 317 of file app_queue.c.

Referenced by queue_set_param(), and say_position().

#define AST_MAX_WATCHERS   256
 

Definition at line 1755 of file app_queue.c.

#define BUILD_WATCHERS
 

Definition at line 1757 of file app_queue.c.

Referenced by wait_for_answer().

#define DEFAULT_RETRY   5
 

Definition at line 112 of file app_queue.c.

Referenced by init_queue(), and queue_set_param().

#define DEFAULT_TIMEOUT   15
 

Definition at line 113 of file app_queue.c.

Referenced by queue_set_param().

#define PM_MAX_LEN   2048
 

Definition at line 225 of file app_queue.c.

Referenced by dump_queue_members(), and reload_queue_members().

#define QUEUE_EMPTY_NORMAL   1
 

Definition at line 314 of file app_queue.c.

Referenced by queue_set_param().

#define QUEUE_EMPTY_STRICT   2
 

Definition at line 315 of file app_queue.c.

Referenced by join_queue(), and queue_set_param().

#define QUEUE_STRATEGY_FEWESTCALLS   3
 

Definition at line 96 of file app_queue.c.

Referenced by calc_metric().

#define QUEUE_STRATEGY_LEASTRECENT   2
 

Definition at line 95 of file app_queue.c.

Referenced by calc_metric().

#define QUEUE_STRATEGY_RANDOM   4
 

Definition at line 97 of file app_queue.c.

Referenced by calc_metric().

#define QUEUE_STRATEGY_RINGALL   0
 

Definition at line 93 of file app_queue.c.

Referenced by calc_metric().

#define QUEUE_STRATEGY_ROUNDROBIN   1
 

Definition at line 94 of file app_queue.c.

Referenced by calc_metric().

#define QUEUE_STRATEGY_RRMEMORY   5
 

Definition at line 98 of file app_queue.c.

Referenced by calc_metric().

#define RECHECK   1
 

Definition at line 114 of file app_queue.c.

#define RES_EXISTS   (-1)
 

Definition at line 117 of file app_queue.c.

Referenced by add_to_queue(), aqm_exec(), handle_add_queue_member(), handle_remove_queue_member(), manager_add_queue_member(), manager_remove_queue_member(), and rqm_exec().

#define RES_NOSUCHQUEUE   (-3)
 

Definition at line 119 of file app_queue.c.

Referenced by add_to_queue(), aqm_exec(), handle_add_queue_member(), handle_remove_queue_member(), manager_add_queue_member(), manager_remove_queue_member(), remove_from_queue(), and rqm_exec().

#define RES_OKAY   0
 

Definition at line 116 of file app_queue.c.

Referenced by add_to_queue(), aqm_exec(), handle_add_queue_member(), handle_remove_queue_member(), manager_add_queue_member(), manager_remove_queue_member(), and rqm_exec().

#define RES_OUTOFMEMORY   (-2)
 

Definition at line 118 of file app_queue.c.

Referenced by add_to_queue(), aqm_exec(), handle_add_queue_member(), handle_remove_queue_member(), manager_add_queue_member(), manager_remove_queue_member(), and rqm_exec().


Enumeration Type Documentation

enum queue_member_status
 

Enumerator:
QUEUE_NO_MEMBERS 
QUEUE_NO_REACHABLE_MEMBERS 
QUEUE_NORMAL 

Definition at line 429 of file app_queue.c.

00429                          {
00430    QUEUE_NO_MEMBERS,
00431    QUEUE_NO_REACHABLE_MEMBERS,
00432    QUEUE_NORMAL
00433 };

enum queue_result
 

Enumerator:
QUEUE_UNKNOWN 
QUEUE_TIMEOUT 
QUEUE_JOINEMPTY 
QUEUE_LEAVEEMPTY 
QUEUE_JOINUNAVAIL 
QUEUE_LEAVEUNAVAIL 
QUEUE_FULL 

Definition at line 233 of file app_queue.c.

00233                   {
00234    QUEUE_UNKNOWN = 0,
00235    QUEUE_TIMEOUT = 1,
00236    QUEUE_JOINEMPTY = 2,
00237    QUEUE_LEAVEEMPTY = 3,
00238    QUEUE_JOINUNAVAIL = 4,
00239    QUEUE_LEAVEUNAVAIL = 5,
00240    QUEUE_FULL = 6,
00241 };


Function Documentation

static int __queues_show int  manager,
int  fd,
int  argc,
char **  argv,
int  queue_show
[static]
 

Definition at line 3463 of file app_queue.c.

References ast_build_string(), ast_cli(), ast_mutex_lock(), ast_mutex_unlock(), member::calls, call_queue::callsabandoned, call_queue::callscompleted, call_queue::callscompletedinsl, queue_ent::chan, call_queue::count, devstate2str(), member::dynamic, call_queue::head, call_queue::holdtime, int2strat(), member::interface, member::lastcall, load_realtime_queue(), call_queue::lock, call_queue::maxlen, call_queue::members, ast_channel::name, call_queue::name, queue_ent::next, member::next, call_queue::next, member::paused, member::penalty, queue_ent::prio, queues, RESULT_SHOWUSAGE, RESULT_SUCCESS, call_queue::servicelevel, queue_ent::start, member::status, call_queue::strategy, and call_queue::weight.

Referenced by manager_queues_show(), queue_show(), and queues_show().

03464 {
03465    struct call_queue *q;
03466    struct queue_ent *qe;
03467    struct member *mem;
03468    int pos;
03469    time_t now;
03470    char max_buf[80];
03471    char *max;
03472    size_t max_left;
03473    float sl = 0;
03474    char *term = manager ? "\r\n" : "\n";
03475 
03476    time(&now);
03477    if ((!queue_show && argc != 2) || (queue_show && argc != 3))
03478       return RESULT_SHOWUSAGE;
03479 
03480    /* We only want to load realtime queues when a specific queue is asked for. */
03481    if (queue_show)
03482       load_realtime_queue(argv[2]);
03483 
03484    ast_mutex_lock(&qlock);
03485 
03486    q = queues;
03487    if (!q) {   
03488       ast_mutex_unlock(&qlock);
03489       if (queue_show)
03490          ast_cli(fd, "No such queue: %s.%s",argv[2], term);
03491       else
03492          ast_cli(fd, "No queues.%s", term);
03493       return RESULT_SUCCESS;
03494    }
03495    while (q) {
03496       ast_mutex_lock(&q->lock);
03497       if (queue_show) {
03498          if (strcasecmp(q->name, argv[2]) != 0) {
03499             ast_mutex_unlock(&q->lock);
03500             q = q->next;
03501             if (!q) {
03502                ast_cli(fd, "No such queue: %s.%s",argv[2], term);
03503                break;
03504             }
03505             continue;
03506          }
03507       }
03508       max_buf[0] = '\0';
03509       max = max_buf;
03510       max_left = sizeof(max_buf);
03511       if (q->maxlen)
03512          ast_build_string(&max, &max_left, "%d", q->maxlen);
03513       else
03514          ast_build_string(&max, &max_left, "unlimited");
03515       sl = 0;
03516       if(q->callscompleted > 0)
03517          sl = 100*((float)q->callscompletedinsl/(float)q->callscompleted);
03518       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",
03519          q->name, q->count, max_buf, int2strat(q->strategy), q->holdtime, q->weight, q->callscompleted, q->callsabandoned,sl,q->servicelevel, term);
03520       if (q->members) {
03521          ast_cli(fd, "   Members: %s", term);
03522          for (mem = q->members; mem; mem = mem->next) {
03523             max_buf[0] = '\0';
03524             max = max_buf;
03525             max_left = sizeof(max_buf);
03526             if (mem->penalty)
03527                ast_build_string(&max, &max_left, " with penalty %d", mem->penalty);
03528             if (mem->dynamic)
03529                ast_build_string(&max, &max_left, " (dynamic)");
03530             if (mem->paused)
03531                ast_build_string(&max, &max_left, " (paused)");
03532             ast_build_string(&max, &max_left, " (%s)", devstate2str(mem->status));
03533             if (mem->calls) {
03534                ast_build_string(&max, &max_left, " has taken %d calls (last was %ld secs ago)",
03535                       mem->calls, (long)(time(NULL) - mem->lastcall));
03536             } else
03537                ast_build_string(&max, &max_left, " has taken no calls yet");
03538             ast_cli(fd, "      %s%s%s", mem->interface, max_buf, term);
03539          }
03540       } else
03541          ast_cli(fd, "   No Members%s", term);
03542       if (q->head) {
03543          pos = 1;
03544          ast_cli(fd, "   Callers: %s", term);
03545          for (qe = q->head; qe; qe = qe->next) 
03546             ast_cli(fd, "      %d. %s (wait: %ld:%2.2ld, prio: %d)%s", pos++, qe->chan->name,
03547                (long)(now - qe->start) / 60, (long)(now - qe->start) % 60, qe->prio, term);
03548       } else
03549          ast_cli(fd, "   No Callers%s", term);
03550       ast_cli(fd, "%s", term);
03551       ast_mutex_unlock(&q->lock);
03552       q = q->next;
03553       if (queue_show)
03554          break;
03555    }
03556    ast_mutex_unlock(&qlock);
03557    return RESULT_SUCCESS;
03558 }

static int add_to_interfaces char *  interface  )  [static]
 

Definition at line 639 of file app_queue.c.

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

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

00640 {
00641    struct member_interface *curint;
00642 
00643    if (!interface)
00644       return 0;
00645 
00646    AST_LIST_LOCK(&interfaces);
00647    AST_LIST_TRAVERSE(&interfaces, curint, list) {
00648       if (!strcasecmp(curint->interface, interface))
00649          break; 
00650    }
00651 
00652    if (curint) {
00653       AST_LIST_UNLOCK(&interfaces);
00654       return 0;
00655    }
00656 
00657    if (option_debug)
00658       ast_log(LOG_DEBUG, "Adding %s to the list of interfaces that make up all of our queue members.\n", interface);
00659    
00660    if ((curint = malloc(sizeof(*curint)))) {
00661       memset(curint, 0, sizeof(*curint));
00662       ast_copy_string(curint->interface, interface, sizeof(curint->interface));
00663       AST_LIST_INSERT_HEAD(&interfaces, curint, list);
00664    }
00665    AST_LIST_UNLOCK(&interfaces);
00666 
00667    return 0;
00668 }

static int add_to_queue char *  queuename,
char *  interface,
int  penalty,
int  paused,
int  dump
[static]
 

Definition at line 2567 of file app_queue.c.

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

Referenced by aqm_exec(), handle_add_queue_member(), and manager_add_queue_member().

02568 {
02569    struct call_queue *q;
02570    struct member *new_member;
02571    int res = RES_NOSUCHQUEUE;
02572 
02573    /* \note Ensure the appropriate realtime queue is loaded.  Note that this
02574     * short-circuits if the queue is already in memory. */
02575    q = load_realtime_queue(queuename);
02576 
02577    ast_mutex_lock(&qlock);
02578 
02579    if (q) {
02580       ast_mutex_lock(&q->lock);
02581       if (interface_exists(q, interface) == NULL) {
02582 
02583          add_to_interfaces(interface);
02584 
02585          new_member = create_queue_member(interface, penalty, paused);
02586 
02587          if (new_member != NULL) {
02588             new_member->dynamic = 1;
02589             new_member->next = q->members;
02590             q->members = new_member;
02591             manager_event(EVENT_FLAG_AGENT, "QueueMemberAdded",
02592                   "Queue: %s\r\n"
02593                   "Location: %s\r\n"
02594                   "Membership: %s\r\n"
02595                   "Penalty: %d\r\n"
02596                   "CallsTaken: %d\r\n"
02597                   "LastCall: %d\r\n"
02598                   "Status: %d\r\n"
02599                   "Paused: %d\r\n",
02600                   q->name, new_member->interface, new_member->dynamic ? "dynamic" : "static",
02601                   new_member->penalty, new_member->calls, (int)new_member->lastcall, new_member->status, new_member->paused);
02602 
02603             if (dump)
02604                dump_queue_members(q);
02605 
02606             res = RES_OKAY;
02607          } else {
02608             res = RES_OUTOFMEMORY;
02609          }
02610       } else {
02611          res = RES_EXISTS;
02612       }
02613       ast_mutex_unlock(&q->lock);
02614    }
02615    ast_mutex_unlock(&qlock);
02616    return res;
02617 }

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

Definition at line 590 of file app_queue.c.

References ast_mutex_init(), and malloc.

Referenced by reload_queues().

00591 {
00592    struct call_queue *q;
00593 
00594    q = malloc(sizeof(*q));
00595    if (q) {
00596       memset(q, 0, sizeof(*q));
00597       ast_mutex_init(&q->lock);
00598       ast_copy_string(q->name, queuename, sizeof(q->name));
00599    }
00600    return q;
00601 }

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

Definition at line 2931 of file app_queue.c.

References add_to_queue(), AST_APP_ARG, AST_DECLARE_APP_ARGS, ast_goto_if_exists(), ast_log(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), localuser::chan, ast_channel::context, localuser::interface, LOCAL_USER_ADD, LOCAL_USER_REMOVE, LOG_ERROR, LOG_NOTICE, LOG_WARNING, ast_channel::name, option_priority_jumping, parse(), pbx_builtin_setvar_helper(), queue_persistent_members, RES_EXISTS, RES_NOSUCHQUEUE, RES_OKAY, and RES_OUTOFMEMORY.

Referenced by load_module().

02932 {
02933    int res=-1;
02934    struct localuser *u;
02935    char *parse, *temppos = NULL;
02936    int priority_jump = 0;
02937    AST_DECLARE_APP_ARGS(args,
02938       AST_APP_ARG(queuename);
02939       AST_APP_ARG(interface);
02940       AST_APP_ARG(penalty);
02941       AST_APP_ARG(options);
02942    );
02943    int penalty = 0;
02944 
02945    if (ast_strlen_zero(data)) {
02946       ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename[|[interface]|[penalty][|options]])\n");
02947       return -1;
02948    }
02949 
02950    LOCAL_USER_ADD(u);
02951 
02952    if (!(parse = ast_strdupa(data))) {
02953       ast_log(LOG_WARNING, "Memory Error!\n");
02954       LOCAL_USER_REMOVE(u);
02955       return -1;
02956    }
02957 
02958    AST_STANDARD_APP_ARGS(args, parse);
02959 
02960    if (ast_strlen_zero(args.interface)) {
02961       args.interface = ast_strdupa(chan->name);
02962       temppos = strrchr(args.interface, '-');
02963       if (temppos)
02964          *temppos = '\0';
02965    }
02966 
02967    if (!ast_strlen_zero(args.penalty)) {
02968       if ((sscanf(args.penalty, "%d", &penalty) != 1) || penalty < 0) {
02969          ast_log(LOG_WARNING, "Penalty '%s' is invalid, must be an integer >= 0\n", args.penalty);
02970          penalty = 0;
02971       }
02972    }
02973    
02974    if (args.options) {
02975       if (strchr(args.options, 'j'))
02976          priority_jump = 1;
02977    }
02978 
02979 
02980    switch (add_to_queue(args.queuename, args.interface, penalty, 0, queue_persistent_members)) {
02981    case RES_OKAY:
02982       ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", args.interface, args.queuename);
02983       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "ADDED");
02984       res = 0;
02985       break;
02986    case RES_EXISTS:
02987       ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': Already there\n", args.interface, args.queuename);
02988       if (priority_jump || option_priority_jumping) 
02989          ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
02990       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "MEMBERALREADY");
02991       res = 0;
02992       break;
02993    case RES_NOSUCHQUEUE:
02994       ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", args.queuename);
02995       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "NOSUCHQUEUE");
02996       res = 0;
02997       break;
02998    case RES_OUTOFMEMORY:
02999       ast_log(LOG_ERROR, "Out of memory adding member %s to queue %s\n", args.interface, args.queuename);
03000       break;
03001    }
03002 
03003    LOCAL_USER_REMOVE(u);
03004    return res;
03005 }

static AST_LIST_HEAD_STATIC interfaces  ,
member_interface 
[static]
 

AST_MUTEX_DEFINE_STATIC qlock   ) 
 

static int background_file struct queue_ent qe,
struct ast_channel chan,
char *  filename
[static]
 

Definition at line 1689 of file app_queue.c.

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

Referenced by say_periodic_announcement().

01690 {
01691    int res;
01692 
01693    ast_stopstream(chan);
01694    res = ast_streamfile(chan, filename, chan->language);
01695 
01696    if (!res) {
01697       /* Wait for a keypress */
01698       res = ast_waitstream(chan, AST_DIGIT_ANY);
01699       if (res < 0 || !valid_exit(qe, res))
01700          res = 0;
01701 
01702       /* Stop playback */
01703       ast_stopstream(chan);
01704    } else {
01705       res = 0;
01706    }
01707    
01708    /*if (res) {
01709       ast_log(LOG_WARNING, "ast_streamfile failed on %s \n", chan->name);
01710       res = 0;
01711    }*/
01712 
01713    return res;
01714 }

static int calc_metric struct call_queue q,
struct member mem,
int  pos,
struct queue_ent qe,
struct localuser tmp
[static]
 

Definition at line 2101 of file app_queue.c.

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

02102 {
02103    switch (q->strategy) {
02104    case QUEUE_STRATEGY_RINGALL:
02105       /* Everyone equal, except for penalty */
02106       tmp->metric = mem->penalty * 1000000;
02107       break;
02108    case QUEUE_STRATEGY_ROUNDROBIN:
02109       if (!pos) {
02110          if (!q->wrapped) {
02111             /* No more channels, start over */
02112             q->rrpos = 0;
02113          } else {
02114             /* Prioritize next entry */
02115             q->rrpos++;
02116          }
02117          q->wrapped = 0;
02118       }
02119       /* Fall through */
02120    case QUEUE_STRATEGY_RRMEMORY:
02121       if (pos < q->rrpos) {
02122          tmp->metric = 1000 + pos;
02123       } else {
02124          if (pos > q->rrpos)
02125             /* Indicate there is another priority */
02126             q->wrapped = 1;
02127          tmp->metric = pos;
02128       }
02129       tmp->metric += mem->penalty * 1000000;
02130       break;
02131    case QUEUE_STRATEGY_RANDOM:
02132       tmp->metric = rand() % 1000;
02133       tmp->metric += mem->penalty * 1000000;
02134       break;
02135    case QUEUE_STRATEGY_FEWESTCALLS:
02136       tmp->metric = mem->calls;
02137       tmp->metric += mem->penalty * 1000000;
02138       break;
02139    case QUEUE_STRATEGY_LEASTRECENT:
02140       if (!mem->lastcall)
02141          tmp->metric = 0;
02142       else
02143          tmp->metric = 1000000 - (time(NULL) - mem->lastcall);
02144       tmp->metric += mem->penalty * 1000000;
02145       break;
02146    default:
02147       ast_log(LOG_WARNING, "Can't calculate metric for unknown strategy %d\n", q->strategy);
02148       break;
02149    }
02150    return 0;
02151 }

static void* changethread void *  data  )  [static]
 

Definition at line 466 of file app_queue.c.

References AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), ast_mutex_lock(), ast_strdupa, member::calls, statechange::dev, devstate2str(), member::dynamic, EVENT_FLAG_AGENT, free, member::interface, member_interface::interface, member::lastcall, list, call_queue::lock, LOG_DEBUG, manager_event(), call_queue::maskmemberstatus, call_queue::members, call_queue::name, member::next, call_queue::next, option_debug, member::paused, member::penalty, queues, statechange::state, and member::status.

Referenced by statechange_queue().

00467 {
00468    struct call_queue *q;
00469    struct statechange *sc = data;
00470    struct member *cur;
00471    struct member_interface *curint;
00472    char *loc;
00473    char *technology;
00474 
00475    technology = ast_strdupa(sc->dev);
00476    loc = strchr(technology, '/');
00477    if (loc) {
00478       *loc++ = '\0';
00479    } else {
00480       free(sc);
00481       return NULL;
00482    }
00483 
00484    AST_LIST_LOCK(&interfaces);
00485    AST_LIST_TRAVERSE(&interfaces, curint, list) {
00486       char *interface;
00487       char *slash_pos;
00488       interface = ast_strdupa(curint->interface);
00489       if ((slash_pos = strchr(interface, '/')))
00490          if ((slash_pos = strchr(slash_pos + 1, '/')))
00491             *slash_pos = '\0';
00492 
00493       if (!strcasecmp(interface, sc->dev))
00494          break;
00495    }
00496    AST_LIST_UNLOCK(&interfaces);
00497    
00498    if (!curint) {
00499       if (option_debug)
00500          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));
00501       free(sc);
00502       return NULL;
00503         }
00504 
00505    if (option_debug)
00506       ast_log(LOG_DEBUG, "Device '%s/%s' changed to state '%d' (%s)\n", technology, loc, sc->state, devstate2str(sc->state));
00507    ast_mutex_lock(&qlock);
00508    for (q = queues; q; q = q->next) {
00509       ast_mutex_lock(&q->lock);
00510       for (cur = q->members; cur; cur = cur->next) {
00511          char *interface;
00512          char *slash_pos;
00513          interface = ast_strdupa(cur->interface);
00514          if ((slash_pos = strchr(interface, '/')))
00515             if ((slash_pos = strchr(slash_pos + 1, '/')))
00516                *slash_pos = '\0';
00517 
00518          if (strcasecmp(sc->dev, interface))
00519             continue;
00520 
00521          if (cur->status != sc->state) {
00522             cur->status = sc->state;
00523             if (q->maskmemberstatus)
00524                continue;
00525 
00526             manager_event(EVENT_FLAG_AGENT, "QueueMemberStatus",
00527                      "Queue: %s\r\n"
00528                      "Location: %s\r\n"
00529                      "Membership: %s\r\n"
00530                      "Penalty: %d\r\n"
00531                      "CallsTaken: %d\r\n"
00532                      "LastCall: %d\r\n"
00533                      "Status: %d\r\n"
00534                      "Paused: %d\r\n",
00535                      q->name, cur->interface, cur->dynamic ? "dynamic" : "static",
00536                      cur->penalty, cur->calls, (int)cur->lastcall, cur->status, cur->paused);
00537          }
00538       }
00539       ast_mutex_unlock(&q->lock);
00540    }
00541    ast_mutex_unlock(&qlock);
00542 
00543    return NULL;
00544 }

static void clear_and_free_interfaces void   )  [static]
 

Definition at line 718 of file app_queue.c.

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

Referenced by unload_module().

00719 {
00720    struct member_interface *curint;
00721 
00722    AST_LIST_LOCK(&interfaces);
00723    while ((curint = AST_LIST_REMOVE_HEAD(&interfaces, list)))
00724       free(curint);
00725    AST_LIST_UNLOCK(&interfaces);
00726 }

static void clear_queue struct call_queue q  )  [static]
 

Definition at line 630 of file app_queue.c.

References call_queue::callsabandoned, call_queue::callscompleted, call_queue::callscompletedinsl, call_queue::holdtime, and call_queue::wrapuptime.

Referenced by reload_queues().

00631 {
00632    q->holdtime = 0;
00633    q->callscompleted = 0;
00634    q->callsabandoned = 0;
00635    q->callscompletedinsl = 0;
00636    q->wrapuptime = 0;
00637 }

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

Definition at line 1456 of file app_queue.c.

References ast_log(), ast_mutex_lock(), ast_mutex_unlock(), call_queue::count, member::interface, call_queue::lock, LOG_DEBUG, call_queue::members, call_queue::name, member::next, call_queue::next, queues, and call_queue::weight.

Referenced by ring_entry().

01457 {
01458    struct call_queue *q;
01459    struct member *mem;
01460    int found = 0;
01461    
01462    /* &qlock and &rq->lock already set by try_calling()
01463     * to solve deadlock */
01464    for (q = queues; q; q = q->next) {
01465       if (q == rq) /* don't check myself, could deadlock */
01466          continue; 
01467       ast_mutex_lock(&q->lock);
01468       if (q->count && q->members) {
01469          for (mem = q->members; mem; mem = mem->next) {
01470             if (!strcmp(mem->interface, member->interface)) {
01471                ast_log(LOG_DEBUG, "Found matching member %s in queue '%s'\n", mem->interface, q->name);
01472                if (q->weight > rq->weight) {
01473                   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);
01474                   found = 1;
01475                   break;
01476                }
01477             }
01478          }
01479       }
01480       ast_mutex_unlock(&q->lock);
01481       if (found) 
01482          break;
01483    }
01484    ast_mutex_unlock(&qlock);
01485    return found;
01486 }

static char* complete_add_queue_member char *  line,
char *  word,
int  pos,
int  state
[static]
 

Definition at line 3839 of file app_queue.c.

References complete_queue(), malloc, and strdup.

03840 {
03841    /* 0 - add; 1 - queue; 2 - member; 3 - <member>; 4 - to; 5 - <queue>; 6 - penalty; 7 - <penalty> */
03842    switch (pos) {
03843    case 3:
03844       /* Don't attempt to complete name of member (infinite possibilities) */
03845       return NULL;
03846    case 4:
03847       if (state == 0) {
03848          return strdup("to");
03849       } else {
03850          return NULL;
03851       }
03852    case 5:
03853       /* No need to duplicate code */
03854       return complete_queue(line, word, pos, state);
03855    case 6:
03856       if (state == 0) {
03857          return strdup("penalty");
03858       } else {
03859          return NULL;
03860       }
03861    case 7:
03862       if (state < 100) {   /* 0-99 */
03863          char *num = malloc(3);
03864          if (num) {
03865             sprintf(num, "%d", state);
03866          }
03867          return num;
03868       } else {
03869          return NULL;
03870       }
03871    default:
03872       return NULL;
03873    }
03874 }

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

Definition at line 3570 of file app_queue.c.

References ast_mutex_lock(), call_queue::name, call_queue::next, and queues.

Referenced by complete_add_queue_member(), and complete_remove_queue_member().

03571 {
03572    struct call_queue *q;
03573    int which=0;
03574    
03575    ast_mutex_lock(&qlock);
03576    for (q = queues; q; q = q->next) {
03577       if (!strncasecmp(word, q->name, strlen(word))) {
03578          if (++which > state)
03579             break;
03580       }
03581    }
03582    ast_mutex_unlock(&qlock);
03583    return q ? strdup(q->name) : NULL;
03584 }

static char* complete_remove_queue_member char *  line,
char *  word,
int  pos,
int  state
[static]
 

Definition at line 3907 of file app_queue.c.

References ast_mutex_lock(), ast_mutex_unlock(), complete_queue(), member::interface, call_queue::lock, call_queue::members, member::next, call_queue::next, queues, and strdup.

03908 {
03909    int which = 0;
03910    struct call_queue *q;
03911    struct member *m;
03912 
03913    /* 0 - add; 1 - queue; 2 - member; 3 - <member>; 4 - to; 5 - <queue> */
03914    if ((pos > 5) || (pos < 3)) {
03915       return NULL;
03916    }
03917    if (pos == 4) {
03918       if (state == 0) {
03919          return strdup("from");
03920       } else {
03921          return NULL;
03922       }
03923    }
03924 
03925    if (pos == 5) {
03926       /* No need to duplicate code */
03927       return complete_queue(line, word, pos, state);
03928    }
03929 
03930    if (queues != NULL) {
03931       for (q = queues ; q ; q = q->next) {
03932          ast_mutex_lock(&q->lock);
03933          for (m = q->members ; m ; m = m->next) {
03934             if (++which > state) {
03935                ast_mutex_unlock(&q->lock);
03936                return strdup(m->interface);
03937             }
03938          }
03939          ast_mutex_unlock(&q->lock);
03940       }
03941    }
03942    return NULL;
03943 }

static struct member* create_queue_member char *  interface,
int  penalty,
int  paused
[static]
 

Definition at line 569 of file app_queue.c.

References ast_device_state(), ast_log(), LOG_WARNING, and malloc.

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

00570 {
00571    struct member *cur;
00572    
00573    /* Add a new member */
00574 
00575    cur = malloc(sizeof(struct member));
00576 
00577    if (cur) {
00578       memset(cur, 0, sizeof(struct member));
00579       cur->penalty = penalty;
00580       cur->paused = paused;
00581       ast_copy_string(cur->interface, interface, sizeof(cur->interface));
00582       if (!strchr(cur->interface, '/'))
00583          ast_log(LOG_WARNING, "No location at interface '%s'\n", interface);
00584       cur->status = ast_device_state(interface);
00585    }
00586 
00587    return cur;
00588 }

char* description void   ) 
 

Provides a description of the module.

Returns:
a short description of your module

Definition at line 4039 of file app_queue.c.

References tdesc.

04040 {
04041    return tdesc;
04042 }

static void destroy_queue struct call_queue q  )  [static]
 

Definition at line 910 of file app_queue.c.

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

Referenced by leave_queue(), and reload_queues().

00911 {
00912    free_members(q, 1);
00913    ast_mutex_destroy(&q->lock);
00914    free(q);
00915 }

static void dump_queue_members struct call_queue pm_queue  )  [static]
 

Definition at line 2486 of file app_queue.c.

References ast_db_del(), ast_db_put(), ast_log(), member::dynamic, member::interface, LOG_WARNING, call_queue::members, call_queue::name, member::next, member::paused, member::penalty, pm_family, and PM_MAX_LEN.

Referenced by add_to_queue(), and set_member_paused().

02487 {
02488    struct member *cur_member;
02489    char value[PM_MAX_LEN];
02490    int value_len = 0;
02491    int res;
02492 
02493    memset(value, 0, sizeof(value));
02494 
02495    if (!pm_queue)
02496       return;
02497 
02498    for (cur_member = pm_queue->members; cur_member; cur_member = cur_member->next) {
02499       if (!cur_member->dynamic)
02500          continue;
02501 
02502       res = snprintf(value + value_len, sizeof(value) - value_len, "%s;%d;%d%s",
02503                 cur_member->interface, cur_member->penalty, cur_member->paused,
02504                 cur_member->next ? "|" : "");
02505       if (res != strlen(value + value_len)) {
02506          ast_log(LOG_WARNING, "Could not create persistent member string, out of space\n");
02507          break;
02508       }
02509       value_len += res;
02510    }
02511    
02512    if (value_len && !cur_member) {
02513       if (ast_db_put(pm_family, pm_queue->name, value))
02514          ast_log(LOG_WARNING, "failed to create persistent dynamic entry!\n");
02515    } else
02516       /* Delete the entry if the queue is empty or there is an error */
02517       ast_db_del(pm_family, pm_queue->name);
02518 }

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

Reload a single queue via realtime.

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

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

References member::interface, call_queue::name, call_queue::next, and queues.

00939 {
00940    struct ast_variable *v;
00941    struct call_queue *q, *prev_q = NULL;
00942    struct member *m, *prev_m, *next_m;
00943    char *interface;
00944    char *tmp, *tmp_name;
00945    char tmpbuf[64];  /* Must be longer than the longest queue param name. */
00946 
00947    /* Find the queue in the in-core list (we will create a new one if not found). */
00948    for (q = queues; q; q = q->next) {
00949       if (!strcasecmp(q->name, queuename)) {
00950          break;
00951       }
00952       prev_q = q;
00953    }
00954 
00955    /* Static queues override realtime. */
00956    if (q) {
00957       ast_mutex_lock(&q->lock);
00958       if (!q->realtime) {
00959          if (q->dead) {
00960             ast_mutex_unlock(&q->lock);
00961             return NULL;
00962          } else {
00963             ast_mutex_unlock(&q->lock);
00964             return q;
00965          }
00966       }
00967    } else if (!member_config)
00968       /* Not found in the list, and it's not realtime ... */
00969       return NULL;
00970 
00971    /* Check if queue is defined in realtime. */
00972    if (!queue_vars) {
00973       /* Delete queue from in-core list if it has been deleted in realtime. */
00974       if (q) {
00975          /*! \note Hmm, can't seem to distinguish a DB failure from a not
00976             found condition... So we might delete an in-core queue
00977             in case of DB failure. */
00978          ast_log(LOG_DEBUG, "Queue %s not found in realtime.\n", queuename);
00979 
00980          q->dead = 1;
00981          /* Delete if unused (else will be deleted when last caller leaves). */
00982          if (!q->count) {
00983             /* Delete. */
00984             if (!prev_q) {
00985                queues = q->next;
00986             } else {
00987                prev_q->next = q->next;
00988             }
00989             ast_mutex_unlock(&q->lock);
00990             destroy_queue(q);
00991          } else
00992             ast_mutex_unlock(&q->lock);
00993       }
00994       return NULL;
00995    }
00996 
00997    /* Create a new queue if an in-core entry does not exist yet. */
00998    if (!q) {
00999       q = alloc_queue(queuename);
01000       if (!q)
01001          return NULL;
01002       ast_mutex_lock(&q->lock);
01003       clear_queue(q);
01004       q->realtime = 1;
01005       q->next = queues;
01006       queues = q;
01007    }
01008    init_queue(q);    /* Ensure defaults for all parameters not set explicitly. */
01009 
01010    v = queue_vars;
01011    memset(tmpbuf, 0, sizeof(tmpbuf));
01012    while(v) {
01013       /* Convert to dashes `-' from underscores `_' as the latter are more SQL friendly. */
01014       if((tmp = strchr(v->name, '_')) != NULL) {
01015          ast_copy_string(tmpbuf, v->name, sizeof(tmpbuf));
01016          tmp_name = tmpbuf;
01017          tmp = tmp_name;
01018          while((tmp = strchr(tmp, '_')) != NULL)
01019             *tmp++ = '-';
01020       } else
01021          tmp_name = v->name;
01022       queue_set_param(q, tmp_name, v->value, -1, 0);
01023       v = v->next;
01024    }
01025 
01026    /* Temporarily set non-dynamic members dead so we can detect deleted ones. */
01027    m = q->members;
01028    while (m) {
01029       if (!m->dynamic)
01030          m->dead = 1;
01031       m = m->next;
01032    }
01033 
01034    interface = ast_category_browse(member_config, NULL);
01035    while (interface) {
01036       rt_handle_member_record(q, interface, ast_variable_retrieve(member_config, interface, "penalty"));
01037       interface = ast_category_browse(member_config, interface);
01038    }
01039 
01040    /* Delete all realtime members that have been deleted in DB. */
01041    m = q->members;
01042    prev_m = NULL;
01043    while (m) {
01044       next_m = m->next;
01045       if (m->dead) {
01046          if (prev_m) {
01047             prev_m->next = next_m;
01048          } else {
01049             q->members = next_m;
01050          }
01051          remove_from_interfaces(m->interface);
01052          free(m);
01053       } else {
01054          prev_m = m;
01055       }
01056       m = next_m;
01057    }
01058 
01059    ast_mutex_unlock(&q->lock);
01060 
01061    return q;
01062 }

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

Definition at line 891 of file app_queue.c.

References member::dynamic, free, member::interface, call_queue::members, member::next, and remove_from_interfaces().

Referenced by destroy_queue().

00892 {
00893    /* Free non-dynamic members */
00894    struct member *curm, *next, *prev = NULL;
00895 
00896    for (curm = q->members; curm; curm = next) {
00897       next = curm->next;
00898       if (all || !curm->dynamic) {
00899          if (prev)
00900             prev->next = next;
00901          else
00902             q->members = next;
00903          remove_from_interfaces(curm->interface);
00904          free(curm);
00905       } else 
00906          prev = curm;
00907    }
00908 }

static enum queue_member_status get_member_status struct call_queue q  )  [static]
 

Definition at line 435 of file app_queue.c.

References AST_DEVICE_INVALID, AST_DEVICE_UNAVAILABLE, ast_mutex_lock(), ast_mutex_unlock(), call_queue::lock, call_queue::members, member::next, member::paused, QUEUE_NO_MEMBERS, QUEUE_NO_REACHABLE_MEMBERS, QUEUE_NORMAL, result, and member::status.

Referenced by join_queue().

00436 {
00437    struct member *member;
00438    enum queue_member_status result = QUEUE_NO_MEMBERS;
00439 
00440    ast_mutex_lock(&q->lock);
00441    for (member = q->members; member; member = member->next) {
00442       if (member->paused) continue;
00443 
00444       switch (member->status) {
00445       case AST_DEVICE_INVALID:
00446          /* nothing to do */
00447          break;
00448       case AST_DEVICE_UNAVAILABLE:
00449          result = QUEUE_NO_REACHABLE_MEMBERS;
00450          break;
00451       default:
00452          ast_mutex_unlock(&q->lock);
00453          return QUEUE_NORMAL;
00454       }
00455    }
00456    
00457    ast_mutex_unlock(&q->lock);
00458    return result;
00459 }

static int handle_add_queue_member int  fd,
int  argc,
char *  argv[]
[static]
 

Definition at line 3792 of file app_queue.c.

References add_to_queue(), ast_cli(), member::interface, member::penalty, queue_persistent_members, RES_EXISTS, RES_NOSUCHQUEUE, RES_OKAY, RES_OUTOFMEMORY, RESULT_FAILURE, RESULT_SHOWUSAGE, and RESULT_SUCCESS.

03793 {
03794    char *queuename, *interface;
03795    int penalty;
03796 
03797    if ((argc != 6) && (argc != 8)) {
03798       return RESULT_SHOWUSAGE;
03799    } else if (strcmp(argv[4], "to")) {
03800       return RESULT_SHOWUSAGE;
03801    } else if ((argc == 8) && strcmp(argv[6], "penalty")) {
03802       return RESULT_SHOWUSAGE;
03803    }
03804 
03805    queuename = argv[5];
03806    interface = argv[3];
03807    if (argc == 8) {
03808       if (sscanf(argv[7], "%d", &penalty) == 1) {
03809          if (penalty < 0) {
03810             ast_cli(fd, "Penalty must be >= 0\n");
03811             penalty = 0;
03812          }
03813       } else {
03814          ast_cli(fd, "Penalty must be an integer >= 0\n");
03815          penalty = 0;
03816       }
03817    } else {
03818       penalty = 0;
03819    }
03820 
03821    switch (add_to_queue(queuename, interface, penalty, 0, queue_persistent_members)) {
03822    case RES_OKAY:
03823       ast_cli(fd, "Added interface '%s' to queue '%s'\n", interface, queuename);
03824       return RESULT_SUCCESS;
03825    case RES_EXISTS:
03826       ast_cli(fd, "Unable to add interface '%s' to queue '%s': Already there\n", interface, queuename);
03827       return RESULT_FAILURE;
03828    case RES_NOSUCHQUEUE:
03829       ast_cli(fd, "Unable to add interface to queue '%s': No such queue\n", queuename);
03830       return RESULT_FAILURE;
03831    case RES_OUTOFMEMORY:
03832       ast_cli(fd, "Out of memory\n");
03833       return RESULT_FAILURE;
03834    default:
03835       return RESULT_FAILURE;
03836    }
03837 }

static int handle_remove_queue_member int  fd,
int  argc,
char *  argv[]
[static]
 

Definition at line 3876 of file app_queue.c.

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

03877 {
03878    char *queuename, *interface;
03879 
03880    if (argc != 6) {
03881       return RESULT_SHOWUSAGE;
03882    } else if (strcmp(argv[4], "from")) {
03883       return RESULT_SHOWUSAGE;
03884    }
03885 
03886    queuename = argv[5];
03887    interface = argv[3];
03888 
03889    switch (remove_from_queue(queuename, interface)) {
03890    case RES_OKAY:
03891       ast_cli(fd, "Removed interface '%s' from queue '%s'\n", interface, queuename);
03892       return RESULT_SUCCESS;
03893    case RES_EXISTS:
03894       ast_cli(fd, "Unable to remove interface '%s' from queue '%s': Not there\n", interface, queuename);
03895       return RESULT_FAILURE;
03896    case RES_NOSUCHQUEUE:
03897       ast_cli(fd, "Unable to remove interface from queue '%s': No such queue\n", queuename);
03898       return RESULT_FAILURE;
03899    case RES_OUTOFMEMORY:
03900       ast_cli(fd, "Out of memory\n");
03901       return RESULT_FAILURE;
03902    default:
03903       return RESULT_FAILURE;
03904    }
03905 }

static void hangupcalls struct localuser outgoing,
struct ast_channel exception
[static]
 

Definition at line 1395 of file app_queue.c.

References ast_hangup(), localuser::chan, free, and localuser::next.

01396 {
01397    struct localuser *oo;
01398 
01399    while(outgoing) {
01400       /* Hangup any existing lines we have open */
01401       if (outgoing->chan && (outgoing->chan != exception))
01402          ast_hangup(outgoing->chan);
01403       oo = outgoing;
01404       outgoing=outgoing->next;
01405       free(oo);
01406    }
01407 }

static void init_queue struct call_queue q  )  [static]
 

Definition at line 603 of file app_queue.c.

References call_queue::announce, call_queue::announcefrequency, call_queue::announceholdtime, call_queue::context, call_queue::dead, DEFAULT_RETRY, call_queue::maxlen, call_queue::moh, call_queue::monfmt, call_queue::periodicannouncefrequency, call_queue::retry, call_queue::roundingseconds, call_queue::servicelevel, 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, and call_queue::timeout.

Referenced by reload_queues().

00604 {
00605    q->dead = 0;
00606    q->retry = DEFAULT_RETRY;
00607    q->timeout = -1;
00608    q->maxlen = 0;
00609    q->announcefrequency = 0;
00610    q->announceholdtime = 0;
00611    q->roundingseconds = 0; /* Default - don't announce seconds */
00612    q->servicelevel = 0;
00613    q->moh[0] = '\0';
00614    q->announce[0] = '\0';
00615    q->context[0] = '\0';
00616    q->monfmt[0] = '\0';
00617    q->periodicannouncefrequency = 0;
00618    ast_copy_string(q->sound_next, "queue-youarenext", sizeof(q->sound_next));
00619    ast_copy_string(q->sound_thereare, "queue-thereare", sizeof(q->sound_thereare));
00620    ast_copy_string(q->sound_calls, "queue-callswaiting", sizeof(q->sound_calls));
00621    ast_copy_string(q->sound_holdtime, "queue-holdtime", sizeof(q->sound_holdtime));
00622    ast_copy_string(q->sound_minutes, "queue-minutes", sizeof(q->sound_minutes));
00623    ast_copy_string(q->sound_seconds, "queue-seconds", sizeof(q->sound_seconds));
00624    ast_copy_string(q->sound_thanks, "queue-thankyou", sizeof(q->sound_thanks));
00625    ast_copy_string(q->sound_lessthan, "queue-less-than", sizeof(q->sound_lessthan));
00626    ast_copy_string(q->sound_reporthold, "queue-reporthold", sizeof(q->sound_reporthold));
00627    ast_copy_string(q->sound_periodicannounce, "queue-periodic-announce", sizeof(q->sound_periodicannounce));
00628 }

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

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

Referenced by join_queue().

00411 {
00412    struct queue_ent *cur;
00413 
00414    if (!q || !new)
00415       return;
00416    if (prev) {
00417       cur = prev->next;
00418       prev->next = new;
00419    } else {
00420       cur = q->head;
00421       q->head = new;
00422    }
00423    new->next = cur;
00424    new->parent = q;
00425    new->pos = ++(*pos);
00426    new->opos = *pos;
00427 }

static char* int2strat int  strategy  )  [static]
 

Definition at line 389 of file app_queue.c.

References name, and strategies.

Referenced by __queues_show().

00390 {
00391    int x;
00392    for (x=0;x<sizeof(strategies) / sizeof(strategies[0]);x++) {
00393       if (strategy == strategies[x].strategy)
00394          return strategies[x].name;
00395    }
00396    return "<unknown>";
00397 }

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

Definition at line 2468 of file app_queue.c.

References member::interface, call_queue::members, and member::next.

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

02469 {
02470    struct member *mem;
02471 
02472    if (q)
02473       for (mem = q->members; mem; mem = mem->next)
02474          if (!strcasecmp(interface, mem->interface))
02475             return mem;
02476 
02477    return NULL;
02478 }

static int interface_exists_global char *  interface  )  [static]
 

Definition at line 670 of file app_queue.c.

References ast_mutex_lock(), member::interface, call_queue::lock, call_queue::members, member::next, call_queue::next, and queues.

Referenced by remove_from_interfaces().

00671 {
00672    struct call_queue *q;
00673    struct member *mem;
00674    int ret = 0;
00675 
00676    if (!interface)
00677       return ret;
00678 
00679    ast_mutex_lock(&qlock);
00680    for (q = queues; q && !ret; q = q->next) {
00681       ast_mutex_lock(&q->lock);
00682       for (mem = q->members; mem && !ret; mem = mem->next) {
00683          if (!strcasecmp(interface, mem->interface))
00684             ret = 1;
00685       }
00686       ast_mutex_unlock(&q->lock);
00687    }
00688    ast_mutex_unlock(&qlock);
00689 
00690    return ret;
00691 }

static int is_our_turn struct queue_ent qe  )  [static]
 

Definition at line 2010 of file app_queue.c.

References ast_log(), queue_ent::chan, call_queue::head, LOG_DEBUG, ast_channel::name, option_debug, and queue_ent::parent.

Referenced by wait_our_turn().

02011 {
02012    struct queue_ent *ch;
02013    int res;
02014 
02015    /* Atomically read the parent head -- does not need a lock */
02016    ch = qe->parent->head;
02017    /* If we are now at the top of the head, break out */
02018    if (ch == qe) {
02019       if (option_debug)
02020          ast_log(LOG_DEBUG, "It's our turn (%s).\n", qe->chan->name);
02021       res = 1;
02022    } else {
02023       if (option_debug)
02024          ast_log(LOG_DEBUG, "It's not our turn (%s).\n", qe->chan->name);
02025       res = 0;
02026    }
02027    return res;
02028 }

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

Definition at line 1111 of file app_queue.c.

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

Referenced by queue_exec().

01112 {
01113    struct call_queue *q;
01114    struct queue_ent *cur, *prev = NULL;
01115    int res = -1;
01116    int pos = 0;
01117    int inserted = 0;
01118    enum queue_member_status stat;
01119 
01120    q = load_realtime_queue(queuename);
01121    if (!q)
01122       return res;
01123 
01124    ast_mutex_lock(&qlock);
01125    ast_mutex_lock(&q->lock);
01126 
01127    /* This is our one */
01128    stat = get_member_status(q);
01129    if (!q->joinempty && (stat == QUEUE_NO_MEMBERS))
01130       *reason = QUEUE_JOINEMPTY;
01131    else if ((q->joinempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS))
01132       *reason = QUEUE_JOINUNAVAIL;
01133    else if (q->maxlen && (q->count >= q->maxlen))
01134       *reason = QUEUE_FULL;
01135    else {
01136       /* There's space for us, put us at the right position inside
01137        * the queue. 
01138        * Take into account the priority of the calling user */
01139       inserted = 0;
01140       prev = NULL;
01141       cur = q->head;
01142       while(cur) {
01143          /* We have higher priority than the current user, enter
01144           * before him, after all the other users with priority
01145           * higher or equal to our priority. */
01146          if ((!inserted) && (qe->prio > cur->prio)) {
01147             insert_entry(q, prev, qe, &pos);
01148             inserted = 1;
01149          }
01150          cur->pos = ++pos;
01151          prev = cur;
01152          cur = cur->next;
01153       }
01154       /* No luck, join at the end of the queue */
01155       if (!inserted)
01156          insert_entry(q, prev, qe, &pos);
01157       ast_copy_string(qe->moh, q->moh, sizeof(qe->moh));
01158       ast_copy_string(qe->announce, q->announce, sizeof(qe->announce));
01159       ast_copy_string(qe->context, q->context, sizeof(qe->context));
01160       q->count++;
01161       res = 0;
01162       manager_event(EVENT_FLAG_CALL, "Join", 
01163                "Channel: %s\r\nCallerID: %s\r\nCallerIDName: %s\r\nQueue: %s\r\nPosition: %d\r\nCount: %d\r\n",
01164                qe->chan->name, 
01165                qe->chan->cid.cid_num ? qe->chan->cid.cid_num : "unknown",
01166                qe->chan->cid.cid_name ? qe->chan->cid.cid_name : "unknown",
01167                q->name, qe->pos, q->count );
01168 
01169       if (option_debug)
01170          ast_log(LOG_DEBUG, "Queue '%s' Join, Channel '%s', Position '%d'\n", q->name, qe->chan->name, qe->pos );
01171    }
01172    ast_mutex_unlock(&q->lock);
01173    ast_mutex_unlock(&qlock);
01174    return res;
01175 }

char* key void   ) 
 

Returns the ASTERISK_GPL_KEY.

This returns the ASTERISK_GPL_KEY, signifiying that you agree to the terms of the GPL stated in the ASTERISK_GPL_KEY. Your module will not load if it does not return the EXACT message:

 char *key(void) {
         return ASTERISK_GPL_KEY;
 }

Returns:
ASTERISK_GPL_KEY

Definition at line 4051 of file app_queue.c.

References ASTERISK_GPL_KEY.

04052 {
04053    return ASTERISK_GPL_KEY;
04054 }

static void leave_queue struct queue_ent qe  )  [static]
 

Definition at line 1350 of file app_queue.c.

References ast_log(), ast_mutex_lock(), ast_mutex_unlock(), queue_ent::chan, call_queue::count, call_queue::dead, destroy_queue(), EVENT_FLAG_CALL, call_queue::head, call_queue::lock, LOG_NOTICE, manager_event(), call_queue::name, ast_channel::name, queue_ent::next, queue_ent::parent, queue_ent::pos, and remove_queue().

01351 {
01352    struct call_queue *q;
01353    struct queue_ent *cur, *prev = NULL;
01354    int pos = 0;
01355 
01356    q = qe->parent;
01357    if (!q)
01358       return;
01359    ast_mutex_lock(&q->lock);
01360 
01361    prev = NULL;
01362    cur = q->head;
01363    while(cur) {
01364       if (cur == qe) {
01365          q->count--;
01366 
01367          /* Take us out of the queue */
01368          manager_event(EVENT_FLAG_CALL, "Leave",
01369             "Channel: %s\r\nQueue: %s\r\nCount: %d\r\n",
01370             qe->chan->name, q->name,  q->count);
01371 #if 0
01372 ast_log(LOG_NOTICE, "Queue '%s' Leave, Channel '%s'\n", q->name, qe->chan->name );
01373 #endif
01374          /* Take us out of the queue */
01375          if (prev)
01376             prev->next = cur->next;
01377          else
01378             q->head = cur->next;
01379       } else {
01380          /* Renumber the people after us in the queue based on a new count */
01381          cur->pos = ++pos;
01382          prev = cur;
01383       }
01384       cur = cur->next;
01385    }
01386    ast_mutex_unlock(&q->lock);
01387    if (q->dead && !q->count) {   
01388       /* It's dead and nobody is in it, so kill it */
01389       remove_queue(q);
01390       destroy_queue(q);
01391    }
01392 }

int load_module void   ) 
 

Initialize the module.

Initialize the Agents module. This function is being called by Asterisk when loading the module. Among other thing it registers applications, cli commands and reads the cofiguration file.

Returns:
int Always 0.

Definition at line 4002 of file app_queue.c.

References app, app_aqm, app_aqm_descrip, app_aqm_synopsis, app_pqm, app_pqm_descrip, app_pqm_synopsis, app_rqm, app_rqm_descrip, app_rqm_synopsis, app_upqm, app_upqm_descrip, app_upqm_synopsis, aqm_exec(), ast_cli_register(), ast_custom_function_register(), ast_devstate_add(), ast_manager_register, ast_register_application(), cli_add_queue_member, cli_remove_queue_member, cli_show_queue, cli_show_queues, descrip, EVENT_FLAG_AGENT, manager_add_queue_member(), manager_pause_queue_member(), manager_queues_show(), manager_queues_status(), manager_remove_queue_member(), pqm_exec(), queue_exec(), queue_persistent_members, queueagentcount_function, reload_queue_members(), reload_queues(), rqm_exec(), statechange_queue(), synopsis, and upqm_exec().

04003 {
04004    int res;
04005    
04006    res = ast_register_application(app, queue_exec, synopsis, descrip);
04007    res |= ast_cli_register(&cli_show_queue);
04008    res |= ast_cli_register(&cli_show_queues);
04009    res |= ast_cli_register(&cli_add_queue_member);
04010    res |= ast_cli_register(&cli_remove_queue_member);
04011    res |= ast_devstate_add(statechange_queue, NULL);
04012    res |= ast_manager_register( "Queues", 0, manager_queues_show, "Queues" );
04013    res |= ast_manager_register( "QueueStatus", 0, manager_queues_status, "Queue Status" );
04014    res |= ast_manager_register( "QueueAdd", EVENT_FLAG_AGENT, manager_add_queue_member, "Add interface to queue." );
04015    res |= ast_manager_register( "QueueRemove", EVENT_FLAG_AGENT, manager_remove_queue_member, "Remove interface from queue." );
04016    res |= ast_manager_register( "QueuePause", EVENT_FLAG_AGENT, manager_pause_queue_member, "Makes a queue member temporarily unavailable" );
04017    res |= ast_register_application(app_aqm, aqm_exec, app_aqm_synopsis, app_aqm_descrip) ;
04018    res |= ast_register_application(app_rqm, rqm_exec, app_rqm_synopsis, app_rqm_descrip) ;
04019    res |= ast_register_application(app_pqm, pqm_exec, app_pqm_synopsis, app_pqm_descrip) ;
04020    res |= ast_register_application(app_upqm, upqm_exec, app_upqm_synopsis, app_upqm_descrip) ;
04021    res |= ast_custom_function_register(&queueagentcount_function);
04022 
04023    if (!res) { 
04024       reload_queues();
04025       if (queue_persistent_members)
04026          reload_queue_members();
04027    }
04028 
04029    return res;
04030 }

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

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

References ast_mutex_lock(), call_queue::name, call_queue::next, and queues.

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

01065 {
01066    struct ast_variable *queue_vars = NULL;
01067    struct ast_config *member_config = NULL;
01068    struct call_queue *q;
01069 
01070    /* Find the queue in the in-core list first. */
01071    ast_mutex_lock(&qlock);
01072    for (q = queues; q; q = q->next) {
01073       if (!strcasecmp(q->name, queuename)) {
01074          break;
01075       }
01076    }
01077    ast_mutex_unlock(&qlock);
01078 
01079    if (!q || q->realtime) {
01080       /*! \note Load from realtime before taking the global qlock, to avoid blocking all
01081          queue operations while waiting for the DB.
01082 
01083          This will be two separate database transactions, so we might
01084          see queue parameters as they were before another process
01085          changed the queue and member list as it was after the change.
01086          Thus we might see an empty member list when a queue is
01087          deleted. In practise, this is unlikely to cause a problem. */
01088 
01089       queue_vars = ast_load_realtime("queues", "name", queuename, NULL);
01090       if (queue_vars) {
01091          member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", queuename, NULL);
01092          if (!member_config) {
01093             ast_log(LOG_ERROR, "no queue_members defined in your config (extconfig.conf).\n");
01094             return NULL;
01095          }
01096       }
01097 
01098       ast_mutex_lock(&qlock);
01099 
01100       q = find_queue_by_name_rt(queuename, queue_vars, member_config);
01101       if (member_config)
01102          ast_config_destroy(member_config);
01103       if (queue_vars)
01104          ast_variables_destroy(queue_vars);
01105 
01106       ast_mutex_unlock(&qlock);
01107    }
01108    return q;
01109 }

static int manager_add_queue_member struct mansession s,
struct message m
[static]
 

Definition at line 3688 of file app_queue.c.

References add_to_queue(), ast_strlen_zero(), ast_true(), astman_get_header(), astman_send_ack(), astman_send_error(), member::interface, member::paused, member::penalty, queue_persistent_members, RES_EXISTS, RES_NOSUCHQUEUE, RES_OKAY, RES_OUTOFMEMORY, and s.

Referenced by load_module().

03689 {
03690    char *queuename, *interface, *penalty_s, *paused_s;
03691    int paused, penalty = 0;
03692 
03693    queuename = astman_get_header(m, "Queue");
03694    interface = astman_get_header(m, "Interface");
03695    penalty_s = astman_get_header(m, "Penalty");
03696    paused_s = astman_get_header(m, "Paused");
03697 
03698    if (ast_strlen_zero(queuename)) {
03699       astman_send_error(s, m, "'Queue' not specified.");
03700       return 0;
03701    }
03702 
03703    if (ast_strlen_zero(interface)) {
03704       astman_send_error(s, m, "'Interface' not specified.");
03705       return 0;
03706    }
03707 
03708    if (ast_strlen_zero(penalty_s))
03709       penalty = 0;
03710    else if (sscanf(penalty_s, "%d", &penalty) != 1) {
03711       penalty = 0;
03712    }
03713 
03714    if (ast_strlen_zero(paused_s))
03715       paused = 0;
03716    else
03717       paused = abs(ast_true(paused_s));
03718 
03719    switch (add_to_queue(queuename, interface, penalty, paused, queue_persistent_members)) {
03720    case RES_OKAY:
03721       astman_send_ack(s, m, "Added interface to queue");
03722       break;
03723    case RES_EXISTS:
03724       astman_send_error(s, m, "Unable to add interface: Already there");
03725       break;
03726    case RES_NOSUCHQUEUE:
03727       astman_send_error(s, m, "Unable to add interface to queue: No such queue");
03728       break;
03729    case RES_OUTOFMEMORY:
03730       astman_send_error(s, m, "Out of memory");
03731       break;
03732    }
03733    return 0;
03734 }

static int manager_pause_queue_member struct mansession s,
struct message m
[static]
 

Definition at line 3765 of file app_queue.c.

References ast_strlen_zero(), ast_true(), astman_get_header(), astman_send_ack(), astman_send_error(), member::interface, member::paused, s, and set_member_paused().

Referenced by load_module().

03766 {
03767    char *queuename, *interface, *paused_s;
03768    int paused;
03769 
03770    interface = astman_get_header(m, "Interface");
03771    paused_s = astman_get_header(m, "Paused");
03772    queuename = astman_get_header(m, "Queue");   /* Optional - if not supplied, pause the given Interface in all queues */
03773 
03774    if (ast_strlen_zero(interface) || ast_strlen_zero(paused_s)) {
03775       astman_send_error(s, m, "Need 'Interface' and 'Paused' parameters.");
03776       return 0;
03777    }
03778 
03779    paused = abs(ast_true(paused_s));
03780 
03781    if (set_member_paused(queuename, interface, paused))
03782       astman_send_error(s, m, "Interface not found");
03783    else
03784       if (paused)
03785          astman_send_ack(s, m, "Interface paused successfully");
03786       else
03787          astman_send_ack(s, m, "Interface unpaused successfully");
03788 
03789    return 0;
03790 }

static int manager_queues_show struct mansession s,
struct message m
[static]
 

Definition at line 3589 of file app_queue.c.

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

Referenced by load_module().

03590 {
03591    char *a[] = { "show", "queues" };
03592    __queues_show(1, s->fd, 2, a, 0);
03593    ast_cli(s->fd, "\r\n\r\n");   /* Properly terminate Manager output */
03594 
03595    return RESULT_SUCCESS;
03596 } 

static int manager_queues_status struct mansession s,
struct message m
[static]
 

Definition at line 3599 of file app_queue.c.

References ast_cli(), ast_mutex_lock(), ast_strlen_zero(), astman_get_header(), astman_send_ack(), member::calls, call_queue::callsabandoned, call_queue::callscompleted, call_queue::callscompletedinsl, call_queue::count, member::dynamic, call_queue::holdtime, member::interface, member::lastcall, call_queue::lock, call_queue::maxlen, call_queue::members, call_queue::name, member::next, call_queue::next, member::paused, member::penalty, queues, s, call_queue::servicelevel, member::status, and call_queue::weight.

Referenced by load_module().

03600 {
03601    time_t now;
03602    int pos;
03603    char *id = astman_get_header(m,"ActionID");
03604    char *queuefilter = astman_get_header(m,"Queue");
03605    char *memberfilter = astman_get_header(m,"Member");
03606    char idText[256] = "";
03607    struct call_queue *q;
03608    struct queue_ent *qe;
03609    float sl = 0;
03610    struct member *mem;
03611 
03612    astman_send_ack(s, m, "Queue status will follow");
03613    time(&now);
03614    ast_mutex_lock(&qlock);
03615    if (!ast_strlen_zero(id)) {
03616       snprintf(idText,256,"ActionID: %s\r\n",id);
03617    }
03618    for (q = queues; q; q = q->next) {
03619       ast_mutex_lock(&q->lock);
03620 
03621       /* List queue properties */
03622       if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) {
03623          if(q->callscompleted > 0)
03624             sl = 100*((float)q->callscompletedinsl/(float)q->callscompleted);
03625          ast_cli(s->fd, "Event: QueueParams\r\n"
03626                   "Queue: %s\r\n"
03627                   "Max: %d\r\n"
03628                   "Calls: %d\r\n"
03629                   "Holdtime: %d\r\n"
03630                   "Completed: %d\r\n"
03631                   "Abandoned: %d\r\n"
03632                   "ServiceLevel: %d\r\n"
03633                   "ServicelevelPerf: %2.1f\r\n"
03634                   "Weight: %d\r\n"
03635                   "%s"
03636                   "\r\n",
03637                      q->name, q->maxlen, q->count, q->holdtime, q->callscompleted,
03638                      q->callsabandoned, q->servicelevel, sl, q->weight, idText);
03639          /* List Queue Members */
03640          for (mem = q->members; mem; mem = mem->next) {
03641             if (ast_strlen_zero(memberfilter) || !strcmp(mem->interface, memberfilter)) {
03642                ast_cli(s->fd, "Event: QueueMember\r\n"
03643                   "Queue: %s\r\n"
03644                   "Location: %s\r\n"
03645                   "Membership: %s\r\n"
03646                   "Penalty: %d\r\n"
03647                   "CallsTaken: %d\r\n"
03648                   "LastCall: %d\r\n"
03649                   "Status: %d\r\n"
03650                   "Paused: %d\r\n"
03651                   "%s"
03652                   "\r\n",
03653                      q->name, mem->interface, mem->dynamic ? "dynamic" : "static",
03654                      mem->penalty, mem->calls, (int)mem->lastcall, mem->status, mem->paused, idText);
03655             }
03656          }
03657          /* List Queue Entries */
03658          pos = 1;
03659          for (qe = q->head; qe; qe = qe->next) {
03660             ast_cli(s->fd, "Event: QueueEntry\r\n"
03661                "Queue: %s\r\n"
03662                "Position: %d\r\n"
03663                "Channel: %s\r\n"
03664                "CallerID: %s\r\n"
03665                "CallerIDName: %s\r\n"
03666                "Wait: %ld\r\n"
03667                "%s"
03668                "\r\n", 
03669                   q->name, pos++, qe->chan->name, 
03670                   qe->chan->cid.cid_num ? qe->chan->cid.cid_num : "unknown",
03671                   qe->chan->cid.cid_name ? qe->chan->cid.cid_name : "unknown",
03672                   (long)(now - qe->start), idText);
03673          }
03674       }
03675       ast_mutex_unlock(&q->lock);
03676    }
03677 
03678    ast_cli(s->fd,
03679       "Event: QueueStatusComplete\r\n"
03680       "%s"
03681       "\r\n",idText);
03682 
03683    ast_mutex_unlock(&qlock);
03684 
03685    return RESULT_SUCCESS;
03686 }

static int manager_remove_queue_member struct mansession s,
struct message m
[static]
 

Definition at line 3736 of file app_queue.c.

References ast_strlen_zero(), astman_get_header(), astman_send_ack(), astman_send_error(), member::interface, remove_from_queue(), RES_EXISTS, RES_NOSUCHQUEUE, RES_OKAY, RES_OUTOFMEMORY, and s.

Referenced by load_module().

03737 {
03738    char *queuename, *interface;
03739 
03740    queuename = astman_get_header(m, "Queue");
03741    interface = astman_get_header(m, "Interface");
03742 
03743    if (ast_strlen_zero(queuename) || ast_strlen_zero(interface)) {
03744       astman_send_error(s, m, "Need 'Queue' and 'Interface' parameters.");
03745       return 0;
03746    }
03747 
03748    switch (remove_from_queue(queuename, interface)) {
03749    case RES_OKAY:
03750       astman_send_ack(s, m, "Removed interface from queue");
03751       break;
03752    case RES_EXISTS:
03753       astman_send_error(s, m, "Unable to remove interface: Not there");
03754       break;
03755    case RES_NOSUCHQUEUE:
03756       astman_send_error(s, m, "Unable to remove interface from queue: No such queue");
03757       break;
03758    case RES_OUTOFMEMORY:
03759       astman_send_error(s, m, "Out of memory");
03760       break;
03761    }
03762    return 0;
03763 }

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

Definition at line 1177 of file app_queue.c.

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

Referenced by say_position().

01178 {
01179    int res;
01180 
01181    ast_stopstream(chan);
01182    res = ast_streamfile(chan, filename, chan->language);
01183 
01184    if (!res)
01185       res = ast_waitstream(chan, AST_DIGIT_ANY);
01186    else
01187       res = 0;
01188 
01189    ast_stopstream(chan);
01190 
01191    return res;
01192 }

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

Definition at line 2752 of file app_queue.c.

References AST_APP_ARG, AST_DECLARE_APP_ARGS, ast_goto_if_exists(), ast_log(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), localuser::chan, ast_channel::context, localuser::interface, LOCAL_USER_ADD, LOCAL_USER_REMOVE, LOG_WARNING, option_priority_jumping, parse(), pbx_builtin_setvar_helper(), and set_member_paused().

Referenced by load_module().

02753 {
02754    struct localuser *u;
02755    char *parse;
02756    int priority_jump = 0;
02757    AST_DECLARE_APP_ARGS(args,
02758       AST_APP_ARG(queuename);
02759       AST_APP_ARG(interface);
02760       AST_APP_ARG(options);
02761    );
02762 
02763    if (ast_strlen_zero(data)) {
02764       ast_log(LOG_WARNING, "PauseQueueMember requires an argument ([queuename]|interface[|options])\n");
02765       return -1;
02766    }
02767 
02768    LOCAL_USER_ADD(u);
02769 
02770    if (!(parse = ast_strdupa(data))) {
02771       ast_log(LOG_WARNING, "Memory Error!\n");
02772       LOCAL_USER_REMOVE(u);
02773       return -1;
02774    }
02775 
02776    AST_STANDARD_APP_ARGS(args, parse);
02777 
02778    if (args.options) {
02779       if (strchr(args.options, 'j'))
02780          priority_jump = 1;
02781    }
02782 
02783    if (ast_strlen_zero(args.interface)) {
02784       ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename]|interface[|options])\n");
02785       LOCAL_USER_REMOVE(u);
02786       return -1;
02787    }
02788 
02789    if (set_member_paused(args.queuename, args.interface, 1)) {
02790       ast_log(LOG_WARNING, "Attempt to pause interface %s, not found\n", args.interface);
02791       if (priority_jump || option_priority_jumping) {
02792          if (ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101)) {
02793             pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND");
02794             LOCAL_USER_REMOVE(u);
02795             return 0;
02796          }
02797       }
02798       LOCAL_USER_REMOVE(u);
02799       pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND");
02800       return -1;
02801    }
02802 
02803    LOCAL_USER_REMOVE(u);
02804    pbx_builtin_setvar_helper(chan, "PQMSTATUS", "PAUSED");
02805    return 0;
02806 }

static int queue_exec struct ast_channel chan,
void *  data
[static]
 

Definition at line 3007 of file app_queue.c.

References AST_CONTROL_RINGING, ast_indicate(), ast_log(), ast_moh_start(), ast_queue_log(), ast_strlen_zero(), ast_verbose(), queue_ent::chan, ast_channel::cid, ast_callerid::cid_num, join_queue(), LOCAL_USER_ADD, LOG_DEBUG, LOG_WARNING, ast_channel::name, option_debug, option_verbose, pbx_builtin_getvar_helper(), QUEUE_UNKNOWN, record_abandoned(), strsep(), ast_channel::uniqueid, valid_exit(), VERBOSE_PREFIX_3, and wait_our_turn().

Referenced by load_module().

03008 {
03009    int res=-1;
03010    int ringing=0;
03011    struct localuser *u;
03012    char *queuename;
03013    char info[512];
03014    char *info_ptr = info;
03015    char *options = NULL;
03016    char *url = NULL;
03017    char *announceoverride = NULL;
03018    char *user_priority;
03019    int prio;
03020    char *queuetimeoutstr = NULL;
03021    enum queue_result reason = QUEUE_UNKNOWN;
03022 
03023    /* whether to exit Queue application after the timeout hits */
03024    int go_on = 0;
03025 
03026    /* Our queue entry */
03027    struct queue_ent qe;
03028    
03029    if (ast_strlen_zero(data)) {
03030       ast_log(LOG_WARNING, "Queue requires an argument: queuename[|options[|URL][|announceoverride][|timeout]]\n");
03031       return -1;
03032    }
03033 
03034    LOCAL_USER_ADD(u);
03035 
03036    /* Setup our queue entry */
03037    memset(&qe, 0, sizeof(qe));
03038    qe.start = time(NULL);
03039    
03040    /* Parse our arguments XXX Check for failure XXX */
03041    ast_copy_string(info, (char *) data, sizeof(info));
03042    queuename = strsep(&info_ptr, "|");
03043    options = strsep(&info_ptr, "|");
03044    url = strsep(&info_ptr, "|");
03045    announceoverride = strsep(&info_ptr, "|");
03046    queuetimeoutstr = info_ptr;
03047 
03048    /* set the expire time based on the supplied timeout; */
03049    if (!ast_strlen_zero(queuetimeoutstr))
03050       qe.expire = qe.start + atoi(queuetimeoutstr);
03051    else
03052       qe.expire = 0;
03053 
03054    /* Get the priority from the variable ${QUEUE_PRIO} */
03055    user_priority = pbx_builtin_getvar_helper(chan, "QUEUE_PRIO");
03056    if (user_priority) {
03057       if (sscanf(user_priority, "%d", &prio) == 1) {
03058          if (option_debug)
03059             ast_log(LOG_DEBUG, "%s: Got priority %d from ${QUEUE_PRIO}.\n",
03060                chan->name, prio);
03061       } else {
03062          ast_log(LOG_WARNING, "${QUEUE_PRIO}: Invalid value (%s), channel %s.\n",
03063             user_priority, chan->name);
03064          prio = 0;
03065       }
03066    } else {
03067       if (option_debug > 2)
03068          ast_log(LOG_DEBUG, "NO QUEUE_PRIO variable found. Using default.\n");
03069       prio = 0;
03070    }
03071 
03072    if (options && (strchr(options, 'r')))
03073       ringing = 1;
03074 
03075    if (option_debug)  
03076       ast_log(LOG_DEBUG, "queue: %s, options: %s, url: %s, announce: %s, expires: %ld, priority: %d\n",
03077          queuename, options, url, announceoverride, (long)qe.expire, (int)prio);
03078 
03079    qe.chan = chan;
03080    qe.prio = (int)prio;
03081    qe.last_pos_said = 0;
03082    qe.last_pos = 0;
03083    qe.last_periodic_announce_time = time(NULL);
03084    if (!join_queue(queuename, &qe, &reason)) {
03085       ast_queue_log(queuename, chan->uniqueid, "NONE", "ENTERQUEUE", "%s|%s", url ? url : "",
03086                chan->cid.cid_num ? chan->cid.cid_num : "");
03087 check_turns:
03088       if (ringing) {
03089          ast_indicate(chan, AST_CONTROL_RINGING);
03090       } else {
03091          ast_moh_start(chan, qe.moh);
03092       }
03093       for (;;) {
03094          /* This is the wait loop for callers 2 through maxlen */
03095 
03096          res = wait_our_turn(&qe, ringing, &reason);
03097          /* If they hungup, return immediately */
03098          if (res < 0) {
03099             /* Record this abandoned call */
03100             record_abandoned(&qe);
03101             ast_queue_log(queuename, chan->uniqueid, "NONE", "ABANDON", "%d|%d|%ld", qe.pos, qe.opos, (long)time(NULL) - qe.start);
03102             if (option_verbose > 2) {
03103                ast_verbose(VERBOSE_PREFIX_3 "User disconnected from queue %s while waiting their turn\n", queuename);
03104             }
03105             res = -1;
03106             break;
03107          }
03108          if (!res) 
03109             break;
03110          if (valid_exit(&qe, res)) {
03111             ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%s|%d", qe.digits, qe.pos);
03112             break;
03113          }
03114       }
03115       if (!res) {
03116          int makeannouncement = 0;
03117          for (;;) {
03118             /* This is the wait loop for the head caller*/
03119             /* To exit, they may get their call answered; */
03120             /* they may dial a digit from the queue context; */
03121             /* or, they may timeout. */
03122 
03123             enum queue_member_status stat;
03124 
03125             /* Leave if we have exceeded our queuetimeout */
03126             if (qe.expire && (time(NULL) > qe.expire)) {
03127                record_abandoned(&qe);
03128                reason = QUEUE_TIMEOUT;
03129                res = 0;
03130                ast_queue_log(queuename, chan->uniqueid,"NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
03131                break;
03132             }
03133 
03134             if (makeannouncement) {
03135                /* Make a position announcement, if enabled */
03136                if (qe.parent->announcefrequency && !ringing &&
03137                    (res = say_position(&qe))) {
03138                   ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%s|%d", qe.digits, qe.pos);
03139                   break;
03140                }
03141 
03142             }
03143             makeannouncement = 1;
03144 
03145             /* Make a periodic announcement, if enabled */
03146             if (qe.parent->periodicannouncefrequency && !ringing &&
03147                 (res = say_periodic_announcement(&qe))) {
03148                ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%c|%d", res, qe.pos);
03149                break;
03150             }
03151 
03152             /* Try calling all queue members for 'timeout' seconds */
03153             res = try_calling(&qe, options, announceoverride, url, &go_on);
03154             if (res) {
03155                if (res < 0) {
03156                   if (!qe.handled) {
03157                      record_abandoned(&qe);
03158                      ast_queue_log(queuename, chan->uniqueid, "NONE", "ABANDON", "%d|%d|%ld", qe.pos, qe.opos, (long)time(NULL) - qe.start);
03159                   }
03160                } else if (valid_exit(&qe, res)) {
03161                   ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%s|%d", qe.digits, qe.pos);
03162                }
03163                break;
03164             }
03165 
03166             stat = get_member_status(qe.parent);
03167 
03168             /* leave the queue if no agents, if enabled */
03169             if (qe.parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) {
03170                record_abandoned(&qe);
03171                reason = QUEUE_LEAVEEMPTY;
03172                res = 0;
03173                break;
03174             }
03175 
03176             /* leave the queue if no reachable agents, if enabled */
03177             if ((qe.parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) {
03178                record_abandoned(&qe);
03179                reason = QUEUE_LEAVEUNAVAIL;
03180                res = 0;
03181                break;
03182             }
03183 
03184             /* Leave if we have exceeded our queuetimeout */
03185             if (qe.expire && (time(NULL) > qe.expire)) {
03186                record_abandoned(&qe);
03187                reason = QUEUE_TIMEOUT;
03188                res = 0;
03189                ast_queue_log(queuename, chan->uniqueid,"NONE", "EXITWITHTIMEOUT", "%d", qe.pos);   
03190                break;
03191             }
03192 
03193             /* OK, we didn't get anybody; wait for 'retry' seconds; may get a digit to exit with */
03194             res = wait_a_bit(&qe);
03195             if (res < 0) {
03196                record_abandoned(&qe);
03197                ast_queue_log(queuename, chan->uniqueid, "NONE", "ABANDON", "%d|%d|%ld", qe.pos, qe.opos, (long)time(NULL) - qe.start);
03198                if (option_verbose > 2) {
03199                   ast_verbose(VERBOSE_PREFIX_3 "User disconnected from queue %s when they almost made it\n", queuename);
03200                }
03201                res = -1;
03202                break;
03203             }
03204             if (res && valid_exit(&qe, res)) {
03205                ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%s|%d", qe.digits, qe.pos);
03206                break;
03207             }
03208             /* exit after 'timeout' cycle if 'n' option enabled */
03209             if (go_on) {
03210                if (option_verbose > 2)
03211                   ast_verbose(VERBOSE_PREFIX_3 "Exiting on time-out cycle\n");
03212                ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
03213                record_abandoned(&qe);
03214                reason = QUEUE_TIMEOUT;
03215                res = 0;
03216                break;
03217             }
03218             /* Since this is a priority queue and 
03219              * it is not sure that we are still at the head
03220              * of the queue, go and check for our turn again.
03221              */
03222             if (!is_our_turn(&qe)) {
03223                if (option_debug)
03224                   ast_log(LOG_DEBUG, "Darn priorities, going back in queue (%s)!\n",
03225                      qe.chan->name);
03226                goto check_turns;
03227             }
03228          }
03229       }
03230       /* Don't allow return code > 0 */
03231       if (res >= 0 && res != AST_PBX_KEEPALIVE) {
03232          res = 0; 
03233          if (ringing) {
03234             ast_indicate(chan, -1);
03235          } else {
03236             ast_moh_stop(chan);
03237          }        
03238          ast_stopstream(chan);
03239       }
03240       leave_queue(&qe);
03241       if (reason != QUEUE_UNKNOWN)
03242          set_queue_result(chan, reason);
03243    } else {
03244       ast_log(LOG_WARNING, "Unable to join queue '%s'\n", queuename);
03245       set_queue_result(chan, reason);
03246       res = 0;
03247    }
03248    LOCAL_USER_REMOVE(u);
03249    return res;
03250 }

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

Definition at line 3252 of file app_queue.c.

References ast_log(), ast_mutex_lock(), ast_strlen_zero(), LOCAL_USER_ACF_ADD, LOCAL_USER_REMOVE, call_queue::lock, LOG_ERROR, call_queue::name, call_queue::next, and queues.

03253 {
03254    int count = 0;
03255    struct call_queue *q;
03256    struct localuser *u;
03257    struct member *m;
03258 
03259    LOCAL_USER_ACF_ADD(u);
03260 
03261    ast_copy_string(buf, "0", len);
03262    
03263    if (ast_strlen_zero(data)) {
03264       ast_log(LOG_ERROR, "QUEUEAGENTCOUNT requires an argument: queuename\n");
03265       LOCAL_USER_REMOVE(u);
03266       return buf;
03267    }
03268 
03269    ast_mutex_lock(&qlock);
03270 
03271    /* Find the right queue */
03272    for (q = queues; q; q = q->next) {
03273       if (!strcasecmp(q->name, data)) {
03274          ast_mutex_lock(&q->lock);
03275          break;
03276       }
03277    }
03278 
03279    ast_mutex_unlock(&qlock);
03280 
03281    if (q) {
03282       for (m = q->members; m; m = m->next) {
03283          /* Count the agents who are logged in and presently answering calls */
03284          if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
03285             count++;
03286          }
03287       }
03288       ast_mutex_unlock(&q->lock);
03289    }
03290 
03291    snprintf(buf, len, "%d", count);
03292    LOCAL_USER_REMOVE(u);
03293    return buf;
03294 }

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

References call_queue::announce, call_queue::announcefrequency, call_queue::announceholdtime, ANNOUNCEHOLDTIME_ALWAYS, ANNOUNCEHOLDTIME_ONCE, ast_log(), ast_true(), call_queue::context, DEFAULT_RETRY, DEFAULT_TIMEOUT, call_queue::eventwhencalled, call_queue::joinempty, call_queue::leavewhenempty, LOG_WARNING, call_queue::maskmemberstatus, call_queue::maxlen, call_queue::memberdelay, call_queue::moh, call_queue::monfmt, call_queue::monjoin, call_queue::name, call_queue::periodicannouncefrequency, QUEUE_EMPTY_NORMAL, QUEUE_EMPTY_STRICT, call_queue::reportholdtime, call_queue::retry, call_queue::roundingseconds, call_queue::servicelevel, 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, use_weight, call_queue::weight, and call_queue::wrapuptime.

Referenced by reload_queues().

00736 {
00737    if (!strcasecmp(param, "music") || !strcasecmp(param, "musiconhold")) {
00738       ast_copy_string(q->moh, val, sizeof(q->moh));
00739    } else if (!strcasecmp(param, "announce")) {
00740       ast_copy_string(q->announce, val, sizeof(q->announce));
00741    } else if (!strcasecmp(param, "context")) {
00742       ast_copy_string(q->context, val, sizeof(q->context));
00743    } else if (!strcasecmp(param, "timeout")) {
00744       q->timeout = atoi(val);
00745       if (q->timeout < 0)
00746          q->timeout = DEFAULT_TIMEOUT;
00747    } else if (!strcasecmp(param, "monitor-join")) {
00748       q->monjoin = ast_true(val);
00749    } else if (!strcasecmp(param, "monitor-format")) {
00750       ast_copy_string(q->monfmt, val, sizeof(q->monfmt));
00751    } else if (!strcasecmp(param, "queue-youarenext")) {
00752       ast_copy_string(q->sound_next, val, sizeof(q->sound_next));
00753    } else if (!strcasecmp(param, "queue-thereare")) {
00754       ast_copy_string(q->sound_thereare, val, sizeof(q->sound_thereare));
00755    } else if (!strcasecmp(param, "queue-callswaiting")) {
00756       ast_copy_string(q->sound_calls, val, sizeof(q->sound_calls));
00757    } else if (!strcasecmp(param, "queue-holdtime")) {
00758       ast_copy_string(q->sound_holdtime, val, sizeof(q->sound_holdtime));
00759    } else if (!strcasecmp(param, "queue-minutes")) {
00760       ast_copy_string(q->sound_minutes, val, sizeof(q->sound_minutes));
00761    } else if (!strcasecmp(param, "queue-seconds")) {
00762       ast_copy_string(q->sound_seconds, val, sizeof(q->sound_seconds));
00763    } else if (!strcasecmp(param, "queue-lessthan")) {
00764       ast_copy_string(q->sound_lessthan, val, sizeof(q->sound_lessthan));
00765    } else if (!strcasecmp(param, "queue-thankyou")) {
00766       ast_copy_string(q->sound_thanks, val, sizeof(q->sound_thanks));
00767    } else if (!strcasecmp(param, "queue-reporthold")) {
00768       ast_copy_string(q->sound_reporthold, val, sizeof(q->sound_reporthold));
00769    } else if (!strcasecmp(param, "announce-frequency")) {
00770       q->announcefrequency = atoi(val);
00771    } else if (!strcasecmp(param, "announce-round-seconds")) {
00772       q->roundingseconds = atoi(val);
00773       if (q->roundingseconds>60 || q->roundingseconds<0) {
00774          if (linenum >= 0) {
00775             ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
00776                "using 0 instead for queue '%s' at line %d of queues.conf\n",
00777                val, param, q->name, linenum);
00778          } else {
00779             ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
00780                "using 0 instead for queue '%s'\n", val, param, q->name);
00781          }
00782          q->roundingseconds=0;
00783       }
00784    } else if (!strcasecmp(param, "announce-holdtime")) {
00785       if (!strcasecmp(val, "once"))
00786          q->announceholdtime = ANNOUNCEHOLDTIME_ONCE;
00787       else if (ast_true(val))
00788          q->announceholdtime = ANNOUNCEHOLDTIME_ALWAYS;
00789       else
00790          q->announceholdtime = 0;
00791     } else if (!strcasecmp(param, "periodic-announce")) {
00792       ast_copy_string(q->sound_periodicannounce, val, sizeof(q->sound_periodicannounce));
00793    } else if (!strcasecmp(param, "periodic-announce-frequency")) {
00794       q->periodicannouncefrequency = atoi(val);
00795    } else if (!strcasecmp(param, "retry")) {
00796       q->retry = atoi(val);
00797       if (q->retry <= 0)
00798          q->retry = DEFAULT_RETRY;
00799    } else if (!strcasecmp(param, "wrapuptime")) {
00800       q->wrapuptime = atoi(val);
00801    } else if (!strcasecmp(param, "maxlen")) {
00802       q->maxlen = atoi(val);
00803       if (q->maxlen < 0)
00804          q->maxlen = 0;
00805    } else if (!strcasecmp(param, "servicelevel")) {
00806       q->servicelevel= atoi(val);
00807    } else if (!strcasecmp(param, "strategy")) {
00808       q->strategy = strat2int(val);
00809       if (q->strategy < 0) {
00810          ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
00811             val, q->name);
00812          q->strategy = 0;
00813       }
00814    } else if (!strcasecmp(param, "joinempty")) {
00815       if (!strcasecmp(val, "strict"))
00816          q->joinempty = QUEUE_EMPTY_STRICT;
00817       else if (ast_true(val))
00818          q->joinempty = QUEUE_EMPTY_NORMAL;
00819       else
00820          q->joinempty = 0;
00821    } else if (!strcasecmp(param, "leavewhenempty")) {
00822       if (!strcasecmp(val, "strict"))
00823          q->leavewhenempty = QUEUE_EMPTY_STRICT;
00824       else if (ast_true(val))
00825          q->leavewhenempty = QUEUE_EMPTY_NORMAL;
00826       else
00827          q->leavewhenempty = 0;
00828    } else if (!strcasecmp(param, "eventmemberstatus")) {
00829       q->maskmemberstatus = !ast_true(val);
00830    } else if (!strcasecmp(param, "eventwhencalled")) {
00831       q->eventwhencalled = ast_true(val);
00832    } else if (!strcasecmp(param, "reportholdtime")) {
00833       q->reportholdtime = ast_true(val);
00834    } else if (!strcasecmp(param, "memberdelay")) {
00835       q->memberdelay = atoi(val);
00836    } else if (!strcasecmp(param, "weight")) {
00837       q->weight = atoi(val);
00838       if (q->weight)
00839          use_weight++;
00840       /* With Realtime queues, if the last queue using weights is deleted in realtime,
00841          we will not see any effect on use_weight until next reload. */
00842    } else if (!strcasecmp(param, "timeoutrestart")) {
00843       q->timeoutrestart = ast_true(val);
00844    } else if(failunknown) {
00845       if (linenum >= 0) {
00846          ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s at line %d of queues.conf\n",
00847             q->name, param, linenum);
00848       } else {
00849          ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s\n", q->name, param);
00850       }
00851    }
00852 }

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

Definition at line 3565 of file app_queue.c.

References __queues_show().

03566 {
03567    return __queues_show(0, fd, argc, argv, 1);
03568 }

static int queues_show int  fd,
int  argc,
char **  argv
[static]
 

Definition at line 3560 of file app_queue.c.

References __queues_show().

03561 {
03562    return __queues_show(0, fd, argc, argv, 0);
03563 }

static void recalc_holdtime struct queue_ent qe  )  [static]
 

Definition at line 1331 of file app_queue.c.

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

01332 {
01333    int oldvalue, newvalue;
01334 
01335    /* Calculate holdtime using a recursive boxcar filter */
01336    /* Thanks to SRT for this contribution */
01337    /* 2^2 (4) is the filter coefficient; a higher exponent would give old entries more weight */
01338 
01339    newvalue = time(NULL) - qe->start;
01340 
01341    ast_mutex_lock(&qe->parent->lock);
01342    if (newvalue <= qe->parent->servicelevel)
01343       qe->parent->callscompletedinsl++;
01344    oldvalue = qe->parent->holdtime;
01345    qe->parent->holdtime = (((oldvalue << 2) - oldvalue) + newvalue) >> 2;
01346    ast_mutex_unlock(&qe->parent->lock);
01347 }

static void record_abandoned struct queue_ent qe  )  [static]
 

Definition at line 1747 of file app_queue.c.

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

Referenced by queue_exec().

01748 {
01749    ast_mutex_lock(&qe->parent->lock);
01750    qe->parent->callsabandoned++;
01751    ast_mutex_unlock(&qe->parent->lock);
01752 }

int reload void   ) 
 

Reload stuff.

This function is where any reload routines take place. Re-read config files, change signalling, whatever is appropriate on a reload.

Returns:
The return value is not used.

Definition at line 4033 of file app_queue.c.

References reload_queues().

04034 {
04035    reload_queues();
04036    return 0;
04037 }

static void reload_queue_members void   )  [static]
 

Definition at line 2663 of file app_queue.c.

References ast_db_gettree(), ast_mutex_lock(), ast_mutex_unlock(), member::interface, ast_db_entry::key, call_queue::lock, call_queue::name, call_queue::next, ast_db_entry::next, member::paused, member::penalty, pm_family, PM_MAX_LEN, and queues.

Referenced by load_module().

02664 {
02665    char *cur_ptr; 
02666    char *queue_name;
02667    char *member;
02668    char *interface;
02669    char *penalty_tok;
02670    int penalty = 0;
02671    char *paused_tok;
02672    int paused = 0;
02673    struct ast_db_entry *db_tree;
02674    struct ast_db_entry *entry;
02675    struct call_queue *cur_queue;
02676    char queue_data[PM_MAX_LEN];
02677 
02678    ast_mutex_lock(&qlock);
02679 
02680    /* Each key in 'pm_family' is the name of a queue */
02681    db_tree = ast_db_gettree(pm_family, NULL);
02682    for (entry = db_tree; entry; entry = entry->next) {
02683 
02684       queue_name = entry->key + strlen(pm_family) + 2;
02685 
02686       cur_queue = queues;
02687       while (cur_queue) {
02688          ast_mutex_lock(&cur_queue->lock);
02689          if (!strcmp(queue_name, cur_queue->name))
02690             break;
02691          ast_mutex_unlock(&cur_queue->lock);
02692          cur_queue = cur_queue->next;
02693       }
02694 
02695       if (!cur_queue) {
02696          /* If the queue no longer exists, remove it from the
02697           * database */
02698          ast_db_del(pm_family, queue_name);
02699          continue;
02700       } else
02701          ast_mutex_unlock(&cur_queue->lock);
02702 
02703       if (ast_db_get(pm_family, queue_name, queue_data, PM_MAX_LEN))
02704          continue;
02705 
02706       cur_ptr = queue_data;
02707       while ((member = strsep(&cur_ptr, "|"))) {
02708          if (ast_strlen_zero(member))
02709             continue;
02710 
02711          interface = strsep(&member, ";");
02712          penalty_tok = strsep(&member, ";");
02713          paused_tok = strsep(&member, ";");
02714 
02715          if (!penalty_tok) {
02716             ast_log(LOG_WARNING, "Error parsing persisent member string for '%s' (penalty)\n", queue_name);
02717             break;
02718          }
02719          penalty = strtol(penalty_tok, NULL, 10);
02720          if (errno == ERANGE) {
02721             ast_log(LOG_WARNING, "Error converting penalty: %s: Out of range.\n", penalty_tok);
02722             break;
02723          }
02724          
02725          if (!paused_tok) {
02726             ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (paused)\n", queue_name);
02727             break;
02728          }
02729          paused = strtol(paused_tok, NULL, 10);
02730          if ((errno == ERANGE) || paused < 0 || paused > 1) {
02731             ast_log(LOG_WARNING, "Error converting paused: %s: Expected 0 or 1.\n", paused_tok);
02732             break;
02733          }
02734 
02735          if (option_debug)
02736             ast_log(LOG_DEBUG, "Reload Members: Queue: %s  Member: %s  Penalty: %d  Paused: %d\n", queue_name, interface, penalty, paused);
02737          
02738          if (add_to_queue(queue_name, interface, penalty, paused, 0) == RES_OUTOFMEMORY) {
02739             ast_log(LOG_ERROR, "Out of Memory when reloading persistent queue member\n");
02740             break;
02741          }
02742       }
02743    }
02744 
02745    ast_mutex_unlock(&qlock);
02746    if (db_tree) {
02747       ast_log(LOG_NOTICE, "Queue members sucessfully reloaded from database.\n");
02748       ast_db_freetree(db_tree);
02749    }
02750 }

static void reload_queues void   )  [static]
 

Definition at line 3303 of file app_queue.c.

References add_to_interfaces(), alloc_queue(), ast_category_browse(), ast_config_destroy(), ast_config_load(), ast_device_state(), ast_log(), ast_mutex_lock(), ast_mutex_unlock(), ast_true(), ast_variable_browse(), ast_variable_retrieve(), cfg, clear_queue(), call_queue::count, create_queue_member(), call_queue::dead, member::delme, destroy_queue(), member::dynamic, free, init_queue(), member::interface, call_queue::lock, LOG_NOTICE, LOG_WARNING, call_queue::members, call_queue::name, call_queue::next, member::next, member::paused, member::penalty, queue_persistent_members, queue_set_param(), queues, remove_from_interfaces(), member::status, and var.

Referenced by load_module(), and reload().

03304 {
03305    struct call_queue *q, *ql, *qn;
03306    struct ast_config *cfg;
03307    char *cat, *tmp;
03308    struct ast_variable *var;
03309    struct member *prev, *cur, *newm, *next;
03310    int new;
03311    char *general_val = NULL;
03312    char interface[80];
03313    int penalty;
03314    
03315    cfg = ast_config_load("queues.conf");
03316    if (!cfg) {
03317       ast_log(LOG_NOTICE, "No call queueing config file (queues.conf), so no call queues\n");
03318       return;
03319    }
03320    memset(interface, 0, sizeof(interface));
03321    ast_mutex_lock(&qlock);
03322    use_weight=0;
03323    /* Mark all queues as dead for the moment */
03324    q = queues;
03325    while(q) {
03326       q->dead = 1;
03327       q = q->next;
03328    }
03329    /* Chug through config file */
03330    cat = ast_category_browse(cfg, NULL);
03331    while(cat) {
03332       if (!strcasecmp(cat, "general")) {  
03333          /* Initialize global settings */
03334          queue_persistent_members = 0;
03335          if ((general_val = ast_variable_retrieve(cfg, "general", "persistentmembers")))
03336             queue_persistent_members = ast_true(general_val);
03337       } else { /* Define queue */
03338          /* Look for an existing one */
03339          q = queues;
03340          while(q) {
03341             if (!strcmp(q->name, cat))
03342                break;
03343             q = q->next;
03344          }
03345          if (!q) {
03346             /* Make one then */
03347             q = alloc_queue(cat);
03348             new = 1;
03349          } else
03350             new = 0;
03351          if (q) {
03352             if (!new)
03353                ast_mutex_lock(&q->lock);
03354             /* Re-initialize the queue, and clear statistics */
03355             init_queue(q);
03356             clear_queue(q);
03357             for (cur = q->members; cur; cur = cur->next) {
03358                if (!cur->dynamic) {
03359                   cur->delme = 1;
03360                }
03361             }
03362             var = ast_variable_browse(cfg, cat);
03363             while (var) {
03364                if (!strcasecmp(var->name, "member")) {
03365                   /* Add a new member */
03366                   ast_copy_string(interface, var->value, sizeof(interface));
03367                   if ((tmp = strchr(interface, ','))) {
03368                      *tmp = '\0';
03369                      tmp++;
03370                      penalty = atoi(tmp);
03371                      if (penalty < 0) {
03372                         penalty = 0;
03373                      }
03374                   } else
03375                      penalty = 0;
03376 
03377                   /* Find the old position in the list */
03378                   for (prev = NULL, cur = q->members; cur; prev = cur, cur = cur->next) {
03379                      if (!strcmp(cur->interface, interface)) {
03380                         break;
03381                      }
03382                   }
03383 
03384                   newm = create_queue_member(interface, penalty, cur ? cur->paused : 0);
03385 
03386                   if (cur) {
03387                      /* Delete it now */
03388                      newm->next = cur->next;
03389                      if (prev) {
03390                         prev->next = newm;
03391                      } else {
03392                         q->members = newm;
03393                      }
03394                      free(cur);
03395                   } else {
03396                      /* Add them to the master int list if necessary */
03397                      add_to_interfaces(interface);
03398                      newm->next = q->members;
03399                      q->members = newm;
03400                   }
03401                } else {
03402                   queue_set_param(q, var->name, var->value, var->lineno, 1);
03403                }
03404                var = var->next;
03405             }
03406 
03407             /* Free remaining members marked as delme */
03408             for (prev = NULL, cur = q->members;
03409                  cur;
03410                  cur = next) {
03411                next = cur->next;
03412 
03413                if (!cur->delme) {
03414                   prev = cur;
03415                   continue;
03416                }
03417 
03418                if (prev)
03419                   prev->next = next;
03420                else
03421                   q->members = next;
03422 
03423                remove_from_interfaces(cur->interface);
03424                free(cur);
03425             }
03426 
03427             if (!new) 
03428                ast_mutex_unlock(&q->lock);
03429             if (new) {
03430                q->next = queues;
03431                queues = q;
03432             }
03433          }
03434       }
03435       cat = ast_category_browse(cfg, cat);
03436    }
03437    ast_config_destroy(cfg);
03438    q = queues;
03439    ql = NULL;
03440    while(q) {
03441       qn = q->next;
03442       if (q->dead) {
03443          if (ql)
03444             ql->next = q->next;
03445          else
03446             queues = q->next;
03447          if (!q->count) {
03448             destroy_queue(q);
03449          } else
03450             ast_log(LOG_WARNING, "XXX Leaking a little memory :( XXX\n");
03451       } else {
03452          ast_mutex_lock(&q->lock);
03453          for (cur = q->members; cur; cur = cur->next)
03454             cur->status = ast_device_state(cur->interface);
03455          ql = q;
03456          ast_mutex_unlock(&q->lock);
03457       }
03458       q = qn;
03459    }
03460    ast_mutex_unlock(&qlock);
03461 }

static int remove_from_interfaces char *  interface  )  [static]
 

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

Referenced by free_members(), and reload_queues().

00694 {
00695    struct member_interface *curint;
00696 
00697    if (!interface)
00698       return 0;
00699 
00700    AST_LIST_LOCK(&interfaces);
00701    AST_LIST_TRAVERSE_SAFE_BEGIN(&interfaces, curint, list) {
00702       if (!strcasecmp(curint->interface, interface)) {
00703          if (!interface_exists_global(interface)) {
00704             if (option_debug)
00705                ast_log(LOG_DEBUG, "Removing %s from the list of interfaces that make up all of our queue members.\n", interface);
00706             AST_LIST_REMOVE_CURRENT(&interfaces, list);
00707             free(curint);
00708          }
00709          break;
00710       }
00711    }
00712    AST_LIST_TRAVERSE_SAFE_END;
00713    AST_LIST_UNLOCK(&interfaces);
00714 
00715    return 0;
00716 }

static int remove_from_queue char *  queuename,
char *  interface
[static]
 

Definition at line 2520 of file app_queue.c.

References ast_mutex_lock(), interface_exists(), call_queue::lock, call_queue::members, call_queue::name, member::next, call_queue::next, queues, and RES_NOSUCHQUEUE.

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

02521 {
02522    struct call_queue *q;
02523    struct member *last_member, *look;
02524    int res = RES_NOSUCHQUEUE;
02525 
02526    ast_mutex_lock(&qlock);
02527    for (q = queues ; q ; q = q->next) {
02528       ast_mutex_lock(&q->lock);
02529       if (!strcmp(q->name, queuename)) {
02530          if ((last_member = interface_exists(q, interface))) {
02531             if ((look = q->members) == last_member) {
02532                q->members = last_member->next;
02533             } else {
02534                while (look != NULL) {
02535                   if (look->next == last_member) {
02536                      look->next = last_member->next;
02537                      break;
02538                   } else {
02539                       look = look->next;
02540                   }
02541                }
02542             }
02543             manager_event(EVENT_FLAG_AGENT, "QueueMemberRemoved",
02544                   "Queue: %s\r\n"
02545                   "Location: %s\r\n",
02546                q->name, last_member->interface);
02547             free(last_member);
02548 
02549             if (queue_persistent_members)
02550                dump_queue_members(q);
02551 
02552             res = RES_OKAY;
02553          } else {
02554             res = RES_EXISTS;
02555          }
02556          ast_mutex_unlock(&q->lock);
02557          break;
02558       }
02559       ast_mutex_unlock(&q->lock);
02560    }
02561    if (res == RES_OKAY)
02562       remove_from_interfaces(interface);
02563    ast_mutex_unlock(&qlock);
02564    return res;
02565 }

static void remove_queue struct call_queue q  )  [static]
 

Definition at line 917 of file app_queue.c.

References ast_mutex_lock(), call_queue::next, and queues.

Referenced by leave_queue().

00918 {
00919    struct call_queue *cur, *prev = NULL;
00920 
00921    ast_mutex_lock(&qlock);
00922    for (cur = queues; cur; cur = cur->next) {
00923       if (cur == q) {
00924          if (prev)
00925             prev->next = cur->next;
00926          else
00927             queues = cur->next;
00928       } else {
00929          prev = cur;
00930       }
00931    }
00932    ast_mutex_unlock(&qlock);
00933 }

static int ring_entry struct queue_ent qe,
struct localuser tmp,
int *  busies
[static]
 

Definition at line 1488 of file app_queue.c.

References ast_channel::adsicpe, ast_channel::appl, ast_call(), ast_cdr_busy(), ast_channel_inherit_variables(), ast_hangup(), ast_log(), ast_mutex_lock(), ast_mutex_unlock(), ast_request(), ast_verbose(), ast_channel::cdr, localuser::chan, queue_ent::chan, ast_channel::cid, ast_callerid::cid_ani, ast_callerid::cid_name, ast_callerid::cid_num, compare_weight(), ast_channel::context, ast_channel::data, EVENT_FLAG_AGENT, call_queue::eventwhencalled, ast_channel::exten, free, localuser::interface, localuser::lastcall, call_queue::lock, LOG_DEBUG, LOG_NOTICE, manager_event(), localuser::member, ast_channel::name, call_queue::name, ast_channel::nativeformats, localuser::oldstatus, option_debug, option_verbose, queue_ent::parent, member::paused, ast_channel::priority, call_queue::rrpos, member::status, localuser::stillgoing, strdup, update_dial_status(), use_weight, VERBOSE_PREFIX_3, ast_channel::whentohangup, and call_queue::wrapuptime.

Referenced by ring_one().

01489 {
01490    int res;
01491    int status;
01492    char tech[256];
01493    char *location;
01494 
01495    if (qe->parent->wrapuptime && (time(NULL) - tmp->lastcall < qe->parent->wrapuptime)) {
01496       if (option_debug)
01497          ast_log(LOG_DEBUG, "Wrapuptime not yet expired for %s\n", tmp->interface);
01498       if (qe->chan->cdr)
01499          ast_cdr_busy(qe->chan->cdr);
01500       tmp->stillgoing = 0;
01501       (*busies)++;
01502       return 0;
01503    }
01504    
01505    if (tmp->member->paused) {
01506       if (option_debug)
01507          ast_log(LOG_DEBUG, "%s paused, can't receive call\n", tmp->interface);
01508       if (qe->chan->cdr)
01509          ast_cdr_busy(qe->chan->cdr);
01510       tmp->stillgoing = 0;
01511       return 0;
01512    }
01513    if (use_weight && compare_weight(qe->parent,tmp->member)) {
01514       ast_log(LOG_DEBUG, "Priority queue delaying call to %s:%s\n", qe->parent->name, tmp->interface);
01515       if (qe->chan->cdr)
01516          ast_cdr_busy(qe->chan->cdr);
01517       tmp->stillgoing = 0;
01518       (*busies)++;
01519       return 0;
01520    }
01521 
01522    ast_copy_string(tech, tmp->interface, sizeof(tech));
01523    if ((location = strchr(tech, '/')))
01524       *location++ = '\0';
01525    else
01526       location = "";
01527 
01528    /* Request the peer */
01529    tmp->chan = ast_request(tech, qe->chan->nativeformats, location, &status);
01530    if (!tmp->chan) {       /* If we can't, just go on to the next call */
01531 #if 0
01532       ast_log(LOG_NOTICE, "Unable to create channel of type '%s' for Queue\n", cur->tech);
01533 #endif         
01534       if (qe->chan->cdr)
01535          ast_cdr_busy(qe->chan->cdr);
01536       tmp->stillgoing = 0;
01537       update_dial_status(qe->parent, tmp->member, status);
01538 
01539       ast_mutex_lock(&qe->parent->lock);
01540       qe->parent->rrpos++;
01541       ast_mutex_unlock(&qe->parent->lock);
01542 
01543       (*busies)++;
01544       return 0;
01545    } else if (status != tmp->oldstatus) 
01546       update_dial_status(qe->parent, tmp->member, status);
01547    
01548    tmp->chan->appl = "AppQueue";
01549    tmp->chan->data = "(Outgoing Line)";
01550    tmp->chan->whentohangup = 0;
01551    if (tmp->chan->cid.cid_num)
01552       free(tmp->chan->cid.cid_num);
01553    tmp->chan->cid.cid_num = NULL;
01554    if (tmp->chan->cid.cid_name)
01555       free(tmp->chan->cid.cid_name);
01556    tmp->chan->cid.cid_name = NULL;
01557    if (tmp->chan->cid.cid_ani)
01558       free(tmp->chan->cid.cid_ani);
01559    tmp->chan->cid.cid_ani = NULL;
01560    if (qe->chan->cid.cid_num)
01561       tmp->chan->cid.cid_num = strdup(qe->chan->cid.cid_num);
01562    if (qe->chan->cid.cid_name)
01563       tmp->chan->cid.cid_name = strdup(qe->chan->cid.cid_name);
01564    if (qe->chan->cid.cid_ani)
01565       tmp->chan->cid.cid_ani = strdup(qe->chan->cid.cid_ani);
01566 
01567    /* Inherit specially named variables from parent channel */
01568    ast_channel_inherit_variables(qe->chan, tmp->chan);
01569 
01570    /* Presense of ADSI CPE on outgoing channel follows ours */
01571    tmp->chan->adsicpe = qe->chan->adsicpe;
01572 
01573    /* Place the call, but don't wait on the answer */
01574    res = ast_call(tmp->chan, location, 0);
01575    if (res) {
01576       /* Again, keep going even if there's an error */
01577       if (option_debug)
01578          ast_log(LOG_DEBUG, "ast call on peer returned %d\n", res);
01579       if (option_verbose > 2)
01580          ast_verbose(VERBOSE_PREFIX_3 "Couldn't call %s\n", tmp->interface);
01581       ast_hangup(tmp->chan);
01582       tmp->chan = NULL;
01583       tmp->stillgoing = 0;
01584       (*busies)++;
01585       return 0;
01586    } else {
01587       if (qe->parent->eventwhencalled) {
01588          manager_event(EVENT_FLAG_AGENT, "AgentCalled",
01589                   "AgentCalled: %s\r\n"
01590                   "ChannelCalling: %s\r\n"
01591                   "CallerID: %s\r\n"
01592                   "CallerIDName: %s\r\n"
01593                   "Context: %s\r\n"
01594                   "Extension: %s\r\n"
01595                   "Priority: %d\r\n",
01596                   tmp->interface, qe->chan->name,
01597                   tmp->chan->cid.cid_num ? tmp->chan->cid.cid_num : "unknown",
01598                   tmp->chan->cid.cid_name ? tmp->chan->cid.cid_name : "unknown",
01599                   qe->chan->context, qe->chan->exten, qe->chan->priority);
01600       }
01601       if (option_verbose > 2)
01602          ast_verbose(VERBOSE_PREFIX_3 "Called %s\n", tmp->interface);
01603    }
01604    return 1;
01605 }

static int ring_one struct queue_ent qe,
struct localuser outgoing,
int *  busies
[static]
 

Definition at line 1607 of file app_queue.c.

References ast_log(), localuser::chan, localuser::interface, LOG_DEBUG, localuser::metric, localuser::next, option_debug, queue_ent::parent, ring_entry(), localuser::stillgoing, and call_queue::strategy.

Referenced by wait_for_answer().

01608 {
01609    struct localuser *cur;
01610    struct localuser *best;
01611    int bestmetric=0;
01612 
01613    do {
01614       best = NULL;
01615       cur = outgoing;
01616       while(cur) {
01617          if (cur->stillgoing &&              /* Not already done */
01618             !cur->chan &&              /* Isn't already going */
01619             (!best || (cur->metric < bestmetric))) {  /* We haven't found one yet, or it's better */
01620                bestmetric = cur->metric;
01621                best = cur;
01622          }
01623          cur = cur->next;
01624       }
01625       if (best) {
01626          if (!qe->parent->strategy) {
01627             /* Ring everyone who shares this best metric (for ringall) */
01628             cur = outgoing;
01629             while(cur) {
01630                if (cur->stillgoing && !cur->chan && (cur->metric <= bestmetric)) {
01631                   if (option_debug)
01632                      ast_log(LOG_DEBUG, "(Parallel) Trying '%s' with metric %d\n", cur->interface, cur->metric);
01633                   ring_entry(qe, cur, busies);
01634                }
01635                cur = cur->next;
01636             }
01637          } else {
01638             /* Ring just the best channel */
01639             if (option_debug)
01640                ast_log(LOG_DEBUG, "Trying '%s' with metric %d\n", best->interface, best->metric);
01641             ring_entry(qe, best, busies);
01642          }
01643       }
01644    } while (best && !best->chan);
01645    if (!best) {
01646       if (option_debug)
01647          ast_log(LOG_DEBUG, "Nobody left to try ringing in queue\n");
01648       return 0;
01649    }
01650    return 1;
01651 }

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

Definition at line 2864 of file app_queue.c.

References AST_APP_ARG, AST_DECLARE_APP_ARGS, ast_goto_if_exists(), ast_log(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), localuser::chan, ast_channel::context, localuser::interface, LOCAL_USER_ADD, LOCAL_USER_REMOVE, LOG_ERROR, LOG_NOTICE, LOG_WARNING, ast_channel::name, option_priority_jumping, parse(), pbx_builtin_setvar_helper(), remove_from_queue(), RES_EXISTS, RES_NOSUCHQUEUE, RES_OKAY, and RES_OUTOFMEMORY.

Referenced by load_module().

02865 {
02866    int res=-1;
02867    struct localuser *u;
02868    char *parse, *temppos = NULL;
02869    int priority_jump = 0;
02870    AST_DECLARE_APP_ARGS(args,
02871       AST_APP_ARG(queuename);
02872       AST_APP_ARG(interface);
02873       AST_APP_ARG(options);
02874    );
02875 
02876 
02877    if (ast_strlen_zero(data)) {
02878       ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename[|interface[|options]])\n");
02879       return -1;
02880    }
02881 
02882    LOCAL_USER_ADD(u);
02883 
02884    if (!(parse = ast_strdupa(data))) {
02885       ast_log(LOG_WARNING, "Memory Error!\n");
02886       LOCAL_USER_REMOVE(u);
02887       return -1;
02888    }
02889 
02890    AST_STANDARD_APP_ARGS(args, parse);
02891 
02892    if (ast_strlen_zero(args.interface)) {
02893       args.interface = ast_strdupa(chan->name);
02894       temppos = strrchr(args.interface, '-');
02895       if (temppos)
02896          *temppos = '\0';
02897    }
02898 
02899    if (args.options) {
02900       if (strchr(args.options, 'j'))
02901          priority_jump = 1;
02902    }
02903 
02904    switch (remove_from_queue(args.queuename, args.interface)) {
02905    case RES_OKAY:
02906       ast_log(LOG_NOTICE, "Removed interface '%s' from queue '%s'\n", args.interface, args.queuename);
02907       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "REMOVED");
02908       res = 0;
02909       break;
02910    case RES_EXISTS:
02911       ast_log(LOG_WARNING, "Unable to remove interface '%s' from queue '%s': Not there\n", args.interface, args.queuename);
02912       if (priority_jump || option_priority_jumping) 
02913          ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
02914       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTINQUEUE");
02915       res = 0;
02916       break;
02917    case RES_NOSUCHQUEUE:
02918       ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", args.queuename);
02919       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOSUCHQUEUE");
02920       res = 0;
02921       break;
02922    case RES_OUTOFMEMORY:
02923       ast_log(LOG_ERROR, "Out of memory\n");
02924       break;
02925    }
02926 
02927    LOCAL_USER_REMOVE(u);
02928    return res;
02929 }

static void rt_handle_member_record struct call_queue q,
char *  interface,
const char *  penalty_str
[static]
 

Definition at line 854 of file app_queue.c.

References add_to_interfaces(), create_queue_member(), member::dead, member::interface, call_queue::members, member::next, and member::penalty.

00855 {
00856    struct member *m, *prev_m;
00857    int penalty = 0;
00858 
00859    if(penalty_str) {
00860       penalty = atoi(penalty_str);
00861       if(penalty < 0)
00862          penalty = 0;
00863    }
00864 
00865    /* Find the member, or the place to put a new one. */
00866    prev_m = NULL;
00867    m = q->members;
00868    while (m && strcmp(m->interface, interface)) {
00869       prev_m = m;
00870       m = m->next;
00871    }
00872 
00873    /* Create a new one if not found, else update penalty */
00874    if (!m) {
00875       m = create_queue_member(interface, penalty, 0);
00876       if (m) {
00877          m->dead = 0;
00878          add_to_interfaces(interface);
00879          if (prev_m) {
00880             prev_m->next = m;
00881          } else {
00882             q->members = m;
00883          }
00884       }
00885    } else {
00886       m->dead = 0;   /* Do not delete this one. */
00887       m->penalty = penalty;
00888    }
00889 }

static int say_periodic_announcement struct queue_ent qe  )  [static]
 

Definition at line 1716 of file app_queue.c.

References ast_moh_start(), ast_moh_stop(), ast_verbose(), background_file(), queue_ent::chan, queue_ent::last_periodic_announce_time, queue_ent::moh, option_verbose, queue_ent::parent, call_queue::periodicannouncefrequency, call_queue::sound_periodicannounce, and VERBOSE_PREFIX_3.

01717 {
01718    int res = 0;
01719    time_t now;
01720 
01721    /* Get the current time */
01722    time(&now);
01723 
01724    /* Check to see if it is time to announce */
01725    if ((now - qe->last_periodic_announce_time) < qe->parent->periodicannouncefrequency)
01726       return 0;
01727 
01728    /* Stop the music on hold so we can play our own file */
01729    ast_moh_stop(qe->chan);
01730 
01731    if (option_verbose > 2)
01732       ast_verbose(VERBOSE_PREFIX_3 "Playing periodic announcement\n");
01733 
01734    /* play the announcement */
01735    res = background_file(qe, qe->chan, qe->parent->sound_periodicannounce);
01736 
01737    /* Resume Music on Hold if the caller is going to stay in the queue */
01738    if (!res)
01739       ast_moh_start(qe->chan, qe->moh);
01740 
01741    /* update last_periodic_announce_time */
01742    qe->last_periodic_announce_time = now;
01743 
01744    return res;
01745 }

static int say_position struct queue_ent qe  )  [static]
 

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

01226 {
01227    int res = 0, avgholdmins, avgholdsecs;
01228    time_t now;
01229 
01230    /* Check to see if this is ludicrous -- if we just announced position, don't do it again*/
01231    time(&now);
01232    if ( (now - qe->last_pos) < 15 )
01233       return 0;
01234 
01235    /* If either our position has changed, or we are over the freq timer, say position */
01236    if ( (qe->last_pos_said == qe->pos) && ((now - qe->last_pos) < qe->parent->announcefrequency) )
01237       return 0;
01238 
01239    ast_moh_stop(qe->chan);
01240    /* Say we're next, if we are */
01241    if (qe->pos == 1) {
01242       res = play_file(qe->chan, qe->parent->sound_next);
01243       if (res && valid_exit(qe, res))
01244          goto playout;
01245       else
01246          goto posout;
01247    } else {
01248       res = play_file(qe->chan, qe->parent->sound_thereare);
01249       if (res && valid_exit(qe, res))
01250          goto playout;
01251       res = ast_say_number(qe->chan, qe->pos, AST_DIGIT_ANY, qe->chan->language, (char *) NULL); /* Needs gender */
01252       if (res && valid_exit(qe, res))
01253          goto playout;
01254       res = play_file(qe->chan, qe->parent->sound_calls);
01255       if (res && valid_exit(qe, res))
01256          goto playout;
01257    }
01258    /* Round hold time to nearest minute */
01259    avgholdmins = abs(( (qe->parent->holdtime + 30) - (now - qe->start) ) / 60);
01260 
01261    /* If they have specified a rounding then round the seconds as well */
01262    if(qe->parent->roundingseconds) {
01263       avgholdsecs = (abs(( (qe->parent->holdtime + 30) - (now - qe->start) )) - 60 * avgholdmins) / qe->parent->roundingseconds;
01264       avgholdsecs*= qe->parent->roundingseconds;
01265    } else {
01266       avgholdsecs=0;
01267    }
01268 
01269    if (option_verbose > 2)
01270       ast_verbose(VERBOSE_PREFIX_3 "Hold time for %s is %d minutes %d seconds\n", qe->parent->name, avgholdmins, avgholdsecs);
01271 
01272    /* If the hold time is >1 min, if it's enabled, and if it's not
01273       supposed to be only once and we have already said it, say it */
01274    if ((avgholdmins+avgholdsecs) > 0 && (qe->parent->announceholdtime) &&
01275        (!(qe->parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE) && qe->last_pos)) {
01276       res = play_file(qe->chan, qe->parent->sound_holdtime);
01277       if (res && valid_exit(qe, res))
01278          goto playout;
01279 
01280       if (avgholdmins>0) {
01281          if (avgholdmins < 2) {
01282             res = play_file(qe->chan, qe->parent->sound_lessthan);
01283             if (res && valid_exit(qe, res))
01284                goto playout;
01285 
01286             res = ast_say_number(qe->chan, 2, AST_DIGIT_ANY, qe->chan->language, (char *)NULL);
01287             if (res && valid_exit(qe, res))
01288                goto playout;
01289          } else {
01290             res = ast_say_number(qe->chan, avgholdmins, AST_DIGIT_ANY, qe->chan->language, (char*) NULL);
01291             if (res && valid_exit(qe, res))
01292                goto playout;
01293          }
01294          
01295          res = play_file(qe->chan, qe->parent->sound_minutes);
01296          if (res && valid_exit(qe, res))
01297             goto playout;
01298       }
01299       if (avgholdsecs>0) {
01300          res = ast_say_number(qe->chan, avgholdsecs, AST_DIGIT_ANY, qe->chan->language, (char*) NULL);
01301          if (res && valid_exit(qe, res))
01302             goto playout;
01303 
01304          res = play_file(qe->chan, qe->parent->sound_seconds);
01305          if (res && valid_exit(qe, res))
01306             goto playout;
01307       }
01308 
01309    }
01310 
01311  posout:
01312    if (option_verbose > 2)
01313       ast_verbose(VERBOSE_PREFIX_3 "Told %s in %s their queue position (which was %d)\n",
01314              qe->chan->name, qe->parent->name, qe->pos);
01315    res = play_file(qe->chan, qe->parent->sound_thanks);
01316    if (res && !valid_exit(qe, res))
01317       res = 0;
01318 
01319  playout:
01320    /* Set our last_pos indicators */
01321    qe->last_pos = now;
01322    qe->last_pos_said = qe->pos;
01323 
01324    /* Don't restart music on hold if we're about to exit the caller from the queue */
01325    if (!res)
01326       ast_moh_start(qe->chan, qe->moh);
01327 
01328    return res;
01329 }

static int set_member_paused char *  queuename,
char *  interface,
int  paused
[static]
 

Definition at line 2619 of file app_queue.c.

References ast_log(), ast_mutex_lock(), ast_queue_log(), ast_strlen_zero(), dump_queue_members(), EVENT_FLAG_AGENT, member::interface, interface_exists(), call_queue::lock, LOG_DEBUG, manager_event(), call_queue::name, call_queue::next, member::paused, queue_persistent_members, and queues.

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

02620 {
02621    int found = 0;
02622    struct call_queue *q;
02623    struct member *mem;
02624 
02625    /* Special event for when all queues are paused - individual events still generated */
02626 
02627    if (ast_strlen_zero(queuename))
02628       ast_queue_log("NONE", "NONE", interface, (paused ? "PAUSEALL" : "UNPAUSEALL"), "%s", "");
02629 
02630    ast_mutex_lock(&qlock);
02631    for (q = queues ; q ; q = q->next) {
02632       ast_mutex_lock(&q->lock);
02633       if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
02634          if ((mem = interface_exists(q, interface))) {
02635             found++;
02636             if (mem->paused == paused)
02637                ast_log(LOG_DEBUG, "%spausing already-%spaused queue member %s:%s\n", (paused ? "" : "un"), (paused ? "" : "un"), q->name, interface);
02638             mem->paused = paused;
02639 
02640             if (queue_persistent_members)
02641                dump_queue_members(q);
02642 
02643             ast_queue_log(q->name, "NONE", interface, (paused ? "PAUSE" : "UNPAUSE"), "%s", "");
02644 
02645             manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
02646                "Queue: %s\r\n"
02647                "Location: %s\r\n"
02648                "Paused: %d\r\n",
02649                   q->name, mem->interface, paused);
02650          }
02651       }
02652       ast_mutex_unlock(&q->lock);
02653    }
02654    ast_mutex_unlock(&qlock);
02655 
02656    if (found)
02657       return RESULT_SUCCESS;
02658    else
02659       return RESULT_FAILURE;
02660 }

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

Definition at line 377 of file app_queue.c.

References pbx_builtin_setvar_helper(), queue_results, and text.

00378 {
00379    int i;
00380 
00381    for (i = 0; i < sizeof(queue_results) / sizeof(queue_results[0]); i++) {
00382       if (queue_results[i].id == res) {
00383          pbx_builtin_setvar_helper(chan, "QUEUESTATUS", queue_results[i].text);
00384          return;
00385       }
00386    }
00387 }

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

Definition at line 546 of file app_queue.c.

References ast_log(), ast_pthread_create, changethread(), free, LOG_WARNING, malloc, and t.

Referenced by load_module(), and unload_module().

00547 {
00548    /* Avoid potential for deadlocks by spawning a new thread to handle
00549       the event */
00550    struct statechange *sc;
00551    pthread_t t;
00552    pthread_attr_t attr;
00553 
00554    if (!(sc = malloc(sizeof(*sc) + strlen(dev) + 1)))
00555       return 0;
00556 
00557    sc->state = state;
00558    strcpy(sc->dev, dev);
00559    pthread_attr_init(&attr);
00560    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
00561    if (ast_pthread_create(&t, &attr, changethread, sc)) {
00562       ast_log(LOG_WARNING, "Failed to create update thread!\n");
00563       free(sc);
00564    }
00565 
00566    return 0;
00567 }

static int store_next struct queue_ent qe,
struct localuser outgoing
[static]
 

Definition at line 1653 of file app_queue.c.

References ast_log(), localuser::chan, localuser::interface, LOG_DEBUG, localuser::metric, localuser::next, option_debug, queue_ent::parent, call_queue::rrpos, localuser::stillgoing, and call_queue::wrapped.

01654 {
01655    struct localuser *cur;
01656    struct localuser *best;
01657    int bestmetric=0;
01658 
01659    best = NULL;
01660    cur = outgoing;
01661    while(cur) {
01662       if (cur->stillgoing &&              /* Not already done */
01663          !cur->chan &&              /* Isn't already going */
01664          (!best || (cur->metric < bestmetric))) {  /* We haven't found one yet, or it's better */
01665             bestmetric = cur->metric;
01666             best = cur;
01667       }
01668       cur = cur->next;
01669    }
01670    if (best) {
01671       /* Ring just the best channel */
01672       if (option_debug)
01673          ast_log(LOG_DEBUG, "Next is '%s' with metric %d\n", best->interface, best->metric);
01674       qe->parent->rrpos = best->metric % 1000;
01675    } else {
01676       /* Just increment rrpos */
01677       if (qe->parent->wrapped) {
01678          /* No more channels, start over */
01679          qe->parent->rrpos = 0;
01680       } else {
01681          /* Prioritize next entry */
01682          qe->parent->rrpos++;
01683       }
01684    }
01685    qe->parent->wrapped = 0;
01686    return 0;
01687 }

static int strat2int const char *  strategy  )  [static]
 

Definition at line 399 of file app_queue.c.

References name, and strategies.

Referenced by queue_set_param().

00400 {
00401    int x;
00402    for (x=0;x<sizeof(strategies) / sizeof(strategies[0]);x++) {
00403       if (!strcasecmp(strategy, strategies[x].name))
00404          return strategies[x].strategy;
00405    }
00406    return -1;
00407 }

static int try_calling struct queue_ent qe,
const char *  options,
char *  announceoverride,
const char *  url,
int *  go_on
[static]
 

Definition at line 2153 of file app_queue.c.

References AST_FEATURE_AUTOMON, AST_FEATURE_DISCONNECT, AST_FEATURE_REDIRECT, AST_MAX_CONTEXT, AST_MAX_EXTENSION, ast_set_flag, queue_ent::parent, queue_ent::start, and call_queue::timeout.

02154 {
02155    struct member *cur;
02156    struct localuser *outgoing=NULL, *tmp = NULL;
02157    int to;
02158    char restofit[AST_MAX_EXTENSION];
02159    char oldexten[AST_MAX_EXTENSION]="";
02160    char oldcontext[AST_MAX_CONTEXT]="";
02161    char queuename[256]="";
02162    char *newnum;
02163    char *monitorfilename;
02164    struct ast_channel *peer;
02165    struct ast_channel *which;
02166    struct localuser *lpeer;
02167    struct member *member;
02168    int res = 0, bridge = 0;
02169    int numbusies = 0;
02170    int x=0;
02171    char *announce = NULL;
02172    char digit = 0;
02173    time_t callstart;
02174    time_t now = time(NULL);
02175    struct ast_bridge_config bridge_config;
02176    char nondataquality = 1;
02177 
02178    memset(&bridge_config, 0, sizeof(bridge_config));
02179    time(&now);
02180       
02181    for (; options && *options; options++)
02182       switch (*options) {
02183       case 't':
02184          ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_REDIRECT);
02185          break;
02186       case 'T':
02187          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_REDIRECT);
02188          break;
02189       case 'w':
02190          ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMON);
02191          break;
02192       case 'W':
02193          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMON);
02194          break;
02195       case 'd':
02196          nondataquality = 0;
02197          break;
02198       case 'h':
02199          ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_DISCONNECT);
02200          break;
02201       case 'H':
02202          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT);
02203          break;
02204       case 'n':
02205          if ((now - qe->start >= qe->parent->timeout))
02206             *go_on = 1;
02207          break;
02208       }
02209 
02210    /* Hold the lock while we setup the outgoing calls */
02211    if (use_weight) 
02212       ast_mutex_lock(&qlock);
02213    ast_mutex_lock(&qe->parent->lock);
02214    if (option_debug)
02215       ast_log(LOG_DEBUG, "%s is trying to call a queue member.\n", 
02216                      qe->chan->name);
02217    ast_copy_string(queuename, qe->parent->name, sizeof(queuename));
02218    cur = qe->parent->members;
02219    if (!ast_strlen_zero(qe->announce))
02220       announce = qe->announce;
02221    if (!ast_strlen_zero(announceoverride))
02222       announce = announceoverride;
02223 
02224    while(cur) {
02225       tmp = malloc(sizeof(*tmp));
02226       if (!tmp) {
02227          ast_mutex_unlock(&qe->parent->lock);
02228          if (use_weight) 
02229             ast_mutex_unlock(&qlock);
02230          ast_log(LOG_WARNING, "Out of memory\n");
02231          goto out;
02232       }
02233       memset(tmp, 0, sizeof(*tmp));
02234       tmp->stillgoing = -1;
02235       if (option_debug) {
02236          if (url)
02237             ast_log(LOG_DEBUG, "Queue with URL=%s_\n", url);
02238          else 
02239             ast_log(LOG_DEBUG, "Simple queue (no URL)\n");
02240       }
02241 
02242       tmp->member = cur;      /* Never directly dereference!  Could change on reload */
02243       tmp->oldstatus = cur->status;
02244       tmp->lastcall = cur->lastcall;
02245       ast_copy_string(tmp->interface, cur->interface, sizeof(tmp->interface));
02246       /* If we're dialing by extension, look at the extension to know what to dial */
02247       if ((newnum = strstr(tmp->interface, "/BYEXTENSION"))) {
02248          newnum++;
02249          strncpy(restofit, newnum + strlen("BYEXTENSION"), sizeof(restofit) - 1);
02250          snprintf(newnum, sizeof(tmp->interface) - (newnum - tmp->interface), "%s%s", qe->chan->exten, restofit);
02251          if (option_debug)
02252             ast_log(LOG_DEBUG, "Dialing by extension %s\n", tmp->interface);
02253       }
02254       /* Special case: If we ring everyone, go ahead and ring them, otherwise
02255          just calculate their metric for the appropriate strategy */
02256       calc_metric(qe->parent, cur, x++, qe, tmp);
02257       /* Put them in the list of outgoing thingies...  We're ready now. 
02258          XXX If we're forcibly removed, these outgoing calls won't get
02259          hung up XXX */
02260       tmp->next = outgoing;
02261       outgoing = tmp;      
02262       /* If this line is up, don't try anybody else */
02263       if (outgoing->chan && (outgoing->chan->_state == AST_STATE_UP))
02264          break;
02265 
02266       cur = cur->next;
02267    }
02268    if (qe->expire && (!qe->parent->timeout || (qe->expire - now) <= qe->parent->timeout))
02269       to = (qe->expire - now) * 1000;
02270    else
02271       to = (qe->parent->timeout) ? qe->parent->timeout * 1000 : -1;
02272    ring_one(qe, outgoing, &numbusies);
02273    ast_mutex_unlock(&qe->parent->lock);
02274    if (use_weight) 
02275       ast_mutex_unlock(&qlock);
02276    lpeer = wait_for_answer(qe, outgoing, &to, &digit, numbusies, ast_test_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT));
02277    ast_mutex_lock(&qe->parent->lock);
02278    if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY) {
02279       store_next(qe, outgoing);
02280    }
02281    ast_mutex_unlock(&qe->parent->lock);
02282    if (lpeer)
02283       peer = lpeer->chan;
02284    else
02285       peer = NULL;
02286    if (!peer) {
02287       if (to) {
02288          /* Musta gotten hung up */
02289          res = -1;
02290       } else {
02291          res = digit;
02292       }
02293       if (option_debug)
02294          ast_log(LOG_DEBUG, "%s: Nobody answered.\n", qe->chan->name);
02295       goto out;
02296    }
02297    if (peer) {
02298       /* Ah ha!  Someone answered within the desired timeframe.  Of course after this
02299          we will always return with -1 so that it is hung up properly after the 
02300          conversation.  */
02301       qe->handled++;
02302       if (!strcmp(qe->chan->type,"Zap"))
02303          ast_channel_setoption(qe->chan, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
02304       if (!strcmp(peer->type,"Zap"))
02305          ast_channel_setoption(peer, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
02306       /* Update parameters for the queue */
02307       recalc_holdtime(qe);
02308       member = lpeer->member;
02309       hangupcalls(outgoing, peer);
02310       outgoing = NULL;
02311       if (announce || qe->parent->reportholdtime || qe->parent->memberdelay) {
02312          int res2;
02313          res2 = ast_autoservice_start(qe->chan);
02314          if (!res2) {
02315             if (qe->parent->memberdelay) {
02316                ast_log(LOG_NOTICE, "Delaying member connect for %d seconds\n", qe->parent->memberdelay);
02317                res2 |= ast_safe_sleep(peer, qe->parent->memberdelay * 1000);
02318             }
02319             if (!res2 && announce) {
02320                if (play_file(peer, announce))
02321                   ast_log(LOG_WARNING, "Announcement file '%s' is unavailable, continuing anyway...\n", announce);
02322             }
02323             if (!res2 && qe->parent->reportholdtime) {
02324                if (!play_file(peer, qe->parent->sound_reporthold)) {
02325                   int holdtime;
02326 
02327                   time(&now);
02328                   holdtime = abs((now - qe->start) / 60);
02329                   if (holdtime < 2) {
02330                      play_file(peer, qe->parent->sound_lessthan);
02331                      ast_say_number(peer, 2, AST_DIGIT_ANY, peer->language, NULL);
02332                   } else 
02333                      ast_say_number(peer, holdtime, AST_DIGIT_ANY, peer->language, NULL);
02334                   play_file(peer, qe->parent->sound_minutes);
02335                }
02336             }
02337          }
02338          res2 |= ast_autoservice_stop(qe->chan);
02339          if (peer->_softhangup) {
02340             /* Agent must have hung up */
02341             ast_log(LOG_WARNING, "Agent on %s hungup on the customer.  They're going to be pissed.\n", peer->name);
02342             ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "AGENTDUMP", "%s", "");
02343             record_abandoned(qe);
02344             if (qe->parent->eventwhencalled) {
02345                manager_event(EVENT_FLAG_AGENT, "AgentDump",
02346                         "Queue: %s\r\n"
02347                         "Uniqueid: %s\r\n"
02348                         "Channel: %s\r\n"
02349                         "Member: %s\r\n",
02350                         queuename, qe->chan->uniqueid, peer->name, member->interface);
02351             }
02352             ast_hangup(peer);
02353             goto out;
02354          } else if (res2) {
02355             /* Caller must have hung up just before being connected*/
02356             ast_log(LOG_NOTICE, "Caller was about to talk to agent on %s but the caller hungup.\n", peer->name);
02357             ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "ABANDON", "%d|%d|%ld", qe->pos, qe->opos, (long)time(NULL) - qe->start);
02358             record_abandoned(qe);
02359             ast_hangup(peer);
02360             return -1;
02361          }
02362       }
02363       /* Stop music on hold */
02364       ast_moh_stop(qe->chan);
02365       /* If appropriate, log that we have a destination channel */
02366       if (qe->chan->cdr)
02367          ast_cdr_setdestchan(qe->chan->cdr, peer->name);
02368       /* Make sure channels are compatible */
02369       res = ast_channel_make_compatible(qe->chan, peer);
02370       if (res < 0) {
02371          ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "SYSCOMPAT", "%s", "");
02372          ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", qe->chan->name, peer->name);
02373          record_abandoned(qe);
02374          ast_hangup(peer);
02375          return -1;
02376       }
02377       /* Begin Monitoring */
02378       if (qe->parent->monfmt && *qe->parent->monfmt) {
02379          monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME");
02380          if (pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC") || pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC_ARGS"))
02381             which = qe->chan;
02382          else
02383             which = peer;
02384          if (monitorfilename)
02385             ast_monitor_start(which, qe->parent->monfmt, monitorfilename, 1 );
02386          else if (qe->chan->cdr) 
02387             ast_monitor_start(which, qe->parent->monfmt, qe->chan->cdr->uniqueid, 1 );
02388          else {
02389             /* Last ditch effort -- no CDR, make up something */
02390             char tmpid[256];
02391             snprintf(tmpid, sizeof(tmpid), "chan-%x", rand());
02392             ast_monitor_start(which, qe->parent->monfmt, tmpid, 1 );
02393          }
02394          if (qe->parent->monjoin)
02395             ast_monitor_setjoinfiles(which, 1);
02396       }
02397       /* Drop out of the queue at this point, to prepare for next caller */
02398       leave_queue(qe);        
02399       if (!ast_strlen_zero(url) && ast_channel_supports_html(peer)) {
02400          if (option_debug)
02401             ast_log(LOG_DEBUG, "app_queue: sendurl=%s.\n", url);
02402          ast_channel_sendurl(peer, url);
02403       }
02404       ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "CONNECT", "%ld", (long)time(NULL) - qe->start);
02405       if (qe->parent->eventwhencalled)
02406          manager_event(EVENT_FLAG_AGENT, "AgentConnect",
02407                   "Queue: %s\r\n"
02408                   "Uniqueid: %s\r\n"
02409                   "Channel: %s\r\n"
02410                   "Member: %s\r\n"
02411                   "Holdtime: %ld\r\n",
02412                   queuename, qe->chan->uniqueid, peer->name, member->interface,
02413                   (long)time(NULL) - qe->start);
02414       ast_copy_string(oldcontext, qe->chan->context, sizeof(oldcontext));
02415       ast_copy_string(oldexten, qe->chan->exten, sizeof(oldexten));
02416       time(&callstart);
02417 
02418       bridge = ast_bridge_call(qe->chan,peer, &bridge_config);
02419 
02420       if (strcasecmp(oldcontext, qe->chan->context) || strcasecmp(oldexten, qe->chan->exten)) {
02421          ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "TRANSFER", "%s|%s", qe->chan->exten, qe->chan->context);
02422       } else if (qe->chan->_softhangup) {
02423          ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "COMPLETECALLER", "%ld|%ld",
02424                   (long)(callstart - qe->start), (long)(time(NULL) - callstart));
02425          if (qe->parent->eventwhencalled)
02426             manager_event(EVENT_FLAG_AGENT, "AgentComplete",
02427                      "Queue: %s\r\n"
02428                      "Uniqueid: %s\r\n"
02429                      "Channel: %s\r\n"
02430                      "Member: %s\r\n"
02431                      "HoldTime: %ld\r\n"
02432                      "TalkTime: %ld\r\n"
02433                      "Reason: caller\r\n",
02434                      queuename, qe->chan->uniqueid, peer->name, member->interface,
02435                      (long)(callstart - qe->start), (long)(time(NULL) - callstart));
02436       } else {
02437          ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "COMPLETEAGENT", "%ld|%ld", (long)(callstart - qe->start), (long)(time(NULL) - callstart));
02438          if (qe->parent->eventwhencalled)
02439             manager_event(EVENT_FLAG_AGENT, "AgentComplete",
02440                      "Queue: %s\r\n"
02441                      "Uniqueid: %s\r\n"
02442                      "Channel: %s\r\n"
02443                      "HoldTime: %ld\r\n"
02444                      "TalkTime: %ld\r\n"
02445                      "Reason: agent\r\n",
02446                      queuename, qe->chan->uniqueid, peer->name, (long)(callstart - qe->start),
02447                      (long)(time(NULL) - callstart));
02448       }
02449 
02450       if (bridge != AST_PBX_NO_HANGUP_PEER)
02451          ast_hangup(peer);
02452       update_queue(qe->parent, member);
02453       res = bridge ? bridge : 1;
02454    }  
02455 out:
02456    hangupcalls(outgoing, NULL);
02457    return res;
02458 }

int unload_module void   ) 
 

Cleanup all module structures, sockets, etc.

This is called at exit. Any registrations and memory allocations need to be unregistered and free'd here. Nothing else will do these for you (until exit).

Returns:
Zero on success, or non-zero on error.

Definition at line 3975 of file app_queue.c.

References app, app_aqm, app_pqm, app_rqm, app_upqm, ast_cli_unregister(), ast_custom_function_unregister(), ast_devstate_del(), ast_manager_unregister(), ast_unregister_application(), clear_and_free_interfaces(), cli_add_queue_member, cli_remove_queue_member, cli_show_queue, cli_show_queues, queueagentcount_function, STANDARD_HANGUP_LOCALUSERS, and statechange_queue().

03976 {
03977    int res;
03978 
03979    clear_and_free_interfaces();
03980    res = ast_cli_unregister(&cli_show_queue);
03981    res |= ast_cli_unregister(&cli_show_queues);
03982    res |= ast_cli_unregister(&cli_add_queue_member);
03983    res |= ast_cli_unregister(&cli_remove_queue_member);
03984    res |= ast_manager_unregister("Queues");
03985    res |= ast_manager_unregister("QueueStatus");
03986    res |= ast_manager_unregister("QueueAdd");
03987    res |= ast_manager_unregister("QueueRemove");
03988    res |= ast_manager_unregister("QueuePause");
03989    ast_devstate_del(statechange_queue, NULL);
03990    res |= ast_unregister_application(app_aqm);
03991    res |= ast_unregister_application(app_rqm);
03992    res |= ast_unregister_application(app_pqm);
03993    res |= ast_unregister_application(app_upqm);
03994    res |= ast_custom_function_unregister(&queueagentcount_function);
03995    res |= ast_unregister_application(app);
03996 
03997    STANDARD_HANGUP_LOCALUSERS;
03998 
03999    return res;
04000 }

static int update_dial_status struct call_queue q,
struct member member,
int  status
[static]
 

Definition at line 1441 of file app_queue.c.

References AST_CAUSE_BUSY, AST_CAUSE_NOSUCHDRIVER, AST_CAUSE_UNREGISTERED, AST_DEVICE_BUSY, AST_DEVICE_INVALID, AST_DEVICE_UNAVAILABLE, AST_DEVICE_UNKNOWN, and update_status().

Referenced by ring_entry(), and wait_for_answer().

01442 {
01443    if (status == AST_CAUSE_BUSY)
01444       status = AST_DEVICE_BUSY;
01445    else if (status == AST_CAUSE_UNREGISTERED)
01446       status = AST_DEVICE_UNAVAILABLE;
01447    else if (status == AST_CAUSE_NOSUCHDRIVER)
01448       status = AST_DEVICE_INVALID;
01449    else
01450       status = AST_DEVICE_UNKNOWN;
01451    return update_status(q, member, status);
01452 }

static int update_queue struct call_queue q,
struct member member
[static]
 

Definition at line 2080 of file app_queue.c.

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

02081 {
02082    struct member *cur;
02083 
02084    /* Since a reload could have taken place, we have to traverse the list to
02085       be sure it's still valid */
02086    ast_mutex_lock(&q->lock);
02087    cur = q->members;
02088    while(cur) {
02089       if (member == cur) {
02090          time(&cur->lastcall);
02091          cur->calls++;
02092          break;
02093       }
02094       cur = cur->next;
02095    }
02096    q->callscompleted++;
02097    ast_mutex_unlock(&q->lock);
02098    return 0;
02099 }

static int update_status struct call_queue q,
struct member member,
int  status
[static]
 

Definition at line 1409 of file app_queue.c.

References ast_mutex_lock(), ast_mutex_unlock(), member::calls, member::dynamic, EVENT_FLAG_AGENT, member::interface, member::lastcall, call_queue::lock, manager_event(), call_queue::maskmemberstatus, call_queue::members, call_queue::name, member::next, member::paused, member::penalty, and member::status.

Referenced by update_dial_status().

01410 {
01411    struct member *cur;
01412 
01413    /* Since a reload could have taken place, we have to traverse the list to
01414       be sure it's still valid */
01415    ast_mutex_lock(&q->lock);
01416    cur = q->members;
01417    while(cur) {
01418       if (member == cur) {
01419          cur->status = status;
01420          if (!q->maskmemberstatus) {
01421             manager_event(EVENT_FLAG_AGENT, "QueueMemberStatus",
01422                "Queue: %s\r\n"
01423                "Location: %s\r\n"
01424                "Membership: %s\r\n"
01425                "Penalty: %d\r\n"
01426                "CallsTaken: %d\r\n"
01427                "LastCall: %d\r\n"
01428                "Status: %d\r\n"
01429                "Paused: %d\r\n",
01430             q->name, cur->interface, cur->dynamic ? "dynamic" : "static",
01431             cur->penalty, cur->calls, (int)cur->lastcall, cur->status, cur->paused);
01432          }
01433          break;
01434       }
01435       cur = cur->next;
01436    }
01437    ast_mutex_unlock(&q->lock);
01438    return 0;
01439 }

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

Definition at line 2808 of file app_queue.c.

References AST_APP_ARG, AST_DECLARE_APP_ARGS, ast_goto_if_exists(), ast_log(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), localuser::chan, ast_channel::context, localuser::interface, LOCAL_USER_ADD, LOCAL_USER_REMOVE, LOG_WARNING, option_priority_jumping, parse(), pbx_builtin_setvar_helper(), and set_member_paused().

Referenced by load_module().

02809 {
02810    struct localuser *u;
02811    char *parse;
02812    int priority_jump = 0;
02813    AST_DECLARE_APP_ARGS(args,
02814       AST_APP_ARG(queuename);
02815       AST_APP_ARG(interface);
02816       AST_APP_ARG(options);
02817    );
02818 
02819    if (ast_strlen_zero(data)) {
02820       ast_log(LOG_WARNING, "UnpauseQueueMember requires an argument ([queuename]|interface[|options])\n");
02821       return -1;
02822    }
02823 
02824    LOCAL_USER_ADD(u);
02825 
02826    if (!(parse = ast_strdupa(data))) {
02827       ast_log(LOG_WARNING, "Memory Error!\n");
02828       LOCAL_USER_REMOVE(u);
02829       return -1;
02830    }
02831 
02832    AST_STANDARD_APP_ARGS(args, parse);
02833 
02834    if (args.options) {
02835       if (strchr(args.options, 'j'))
02836          priority_jump = 1;
02837    }
02838 
02839    if (ast_strlen_zero(args.interface)) {
02840       ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename]|interface[|options])\n");
02841       LOCAL_USER_REMOVE(u);
02842       return -1;
02843    }
02844 
02845    if (set_member_paused(args.queuename, args.interface, 0)) {
02846       ast_log(LOG_WARNING, "Attempt to unpause interface %s, not found\n", args.interface);
02847       if (priority_jump || option_priority_jumping) {
02848          if (ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101)) {
02849             pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND");
02850             LOCAL_USER_REMOVE(u);
02851             return 0;
02852          }
02853       }
02854       LOCAL_USER_REMOVE(u);
02855       pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND");
02856       return -1;
02857    }
02858 
02859    LOCAL_USER_REMOVE(u);
02860    pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "UNPAUSED");
02861    return 0;
02862 }

int usecount void   ) 
 

Provides a usecount.

This function will be called by various parts of asterisk. Basically, all it has to do is to return a usecount when called. You will need to maintain your usecount within the module somewhere. The usecount should be how many channels provided by this module are in use.

Returns:
The module's usecount.

Definition at line 4044 of file app_queue.c.

References STANDARD_USECOUNT.

04045 {
04046    int res;
04047    STANDARD_USECOUNT(res);
04048    return res;
04049 }

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

Definition at line 1194 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, and queue_ent::digits.

Referenced by background_file(), queue_exec(), say_position(), and wait_for_answer().

01195 {
01196    int digitlen = strlen(qe->digits);
01197 
01198    /* Prevent possible buffer overflow */
01199    if (digitlen < sizeof(qe->digits) - 2) {
01200       qe->digits[digitlen] = digit;
01201       qe->digits[digitlen + 1] = '\0';
01202    } else {
01203       qe->digits[0] = '\0';
01204       return 0;
01205    }
01206 
01207    /* If there's no context to goto, short-circuit */
01208    if (ast_strlen_zero(qe->context))
01209       return 0;
01210 
01211    /* If the extension is bad, then reset the digits to blank */
01212    if (!ast_canmatch_extension(qe->chan, qe->context, qe->digits, 1, qe->chan->cid.cid_num)) {
01213       qe->digits[0] = '\0';
01214       return 0;
01215    }
01216 
01217    /* We have an exact match */
01218    if (!ast_goto_if_exists(qe->chan, qe->context, qe->digits, 1)) {
01219       /* Return 1 on a successful goto */
01220       return 1;
01221    }
01222    return 0;
01223 }

static int wait_a_bit struct queue_ent qe  )  [static]
 

Definition at line 2460 of file app_queue.c.

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

02461 {
02462    /* Don't need to hold the lock while we setup the outgoing calls */
02463    int retrywait = qe->parent->retry * 1000;
02464 
02465    return ast_waitfordigit(qe->chan, retrywait);
02466 }

static struct localuser* wait_for_answer struct queue_ent qe,
struct localuser outgoing,
int *  to,
char *  digit,
int  prebusies,
int  caller_disconnect
[static]
 

Definition at line 1777 of file app_queue.c.

References ast_channel::_state, ast_channel::accountcode, ast_call(), ast_cdr_busy(), ast_channel_inherit_variables(), AST_CONTROL_ANSWER, AST_CONTROL_BUSY, AST_CONTROL_CONGESTION, AST_CONTROL_HANGUP, AST_CONTROL_OFFHOOK, AST_CONTROL_RINGING, AST_FRAME_CONTROL, AST_FRAME_DTMF, AST_FRAME_VOICE, ast_frfree(), ast_hangup(), ast_indicate(), ast_log(), AST_MAX_WATCHERS, ast_read(), ast_request(), AST_STATE_UP, ast_strlen_zero(), ast_verbose(), ast_waitfor_n(), BUILD_WATCHERS, ast_channel::call_forward, ast_channel::cdr, ast_channel::cdrflags, localuser::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, ast_channel::exten, ast_frame::frametype, free, LOG_DEBUG, LOG_NOTICE, LOG_WARNING, ast_channel::macroexten, malloc, localuser::member, ast_channel::name, call_queue::name, ast_channel::nativeformats, localuser::next, localuser::oldstatus, option_verbose, queue_ent::parent, ring_one(), localuser::stillgoing, call_queue::strategy, strdup, ast_frame::subclass, ast_channel::tech, call_queue::timeoutrestart, update_dial_status(), valid_exit(), and VERBOSE_PREFIX_3.

01778 {
01779    char *queue = qe->parent->name;
01780    struct localuser *o;
01781    int found;
01782    int numlines;
01783    int status;
01784    int sentringing = 0;
01785    int numbusies = prebusies;
01786    int numnochan = 0;
01787    int stillgoing = 0;
01788    int orig = *to;
01789    struct ast_frame *f;
01790    struct localuser *peer = NULL;
01791    struct ast_channel *watchers[AST_MAX_WATCHERS];
01792    int pos;
01793    struct ast_channel *winner;
01794    struct ast_channel *in = qe->chan;
01795    
01796    while(*to && !peer) {
01797       BUILD_WATCHERS;
01798       if ((found < 0) && stillgoing && !qe->parent->strategy) {
01799          /* On "ringall" strategy we only move to the next penalty level
01800             when *all* ringing phones are done in the current penalty level */
01801          ring_one(qe, outgoing, &numbusies);
01802          BUILD_WATCHERS;
01803       }
01804       if (found < 0) {
01805          if (numlines == (numbusies + numnochan)) {
01806             ast_log(LOG_DEBUG, "Everyone is busy at this time\n");
01807          } else {
01808             ast_log(LOG_NOTICE, "No one is answering queue '%s' (%d/%d/%d)\n", queue, numlines, numbusies, numnochan);
01809          }
01810          *to = 0;
01811          return NULL;
01812       }
01813       winner = ast_waitfor_n(watchers, pos, to);
01814       o = outgoing;
01815       while(o) {
01816          if (o->stillgoing && (o->chan) &&  (o->chan->_state == AST_STATE_UP)) {
01817             if (!peer) {
01818                if (option_verbose > 2)
01819                   ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
01820                peer = o;
01821             }
01822          } else if (o->chan && (o->chan == winner)) {
01823             if (!ast_strlen_zero(o->chan->call_forward)) {
01824                char tmpchan[256]="";
01825                char *stuff;
01826                char *tech;
01827                ast_copy_string(tmpchan, o->chan->call_forward, sizeof(tmpchan));
01828                if ((stuff = strchr(tmpchan, '/'))) {
01829                   *stuff = '\0';
01830                   stuff++;
01831                   tech = tmpchan;
01832                } else {
01833                   snprintf(tmpchan, sizeof(tmpchan), "%s@%s", o->chan->call_forward, o->chan->context);
01834                   stuff = tmpchan;
01835                   tech = "Local";
01836                }
01837                /* Before processing channel, go ahead and check for forwarding */
01838                if (option_verbose > 2)
01839                   ast_verbose(VERBOSE_PREFIX_3 "Now forwarding %s to '%s/%s' (thanks to %s)\n", in->name, tech, stuff, o->chan->name);
01840                /* Setup parameters */
01841                o->chan = ast_request(tech, in->nativeformats, stuff, &status);
01842                if (status != o->oldstatus) 
01843                   update_dial_status(qe->parent, o->member, status);                
01844                if (!o->chan) {
01845                   ast_log(LOG_NOTICE, "Unable to create local channel for call forward to '%s/%s'\n", tech, stuff);
01846                   o->stillgoing = 0;
01847                   numnochan++;
01848                } else {
01849                   ast_channel_inherit_variables(in, o->chan);
01850                   if (o->chan->cid.cid_num)
01851                      free(o->chan->cid.cid_num);
01852                   o->chan->cid.cid_num = NULL;
01853                   if (o->chan->cid.cid_name)
01854                      free(o->chan->cid.cid_name);
01855                   o->chan->cid.cid_name = NULL;
01856 
01857                   if (in->cid.cid_num) {
01858                      o->chan->cid.cid_num = strdup(in->cid.cid_num);
01859                      if (!o->chan->cid.cid_num)
01860                         ast_log(LOG_WARNING, "Out of memory\n");  
01861                   }
01862                   if (in->cid.cid_name) {
01863                      o->chan->cid.cid_name = strdup(in->cid.cid_name);
01864                      if (!o->chan->cid.cid_name)
01865                         ast_log(LOG_WARNING, "Out of memory\n");  
01866                   }
01867                   ast_copy_string(o->chan->accountcode, in->accountcode, sizeof(o->chan->accountcode));
01868                   o->chan->cdrflags = in->cdrflags;
01869 
01870                   if (in->cid.cid_ani) {
01871                      if (o->chan->cid.cid_ani)
01872                         free(o->chan->cid.cid_ani);
01873                      o->chan->cid.cid_ani = malloc(strlen(in->cid.cid_ani) + 1);
01874                      if (o->chan->cid.cid_ani)
01875                         strncpy(o->chan->cid.cid_ani, in->cid.cid_ani, strlen(in->cid.cid_ani) + 1);
01876                      else
01877                         ast_log(LOG_WARNING, "Out of memory\n");
01878                   }
01879                   if (o->chan->cid.cid_rdnis) 
01880                      free(o->chan->cid.cid_rdnis);
01881                   if (!ast_strlen_zero(in->macroexten))
01882                      o->chan->cid.cid_rdnis = strdup(in->macroexten);
01883                   else
01884                      o->chan->cid.cid_rdnis = strdup(in->exten);
01885                   if (ast_call(o->chan, tmpchan, 0)) {
01886                      ast_log(LOG_NOTICE, "Failed to dial on local channel for call forward to '%s'\n", tmpchan);
01887                      o->stillgoing = 0;
01888                      ast_hangup(o->chan);
01889                      o->chan = NULL;
01890                      numnochan++;
01891                   }
01892                }
01893                /* Hangup the original channel now, in case we needed it */
01894                ast_hangup(winner);
01895                continue;
01896             }
01897             f = ast_read(winner);
01898             if (f) {
01899                if (f->frametype == AST_FRAME_CONTROL) {
01900                   switch(f->subclass) {
01901                   case AST_CONTROL_ANSWER:
01902                      /* This is our guy if someone answered. */
01903                      if (!peer) {
01904                         if (option_verbose > 2)
01905                            ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
01906                         peer = o;
01907                      }
01908                      break;
01909                   case AST_CONTROL_BUSY:
01910                      if (option_verbose > 2)
01911                         ast_verbose( VERBOSE_PREFIX_3 "%s is busy\n", o->chan->name);
01912                      o->stillgoing = 0;
01913                      if (in->cdr)
01914                         ast_cdr_busy(in->cdr);
01915                      ast_hangup(o->chan);
01916                      o->chan = NULL;
01917                      if (qe->parent->strategy) {
01918                         if (qe->parent->timeoutrestart)
01919                            *to = orig;
01920                         ring_one(qe, outgoing, &numbusies);
01921                      }
01922                      numbusies++;
01923                      break;
01924                   case AST_CONTROL_CONGESTION:
01925                      if (option_verbose > 2)
01926                         ast_verbose( VERBOSE_PREFIX_3 "%s is circuit-busy\n", o->chan->name);
01927                      o->stillgoing = 0;
01928                      if (in->cdr)
01929                         ast_cdr_busy(in->cdr);
01930                      ast_hangup(o->chan);
01931                      o->chan = NULL;
01932                      if (qe->parent->strategy) {
01933                         if (qe->parent->timeoutrestart)
01934                            *to = orig;
01935                         ring_one(qe, outgoing, &numbusies);
01936                      }
01937                      numbusies++;
01938                      break;
01939                   case AST_CONTROL_RINGING:
01940                      if (option_verbose > 2)
01941                         ast_verbose( VERBOSE_PREFIX_3 "%s is ringing\n", o->chan->name);
01942                      if (!sentringing) {
01943 #if 0
01944                         ast_indicate(in, AST_CONTROL_RINGING);
01945 #endif                        
01946                         sentringing++;
01947                      }
01948                      break;
01949                   case AST_CONTROL_OFFHOOK:
01950                      /* Ignore going off hook */
01951                      break;
01952                   default:
01953                      ast_log(LOG_DEBUG, "Dunno what to do with control type %d\n", f->subclass);
01954                   }
01955                }
01956                ast_frfree(f);
01957             } else {
01958                o->stillgoing = 0;
01959                ast_hangup(o->chan);
01960                o->chan = NULL;
01961                if (qe->parent->strategy) {
01962                   if (qe->parent->timeoutrestart)
01963                      *to = orig;
01964                   ring_one(qe, outgoing, &numbusies);
01965                }
01966             }
01967          }
01968          o = o->next;
01969       }
01970       if (winner == in) {
01971          f = ast_read(in);
01972 #if 0
01973          if (f && (f->frametype != AST_FRAME_VOICE))
01974                printf("Frame type: %d, %d\n", f->frametype, f->subclass);
01975          else if (!f || (f->frametype != AST_FRAME_VOICE))
01976             printf("Hangup received on %s\n", in->name);
01977 #endif
01978          if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) {
01979             /* Got hung up */
01980             *to=-1;
01981             if (f)
01982                ast_frfree(f);
01983             return NULL;
01984          }
01985          if ((f->frametype == AST_FRAME_DTMF) && caller_disconnect && (f->subclass == '*')) {
01986             if (option_verbose > 3)
01987                ast_verbose(VERBOSE_PREFIX_3 "User hit %c to disconnect call.\n", f->subclass);
01988             *to=0;
01989             ast_frfree(f);
01990             return NULL;
01991          }
01992          if ((f->frametype == AST_FRAME_DTMF) && (f->subclass != '*') && valid_exit(qe, f->subclass)) {
01993             if (option_verbose > 3)
01994                ast_verbose(VERBOSE_PREFIX_3 "User pressed digit: %c\n", f->subclass);
01995             *to=0;
01996             *digit=f->subclass;
01997             ast_frfree(f);
01998             return NULL;
01999          }
02000          ast_frfree(f);
02001       }
02002       if (!*to && (option_verbose > 2))
02003          ast_verbose( VERBOSE_PREFIX_3 "Nobody picked up in %d ms\n", orig);
02004    }
02005 
02006    return peer;
02007    
02008 }

static int wait_our_turn struct queue_ent qe,
int  ringing,
enum queue_result reason
[static]
 

Definition at line 2030 of file app_queue.c.

References is_our_turn(), and QUEUE_TIMEOUT.

Referenced by queue_exec().

02031 {
02032    int res = 0;
02033 
02034    /* This is the holding pen for callers 2 through maxlen */
02035    for (;;) {
02036       enum queue_member_status stat;
02037 
02038       if (is_our_turn(qe))
02039          break;
02040 
02041       /* If we have timed out, break out */
02042       if (qe->expire && (time(NULL) > qe->expire)) {
02043          *reason = QUEUE_TIMEOUT;
02044          break;
02045       }
02046 
02047       stat = get_member_status(qe->parent);
02048 
02049       /* leave the queue if no agents, if enabled */
02050       if (qe->parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) {
02051          *reason = QUEUE_LEAVEEMPTY;
02052          leave_queue(qe);
02053          break;
02054       }
02055 
02056       /* leave the queue if no reachable agents, if enabled */
02057       if ((qe->parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) {
02058          *reason = QUEUE_LEAVEUNAVAIL;
02059          leave_queue(qe);
02060          break;
02061       }
02062 
02063       /* Make a position announcement, if enabled */
02064       if (qe->parent->announcefrequency && !ringing &&
02065           (res = say_position(qe)))
02066          break;
02067 
02068       /* Make a periodic announcement, if enabled */
02069       if (qe->parent->periodicannouncefrequency && !ringing &&
02070           (res = say_periodic_announcement(qe)))
02071          break;
02072 
02073       /* Wait a second before checking again */
02074       if ((res = ast_waitfordigit(qe->chan, RECHECK * 1000)))
02075          break;
02076    }
02077    return res;
02078 }


Variable Documentation

char* app = "Queue" [static]
 

Definition at line 123 of file app_queue.c.

char* app_aqm = "AddQueueMember" [static]
 

Definition at line 153 of file app_queue.c.

Referenced by load_module(), and unload_module().

char* app_aqm_descrip [static]
 

Definition at line 155 of file app_queue.c.

Referenced by load_module().

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

Definition at line 154 of file app_queue.c.

Referenced by load_module().

char* app_pqm = "PauseQueueMember" [static]
 

Definition at line 185 of file app_queue.c.

Referenced by load_module(), and unload_module().

char* app_pqm_descrip [static]
 

Definition at line 187 of file app_queue.c.

Referenced by load_module().

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

Definition at line 186 of file app_queue.c.

Referenced by load_module().

char* app_rqm = "RemoveQueueMember" [static]
 

Definition at line 169 of file app_queue.c.

Referenced by load_module(), and unload_module().

char* app_rqm_descrip [static]
 

Definition at line 171 of file app_queue.c.

Referenced by load_module().

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

Definition at line 170 of file app_queue.c.

Referenced by load_module().

char* app_upqm = "UnpauseQueueMember" [static]
 

Definition at line 207 of file app_queue.c.

Referenced by load_module(), and unload_module().

char* app_upqm_descrip [static]
 

Definition at line 209 of file app_queue.c.

Referenced by load_module().

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

Definition at line 208 of file app_queue.c.

Referenced by load_module().

char aqm_cmd_usage[] [static]
 

Initial value:

"Usage: add queue member <channel> to <queue> [penalty <penalty>]\n"

Definition at line 3961 of file app_queue.c.

struct ast_cli_entry cli_add_queue_member [static]
 

Initial value:

 {
   { "add", "queue", "member", NULL }, handle_add_queue_member,
   "Add a channel to a specified queue", aqm_cmd_usage, complete_add_queue_member }

Definition at line 3964 of file app_queue.c.

Referenced by load_module(), and unload_module().

struct ast_cli_entry cli_remove_queue_member [static]
 

Initial value:

 {
   { "remove", "queue", "member", NULL }, handle_remove_queue_member,
   "Removes a channel from a specified queue", rqm_cmd_usage, complete_remove_queue_member }

Definition at line 3971 of file app_queue.c.

Referenced by load_module(), and unload_module().

struct ast_cli_entry cli_show_queue [static]
 

Initial value:

 {
   { "show", "queue", NULL }, queue_show, 
   "Show status of a specified queue", show_queue_usage, complete_queue }

Definition at line 3957 of file app_queue.c.

Referenced by load_module(), and unload_module().

struct ast_cli_entry cli_show_queues [static]
 

Initial value:

 {
   { "show", "queues", NULL }, queues_show, 
   "Show status of queues", show_queues_usage, NULL }

Definition at line 3949 of file app_queue.c.

Referenced by load_module(), and unload_module().

char* descrip [static]
 

Definition at line 127 of file app_queue.c.

enum queue_result id
 

Definition at line 244 of file app_queue.c.

Referenced by _sip_show_peers(), aPGSQL_clear(), aPGSQL_connect(), aPGSQL_disconnect(), aPGSQL_fetch(), aPGSQL_query(), aPGSQL_reset(), config_odbc(), and manager_iax2_show_peers().

LOCAL_USER_DECL
 

Definition at line 271 of file app_queue.c.

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

Persistent Members astdb family.

Definition at line 223 of file app_queue.c.

Referenced by dump_queue_members(), and reload_queue_members().

int queue_persistent_members = 0 [static]
 

queues.conf [general] option

Definition at line 228 of file app_queue.c.

Referenced by aqm_exec(), handle_add_queue_member(), load_module(), manager_add_queue_member(), reload_queues(), and set_member_paused().

const { ... } queue_results[]
 

Referenced by set_queue_result().

struct ast_custom_function queueagentcount_function [static]
 

Definition at line 3296 of file app_queue.c.

Referenced by load_module(), and unload_module().

struct call_queue* queues = NULL [static]
 

Definition at line 374 of file app_queue.c.

Referenced by __queues_show(), changethread(), compare_weight(), complete_queue(), complete_remove_queue_member(), find_queue_by_name_rt(), interface_exists_global(), load_realtime_queue(), manager_queues_status(), queue_function_qac(), reload_queue_members(), reload_queues(), remove_from_queue(), remove_queue(), and set_member_paused().

char rqm_cmd_usage[] [static]
 

Initial value:

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

Definition at line 3968 of file app_queue.c.

char show_queue_usage[] [static]
 

Initial value:

 
"Usage: show queue\n"
"       Provides summary information on a specified queue.\n"

Definition at line 3953 of file app_queue.c.

char show_queues_usage[] [static]
 

Initial value:

 
"Usage: show queues\n"
"       Provides summary information on call queues.\n"

Definition at line 3945 of file app_queue.c.

struct strategy strategies[] [static]
 

Referenced by int2strat(), and strat2int().

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

Definition at line 125 of file app_queue.c.

char* tdesc = "True Call Queueing" [static]
 

Definition at line 121 of file app_queue.c.

char* text
 

Definition at line 245 of file app_queue.c.

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

int use_weight = 0 [static]
 

queues.conf per-queue weight option

Definition at line 231 of file app_queue.c.

Referenced by queue_set_param(), and ring_entry().


Generated on Sat Nov 25 00:45:46 2006 for Asterisk - the Open Source PBX by  doxygen 1.4.6