Fri Sep 25 19:28:04 2009

Asterisk developer's documentation


app_queue.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2006, Digium, Inc.
00005  *
00006  * Mark Spencer <markster@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*! \file
00020  *
00021  * \brief True call queues with optional send URL on answer
00022  *
00023  * \author Mark Spencer <markster@digium.com>
00024  *
00025  * \arg Config in \ref Config_qu queues.conf
00026  *
00027  * \par Development notes
00028  * \note 2004-11-25: Persistent Dynamic Members added by:
00029  *             NetNation Communications (www.netnation.com)
00030  *             Kevin Lindsay <kevinl@netnation.com>
00031  *
00032  *             Each dynamic agent in each queue is now stored in the astdb.
00033  *             When asterisk is restarted, each agent will be automatically
00034  *             readded into their recorded queues. This feature can be
00035  *             configured with the 'persistent_members=<1|0>' setting in the
00036  *             '[general]' category in queues.conf. The default is on.
00037  *
00038  * \note 2004-06-04: Priorities in queues added by inAccess Networks (work funded by Hellas On Line (HOL) www.hol.gr).
00039  *
00040  * \note These features added by David C. Troy <dave@toad.net>:
00041  *    - Per-queue holdtime calculation
00042  *    - Estimated holdtime announcement
00043  *    - Position announcement
00044  *    - Abandoned/completed call counters
00045  *    - Failout timer passed as optional app parameter
00046  *    - Optional monitoring of calls, started when call is answered
00047  *
00048  * Patch Version 1.07 2003-12-24 01
00049  *
00050  * Added servicelevel statistic by Michiel Betel <michiel@betel.nl>
00051  * Added Priority jumping code for adding and removing queue members by Jonathan Stanton <asterisk@doilooklikeicare.com>
00052  *
00053  * Fixed to work with CVS as of 2004-02-25 and released as 1.07a
00054  * by Matthew Enger <m.enger@xi.com.au>
00055  *
00056  * \ingroup applications
00057  */
00058 
00059 /*** MODULEINFO
00060         <depend>res_monitor</depend>
00061  ***/
00062 
00063 #include "asterisk.h"
00064 
00065 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 95890 $")
00066 
00067 #include <stdlib.h>
00068 #include <errno.h>
00069 #include <unistd.h>
00070 #include <string.h>
00071 #include <stdlib.h>
00072 #include <stdio.h>
00073 #include <sys/time.h>
00074 #include <sys/signal.h>
00075 #include <netinet/in.h>
00076 
00077 #include "asterisk/lock.h"
00078 #include "asterisk/file.h"
00079 #include "asterisk/logger.h"
00080 #include "asterisk/channel.h"
00081 #include "asterisk/pbx.h"
00082 #include "asterisk/options.h"
00083 #include "asterisk/app.h"
00084 #include "asterisk/linkedlists.h"
00085 #include "asterisk/module.h"
00086 #include "asterisk/translate.h"
00087 #include "asterisk/say.h"
00088 #include "asterisk/features.h"
00089 #include "asterisk/musiconhold.h"
00090 #include "asterisk/cli.h"
00091 #include "asterisk/manager.h"
00092 #include "asterisk/config.h"
00093 #include "asterisk/monitor.h"
00094 #include "asterisk/utils.h"
00095 #include "asterisk/causes.h"
00096 #include "asterisk/astdb.h"
00097 #include "asterisk/devicestate.h"
00098 #include "asterisk/stringfields.h"
00099 #include "asterisk/astobj2.h"
00100 #include "asterisk/global_datastores.h"
00101 
00102 enum {
00103    QUEUE_STRATEGY_RINGALL = 0,
00104    QUEUE_STRATEGY_ROUNDROBIN,
00105    QUEUE_STRATEGY_LEASTRECENT,
00106    QUEUE_STRATEGY_FEWESTCALLS,
00107    QUEUE_STRATEGY_RANDOM,
00108    QUEUE_STRATEGY_RRMEMORY
00109 };
00110 
00111 static struct strategy {
00112    int strategy;
00113    char *name;
00114 } strategies[] = {
00115    { QUEUE_STRATEGY_RINGALL, "ringall" },
00116    { QUEUE_STRATEGY_ROUNDROBIN, "roundrobin" },
00117    { QUEUE_STRATEGY_LEASTRECENT, "leastrecent" },
00118    { QUEUE_STRATEGY_FEWESTCALLS, "fewestcalls" },
00119    { QUEUE_STRATEGY_RANDOM, "random" },
00120    { QUEUE_STRATEGY_RRMEMORY, "rrmemory" },
00121 };
00122 
00123 #define DEFAULT_RETRY      5
00124 #define DEFAULT_TIMEOUT    15
00125 #define RECHECK         1     /* Recheck every second to see we we're at the top yet */
00126 #define MAX_PERIODIC_ANNOUNCEMENTS 10 /* The maximum periodic announcements we can have */
00127 
00128 #define  RES_OKAY 0     /* Action completed */
00129 #define  RES_EXISTS  (-1)     /* Entry already exists */
00130 #define  RES_OUTOFMEMORY   (-2)     /* Out of memory */
00131 #define  RES_NOSUCHQUEUE   (-3)     /* No such queue */
00132 #define RES_NOT_DYNAMIC (-4)     /* Member is not dynamic */
00133 
00134 static char *app = "Queue";
00135 
00136 static char *synopsis = "Queue a call for a call queue";
00137 
00138 static char *descrip =
00139 "  Queue(queuename[|options[|URL][|announceoverride][|timeout][|AGI]):\n"
00140 "Queues an incoming call in a particular call queue as defined in queues.conf.\n"
00141 "This application will return to the dialplan if the queue does not exist, or\n"
00142 "any of the join options cause the caller to not enter the queue.\n"
00143 "The option string may contain zero or more of the following characters:\n"
00144 "      'd' -- data-quality (modem) call (minimum delay).\n"
00145 "      'h' -- allow callee to hang up by hitting *.\n"
00146 "      'H' -- allow caller to hang up by hitting *.\n"
00147 "      'n' -- no retries on the timeout; will exit this application and \n"
00148 "             go to the next step.\n"
00149 "      'i' -- ignore call forward requests from queue members and do nothing\n"
00150 "             when they are requested.\n"
00151 "      'r' -- ring instead of playing MOH\n"
00152 "      't' -- allow the called user transfer the calling user\n"
00153 "      'T' -- to allow the calling user to transfer the call.\n"
00154 "      'w' -- allow the called user to write the conversation to disk via Monitor\n"
00155 "      'W' -- allow the calling user to write the conversation to disk via Monitor\n"
00156 "  In addition to transferring the call, a call may be parked and then picked\n"
00157 "up by another user.\n"
00158 "  The optional URL will be sent to the called party if the channel supports\n"
00159 "it.\n"
00160 "  The optional AGI parameter will setup an AGI script to be executed on the \n"
00161 "calling party's channel once they are connected to a queue member.\n"
00162 "  The timeout will cause the queue to fail out after a specified number of\n"
00163 "seconds, checked between each queues.conf 'timeout' and 'retry' cycle.\n"
00164 "  This application sets the following channel variable upon completion:\n"
00165 "      QUEUESTATUS    The status of the call as a text string, one of\n"
00166 "             TIMEOUT | FULL | JOINEMPTY | LEAVEEMPTY | JOINUNAVAIL | LEAVEUNAVAIL\n";
00167 
00168 static char *app_aqm = "AddQueueMember" ;
00169 static char *app_aqm_synopsis = "Dynamically adds queue members" ;
00170 static char *app_aqm_descrip =
00171 "   AddQueueMember(queuename[|interface[|penalty[|options[|membername]]]]):\n"
00172 "Dynamically adds interface to an existing queue.\n"
00173 "If the interface is already in the queue and there exists an n+101 priority\n"
00174 "then it will then jump to this priority.  Otherwise it will return an error\n"
00175 "The option string may contain zero or more of the following characters:\n"
00176 "       'j' -- jump to +101 priority when appropriate.\n"
00177 "  This application sets the following channel variable upon completion:\n"
00178 "     AQMSTATUS    The status of the attempt to add a queue member as a \n"
00179 "                     text string, one of\n"
00180 "           ADDED | MEMBERALREADY | NOSUCHQUEUE \n"
00181 "Example: AddQueueMember(techsupport|SIP/3000)\n"
00182 "";
00183 
00184 static char *app_rqm = "RemoveQueueMember" ;
00185 static char *app_rqm_synopsis = "Dynamically removes queue members" ;
00186 static char *app_rqm_descrip =
00187 "   RemoveQueueMember(queuename[|interface[|options]]):\n"
00188 "Dynamically removes interface to an existing queue\n"
00189 "If the interface is NOT in the queue and there exists an n+101 priority\n"
00190 "then it will then jump to this priority.  Otherwise it will return an error\n"
00191 "The option string may contain zero or more of the following characters:\n"
00192 "       'j' -- jump to +101 priority when appropriate.\n"
00193 "  This application sets the following channel variable upon completion:\n"
00194 "     RQMSTATUS      The status of the attempt to remove a queue member as a\n"
00195 "                     text string, one of\n"
00196 "           REMOVED | NOTINQUEUE | NOSUCHQUEUE \n"
00197 "Example: RemoveQueueMember(techsupport|SIP/3000)\n"
00198 "";
00199 
00200 static char *app_pqm = "PauseQueueMember" ;
00201 static char *app_pqm_synopsis = "Pauses a queue member" ;
00202 static char *app_pqm_descrip =
00203 "   PauseQueueMember([queuename]|interface[|options]):\n"
00204 "Pauses (blocks calls for) a queue member.\n"
00205 "The given interface will be paused in the given queue.  This prevents\n"
00206 "any calls from being sent from the queue to the interface until it is\n"
00207 "unpaused with UnpauseQueueMember or the manager interface.  If no\n"
00208 "queuename is given, the interface is paused in every queue it is a\n"
00209 "member of.  If the interface is not in the named queue, or if no queue\n"
00210 "is given and the interface is not in any queue, it will jump to\n"
00211 "priority n+101, if it exists and the appropriate options are set.\n"
00212 "The application will fail if the interface is not found and no extension\n"
00213 "to jump to exists.\n"
00214 "The option string may contain zero or more of the following characters:\n"
00215 "       'j' -- jump to +101 priority when appropriate.\n"
00216 "  This application sets the following channel variable upon completion:\n"
00217 "     PQMSTATUS      The status of the attempt to pause a queue member as a\n"
00218 "                     text string, one of\n"
00219 "           PAUSED | NOTFOUND\n"
00220 "Example: PauseQueueMember(|SIP/3000)\n";
00221 
00222 static char *app_upqm = "UnpauseQueueMember" ;
00223 static char *app_upqm_synopsis = "Unpauses a queue member" ;
00224 static char *app_upqm_descrip =
00225 "   UnpauseQueueMember([queuename]|interface[|options]):\n"
00226 "Unpauses (resumes calls to) a queue member.\n"
00227 "This is the counterpart to PauseQueueMember and operates exactly the\n"
00228 "same way, except it unpauses instead of pausing the given interface.\n"
00229 "The option string may contain zero or more of the following characters:\n"
00230 "       'j' -- jump to +101 priority when appropriate.\n"
00231 "  This application sets the following channel variable upon completion:\n"
00232 "     UPQMSTATUS       The status of the attempt to unpause a queue \n"
00233 "                      member as a text string, one of\n"
00234 "            UNPAUSED | NOTFOUND\n"
00235 "Example: UnpauseQueueMember(|SIP/3000)\n";
00236 
00237 static char *app_ql = "QueueLog" ;
00238 static char *app_ql_synopsis = "Writes to the queue_log" ;
00239 static char *app_ql_descrip =
00240 "   QueueLog(queuename|uniqueid|agent|event[|additionalinfo]):\n"
00241 "Allows you to write your own events into the queue log\n"
00242 "Example: QueueLog(101|${UNIQUEID}|${AGENT}|WENTONBREAK|600)\n";
00243 
00244 /*! \brief Persistent Members astdb family */
00245 static const char *pm_family = "Queue/PersistentMembers";
00246 /* The maximum length of each persistent member queue database entry */
00247 #define PM_MAX_LEN 8192
00248 
00249 /*! \brief queues.conf [general] option */
00250 static int queue_persistent_members = 0;
00251 
00252 /*! \brief queues.conf per-queue weight option */
00253 static int use_weight = 0;
00254 
00255 /*! \brief queues.conf [general] option */
00256 static int autofill_default = 0;
00257 
00258 /*! \brief queues.conf [general] option */
00259 static int montype_default = 0;
00260 
00261 enum queue_result {
00262    QUEUE_UNKNOWN = 0,
00263    QUEUE_TIMEOUT = 1,
00264    QUEUE_JOINEMPTY = 2,
00265    QUEUE_LEAVEEMPTY = 3,
00266    QUEUE_JOINUNAVAIL = 4,
00267    QUEUE_LEAVEUNAVAIL = 5,
00268    QUEUE_FULL = 6,
00269 };
00270 
00271 const struct {
00272    enum queue_result id;
00273    char *text;
00274 } queue_results[] = {
00275    { QUEUE_UNKNOWN, "UNKNOWN" },
00276    { QUEUE_TIMEOUT, "TIMEOUT" },
00277    { QUEUE_JOINEMPTY,"JOINEMPTY" },
00278    { QUEUE_LEAVEEMPTY, "LEAVEEMPTY" },
00279    { QUEUE_JOINUNAVAIL, "JOINUNAVAIL" },
00280    { QUEUE_LEAVEUNAVAIL, "LEAVEUNAVAIL" },
00281    { QUEUE_FULL, "FULL" },
00282 };
00283 
00284 /*! \brief We define a custom "local user" structure because we
00285    use it not only for keeping track of what is in use but
00286    also for keeping track of who we're dialing.
00287 
00288    There are two "links" defined in this structure, q_next and call_next.
00289    q_next links ALL defined callattempt structures into a linked list. call_next is
00290    a link which allows for a subset of the callattempts to be traversed. This subset
00291    is used in wait_for_answer so that irrelevant callattempts are not traversed. This
00292    also is helpful so that queue logs are always accurate in the case where a call to 
00293    a member times out, especially if using the ringall strategy. */
00294 
00295 struct callattempt {
00296    struct callattempt *q_next;
00297    struct callattempt *call_next;
00298    struct ast_channel *chan;
00299    char interface[256];
00300    int stillgoing;
00301    int metric;
00302    int oldstatus;
00303    time_t lastcall;
00304    struct member *member;
00305 };
00306 
00307 
00308 struct queue_ent {
00309    struct call_queue *parent;          /*!< What queue is our parent */
00310    char moh[80];                       /*!< Name of musiconhold to be used */
00311    char announce[80];                  /*!< Announcement to play for member when call is answered */
00312    char context[AST_MAX_CONTEXT];      /*!< Context when user exits queue */
00313    char digits[AST_MAX_EXTENSION];     /*!< Digits entered while in queue */
00314    int valid_digits;        /*!< Digits entered correspond to valid extension. Exited */
00315    int pos;                            /*!< Where we are in the queue */
00316    int prio;                           /*!< Our priority */
00317    int last_pos_said;                  /*!< Last position we told the user */
00318    time_t last_periodic_announce_time; /*!< The last time we played a periodic announcement */
00319    int last_periodic_announce_sound;   /*!< The last periodic announcement we made */
00320    time_t last_pos;                    /*!< Last time we told the user their position */
00321    int opos;                           /*!< Where we started in the queue */
00322    int handled;                        /*!< Whether our call was handled */
00323    int pending;                        /*!< Non-zero if we are attempting to call a member */
00324    int max_penalty;                    /*!< Limit the members that can take this call to this penalty or lower */
00325    time_t start;                       /*!< When we started holding */
00326    time_t expire;                      /*!< When this entry should expire (time out of queue) */
00327    struct ast_channel *chan;           /*!< Our channel */
00328    struct queue_ent *next;             /*!< The next queue entry */
00329 };
00330 
00331 struct member {
00332    char interface[80];                 /*!< Technology/Location */
00333    char membername[80];                /*!< Member name to use in queue logs */
00334    int penalty;                        /*!< Are we a last resort? */
00335    int calls;                          /*!< Number of calls serviced by this member */
00336    int dynamic;                        /*!< Are we dynamically added? */
00337    int realtime;                       /*!< Is this member realtime? */
00338    int status;                         /*!< Status of queue member */
00339    int paused;                         /*!< Are we paused (not accepting calls)? */
00340    time_t lastcall;                    /*!< When last successful call was hungup */
00341    unsigned int dead:1;                /*!< Used to detect members deleted in realtime */
00342    unsigned int delme:1;               /*!< Flag to delete entry on reload */
00343 };
00344 
00345 struct member_interface {
00346    char interface[80];
00347    AST_LIST_ENTRY(member_interface) list;    /*!< Next call queue */
00348 };
00349 
00350 static AST_LIST_HEAD_STATIC(interfaces, member_interface);
00351 
00352 /* values used in multi-bit flags in call_queue */
00353 #define QUEUE_EMPTY_NORMAL 1
00354 #define QUEUE_EMPTY_STRICT 2
00355 #define ANNOUNCEHOLDTIME_ALWAYS 1
00356 #define ANNOUNCEHOLDTIME_ONCE 2
00357 #define QUEUE_EVENT_VARIABLES 3
00358 
00359 struct call_queue {
00360    ast_mutex_t lock; 
00361    char name[80];                      /*!< Name */
00362    char moh[80];                       /*!< Music On Hold class to be used */
00363    char announce[80];                  /*!< Announcement to play when call is answered */
00364    char context[AST_MAX_CONTEXT];      /*!< Exit context */
00365    unsigned int monjoin:1;
00366    unsigned int dead:1;
00367    unsigned int joinempty:2;
00368    unsigned int eventwhencalled:2;
00369    unsigned int leavewhenempty:2;
00370    unsigned int ringinuse:1;
00371    unsigned int setinterfacevar:1;
00372    unsigned int reportholdtime:1;
00373    unsigned int wrapped:1;
00374    unsigned int timeoutrestart:1;
00375    unsigned int announceholdtime:2;
00376    int strategy:4;
00377    unsigned int maskmemberstatus:1;
00378    unsigned int realtime:1;
00379    unsigned int found:1;
00380    int announcefrequency;              /*!< How often to announce their position */
00381    int periodicannouncefrequency;      /*!< How often to play periodic announcement */
00382    int roundingseconds;                /*!< How many seconds do we round to? */
00383    int holdtime;                       /*!< Current avg holdtime, based on recursive boxcar filter */
00384    int callscompleted;                 /*!< Number of queue calls completed */
00385    int callsabandoned;                 /*!< Number of queue calls abandoned */
00386    int servicelevel;                   /*!< seconds setting for servicelevel*/
00387    int callscompletedinsl;             /*!< Number of calls answered with servicelevel*/
00388    char monfmt[8];                     /*!< Format to use when recording calls */
00389    int montype;                        /*!< Monitor type  Monitor vs. MixMonitor */
00390    char sound_next[80];                /*!< Sound file: "Your call is now first in line" (def. queue-youarenext) */
00391    char sound_thereare[80];            /*!< Sound file: "There are currently" (def. queue-thereare) */
00392    char sound_calls[80];               /*!< Sound file: "calls waiting to speak to a representative." (def. queue-callswaiting)*/
00393    char sound_holdtime[80];            /*!< Sound file: "The current estimated total holdtime is" (def. queue-holdtime) */
00394    char sound_minutes[80];             /*!< Sound file: "minutes." (def. queue-minutes) */
00395    char sound_lessthan[80];            /*!< Sound file: "less-than" (def. queue-lessthan) */
00396    char sound_seconds[80];             /*!< Sound file: "seconds." (def. queue-seconds) */
00397    char sound_thanks[80];              /*!< Sound file: "Thank you for your patience." (def. queue-thankyou) */
00398    char sound_reporthold[80];          /*!< Sound file: "Hold time" (def. queue-reporthold) */
00399    char sound_periodicannounce[MAX_PERIODIC_ANNOUNCEMENTS][80];/*!< Sound files: Custom announce, no default */
00400 
00401    int count;                          /*!< How many entries */
00402    int maxlen;                         /*!< Max number of entries */
00403    int wrapuptime;                     /*!< Wrapup Time */
00404 
00405    int retry;                          /*!< Retry calling everyone after this amount of time */
00406    int timeout;                        /*!< How long to wait for an answer */
00407    int weight;                         /*!< Respective weight */
00408    int autopause;                      /*!< Auto pause queue members if they fail to answer */
00409 
00410    /* Queue strategy things */
00411    int rrpos;                          /*!< Round Robin - position */
00412    int memberdelay;                    /*!< Seconds to delay connecting member to caller */
00413    int autofill;                       /*!< Ignore the head call status and ring an available agent */
00414    
00415    struct ao2_container *members;             /*!< Head of the list of members */
00416    /*! 
00417     * \brief Number of members _logged in_
00418     * \note There will be members in the members container that are not logged
00419     *       in, so this can not simply be replaced with ao2_container_count(). 
00420     */
00421    int membercount;
00422    struct queue_ent *head;             /*!< Head of the list of callers */
00423    AST_LIST_ENTRY(call_queue) list;    /*!< Next call queue */
00424 };
00425 
00426 static AST_LIST_HEAD_STATIC(queues, call_queue);
00427 
00428 static int set_member_paused(const char *queuename, const char *interface, int paused);
00429 
00430 static void rr_dep_warning(void)
00431 {
00432    static unsigned int warned = 0;
00433 
00434    if (!warned) {
00435       ast_log(LOG_NOTICE, "The 'roundrobin' queue strategy is deprecated. Please use the 'rrmemory' strategy instead.\n");
00436       warned = 1;
00437    }
00438 }
00439 
00440 static void monjoin_dep_warning(void)
00441 {
00442    static unsigned int warned = 0;
00443    if (!warned) {
00444       ast_log(LOG_NOTICE, "The 'monitor-join' queue option is deprecated. Please use monitor-type=mixmonitor instead.\n");
00445       warned = 1;
00446    }
00447 }
00448 
00449 static void set_queue_result(struct ast_channel *chan, enum queue_result res)
00450 {
00451    int i;
00452 
00453    for (i = 0; i < sizeof(queue_results) / sizeof(queue_results[0]); i++) {
00454       if (queue_results[i].id == res) {
00455          pbx_builtin_setvar_helper(chan, "QUEUESTATUS", queue_results[i].text);
00456          return;
00457       }
00458    }
00459 }
00460 
00461 static char *int2strat(int strategy)
00462 {
00463    int x;
00464 
00465    for (x = 0; x < sizeof(strategies) / sizeof(strategies[0]); x++) {
00466       if (strategy == strategies[x].strategy)
00467          return strategies[x].name;
00468    }
00469 
00470    return "<unknown>";
00471 }
00472 
00473 static int strat2int(const char *strategy)
00474 {
00475    int x;
00476 
00477    for (x = 0; x < sizeof(strategies) / sizeof(strategies[0]); x++) {
00478       if (!strcasecmp(strategy, strategies[x].name))
00479          return strategies[x].strategy;
00480    }
00481 
00482    return -1;
00483 }
00484 
00485 /*! \brief Insert the 'new' entry after the 'prev' entry of queue 'q' */
00486 static inline void insert_entry(struct call_queue *q, struct queue_ent *prev, struct queue_ent *new, int *pos)
00487 {
00488    struct queue_ent *cur;
00489 
00490    if (!q || !new)
00491       return;
00492    if (prev) {
00493       cur = prev->next;
00494       prev->next = new;
00495    } else {
00496       cur = q->head;
00497       q->head = new;
00498    }
00499    new->next = cur;
00500    new->parent = q;
00501    new->pos = ++(*pos);
00502    new->opos = *pos;
00503 }
00504 
00505 enum queue_member_status {
00506    QUEUE_NO_MEMBERS,
00507    QUEUE_NO_REACHABLE_MEMBERS,
00508    QUEUE_NORMAL
00509 };
00510 
00511 static enum queue_member_status get_member_status(struct call_queue *q, int max_penalty)
00512 {
00513    struct member *member;
00514    struct ao2_iterator mem_iter;
00515    enum queue_member_status result = QUEUE_NO_MEMBERS;
00516 
00517    ast_mutex_lock(&q->lock);
00518    mem_iter = ao2_iterator_init(q->members, 0);
00519    while ((member = ao2_iterator_next(&mem_iter))) {
00520       if (max_penalty && (member->penalty > max_penalty)) {
00521          ao2_ref(member, -1);
00522          continue;
00523       }
00524 
00525       if (member->paused) {
00526          ao2_ref(member, -1);
00527          continue;
00528       }
00529 
00530       switch (member->status) {
00531       case AST_DEVICE_INVALID:
00532          /* nothing to do */
00533          ao2_ref(member, -1);
00534          break;
00535       case AST_DEVICE_UNAVAILABLE:
00536          result = QUEUE_NO_REACHABLE_MEMBERS;
00537          ao2_ref(member, -1);
00538          break;
00539       default:
00540          ast_mutex_unlock(&q->lock);
00541          ao2_ref(member, -1);
00542          return QUEUE_NORMAL;
00543       }
00544    }
00545 
00546    ast_mutex_unlock(&q->lock);
00547    return result;
00548 }
00549 
00550 struct statechange {
00551    AST_LIST_ENTRY(statechange) entry;
00552    int state;
00553    char dev[0];
00554 };
00555 
00556 static void *handle_statechange(struct statechange *sc)
00557 {
00558    struct call_queue *q;
00559    struct member *cur;
00560    struct ao2_iterator mem_iter;
00561    struct member_interface *curint;
00562    char *loc;
00563    char *technology;
00564 
00565    technology = ast_strdupa(sc->dev);
00566    loc = strchr(technology, '/');
00567    if (loc) {
00568       *loc++ = '\0';
00569    } else {
00570       return NULL;
00571    }
00572 
00573    AST_LIST_LOCK(&interfaces);
00574    AST_LIST_TRAVERSE(&interfaces, curint, list) {
00575       char *interface;
00576       char *slash_pos;
00577       interface = ast_strdupa(curint->interface);
00578       if ((slash_pos = strchr(interface, '/')))
00579          if ((slash_pos = strchr(slash_pos + 1, '/')))
00580             *slash_pos = '\0';
00581 
00582       if (!strcasecmp(interface, sc->dev))
00583          break;
00584    }
00585    AST_LIST_UNLOCK(&interfaces);
00586 
00587    if (!curint) {
00588       if (option_debug > 2)
00589          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));
00590       return NULL;
00591    }
00592 
00593    if (option_debug)
00594       ast_log(LOG_DEBUG, "Device '%s/%s' changed to state '%d' (%s)\n", technology, loc, sc->state, devstate2str(sc->state));
00595    AST_LIST_LOCK(&queues);
00596    AST_LIST_TRAVERSE(&queues, q, list) {
00597       ast_mutex_lock(&q->lock);
00598       mem_iter = ao2_iterator_init(q->members, 0);
00599       while ((cur = ao2_iterator_next(&mem_iter))) {
00600          char *interface;
00601          char *slash_pos;
00602          interface = ast_strdupa(cur->interface);
00603          if ((slash_pos = strchr(interface, '/')))
00604             if ((slash_pos = strchr(slash_pos + 1, '/')))
00605                *slash_pos = '\0';
00606 
00607          if (strcasecmp(sc->dev, interface)) {
00608             ao2_ref(cur, -1);
00609             continue;
00610          }
00611 
00612          if (cur->status != sc->state) {
00613             cur->status = sc->state;
00614             if (q->maskmemberstatus) {
00615                ao2_ref(cur, -1);
00616                continue;
00617             }
00618 
00619             manager_event(EVENT_FLAG_AGENT, "QueueMemberStatus",
00620                "Queue: %s\r\n"
00621                "Location: %s\r\n"
00622                "MemberName: %s\r\n"
00623                "Membership: %s\r\n"
00624                "Penalty: %d\r\n"
00625                "CallsTaken: %d\r\n"
00626                "LastCall: %d\r\n"
00627                "Status: %d\r\n"
00628                "Paused: %d\r\n",
00629                q->name, cur->interface, cur->membername, cur->dynamic ? "dynamic" : cur->realtime ? "realtime" : "static",
00630                cur->penalty, cur->calls, (int)cur->lastcall, cur->status, cur->paused);
00631          }
00632          ao2_ref(cur, -1);
00633       }
00634       ast_mutex_unlock(&q->lock);
00635    }
00636    AST_LIST_UNLOCK(&queues);
00637 
00638    return NULL;
00639 }
00640 
00641 /*!
00642  * \brief Data used by the device state thread
00643  */
00644 static struct {
00645    /*! Set to 1 to stop the thread */
00646    unsigned int stop:1;
00647    /*! The device state monitoring thread */
00648    pthread_t thread;
00649    /*! Lock for the state change queue */
00650    ast_mutex_t lock;
00651    /*! Condition for the state change queue */
00652    ast_cond_t cond;
00653    /*! Queue of state changes */
00654    AST_LIST_HEAD_NOLOCK(, statechange) state_change_q;
00655 } device_state = {
00656    .thread = AST_PTHREADT_NULL,
00657 };
00658 
00659 static void *device_state_thread(void *data)
00660 {
00661    struct statechange *sc = NULL;
00662 
00663    while (!device_state.stop) {
00664       ast_mutex_lock(&device_state.lock);
00665       if (!(sc = AST_LIST_REMOVE_HEAD(&device_state.state_change_q, entry))) {
00666          ast_cond_wait(&device_state.cond, &device_state.lock);
00667          sc = AST_LIST_REMOVE_HEAD(&device_state.state_change_q, entry);
00668       }
00669       ast_mutex_unlock(&device_state.lock);
00670 
00671       /* Check to see if we were woken up to see the request to stop */
00672       if (device_state.stop)
00673          break;
00674 
00675       if (!sc)
00676          continue;
00677 
00678       handle_statechange(sc);
00679 
00680       free(sc);
00681       sc = NULL;
00682    }
00683 
00684    if (sc)
00685       free(sc);
00686 
00687    while ((sc = AST_LIST_REMOVE_HEAD(&device_state.state_change_q, entry)))
00688       free(sc);
00689 
00690    return NULL;
00691 }
00692 
00693 static int statechange_queue(const char *dev, int state, void *ign, char *cid_num, char *cid_name)
00694 {
00695    struct statechange *sc;
00696 
00697    if (!(sc = ast_calloc(1, sizeof(*sc) + strlen(dev) + 1)))
00698       return 0;
00699 
00700    sc->state = state;
00701    strcpy(sc->dev, dev);
00702 
00703    ast_mutex_lock(&device_state.lock);
00704    AST_LIST_INSERT_TAIL(&device_state.state_change_q, sc, entry);
00705    ast_cond_signal(&device_state.cond);
00706    ast_mutex_unlock(&device_state.lock);
00707 
00708    return 0;
00709 }
00710 
00711 static struct member *create_queue_member(const char *interface, const char *membername, int penalty, int paused)
00712 {
00713    struct member *cur;
00714    
00715    if ((cur = ao2_alloc(sizeof(*cur), NULL))) {
00716       cur->penalty = penalty;
00717       cur->paused = paused;
00718       ast_copy_string(cur->interface, interface, sizeof(cur->interface));
00719       if(!ast_strlen_zero(membername))
00720          ast_copy_string(cur->membername, membername, sizeof(cur->membername));
00721       else
00722          ast_copy_string(cur->membername, interface, sizeof(cur->membername));
00723       if (!strchr(cur->interface, '/'))
00724          ast_log(LOG_WARNING, "No location at interface '%s'\n", interface);
00725       cur->status = ast_device_state(interface);
00726    }
00727 
00728    return cur;
00729 }
00730 
00731 static struct call_queue *alloc_queue(const char *queuename)
00732 {
00733    struct call_queue *q;
00734 
00735    if ((q = ast_calloc(1, sizeof(*q)))) {
00736       ast_mutex_init(&q->lock);
00737       ast_copy_string(q->name, queuename, sizeof(q->name));
00738    }
00739    return q;
00740 }
00741 
00742 static int compress_char(const char c)
00743 {
00744    if (c < 32)
00745       return 0;
00746    else if (c > 96)
00747       return c - 64;
00748    else
00749       return c - 32;
00750 }
00751 
00752 static int member_hash_fn(const void *obj, const int flags)
00753 {
00754    const struct member *mem = obj;
00755    const char *chname = strchr(mem->interface, '/');
00756    int ret = 0, i;
00757    if (!chname)
00758       chname = mem->interface;
00759    for (i = 0; i < 5 && chname[i]; i++)
00760       ret += compress_char(chname[i]) << (i * 6);
00761    return ret;
00762 }
00763 
00764 static int member_cmp_fn(void *obj1, void *obj2, int flags)
00765 {
00766    struct member *mem1 = obj1, *mem2 = obj2;
00767    return strcmp(mem1->interface, mem2->interface) ? 0 : CMP_MATCH;
00768 }
00769 
00770 static void init_queue(struct call_queue *q)
00771 {
00772    int i;
00773 
00774    q->dead = 0;
00775    q->retry = DEFAULT_RETRY;
00776    q->timeout = -1;
00777    q->maxlen = 0;
00778    q->announcefrequency = 0;
00779    q->announceholdtime = 0;
00780    q->roundingseconds = 0; /* Default - don't announce seconds */
00781    q->servicelevel = 0;
00782    q->ringinuse = 1;
00783    q->setinterfacevar = 0;
00784    q->autofill = autofill_default;
00785    q->montype = montype_default;
00786    q->moh[0] = '\0';
00787    q->announce[0] = '\0';
00788    q->context[0] = '\0';
00789    q->monfmt[0] = '\0';
00790    q->periodicannouncefrequency = 0;
00791    if(!q->members)
00792       q->members = ao2_container_alloc(37, member_hash_fn, member_cmp_fn);
00793    q->membercount = 0;
00794    q->found = 1;
00795    ast_copy_string(q->sound_next, "queue-youarenext", sizeof(q->sound_next));
00796    ast_copy_string(q->sound_thereare, "queue-thereare", sizeof(q->sound_thereare));
00797    ast_copy_string(q->sound_calls, "queue-callswaiting", sizeof(q->sound_calls));
00798    ast_copy_string(q->sound_holdtime, "queue-holdtime", sizeof(q->sound_holdtime));
00799    ast_copy_string(q->sound_minutes, "queue-minutes", sizeof(q->sound_minutes));
00800    ast_copy_string(q->sound_seconds, "queue-seconds", sizeof(q->sound_seconds));
00801    ast_copy_string(q->sound_thanks, "queue-thankyou", sizeof(q->sound_thanks));
00802    ast_copy_string(q->sound_lessthan, "queue-less-than", sizeof(q->sound_lessthan));
00803    ast_copy_string(q->sound_reporthold, "queue-reporthold", sizeof(q->sound_reporthold));
00804    ast_copy_string(q->sound_periodicannounce[0], "queue-periodic-announce", sizeof(q->sound_periodicannounce[0]));
00805    for (i = 1; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) {
00806       q->sound_periodicannounce[i][0]='\0';
00807    }
00808 }
00809 
00810 static void clear_queue(struct call_queue *q)
00811 {
00812    q->holdtime = 0;
00813    q->callscompleted = 0;
00814    q->callsabandoned = 0;
00815    q->callscompletedinsl = 0;
00816    q->wrapuptime = 0;
00817 }
00818 
00819 static int add_to_interfaces(const char *interface)
00820 {
00821    struct member_interface *curint;
00822 
00823    AST_LIST_LOCK(&interfaces);
00824    AST_LIST_TRAVERSE(&interfaces, curint, list) {
00825       if (!strcasecmp(curint->interface, interface))
00826          break;
00827    }
00828 
00829    if (curint) {
00830       AST_LIST_UNLOCK(&interfaces);
00831       return 0;
00832    }
00833 
00834    if (option_debug)
00835       ast_log(LOG_DEBUG, "Adding %s to the list of interfaces that make up all of our queue members.\n", interface);
00836    
00837    if ((curint = ast_calloc(1, sizeof(*curint)))) {
00838       ast_copy_string(curint->interface, interface, sizeof(curint->interface));
00839       AST_LIST_INSERT_HEAD(&interfaces, curint, list);
00840    }
00841    AST_LIST_UNLOCK(&interfaces);
00842 
00843    return 0;
00844 }
00845 
00846 static int interface_exists_global(const char *interface)
00847 {
00848    struct call_queue *q;
00849    struct member *mem, tmpmem;
00850    int ret = 0;
00851 
00852    ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
00853 
00854    AST_LIST_LOCK(&queues);
00855    AST_LIST_TRAVERSE(&queues, q, list) {
00856       ast_mutex_lock(&q->lock);
00857       if ((mem = ao2_find(q->members, &tmpmem, OBJ_POINTER))) {
00858          ao2_ref(mem, -1);
00859          ret = 1;
00860       }
00861       ast_mutex_unlock(&q->lock);
00862       if (ret)
00863          break;
00864    }
00865    AST_LIST_UNLOCK(&queues);
00866 
00867    return ret;
00868 }
00869 
00870 static int remove_from_interfaces(const char *interface)
00871 {
00872    struct member_interface *curint;
00873 
00874    AST_LIST_LOCK(&interfaces);
00875    AST_LIST_TRAVERSE_SAFE_BEGIN(&interfaces, curint, list) {
00876       if (!strcasecmp(curint->interface, interface)) {
00877          if (!interface_exists_global(interface)) {
00878             if (option_debug)
00879                ast_log(LOG_DEBUG, "Removing %s from the list of interfaces that make up all of our queue members.\n", interface);
00880             AST_LIST_REMOVE_CURRENT(&interfaces, list);
00881             free(curint);
00882          }
00883          break;
00884       }
00885    }
00886    AST_LIST_TRAVERSE_SAFE_END;
00887    AST_LIST_UNLOCK(&interfaces);
00888 
00889    return 0;
00890 }
00891 
00892 static void clear_and_free_interfaces(void)
00893 {
00894    struct member_interface *curint;
00895 
00896    AST_LIST_LOCK(&interfaces);
00897    while ((curint = AST_LIST_REMOVE_HEAD(&interfaces, list)))
00898       free(curint);
00899    AST_LIST_UNLOCK(&interfaces);
00900 }
00901 
00902 /*! \brief Configure a queue parameter.
00903 \par
00904    For error reporting, line number is passed for .conf static configuration.
00905    For Realtime queues, linenum is -1.
00906    The failunknown flag is set for config files (and static realtime) to show
00907    errors for unknown parameters. It is cleared for dynamic realtime to allow
00908    extra fields in the tables. */
00909 static void queue_set_param(struct call_queue *q, const char *param, const char *val, int linenum, int failunknown)
00910 {
00911    if (!strcasecmp(param, "musicclass") || 
00912       !strcasecmp(param, "music") || !strcasecmp(param, "musiconhold")) {
00913       ast_copy_string(q->moh, val, sizeof(q->moh));
00914    } else if (!strcasecmp(param, "announce")) {
00915       ast_copy_string(q->announce, val, sizeof(q->announce));
00916    } else if (!strcasecmp(param, "context")) {
00917       ast_copy_string(q->context, val, sizeof(q->context));
00918    } else if (!strcasecmp(param, "timeout")) {
00919       q->timeout = atoi(val);
00920       if (q->timeout < 0)
00921          q->timeout = DEFAULT_TIMEOUT;
00922    } else if (!strcasecmp(param, "ringinuse")) {
00923       q->ringinuse = ast_true(val);
00924    } else if (!strcasecmp(param, "setinterfacevar")) {
00925       q->setinterfacevar = ast_true(val);
00926    } else if (!strcasecmp(param, "monitor-join")) {
00927       monjoin_dep_warning();
00928       q->monjoin = ast_true(val);
00929    } else if (!strcasecmp(param, "monitor-format")) {
00930       ast_copy_string(q->monfmt, val, sizeof(q->monfmt));
00931    } else if (!strcasecmp(param, "queue-youarenext")) {
00932       ast_copy_string(q->sound_next, val, sizeof(q->sound_next));
00933    } else if (!strcasecmp(param, "queue-thereare")) {
00934       ast_copy_string(q->sound_thereare, val, sizeof(q->sound_thereare));
00935    } else if (!strcasecmp(param, "queue-callswaiting")) {
00936       ast_copy_string(q->sound_calls, val, sizeof(q->sound_calls));
00937    } else if (!strcasecmp(param, "queue-holdtime")) {
00938       ast_copy_string(q->sound_holdtime, val, sizeof(q->sound_holdtime));
00939    } else if (!strcasecmp(param, "queue-minutes")) {
00940       ast_copy_string(q->sound_minutes, val, sizeof(q->sound_minutes));
00941    } else if (!strcasecmp(param, "queue-seconds")) {
00942       ast_copy_string(q->sound_seconds, val, sizeof(q->sound_seconds));
00943    } else if (!strcasecmp(param, "queue-lessthan")) {
00944       ast_copy_string(q->sound_lessthan, val, sizeof(q->sound_lessthan));
00945    } else if (!strcasecmp(param, "queue-thankyou")) {
00946       ast_copy_string(q->sound_thanks, val, sizeof(q->sound_thanks));
00947    } else if (!strcasecmp(param, "queue-reporthold")) {
00948       ast_copy_string(q->sound_reporthold, val, sizeof(q->sound_reporthold));
00949    } else if (!strcasecmp(param, "announce-frequency")) {
00950       q->announcefrequency = atoi(val);
00951    } else if (!strcasecmp(param, "announce-round-seconds")) {
00952       q->roundingseconds = atoi(val);
00953       if (q->roundingseconds>60 || q->roundingseconds<0) {
00954          if (linenum >= 0) {
00955             ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
00956                "using 0 instead for queue '%s' at line %d of queues.conf\n",
00957                val, param, q->name, linenum);
00958          } else {
00959             ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
00960                "using 0 instead for queue '%s'\n", val, param, q->name);
00961          }
00962          q->roundingseconds=0;
00963       }
00964    } else if (!strcasecmp(param, "announce-holdtime")) {
00965       if (!strcasecmp(val, "once"))
00966          q->announceholdtime = ANNOUNCEHOLDTIME_ONCE;
00967       else if (ast_true(val))
00968          q->announceholdtime = ANNOUNCEHOLDTIME_ALWAYS;
00969       else
00970          q->announceholdtime = 0;
00971    } else if (!strcasecmp(param, "periodic-announce")) {
00972       if (strchr(val, '|')) {
00973          char *s, *buf = ast_strdupa(val);
00974          unsigned int i = 0;
00975 
00976          while ((s = strsep(&buf, "|"))) {
00977             ast_copy_string(q->sound_periodicannounce[i], s, sizeof(q->sound_periodicannounce[i]));
00978             i++;
00979             if (i == MAX_PERIODIC_ANNOUNCEMENTS)
00980                break;
00981          }
00982       } else {
00983          ast_copy_string(q->sound_periodicannounce[0], val, sizeof(q->sound_periodicannounce[0]));
00984       }
00985    } else if (!strcasecmp(param, "periodic-announce-frequency")) {
00986       q->periodicannouncefrequency = atoi(val);
00987    } else if (!strcasecmp(param, "retry")) {
00988       q->retry = atoi(val);
00989       if (q->retry <= 0)
00990          q->retry = DEFAULT_RETRY;
00991    } else if (!strcasecmp(param, "wrapuptime")) {
00992       q->wrapuptime = atoi(val);
00993    } else if (!strcasecmp(param, "autofill")) {
00994       q->autofill = ast_true(val);
00995    } else if (!strcasecmp(param, "monitor-type")) {
00996       if (!strcasecmp(val, "mixmonitor"))
00997          q->montype = 1;
00998    } else if (!strcasecmp(param, "autopause")) {
00999       q->autopause = ast_true(val);
01000    } else if (!strcasecmp(param, "maxlen")) {
01001       q->maxlen = atoi(val);
01002       if (q->maxlen < 0)
01003          q->maxlen = 0;
01004    } else if (!strcasecmp(param, "servicelevel")) {
01005       q->servicelevel= atoi(val);
01006    } else if (!strcasecmp(param, "strategy")) {
01007       q->strategy = strat2int(val);
01008       if (q->strategy < 0) {
01009          ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
01010             val, q->name);
01011          q->strategy = QUEUE_STRATEGY_RINGALL;
01012       }
01013    } else if (!strcasecmp(param, "joinempty")) {
01014       if (!strcasecmp(val, "strict"))
01015          q->joinempty = QUEUE_EMPTY_STRICT;
01016       else if (ast_true(val))
01017          q->joinempty = QUEUE_EMPTY_NORMAL;
01018       else
01019          q->joinempty = 0;
01020    } else if (!strcasecmp(param, "leavewhenempty")) {
01021       if (!strcasecmp(val, "strict"))
01022          q->leavewhenempty = QUEUE_EMPTY_STRICT;
01023       else if (ast_true(val))
01024          q->leavewhenempty = QUEUE_EMPTY_NORMAL;
01025       else
01026          q->leavewhenempty = 0;
01027    } else if (!strcasecmp(param, "eventmemberstatus")) {
01028       q->maskmemberstatus = !ast_true(val);
01029    } else if (!strcasecmp(param, "eventwhencalled")) {
01030       if (!strcasecmp(val, "vars")) {
01031          q->eventwhencalled = QUEUE_EVENT_VARIABLES;
01032       } else {
01033          q->eventwhencalled = ast_true(val) ? 1 : 0;
01034       }
01035    } else if (!strcasecmp(param, "reportholdtime")) {
01036       q->reportholdtime = ast_true(val);
01037    } else if (!strcasecmp(param, "memberdelay")) {
01038       q->memberdelay = atoi(val);
01039    } else if (!strcasecmp(param, "weight")) {
01040       q->weight = atoi(val);
01041       if (q->weight)
01042          use_weight++;
01043       /* With Realtime queues, if the last queue using weights is deleted in realtime,
01044          we will not see any effect on use_weight until next reload. */
01045    } else if (!strcasecmp(param, "timeoutrestart")) {
01046       q->timeoutrestart = ast_true(val);
01047    } else if (failunknown) {
01048       if (linenum >= 0) {
01049          ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s at line %d of queues.conf\n",
01050             q->name, param, linenum);
01051       } else {
01052          ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s\n", q->name, param);
01053       }
01054    }
01055 }
01056 
01057 static void rt_handle_member_record(struct call_queue *q, char *interface, const char *membername, const char *penalty_str, const char *paused_str)
01058 {
01059    struct member *m, tmpmem;
01060    int penalty = 0;
01061    int paused  = 0;
01062 
01063    if (penalty_str) {
01064       penalty = atoi(penalty_str);
01065       if (penalty < 0)
01066          penalty = 0;
01067    }
01068 
01069    if (paused_str) {
01070       paused = atoi(paused_str);
01071       if (paused < 0)
01072          paused = 0;
01073    }
01074 
01075    /* Find the member, or the place to put a new one. */
01076    ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
01077    m = ao2_find(q->members, &tmpmem, OBJ_POINTER);
01078 
01079    /* Create a new one if not found, else update penalty */
01080    if (!m) {
01081       if ((m = create_queue_member(interface, membername, penalty, paused))) {
01082          m->dead = 0;
01083          m->realtime = 1;
01084          add_to_interfaces(interface);
01085          ao2_link(q->members, m);
01086          ao2_ref(m, -1);
01087          m = NULL;
01088          q->membercount++;
01089       }
01090    } else {
01091       m->dead = 0;   /* Do not delete this one. */
01092       if (paused_str)
01093          m->paused = paused;
01094       m->penalty = penalty;
01095       ao2_ref(m, -1);
01096    }
01097 }
01098 
01099 static void free_members(struct call_queue *q, int all)
01100 {
01101    /* Free non-dynamic members */
01102    struct member *cur;
01103    struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
01104 
01105    while ((cur = ao2_iterator_next(&mem_iter))) {
01106       if (all || !cur->dynamic) {
01107          ao2_unlink(q->members, cur);
01108          remove_from_interfaces(cur->interface);
01109          q->membercount--;
01110       }
01111       ao2_ref(cur, -1);
01112    }
01113 }
01114 
01115 static void destroy_queue(struct call_queue *q)
01116 {
01117    free_members(q, 1);
01118    ast_mutex_destroy(&q->lock);
01119    ao2_ref(q->members, -1);
01120    free(q);
01121 }
01122 
01123 /*!\brief Reload a single queue via realtime.
01124    \return Return the queue, or NULL if it doesn't exist.
01125    \note Should be called with the global qlock locked. */
01126 static struct call_queue *find_queue_by_name_rt(const char *queuename, struct ast_variable *queue_vars, struct ast_config *member_config)
01127 {
01128    struct ast_variable *v;
01129    struct call_queue *q;
01130    struct member *m;
01131    struct ao2_iterator mem_iter;
01132    char *interface = NULL;
01133    char *tmp, *tmp_name;
01134    char tmpbuf[64];  /* Must be longer than the longest queue param name. */
01135 
01136    /* Find the queue in the in-core list (we will create a new one if not found). */
01137    AST_LIST_TRAVERSE(&queues, q, list) {
01138       if (!strcasecmp(q->name, queuename))
01139          break;
01140    }
01141 
01142    /* Static queues override realtime. */
01143    if (q) {
01144       ast_mutex_lock(&q->lock);
01145       if (!q->realtime) {
01146          if (q->dead) {
01147             ast_mutex_unlock(&q->lock);
01148             return NULL;
01149          } else {
01150             ast_log(LOG_WARNING, "Static queue '%s' already exists. Not loading from realtime\n", q->name);
01151             ast_mutex_unlock(&q->lock);
01152             return q;
01153          }
01154       }
01155    } else if (!member_config)
01156       /* Not found in the list, and it's not realtime ... */
01157       return NULL;
01158 
01159    /* Check if queue is defined in realtime. */
01160    if (!queue_vars) {
01161       /* Delete queue from in-core list if it has been deleted in realtime. */
01162       if (q) {
01163          /*! \note Hmm, can't seem to distinguish a DB failure from a not
01164             found condition... So we might delete an in-core queue
01165             in case of DB failure. */
01166          ast_log(LOG_DEBUG, "Queue %s not found in realtime.\n", queuename);
01167 
01168          q->dead = 1;
01169          /* Delete if unused (else will be deleted when last caller leaves). */
01170          if (!q->count) {
01171             /* Delete. */
01172             AST_LIST_REMOVE(&queues, q, list);
01173             ast_mutex_unlock(&q->lock);
01174             destroy_queue(q);
01175          } else
01176             ast_mutex_unlock(&q->lock);
01177       }
01178       return NULL;
01179    }
01180 
01181    /* Create a new queue if an in-core entry does not exist yet. */
01182    if (!q) {
01183       if (!(q = alloc_queue(queuename)))
01184          return NULL;
01185       ast_mutex_lock(&q->lock);
01186       clear_queue(q);
01187       q->realtime = 1;
01188       init_queue(q);    /* Ensure defaults for all parameters not set explicitly. */
01189       AST_LIST_INSERT_HEAD(&queues, q, list);
01190    }
01191 
01192    memset(tmpbuf, 0, sizeof(tmpbuf));
01193    for (v = queue_vars; v; v = v->next) {
01194       /* Convert to dashes `-' from underscores `_' as the latter are more SQL friendly. */
01195       if ((tmp = strchr(v->name, '_'))) {
01196          ast_copy_string(tmpbuf, v->name, sizeof(tmpbuf));
01197          tmp_name = tmpbuf;
01198          tmp = tmp_name;
01199          while ((tmp = strchr(tmp, '_')))
01200             *tmp++ = '-';
01201       } else
01202          tmp_name = v->name;
01203       queue_set_param(q, tmp_name, v->value, -1, 0);
01204    }
01205 
01206    if (q->strategy == QUEUE_STRATEGY_ROUNDROBIN)
01207       rr_dep_warning();
01208 
01209    /* Temporarily set realtime members dead so we can detect deleted ones. 
01210     * Also set the membercount correctly for realtime*/
01211    mem_iter = ao2_iterator_init(q->members, 0);
01212    while ((m = ao2_iterator_next(&mem_iter))) {
01213       q->membercount++;
01214       if (m->realtime)
01215          m->dead = 1;
01216       ao2_ref(m, -1);
01217    }
01218 
01219    while ((interface = ast_category_browse(member_config, interface))) {
01220       rt_handle_member_record(q, interface,
01221          ast_variable_retrieve(member_config, interface, "membername"),
01222          ast_variable_retrieve(member_config, interface, "penalty"),
01223          ast_variable_retrieve(member_config, interface, "paused"));
01224    }
01225 
01226    /* Delete all realtime members that have been deleted in DB. */
01227    mem_iter = ao2_iterator_init(q->members, 0);
01228    while ((m = ao2_iterator_next(&mem_iter))) {
01229       if (m->dead) {
01230          ao2_unlink(q->members, m);
01231          ast_mutex_unlock(&q->lock);
01232          remove_from_interfaces(m->interface);
01233          ast_mutex_lock(&q->lock);
01234          q->membercount--;
01235       }
01236       ao2_ref(m, -1);
01237    }
01238 
01239    ast_mutex_unlock(&q->lock);
01240 
01241    return q;
01242 }
01243 
01244 static int update_realtime_member_field(struct member *mem, const char *queue_name, const char *field, const char *value)
01245 {
01246    struct ast_variable *var;
01247    int ret = -1;
01248 
01249    if(!(var = ast_load_realtime("queue_members", "interface", mem->interface, "queue_name", queue_name, NULL))) 
01250       return ret;
01251    while (var) {
01252       if(!strcmp(var->name, "uniqueid"))
01253          break;
01254       var = var->next;
01255    }
01256    if(var && !ast_strlen_zero(var->value)) {
01257       if ((ast_update_realtime("queue_members", "uniqueid", var->value, field, value, NULL)) > -1)
01258          ret = 0;
01259    }
01260    return ret;
01261 }
01262 
01263 static void update_realtime_members(struct call_queue *q)
01264 {
01265    struct ast_config *member_config = NULL;
01266    struct member *m;
01267    char *interface = NULL;
01268    struct ao2_iterator mem_iter;
01269 
01270    member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", q->name , NULL);
01271    if (!member_config) {
01272       /*This queue doesn't have realtime members*/
01273       if (option_debug > 2)
01274          ast_log(LOG_DEBUG, "Queue %s has no realtime members defined. No need for update\n", q->name);
01275       return;
01276    }
01277 
01278    ast_mutex_lock(&q->lock);
01279    
01280    /* Temporarily set realtime  members dead so we can detect deleted ones.*/ 
01281    mem_iter = ao2_iterator_init(q->members, 0);
01282    while ((m = ao2_iterator_next(&mem_iter))) {
01283       if (m->realtime)
01284          m->dead = 1;
01285       ao2_ref(m, -1);
01286    }
01287 
01288    while ((interface = ast_category_browse(member_config, interface))) {
01289       rt_handle_member_record(q, interface,
01290          S_OR(ast_variable_retrieve(member_config, interface, "membername"), interface),
01291          ast_variable_retrieve(member_config, interface, "penalty"),
01292          ast_variable_retrieve(member_config, interface, "paused"));
01293    }
01294 
01295    /* Delete all realtime members that have been deleted in DB. */
01296    mem_iter = ao2_iterator_init(q->members, 0);
01297    while ((m = ao2_iterator_next(&mem_iter))) {
01298       if (m->dead) {
01299          ao2_unlink(q->members, m);
01300          ast_mutex_unlock(&q->lock);
01301          remove_from_interfaces(m->interface);
01302          ast_mutex_lock(&q->lock);
01303          q->membercount--;
01304       }
01305       ao2_ref(m, -1);
01306    }
01307    ast_mutex_unlock(&q->lock);
01308 }
01309 
01310 static struct call_queue *load_realtime_queue(const char *queuename)
01311 {
01312    struct ast_variable *queue_vars;
01313    struct ast_config *member_config = NULL;
01314    struct call_queue *q;
01315 
01316    /* Find the queue in the in-core list first. */
01317    AST_LIST_LOCK(&queues);
01318    AST_LIST_TRAVERSE(&queues, q, list) {
01319       if (!strcasecmp(q->name, queuename)) {
01320          break;
01321       }
01322    }
01323    AST_LIST_UNLOCK(&queues);
01324 
01325    if (!q || q->realtime) {
01326       /*! \note Load from realtime before taking the global qlock, to avoid blocking all
01327          queue operations while waiting for the DB.
01328 
01329          This will be two separate database transactions, so we might
01330          see queue parameters as they were before another process
01331          changed the queue and member list as it was after the change.
01332          Thus we might see an empty member list when a queue is
01333          deleted. In practise, this is unlikely to cause a problem. */
01334 
01335       queue_vars = ast_load_realtime("queues", "name", queuename, NULL);
01336       if (queue_vars) {
01337          member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", queuename, NULL);
01338          if (!member_config) {
01339             ast_log(LOG_ERROR, "no queue_members defined in your config (extconfig.conf).\n");
01340             ast_variables_destroy(queue_vars);
01341             return NULL;
01342          }
01343       }
01344 
01345       AST_LIST_LOCK(&queues);
01346 
01347       q = find_queue_by_name_rt(queuename, queue_vars, member_config);
01348       if (member_config)
01349          ast_config_destroy(member_config);
01350       if (queue_vars)
01351          ast_variables_destroy(queue_vars);
01352 
01353       AST_LIST_UNLOCK(&queues);
01354    } else { 
01355       update_realtime_members(q);
01356    }
01357    return q;
01358 }
01359 
01360 static int join_queue(char *queuename, struct queue_ent *qe, enum queue_result *reason)
01361 {
01362    struct call_queue *q;
01363    struct queue_ent *cur, *prev = NULL;
01364    int res = -1;
01365    int pos = 0;
01366    int inserted = 0;
01367    enum queue_member_status stat;
01368 
01369    if (!(q = load_realtime_queue(queuename)))
01370       return res;
01371 
01372    AST_LIST_LOCK(&queues);
01373    ast_mutex_lock(&q->lock);
01374 
01375    /* This is our one */
01376    stat = get_member_status(q, qe->max_penalty);
01377    if (!q->joinempty && (stat == QUEUE_NO_MEMBERS))
01378       *reason = QUEUE_JOINEMPTY;
01379    else if ((q->joinempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS))
01380       *reason = QUEUE_JOINUNAVAIL;
01381    else if (q->maxlen && (q->count >= q->maxlen))
01382       *reason = QUEUE_FULL;
01383    else {
01384       /* There's space for us, put us at the right position inside
01385        * the queue.
01386        * Take into account the priority of the calling user */
01387       inserted = 0;
01388       prev = NULL;
01389       cur = q->head;
01390       while (cur) {
01391          /* We have higher priority than the current user, enter
01392           * before him, after all the other users with priority
01393           * higher or equal to our priority. */
01394          if ((!inserted) && (qe->prio > cur->prio)) {
01395             insert_entry(q, prev, qe, &pos);
01396             inserted = 1;
01397          }
01398          cur->pos = ++pos;
01399          prev = cur;
01400          cur = cur->next;
01401       }
01402       /* No luck, join at the end of the queue */
01403       if (!inserted)
01404          insert_entry(q, prev, qe, &pos);
01405       ast_copy_string(qe->moh, q->moh, sizeof(qe->moh));
01406       ast_copy_string(qe->announce, q->announce, sizeof(qe->announce));
01407       ast_copy_string(qe->context, q->context, sizeof(qe->context));
01408       q->count++;
01409       res = 0;
01410       manager_event(EVENT_FLAG_CALL, "Join",
01411          "Channel: %s\r\nCallerID: %s\r\nCallerIDName: %s\r\nQueue: %s\r\nPosition: %d\r\nCount: %d\r\nUniqueid: %s\r\n",
01412          qe->chan->name,
01413          S_OR(qe->chan->cid.cid_num, "unknown"), /* XXX somewhere else it is <unknown> */
01414          S_OR(qe->chan->cid.cid_name, "unknown"),
01415          q->name, qe->pos, q->count, qe->chan->uniqueid );
01416       if (option_debug)
01417          ast_log(LOG_DEBUG, "Queue '%s' Join, Channel '%s', Position '%d'\n", q->name, qe->chan->name, qe->pos );
01418    }
01419    ast_mutex_unlock(&q->lock);
01420    AST_LIST_UNLOCK(&queues);
01421 
01422    return res;
01423 }
01424 
01425 static int play_file(struct ast_channel *chan, char *filename)
01426 {
01427    int res;
01428 
01429    ast_stopstream(chan);
01430 
01431    res = ast_streamfile(chan, filename, chan->language);
01432    if (!res)
01433       res = ast_waitstream(chan, AST_DIGIT_ANY);
01434 
01435    ast_stopstream(chan);
01436 
01437    return res;
01438 }
01439 
01440 static int valid_exit(struct queue_ent *qe, char digit)
01441 {
01442    int digitlen = strlen(qe->digits);
01443 
01444    /* Prevent possible buffer overflow */
01445    if (digitlen < sizeof(qe->digits) - 2) {
01446       qe->digits[digitlen] = digit;
01447       qe->digits[digitlen + 1] = '\0';
01448    } else {
01449       qe->digits[0] = '\0';
01450       return 0;
01451    }
01452 
01453    /* If there's no context to goto, short-circuit */
01454    if (ast_strlen_zero(qe->context))
01455       return 0;
01456 
01457    /* If the extension is bad, then reset the digits to blank */
01458    if (!ast_canmatch_extension(qe->chan, qe->context, qe->digits, 1, qe->chan->cid.cid_num)) {
01459       qe->digits[0] = '\0';
01460       return 0;
01461    }
01462 
01463    /* We have an exact match */
01464    if (!ast_goto_if_exists(qe->chan, qe->context, qe->digits, 1)) {
01465       qe->valid_digits = 1;
01466       /* Return 1 on a successful goto */
01467       return 1;
01468    }
01469 
01470    return 0;
01471 }
01472 
01473 static int say_position(struct queue_ent *qe)
01474 {
01475    int res = 0, avgholdmins, avgholdsecs;
01476    time_t now;
01477 
01478    /* Check to see if this is ludicrous -- if we just announced position, don't do it again*/
01479    time(&now);
01480    if ((now - qe->last_pos) < 15)
01481       return 0;
01482 
01483    /* If either our position has changed, or we are over the freq timer, say position */
01484    if ((qe->last_pos_said == qe->pos) && ((now - qe->last_pos) < qe->parent->announcefrequency))
01485       return 0;
01486 
01487    ast_moh_stop(qe->chan);
01488    /* Say we're next, if we are */
01489    if (qe->pos == 1) {
01490       res = play_file(qe->chan, qe->parent->sound_next);
01491       if (res)
01492          goto playout;
01493       else
01494          goto posout;
01495    } else {
01496       res = play_file(qe->chan, qe->parent->sound_thereare);
01497       if (res)
01498          goto playout;
01499       res = ast_say_number(qe->chan, qe->pos, AST_DIGIT_ANY, qe->chan->language, (char *) NULL); /* Needs gender */
01500       if (res)
01501          goto playout;
01502       res = play_file(qe->chan, qe->parent->sound_calls);
01503       if (res)
01504          goto playout;
01505    }
01506    /* Round hold time to nearest minute */
01507    avgholdmins = abs(((qe->parent->holdtime + 30) - (now - qe->start)) / 60);
01508 
01509    /* If they have specified a rounding then round the seconds as well */
01510    if (qe->parent->roundingseconds) {
01511       avgholdsecs = (abs(((qe->parent->holdtime + 30) - (now - qe->start))) - 60 * avgholdmins) / qe->parent->roundingseconds;
01512       avgholdsecs *= qe->parent->roundingseconds;
01513    } else {
01514       avgholdsecs = 0;
01515    }
01516 
01517    if (option_verbose > 2)
01518       ast_verbose(VERBOSE_PREFIX_3 "Hold time for %s is %d minutes %d seconds\n", qe->parent->name, avgholdmins, avgholdsecs);
01519 
01520    /* If the hold time is >1 min, if it's enabled, and if it's not
01521       supposed to be only once and we have already said it, say it */
01522    if ((avgholdmins+avgholdsecs) > 0 && (qe->parent->announceholdtime) &&
01523       (!(qe->parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE) && qe->last_pos)) {
01524       res = play_file(qe->chan, qe->parent->sound_holdtime);
01525       if (res)
01526          goto playout;
01527 
01528       if (avgholdmins > 0) {
01529          if (avgholdmins < 2) {
01530             res = play_file(qe->chan, qe->parent->sound_lessthan);
01531             if (res)
01532                goto playout;
01533 
01534             res = ast_say_number(qe->chan, 2, AST_DIGIT_ANY, qe->chan->language, NULL);
01535             if (res)
01536                goto playout;
01537          } else {
01538             res = ast_say_number(qe->chan, avgholdmins, AST_DIGIT_ANY, qe->chan->language, NULL);
01539             if (res)
01540                goto playout;
01541          }
01542          
01543          res = play_file(qe->chan, qe->parent->sound_minutes);
01544          if (res)
01545             goto playout;
01546       }
01547       if (avgholdsecs>0) {
01548          res = ast_say_number(qe->chan, avgholdsecs, AST_DIGIT_ANY, qe->chan->language, NULL);
01549          if (res)
01550             goto playout;
01551 
01552          res = play_file(qe->chan, qe->parent->sound_seconds);
01553          if (res)
01554             goto playout;
01555       }
01556 
01557    }
01558 
01559 posout:
01560    if (option_verbose > 2)
01561       ast_verbose(VERBOSE_PREFIX_3 "Told %s in %s their queue position (which was %d)\n",
01562          qe->chan->name, qe->parent->name, qe->pos);
01563    res = play_file(qe->chan, qe->parent->sound_thanks);
01564 
01565 playout:
01566    if ((res > 0 && !valid_exit(qe, res)) || res < 0)
01567       res = 0;
01568 
01569    /* Set our last_pos indicators */
01570    qe->last_pos = now;
01571    qe->last_pos_said = qe->pos;
01572 
01573    /* Don't restart music on hold if we're about to exit the caller from the queue */
01574    if (!res)
01575       ast_moh_start(qe->chan, qe->moh, NULL);
01576 
01577    return res;
01578 }
01579 
01580 static void recalc_holdtime(struct queue_ent *qe, int newholdtime)
01581 {
01582    int oldvalue;
01583 
01584    /* Calculate holdtime using a recursive boxcar filter */
01585    /* Thanks to SRT for this contribution */
01586    /* 2^2 (4) is the filter coefficient; a higher exponent would give old entries more weight */
01587 
01588    ast_mutex_lock(&qe->parent->lock);
01589    oldvalue = qe->parent->holdtime;
01590    qe->parent->holdtime = (((oldvalue << 2) - oldvalue) + newholdtime) >> 2;
01591    ast_mutex_unlock(&qe->parent->lock);
01592 }
01593 
01594 
01595 static void leave_queue(struct queue_ent *qe)
01596 {
01597    struct call_queue *q;
01598    struct queue_ent *cur, *prev = NULL;
01599    int pos = 0;
01600 
01601    if (!(q = qe->parent))
01602       return;
01603    ast_mutex_lock(&q->lock);
01604 
01605    prev = NULL;
01606    for (cur = q->head; cur; cur = cur->next) {
01607       if (cur == qe) {
01608          q->count--;
01609 
01610          /* Take us out of the queue */
01611          manager_event(EVENT_FLAG_CALL, "Leave",
01612             "Channel: %s\r\nQueue: %s\r\nCount: %d\r\nUniqueid: %s\r\n",
01613             qe->chan->name, q->name,  q->count, qe->chan->uniqueid);
01614          if (option_debug)
01615             ast_log(LOG_DEBUG, "Queue '%s' Leave, Channel '%s'\n", q->name, qe->chan->name );
01616          /* Take us out of the queue */
01617          if (prev)
01618             prev->next = cur->next;
01619          else
01620             q->head = cur->next;
01621       } else {
01622          /* Renumber the people after us in the queue based on a new count */
01623          cur->pos = ++pos;
01624          prev = cur;
01625       }
01626    }
01627    ast_mutex_unlock(&q->lock);
01628 
01629    if (q->dead && !q->count) {   
01630       /* It's dead and nobody is in it, so kill it */
01631       AST_LIST_LOCK(&queues);
01632       AST_LIST_REMOVE(&queues, q, list);
01633       AST_LIST_UNLOCK(&queues);
01634       destroy_queue(q);
01635    }
01636 }
01637 
01638 /* Hang up a list of outgoing calls */
01639 static void hangupcalls(struct callattempt *outgoing, struct ast_channel *exception)
01640 {
01641    struct callattempt *oo;
01642 
01643    while (outgoing) {
01644       /* Hangup any existing lines we have open */
01645       if (outgoing->chan && (outgoing->chan != exception))
01646          ast_hangup(outgoing->chan);
01647       oo = outgoing;
01648       outgoing = outgoing->q_next;
01649       if (oo->member)
01650          ao2_ref(oo->member, -1);
01651       free(oo);
01652    }
01653 }
01654 
01655 static int update_status(struct call_queue *q, struct member *member, int status)
01656 {
01657    struct member *cur;
01658    struct ao2_iterator mem_iter;
01659 
01660    /* Since a reload could have taken place, we have to traverse the list to
01661       be sure it's still valid */
01662    ast_mutex_lock(&q->lock);
01663    mem_iter = ao2_iterator_init(q->members, 0);
01664    while ((cur = ao2_iterator_next(&mem_iter))) {
01665       if (member != cur) {
01666          ao2_ref(cur, -1);
01667          continue;
01668       }
01669 
01670       cur->status = status;
01671       if (!q->maskmemberstatus) {
01672          manager_event(EVENT_FLAG_AGENT, "QueueMemberStatus",
01673             "Queue: %s\r\n"
01674             "Location: %s\r\n"
01675             "MemberName: %s\r\n"
01676             "Membership: %s\r\n"
01677             "Penalty: %d\r\n"
01678             "CallsTaken: %d\r\n"
01679             "LastCall: %d\r\n"
01680             "Status: %d\r\n"
01681             "Paused: %d\r\n",
01682             q->name, cur->interface, cur->membername, cur->dynamic ? "dynamic" : cur->realtime ? "realtime": "static",
01683             cur->penalty, cur->calls, (int)cur->lastcall, cur->status, cur->paused);
01684       }
01685       ao2_ref(cur, -1);
01686    }
01687    ast_mutex_unlock(&q->lock);
01688    return 0;
01689 }
01690 
01691 static int update_dial_status(struct call_queue *q, struct member *member, int status)
01692 {
01693    if (status == AST_CAUSE_BUSY)
01694       status = AST_DEVICE_BUSY;
01695    else if (status == AST_CAUSE_UNREGISTERED)
01696       status = AST_DEVICE_UNAVAILABLE;
01697    else if (status == AST_CAUSE_NOSUCHDRIVER)
01698       status = AST_DEVICE_INVALID;
01699    else
01700       status = AST_DEVICE_UNKNOWN;
01701    return update_status(q, member, status);
01702 }
01703 
01704 /* traverse all defined queues which have calls waiting and contain this member
01705    return 0 if no other queue has precedence (higher weight) or 1 if found  */
01706 static int compare_weight(struct call_queue *rq, struct member *member)
01707 {
01708    struct call_queue *q;
01709    struct member *mem;
01710    int found = 0;
01711    
01712    /* &qlock and &rq->lock already set by try_calling()
01713     * to solve deadlock */
01714    AST_LIST_TRAVERSE(&queues, q, list) {
01715       if (q == rq) /* don't check myself, could deadlock */
01716          continue;
01717       ast_mutex_lock(&q->lock);
01718       if (q->count && q->members) {
01719          if ((mem = ao2_find(q->members, member, OBJ_POINTER))) {
01720             ast_log(LOG_DEBUG, "Found matching member %s in queue '%s'\n", mem->interface, q->name);
01721             if (q->weight > rq->weight) {
01722                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);
01723                found = 1;
01724             }
01725             ao2_ref(mem, -1);
01726          }
01727       }
01728       ast_mutex_unlock(&q->lock);
01729       if (found)
01730          break;
01731    }
01732    return found;
01733 }
01734 
01735 /*! \brief common hangup actions */
01736 static void do_hang(struct callattempt *o)
01737 {
01738    o->stillgoing = 0;
01739    ast_hangup(o->chan);
01740    o->chan = NULL;
01741 }
01742 
01743 static char *vars2manager(struct ast_channel *chan, char *vars, size_t len)
01744 {
01745    char *tmp = alloca(len);
01746 
01747    if (pbx_builtin_serialize_variables(chan, tmp, len)) {
01748       int i, j;
01749 
01750       /* convert "\n" to "\nVariable: " */
01751       strcpy(vars, "Variable: ");
01752 
01753       for (i = 0, j = 10; (i < len - 1) && (j < len - 1); i++, j++) {
01754          vars[j] = tmp[i];
01755 
01756          if (tmp[i + 1] == '\0')
01757             break;
01758          if (tmp[i] == '\n') {
01759             vars[j++] = '\r';
01760             vars[j++] = '\n';
01761 
01762             ast_copy_string(&(vars[j]), "Variable: ", len - j);
01763             j += 9;
01764          }
01765       }
01766       if (j > len - 1)
01767          j = len - 1;
01768       vars[j - 2] = '\r';
01769       vars[j - 1] = '\n';
01770       vars[j] = '\0';
01771    } else {
01772       /* there are no channel variables; leave it blank */
01773       *vars = '\0';
01774    }
01775    return vars;
01776 }
01777 
01778 static int ring_entry(struct queue_ent *qe, struct callattempt *tmp, int *busies)
01779 {
01780    int res;
01781    int status;
01782    char tech[256];
01783    char *location;
01784 
01785    /* on entry here, we know that tmp->chan == NULL */
01786    if (qe->parent->wrapuptime && (time(NULL) - tmp->lastcall < qe->parent->wrapuptime)) {
01787       if (option_debug)
01788          ast_log(LOG_DEBUG, "Wrapuptime not yet expired for %s\n", tmp->interface);
01789       if (qe->chan->cdr)
01790          ast_cdr_busy(qe->chan->cdr);
01791       tmp->stillgoing = 0;
01792       (*busies)++;
01793       return 0;
01794    }
01795 
01796    if (!qe->parent->ringinuse && (tmp->member->status != AST_DEVICE_NOT_INUSE) && (tmp->member->status != AST_DEVICE_UNKNOWN)) {
01797       if (option_debug)
01798          ast_log(LOG_DEBUG, "%s in use, can't receive call\n", tmp->interface);
01799       if (qe->chan->cdr)
01800          ast_cdr_busy(qe->chan->cdr);
01801       tmp->stillgoing = 0;
01802       return 0;
01803    }
01804 
01805    if (tmp->member->paused) {
01806       if (option_debug)
01807          ast_log(LOG_DEBUG, "%s paused, can't receive call\n", tmp->interface);
01808       if (qe->chan->cdr)
01809          ast_cdr_busy(qe->chan->cdr);
01810       tmp->stillgoing = 0;
01811       return 0;
01812    }
01813    if (use_weight && compare_weight(qe->parent,tmp->member)) {
01814       ast_log(LOG_DEBUG, "Priority queue delaying call to %s:%s\n", qe->parent->name, tmp->interface);
01815       if (qe->chan->cdr)
01816          ast_cdr_busy(qe->chan->cdr);
01817       tmp->stillgoing = 0;
01818       (*busies)++;
01819       return 0;
01820    }
01821 
01822    ast_copy_string(tech, tmp->interface, sizeof(tech));
01823    if ((location = strchr(tech, '/')))
01824       *location++ = '\0';
01825    else
01826       location = "";
01827 
01828    /* Request the peer */
01829    tmp->chan = ast_request(tech, qe->chan->nativeformats, location, &status);
01830    if (!tmp->chan) {       /* If we can't, just go on to the next call */
01831       if (qe->chan->cdr)
01832          ast_cdr_busy(qe->chan->cdr);
01833       tmp->stillgoing = 0;
01834       update_dial_status(qe->parent, tmp->member, status);
01835 
01836       ast_mutex_lock(&qe->parent->lock);
01837       qe->parent->rrpos++;
01838       ast_mutex_unlock(&qe->parent->lock);
01839 
01840       (*busies)++;
01841       return 0;
01842    } else if (status != tmp->oldstatus)
01843       update_dial_status(qe->parent, tmp->member, status);
01844    
01845    tmp->chan->appl = "AppQueue";
01846    tmp->chan->data = "(Outgoing Line)";
01847    tmp->chan->whentohangup = 0;
01848    if (tmp->chan->cid.cid_num)
01849       free(tmp->chan->cid.cid_num);
01850    tmp->chan->cid.cid_num = ast_strdup(qe->chan->cid.cid_num);
01851    if (tmp->chan->cid.cid_name)
01852       free(tmp->chan->cid.cid_name);
01853    tmp->chan->cid.cid_name = ast_strdup(qe->chan->cid.cid_name);
01854    if (tmp->chan->cid.cid_ani)
01855       free(tmp->chan->cid.cid_ani);
01856    tmp->chan->cid.cid_ani = ast_strdup(qe->chan->cid.cid_ani);
01857 
01858    /* Inherit specially named variables from parent channel */
01859    ast_channel_inherit_variables(qe->chan, tmp->chan);
01860 
01861    /* Presense of ADSI CPE on outgoing channel follows ours */
01862    tmp->chan->adsicpe = qe->chan->adsicpe;
01863 
01864    /* Place the call, but don't wait on the answer */
01865    if ((res = ast_call(tmp->chan, location, 0))) {
01866       /* Again, keep going even if there's an error */
01867       if (option_debug)
01868          ast_log(LOG_DEBUG, "ast call on peer returned %d\n", res);
01869       if (option_verbose > 2)
01870          ast_verbose(VERBOSE_PREFIX_3 "Couldn't call %s\n", tmp->interface);
01871       do_hang(tmp);
01872       (*busies)++;
01873       return 0;
01874    } else if (qe->parent->eventwhencalled) {
01875       char vars[2048];
01876 
01877       manager_event(EVENT_FLAG_AGENT, "AgentCalled",
01878                "AgentCalled: %s\r\n"
01879                "AgentName: %s\r\n"
01880                "ChannelCalling: %s\r\n"
01881                "CallerID: %s\r\n"
01882                "CallerIDName: %s\r\n"
01883                "Context: %s\r\n"
01884                "Extension: %s\r\n"
01885                "Priority: %d\r\n"
01886                "%s",
01887                tmp->interface, tmp->member->membername, qe->chan->name,
01888                tmp->chan->cid.cid_num ? tmp->chan->cid.cid_num : "unknown",
01889                tmp->chan->cid.cid_name ? tmp->chan->cid.cid_name : "unknown",
01890                qe->chan->context, qe->chan->exten, qe->chan->priority,
01891                qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
01892       if (option_verbose > 2)
01893          ast_verbose(VERBOSE_PREFIX_3 "Called %s\n", tmp->interface);
01894    }
01895 
01896    return 1;
01897 }
01898 
01899 /*! \brief find the entry with the best metric, or NULL */
01900 static struct callattempt *find_best(struct callattempt *outgoing)
01901 {
01902    struct callattempt *best = NULL, *cur;
01903 
01904    for (cur = outgoing; cur; cur = cur->q_next) {
01905       if (cur->stillgoing &&              /* Not already done */
01906          !cur->chan &&              /* Isn't already going */
01907          (!best || cur->metric < best->metric)) {     /* We haven't found one yet, or it's better */
01908          best = cur;
01909       }
01910    }
01911 
01912    return best;
01913 }
01914 
01915 static int ring_one(struct queue_ent *qe, struct callattempt *outgoing, int *busies)
01916 {
01917    int ret = 0;
01918 
01919    while (ret == 0) {
01920       struct callattempt *best = find_best(outgoing);
01921       if (!best) {
01922          if (option_debug)
01923             ast_log(LOG_DEBUG, "Nobody left to try ringing in queue\n");
01924          break;
01925       }
01926       if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
01927          struct callattempt *cur;
01928          /* Ring everyone who shares this best metric (for ringall) */
01929          for (cur = outgoing; cur; cur = cur->q_next) {
01930             if (cur->stillgoing && !cur->chan && cur->metric <= best->metric) {
01931                if (option_debug)
01932                   ast_log(LOG_DEBUG, "(Parallel) Trying '%s' with metric %d\n", cur->interface, cur->metric);
01933                ret |= ring_entry(qe, cur, busies);
01934             }
01935          }
01936       } else {
01937          /* Ring just the best channel */
01938          if (option_debug)
01939             ast_log(LOG_DEBUG, "Trying '%s' with metric %d\n", best->interface, best->metric);
01940          ret = ring_entry(qe, best, busies);
01941       }
01942    }
01943 
01944    return ret;
01945 }
01946 
01947 static int store_next(struct queue_ent *qe, struct callattempt *outgoing)
01948 {
01949    struct callattempt *best = find_best(outgoing);
01950 
01951    if (best) {
01952       /* Ring just the best channel */
01953       if (option_debug)
01954          ast_log(LOG_DEBUG, "Next is '%s' with metric %d\n", best->interface, best->metric);
01955       qe->parent->rrpos = best->metric % 1000;
01956    } else {
01957       /* Just increment rrpos */
01958       if (qe->parent->wrapped) {
01959          /* No more channels, start over */
01960          qe->parent->rrpos = 0;
01961       } else {
01962          /* Prioritize next entry */
01963          qe->parent->rrpos++;
01964       }
01965    }
01966    qe->parent->wrapped = 0;
01967 
01968    return 0;
01969 }
01970 
01971 static int say_periodic_announcement(struct queue_ent *qe)
01972 {
01973    int res = 0;
01974    time_t now;
01975 
01976    /* Get the current time */
01977    time(&now);
01978 
01979    /* Check to see if it is time to announce */
01980    if ((now - qe->last_periodic_announce_time) < qe->parent->periodicannouncefrequency)
01981       return 0;
01982 
01983    /* Stop the music on hold so we can play our own file */
01984    ast_moh_stop(qe->chan);
01985 
01986    if (option_verbose > 2)
01987       ast_verbose(VERBOSE_PREFIX_3 "Playing periodic announcement\n");
01988 
01989    /* Check to make sure we have a sound file. If not, reset to the first sound file */
01990    if (qe->last_periodic_announce_sound >= MAX_PERIODIC_ANNOUNCEMENTS || !strlen(qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound])) {
01991       qe->last_periodic_announce_sound = 0;
01992    }
01993    
01994    /* play the announcement */
01995    res = play_file(qe->chan, qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound]);
01996 
01997    if ((res > 0 && !valid_exit(qe, res)) || res < 0)
01998       res = 0;
01999 
02000    /* Resume Music on Hold if the caller is going to stay in the queue */
02001    if (!res)
02002       ast_moh_start(qe->chan, qe->moh, NULL);
02003 
02004    /* update last_periodic_announce_time */
02005    qe->last_periodic_announce_time = now;
02006 
02007    /* Update the current periodic announcement to the next announcement */
02008    qe->last_periodic_announce_sound++;
02009    
02010    return res;
02011 }
02012 
02013 static void record_abandoned(struct queue_ent *qe)
02014 {
02015    ast_mutex_lock(&qe->parent->lock);
02016    manager_event(EVENT_FLAG_AGENT, "QueueCallerAbandon",
02017       "Queue: %s\r\n"
02018       "Uniqueid: %s\r\n"
02019       "Position: %d\r\n"
02020       "OriginalPosition: %d\r\n"
02021       "HoldTime: %d\r\n",
02022       qe->parent->name, qe->chan->uniqueid, qe->pos, qe->opos, (int)(time(NULL) - qe->start));
02023 
02024    qe->parent->callsabandoned++;
02025    ast_mutex_unlock(&qe->parent->lock);
02026 }
02027 
02028 /*! \brief RNA == Ring No Answer. Common code that is executed when we try a queue member and they don't answer. */
02029 static void rna(int rnatime, struct queue_ent *qe, char *interface, char *membername)
02030 {
02031    if (option_verbose > 2)
02032       ast_verbose( VERBOSE_PREFIX_3 "Nobody picked up in %d ms\n", rnatime);
02033    ast_queue_log(qe->parent->name, qe->chan->uniqueid, membername, "RINGNOANSWER", "%d", rnatime);
02034    if (qe->parent->autopause) {
02035       if (!set_member_paused(qe->parent->name, interface, 1)) {
02036          if (option_verbose > 2)
02037             ast_verbose( VERBOSE_PREFIX_3 "Auto-Pausing Queue Member %s in queue %s since they failed to answer.\n", interface, qe->parent->name);
02038       } else {
02039          if (option_verbose > 2)
02040             ast_verbose( VERBOSE_PREFIX_3 "Failed to pause Queue Member %s in queue %s!\n", interface, qe->parent->name);
02041       }
02042    }
02043    return;
02044 }
02045 
02046 #define AST_MAX_WATCHERS 256
02047 
02048 static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callattempt *outgoing, int *to, char *digit, int prebusies, int caller_disconnect, int forwardsallowed)
02049 {
02050    char *queue = qe->parent->name;
02051    struct callattempt *o, *start = NULL, *prev = NULL;
02052    int status;
02053    int numbusies = prebusies;
02054    int numnochan = 0;
02055    int stillgoing = 0;
02056    int orig = *to;
02057    struct ast_frame *f;
02058    struct callattempt *peer = NULL;
02059    struct ast_channel *winner;
02060    struct ast_channel *in = qe->chan;
02061    char on[80] = "";
02062    char membername[80] = "";
02063    long starttime = 0;
02064    long endtime = 0; 
02065 
02066    starttime = (long) time(NULL);
02067    
02068    while (*to && !peer) {
02069       int numlines, retry, pos = 1;
02070       struct ast_channel *watchers[AST_MAX_WATCHERS];
02071       watchers[0] = in;
02072       start = NULL;
02073 
02074       for (retry = 0; retry < 2; retry++) {
02075          numlines = 0;
02076          for (o = outgoing; o; o = o->q_next) { /* Keep track of important channels */
02077             if (o->stillgoing) { /* Keep track of important channels */
02078                stillgoing = 1;
02079                if (o->chan) {
02080                   watchers[pos++] = o->chan;
02081                   if (!start)
02082                      start = o;
02083                   else
02084                      prev->call_next = o;
02085                   prev = o;
02086                }
02087             }
02088             numlines++;
02089          }
02090          if (pos > 1 /* found */ || !stillgoing /* nobody listening */ ||
02091             (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) /* ring would not be delivered */)
02092             break;
02093          /* On "ringall" strategy we only move to the next penalty level
02094             when *all* ringing phones are done in the current penalty level */
02095          ring_one(qe, outgoing, &numbusies);
02096          /* and retry... */
02097       }
02098       if (pos == 1 /* not found */) {
02099          if (numlines == (numbusies + numnochan)) {
02100             ast_log(LOG_DEBUG, "Everyone is busy at this time\n");
02101          } else {
02102             ast_log(LOG_NOTICE, "No one is answering queue '%s' (%d/%d/%d)\n", queue, numlines, numbusies, numnochan);
02103          }
02104          *to = 0;
02105          return NULL;
02106       }
02107       winner = ast_waitfor_n(watchers, pos, to);
02108       for (o = start; o; o = o->call_next) {
02109          if (o->stillgoing && (o->chan) &&  (o->chan->_state == AST_STATE_UP)) {
02110             if (!peer) {
02111                if (option_verbose > 2)
02112                   ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
02113                peer = o;
02114             }
02115          } else if (o->chan && (o->chan == winner)) {
02116 
02117             ast_copy_string(on, o->member->interface, sizeof(on));
02118             ast_copy_string(membername, o->member->membername, sizeof(membername));
02119 
02120             if (!ast_strlen_zero(o->chan->call_forward) && !forwardsallowed) {
02121                if (option_verbose > 2)
02122                   ast_verbose(VERBOSE_PREFIX_3 "Forwarding %s to '%s' prevented.\n", in->name, o->chan->call_forward);
02123                numnochan++;
02124                do_hang(o);
02125                winner = NULL;
02126                continue;
02127             } else if (!ast_strlen_zero(o->chan->call_forward)) {
02128                char tmpchan[256];
02129                char *stuff;
02130                char *tech;
02131 
02132                ast_copy_string(tmpchan, o->chan->call_forward, sizeof(tmpchan));
02133                if ((stuff = strchr(tmpchan, '/'))) {
02134                   *stuff++ = '\0';
02135                   tech = tmpchan;
02136                } else {
02137                   snprintf(tmpchan, sizeof(tmpchan), "%s@%s", o->chan->call_forward, o->chan->context);
02138                   stuff = tmpchan;
02139                   tech = "Local";
02140                }
02141                /* Before processing channel, go ahead and check for forwarding */
02142                if (option_verbose > 2)
02143                   ast_verbose(VERBOSE_PREFIX_3 "Now forwarding %s to '%s/%s' (thanks to %s)\n", in->name, tech, stuff, o->chan->name);
02144                /* Setup parameters */
02145                o->chan = ast_request(tech, in->nativeformats, stuff, &status);
02146                if (status != o->oldstatus)
02147                   update_dial_status(qe->parent, o->member, status);                
02148                if (!o->chan) {
02149                   ast_log(LOG_NOTICE, "Unable to create local channel for call forward to '%s/%s'\n", tech, stuff);
02150                   o->stillgoing = 0;
02151                   numnochan++;
02152                } else {
02153                   ast_channel_inherit_variables(in, o->chan);
02154                   ast_channel_datastore_inherit(in, o->chan);
02155                   if (o->chan->cid.cid_num)
02156                      free(o->chan->cid.cid_num);
02157                   o->chan->cid.cid_num = ast_strdup(in->cid.cid_num);
02158 
02159                   if (o->chan->cid.cid_name)
02160                      free(o->chan->cid.cid_name);
02161                   o->chan->cid.cid_name = ast_strdup(in->cid.cid_name);
02162 
02163                   ast_string_field_set(o->chan, accountcode, in->accountcode);
02164                   o->chan->cdrflags = in->cdrflags;
02165 
02166                   if (in->cid.cid_ani) {
02167                      if (o->chan->cid.cid_ani)
02168                         free(o->chan->cid.cid_ani);
02169                      o->chan->cid.cid_ani = ast_strdup(in->cid.cid_ani);
02170                   }
02171                   if (o->chan->cid.cid_rdnis)
02172                      free(o->chan->cid.cid_rdnis);
02173                   o->chan->cid.cid_rdnis = ast_strdup(S_OR(in->macroexten, in->exten));
02174                   if (ast_call(o->chan, tmpchan, 0)) {
02175                      ast_log(LOG_NOTICE, "Failed to dial on local channel for call forward to '%s'\n", tmpchan);
02176                      do_hang(o);
02177                      numnochan++;
02178                   }
02179                }
02180                /* Hangup the original channel now, in case we needed it */
02181                ast_hangup(winner);
02182                continue;
02183             }
02184             f = ast_read(winner);
02185             if (f) {
02186                if (f->frametype == AST_FRAME_CONTROL) {
02187                   switch (f->subclass) {
02188                   case AST_CONTROL_ANSWER:
02189                      /* This is our guy if someone answered. */
02190                      if (!peer) {
02191                         if (option_verbose > 2)
02192                            ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
02193                         peer = o;
02194                      }
02195                      break;
02196                   case AST_CONTROL_BUSY:
02197                      if (option_verbose > 2)
02198                         ast_verbose( VERBOSE_PREFIX_3 "%s is busy\n", o->chan->name);
02199                      if (in->cdr)
02200                         ast_cdr_busy(in->cdr);
02201                      do_hang(o);
02202                      endtime = (long)time(NULL);
02203                      endtime -= starttime;
02204                      rna(endtime*1000, qe, on, membername);
02205                      if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
02206                         if (qe->parent->timeoutrestart)
02207                            *to = orig;
02208                         ring_one(qe, outgoing, &numbusies);
02209                      }
02210                      numbusies++;
02211                      break;
02212                   case AST_CONTROL_CONGESTION:
02213                      if (option_verbose > 2)
02214                         ast_verbose( VERBOSE_PREFIX_3 "%s is circuit-busy\n", o->chan->name);
02215                      if (in->cdr)
02216                         ast_cdr_busy(in->cdr);
02217                      endtime = (long)time(NULL);
02218                      endtime -= starttime;
02219                      rna(endtime*1000, qe, on, membername);
02220                      do_hang(o);
02221                      if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
02222                         if (qe->parent->timeoutrestart)
02223                            *to = orig;
02224                         ring_one(qe, outgoing, &numbusies);
02225                      }
02226                      numbusies++;
02227                      break;
02228                   case AST_CONTROL_RINGING:
02229                      if (option_verbose > 2)
02230                         ast_verbose( VERBOSE_PREFIX_3 "%s is ringing\n", o->chan->name);
02231                      break;
02232                   case AST_CONTROL_OFFHOOK:
02233                      /* Ignore going off hook */
02234                      break;
02235                   default:
02236                      ast_log(LOG_DEBUG, "Dunno what to do with control type %d\n", f->subclass);
02237                   }
02238                }
02239                ast_frfree(f);
02240             } else {
02241                endtime = (long) time(NULL) - starttime;
02242                rna(endtime * 1000, qe, on, membername);
02243                do_hang(o);
02244                if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
02245                   if (qe->parent->timeoutrestart)
02246                      *to = orig;
02247                   ring_one(qe, outgoing, &numbusies);
02248                }
02249             }
02250          }
02251       }
02252       if (winner == in) {
02253          f = ast_read(in);
02254          if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) {
02255             /* Got hung up */
02256             *to = -1;
02257             if (f)
02258                ast_frfree(f);
02259             return NULL;
02260          }
02261          if ((f->frametype == AST_FRAME_DTMF) && caller_disconnect && (f->subclass == '*')) {
02262             if (option_verbose > 3)
02263                ast_verbose(VERBOSE_PREFIX_3 "User hit %c to disconnect call.\n", f->subclass);
02264             *to = 0;
02265             ast_frfree(f);
02266             return NULL;
02267          }
02268          if ((f->frametype == AST_FRAME_DTMF) && valid_exit(qe, f->subclass)) {
02269             if (option_verbose > 3)
02270                ast_verbose(VERBOSE_PREFIX_3 "User pressed digit: %c\n", f->subclass);
02271             *to = 0;
02272             *digit = f->subclass;
02273             ast_frfree(f);
02274             return NULL;
02275          }
02276          ast_frfree(f);
02277       }
02278       if (!*to) {
02279          for (o = start; o; o = o->call_next)
02280             rna(orig, qe, o->interface, o->member->membername);
02281       }
02282    }
02283 
02284    return peer;
02285 }
02286 
02287 static int is_our_turn(struct queue_ent *qe)
02288 {
02289    struct queue_ent *ch;
02290    struct member *cur;
02291    int avl = 0;
02292    int idx = 0;
02293    int res;
02294 
02295    if (!qe->parent->autofill) {
02296       /* Atomically read the parent head -- does not need a lock */
02297       ch = qe->parent->head;
02298       /* If we are now at the top of the head, break out */
02299       if (ch == qe) {
02300          if (option_debug)
02301             ast_log(LOG_DEBUG, "It's our turn (%s).\n", qe->chan->name);
02302          res = 1;
02303       } else {
02304          if (option_debug)
02305             ast_log(LOG_DEBUG, "It's not our turn (%s).\n", qe->chan->name);
02306          res = 0;
02307       }  
02308 
02309    } else {
02310       /* This needs a lock. How many members are available to be served? */
02311       ast_mutex_lock(&qe->parent->lock);
02312          
02313       ch = qe->parent->head;
02314    
02315       if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
02316          if (option_debug)
02317             ast_log(LOG_DEBUG, "Even though there may be multiple members available, the strategy is ringall so only the head call is allowed in\n");
02318          avl = 1;
02319       } else {
02320          struct ao2_iterator mem_iter = ao2_iterator_init(qe->parent->members, 0);
02321          while ((cur = ao2_iterator_next(&mem_iter))) {
02322             switch (cur->status) {
02323             case AST_DEVICE_NOT_INUSE:
02324             case AST_DEVICE_UNKNOWN:
02325                if (!cur->paused)
02326                   avl++;
02327                break;
02328             }
02329             ao2_ref(cur, -1);
02330          }
02331       }
02332 
02333       if (option_debug)
02334          ast_log(LOG_DEBUG, "There are %d available members.\n", avl);
02335    
02336       while ((idx < avl) && (ch) &&  !ch->pending && (ch != qe)) {
02337          idx++;
02338          ch = ch->next;       
02339       }
02340    
02341       /* If the queue entry is within avl [the number of available members] calls from the top ... */
02342       if (ch && idx < avl) {
02343          if (option_debug)
02344             ast_log(LOG_DEBUG, "It's our turn (%s).\n", qe->chan->name);
02345          res = 1;
02346       } else {
02347          if (option_debug)
02348             ast_log(LOG_DEBUG, "It's not our turn (%s).\n", qe->chan->name);
02349          res = 0;
02350       }
02351       
02352       ast_mutex_unlock(&qe->parent->lock);
02353    }
02354 
02355    return res;
02356 }
02357 
02358 static int wait_our_turn(struct queue_ent *qe, int ringing, enum queue_result *reason)
02359 {
02360    int res = 0;
02361 
02362    /* This is the holding pen for callers 2 through maxlen */
02363    for (;;) {
02364       enum queue_member_status stat;
02365 
02366       if (is_our_turn(qe))
02367          break;
02368 
02369       /* If we have timed out, break out */
02370       if (qe->expire && (time(NULL) > qe->expire)) {
02371          *reason = QUEUE_TIMEOUT;
02372          break;
02373       }
02374 
02375       stat = get_member_status(qe->parent, qe->max_penalty);
02376 
02377       /* leave the queue if no agents, if enabled */
02378       if (qe->parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) {
02379          *reason = QUEUE_LEAVEEMPTY;
02380          ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long)time(NULL) - qe->start);
02381          leave_queue(qe);
02382          break;
02383       }
02384 
02385       /* leave the queue if no reachable agents, if enabled */
02386       if ((qe->parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) {
02387          *reason = QUEUE_LEAVEUNAVAIL;
02388          ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long)time(NULL) - qe->start);
02389          leave_queue(qe);
02390          break;
02391       }
02392 
02393       /* Make a position announcement, if enabled */
02394       if (qe->parent->announcefrequency && !ringing &&
02395          (res = say_position(qe)))
02396          break;
02397 
02398       /* Make a periodic announcement, if enabled */
02399       if (qe->parent->periodicannouncefrequency && !ringing &&
02400          (res = say_periodic_announcement(qe)))
02401          break;
02402 
02403       /* Wait a second before checking again */
02404       if ((res = ast_waitfordigit(qe->chan, RECHECK * 1000))) {
02405          if (res > 0 && !valid_exit(qe, res))
02406             res = 0;
02407          else
02408             break;
02409       }
02410    }
02411 
02412    return res;
02413 }
02414 
02415 static int update_queue(struct call_queue *q, struct member *member, int callcompletedinsl)
02416 {
02417    ast_mutex_lock(&q->lock);
02418    time(&member->lastcall);
02419    member->calls++;
02420    q->callscompleted++;
02421    if (callcompletedinsl)
02422       q->callscompletedinsl++;
02423    ast_mutex_unlock(&q->lock);
02424    return 0;
02425 }
02426 
02427 static int calc_metric(struct call_queue *q, struct member *mem, int pos, struct queue_ent *qe, struct callattempt *tmp)
02428 {
02429    if (qe->max_penalty && (mem->penalty > qe->max_penalty))
02430       return -1;
02431 
02432    switch (q->strategy) {
02433    case QUEUE_STRATEGY_RINGALL:
02434       /* Everyone equal, except for penalty */
02435       tmp->metric = mem->penalty * 1000000;
02436       break;
02437    case QUEUE_STRATEGY_ROUNDROBIN:
02438       if (!pos) {
02439          if (!q->wrapped) {
02440             /* No more channels, start over */
02441             q->rrpos = 0;
02442          } else {
02443             /* Prioritize next entry */
02444             q->rrpos++;
02445          }
02446          q->wrapped = 0;
02447       }
02448       /* Fall through */
02449    case QUEUE_STRATEGY_RRMEMORY:
02450       if (pos < q->rrpos) {
02451          tmp->metric = 1000 + pos;
02452       } else {
02453          if (pos > q->rrpos)
02454             /* Indicate there is another priority */
02455             q->wrapped = 1;
02456          tmp->metric = pos;
02457       }
02458       tmp->metric += mem->penalty * 1000000;
02459       break;
02460    case QUEUE_STRATEGY_RANDOM:
02461       tmp->metric = ast_random() % 1000;
02462       tmp->metric += mem->penalty * 1000000;
02463       break;
02464    case QUEUE_STRATEGY_FEWESTCALLS:
02465       tmp->metric = mem->calls;
02466       tmp->metric += mem->penalty * 1000000;
02467       break;
02468    case QUEUE_STRATEGY_LEASTRECENT:
02469       if (!mem->lastcall)
02470          tmp->metric = 0;
02471       else
02472          tmp->metric = 1000000 - (time(NULL) - mem->lastcall);
02473       tmp->metric += mem->penalty * 1000000;
02474       break;
02475    default:
02476       ast_log(LOG_WARNING, "Can't calculate metric for unknown strategy %d\n", q->strategy);
02477       break;
02478    }
02479    return 0;
02480 }
02481 
02482 static int try_calling(struct queue_ent *qe, const char *options, char *announceoverride, const char *url, int *tries, int *noption, const char *agi)
02483 {
02484    struct member *cur;
02485    struct callattempt *outgoing = NULL; /* the list of calls we are building */
02486    int to;
02487    char oldexten[AST_MAX_EXTENSION]="";
02488    char oldcontext[AST_MAX_CONTEXT]="";
02489    char queuename[256]="";
02490    struct ast_channel *peer;
02491    struct ast_channel *which;
02492    struct callattempt *lpeer;
02493    struct member *member;
02494    struct ast_app *app;
02495    int res = 0, bridge = 0;
02496    int numbusies = 0;
02497    int x=0;
02498    char *announce = NULL;
02499    char digit = 0;
02500    time_t callstart;
02501    time_t now = time(NULL);
02502    struct ast_bridge_config bridge_config;
02503    char nondataquality = 1;
02504    char *agiexec = NULL;
02505    int ret = 0;
02506    const char *monitorfilename;
02507    const char *monitor_exec;
02508    const char *monitor_options;
02509    char tmpid[256], tmpid2[256];
02510    char meid[1024], meid2[1024];
02511    char mixmonargs[1512];
02512    struct ast_app *mixmonapp = NULL;
02513    char *p;
02514    char vars[2048];
02515    int forwardsallowed = 1;
02516    int callcompletedinsl;
02517    struct ao2_iterator memi;
02518    struct ast_datastore *datastore;
02519 
02520    ast_channel_lock(qe->chan);
02521    datastore = ast_channel_datastore_find(qe->chan, &dialed_interface_info, NULL);
02522    ast_channel_unlock(qe->chan);
02523 
02524    memset(&bridge_config, 0, sizeof(bridge_config));
02525    time(&now);
02526       
02527    for (; options && *options; options++)
02528       switch (*options) {
02529       case 't':
02530          ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_REDIRECT);
02531          break;
02532       case 'T':
02533          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_REDIRECT);
02534          break;
02535       case 'w':
02536          ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMON);
02537          break;
02538       case 'W':
02539          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMON);
02540          break;
02541       case 'd':
02542          nondataquality = 0;
02543          break;
02544       case 'h':
02545          ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_DISCONNECT);
02546          break;
02547       case 'H':
02548          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT);
02549          break;
02550       case 'n':
02551          if (qe->parent->strategy == QUEUE_STRATEGY_ROUNDROBIN || qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY)
02552             (*tries)++;
02553          else
02554             *tries = qe->parent->membercount;
02555          *noption = 1;
02556          break;
02557       case 'i':
02558          forwardsallowed = 0;
02559          break;
02560       }
02561 
02562    /* Hold the lock while we setup the outgoing calls */
02563    if (use_weight)
02564       AST_LIST_LOCK(&queues);
02565    ast_mutex_lock(&qe->parent->lock);
02566    if (option_debug)
02567       ast_log(LOG_DEBUG, "%s is trying to call a queue member.\n",
02568                      qe->chan->name);
02569    ast_copy_string(queuename, qe->parent->name, sizeof(queuename));
02570    if (!ast_strlen_zero(qe->announce))
02571       announce = qe->announce;
02572    if (!ast_strlen_zero(announceoverride))
02573       announce = announceoverride;
02574 
02575    memi = ao2_iterator_init(qe->parent->members, 0);
02576    while ((cur = ao2_iterator_next(&memi))) {
02577       struct callattempt *tmp = ast_calloc(1, sizeof(*tmp));
02578       struct ast_dialed_interface *di;
02579       AST_LIST_HEAD(, ast_dialed_interface) *dialed_interfaces;
02580       if (!tmp) {
02581          ao2_ref(cur, -1);
02582          ast_mutex_unlock(&qe->parent->lock);
02583          if (use_weight)
02584             AST_LIST_UNLOCK(&queues);
02585          goto out;
02586       }
02587       if (!datastore) {
02588          if (!(datastore = ast_channel_datastore_alloc(&dialed_interface_info, NULL))) {
02589             ao2_ref(cur, -1);
02590             ast_mutex_unlock(&qe->parent->lock);
02591             if (use_weight)
02592                AST_LIST_UNLOCK(&queues);
02593             free(tmp);
02594             goto out;
02595          }
02596          datastore->inheritance = DATASTORE_INHERIT_FOREVER;
02597          if (!(dialed_interfaces = ast_calloc(1, sizeof(*dialed_interfaces)))) {
02598             ao2_ref(cur, -1);
02599             ast_mutex_unlock(&qe->parent->lock);
02600             if (use_weight)
02601                AST_LIST_UNLOCK(&queues);
02602             free(tmp);
02603             goto out;
02604          }
02605          datastore->data = dialed_interfaces;
02606          AST_LIST_HEAD_INIT(dialed_interfaces);
02607 
02608          ast_channel_lock(qe->chan);
02609          ast_channel_datastore_add(qe->chan, datastore);
02610          ast_channel_unlock(qe->chan);
02611       } else
02612          dialed_interfaces = datastore->data;
02613 
02614       AST_LIST_LOCK(dialed_interfaces);
02615       AST_LIST_TRAVERSE(dialed_interfaces, di, list) {
02616          if (!strcasecmp(cur->interface, di->interface)) {
02617             ast_log(LOG_DEBUG, "Skipping dialing interface '%s' since it has already been dialed\n", 
02618                di->interface);
02619             break;
02620          }
02621       }
02622       AST_LIST_UNLOCK(dialed_interfaces);
02623       
02624       if (di) {
02625          free(tmp);
02626          continue;
02627       }
02628 
02629       /* It is always ok to dial a Local interface.  We only keep track of
02630        * which "real" interfaces have been dialed.  The Local channel will
02631        * inherit this list so that if it ends up dialing a real interface,
02632        * it won't call one that has already been called. */
02633       if (strncasecmp(cur->interface, "Local/", 6)) {
02634          if (!(di = ast_calloc(1, sizeof(*di) + strlen(cur->interface)))) {
02635             ao2_ref(cur, -1);
02636             ast_mutex_unlock(&qe->parent->lock);
02637             if (use_weight)
02638                AST_LIST_UNLOCK(&queues);
02639             free(tmp);
02640             goto out;
02641          }
02642          strcpy(di->interface, cur->interface);
02643 
02644          AST_LIST_LOCK(dialed_interfaces);
02645          AST_LIST_INSERT_TAIL(dialed_interfaces, di, list);
02646          AST_LIST_UNLOCK(dialed_interfaces);
02647       }
02648 
02649       tmp->stillgoing = -1;
02650       tmp->member = cur;
02651       tmp->oldstatus = cur->status;
02652       tmp->lastcall = cur->lastcall;
02653       ast_copy_string(tmp->interface, cur->interface, sizeof(tmp->interface));
02654       /* Special case: If we ring everyone, go ahead and ring them, otherwise
02655          just calculate their metric for the appropriate strategy */
02656       if (!calc_metric(qe->parent, cur, x++, qe, tmp)) {
02657          /* Put them in the list of outgoing thingies...  We're ready now.
02658             XXX If we're forcibly removed, these outgoing calls won't get
02659             hung up XXX */
02660          tmp->q_next = outgoing;
02661          outgoing = tmp;      
02662          /* If this line is up, don't try anybody else */
02663          if (outgoing->chan && (outgoing->chan->_state == AST_STATE_UP))
02664             break;
02665       } else {
02666          ao2_ref(cur, -1);
02667          free(tmp);
02668       }
02669    }
02670    if (qe->expire && (!qe->parent->timeout || (qe->expire - now) <= qe->parent->timeout))
02671       to = (qe->expire - now) * 1000;
02672    else
02673       to = (qe->parent->timeout) ? qe->parent->timeout * 1000 : -1;
02674    ++qe->pending;
02675    ring_one(qe, outgoing, &numbusies);
02676    ast_mutex_unlock(&qe->parent->lock);
02677    if (use_weight)
02678       AST_LIST_UNLOCK(&queues);
02679    lpeer = wait_for_answer(qe, outgoing, &to, &digit, numbusies, ast_test_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT), forwardsallowed);
02680    if (datastore) {
02681       ast_channel_datastore_remove(qe->chan, datastore);
02682       ast_channel_datastore_free(datastore);
02683    }
02684    ast_mutex_lock(&qe->parent->lock);
02685    if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY) {
02686       store_next(qe, outgoing);
02687    }
02688    ast_mutex_unlock(&qe->parent->lock);
02689    peer = lpeer ? lpeer->chan : NULL;
02690    if (!peer) {
02691       qe->pending = 0;
02692       if (to) {
02693          /* Must gotten hung up */
02694          res = -1;
02695       } else {
02696          /* User exited by pressing a digit */
02697          res = digit;
02698       }
02699       if (option_debug && res == -1)
02700          ast_log(LOG_DEBUG, "%s: Nobody answered.\n", qe->chan->name);
02701    } else { /* peer is valid */
02702       /* Ah ha!  Someone answered within the desired timeframe.  Of course after this
02703          we will always return with -1 so that it is hung up properly after the
02704          conversation.  */
02705       qe->handled++;
02706       if (!strcmp(qe->chan->tech->type, "Zap"))
02707          ast_channel_setoption(qe->chan, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
02708       if (!strcmp(peer->tech->type, "Zap"))
02709          ast_channel_setoption(peer, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
02710       /* Update parameters for the queue */
02711       time(&now);
02712       recalc_holdtime(qe, (now - qe->start));
02713       ast_mutex_lock(&qe->parent->lock);
02714       callcompletedinsl = ((now - qe->start) <= qe->parent->servicelevel);
02715       ast_mutex_unlock(&qe->parent->lock);
02716       member = lpeer->member;
02717       /* Increment the refcount for this member, since we're going to be using it for awhile in here. */
02718       ao2_ref(member, 1);
02719       hangupcalls(outgoing, peer);
02720       outgoing = NULL;
02721       if (announce || qe->parent->reportholdtime || qe->parent->memberdelay) {
02722          int res2;
02723 
02724          res2 = ast_autoservice_start(qe->chan);
02725          if (!res2) {
02726             if (qe->parent->memberdelay) {
02727                ast_log(LOG_NOTICE, "Delaying member connect for %d seconds\n", qe->parent->memberdelay);
02728                res2 |= ast_safe_sleep(peer, qe->parent->memberdelay * 1000);
02729             }
02730             if (!res2 && announce) {
02731                play_file(peer, announce);
02732             }
02733             if (!res2 && qe->parent->reportholdtime) {
02734                if (!play_file(peer, qe->parent->sound_reporthold)) {
02735                   int holdtime;
02736 
02737                   time(&now);
02738                   holdtime = abs((now - qe->start) / 60);
02739                   if (holdtime < 2) {
02740                      play_file(peer, qe->parent->sound_lessthan);
02741                      ast_say_number(peer, 2, AST_DIGIT_ANY, peer->language, NULL);
02742                   } else
02743                      ast_say_number(peer, holdtime, AST_DIGIT_ANY, peer->language, NULL);
02744                   play_file(peer, qe->parent->sound_minutes);
02745                }
02746             }
02747          }
02748          res2 |= ast_autoservice_stop(qe->chan);
02749          if (peer->_softhangup) {
02750             /* Agent must have hung up */
02751             ast_log(LOG_WARNING, "Agent on %s hungup on the customer.\n", peer->name);
02752             ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "AGENTDUMP", "%s", "");
02753             record_abandoned(qe);
02754             if (qe->parent->eventwhencalled)
02755                manager_event(EVENT_FLAG_AGENT, "AgentDump",
02756                      "Queue: %s\r\n"
02757                      "Uniqueid: %s\r\n"
02758                      "Channel: %s\r\n"
02759                      "Member: %s\r\n"
02760                      "MemberName: %s\r\n"
02761                      "%s",
02762                      queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
02763                      qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
02764             ast_hangup(peer);
02765             ao2_ref(member, -1);
02766             goto out;
02767          } else if (res2) {
02768             /* Caller must have hung up just before being connected*/
02769             ast_log(LOG_NOTICE, "Caller was about to talk to agent on %s but the caller hungup.\n", peer->name);
02770             ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "ABANDON", "%d|%d|%ld", qe->pos, qe->opos, (long)time(NULL) - qe->start);
02771             record_abandoned(qe);
02772             ast_hangup(peer);
02773             ao2_ref(member, -1);
02774             return -1;
02775          }
02776       }
02777       /* Stop music on hold */
02778       ast_moh_stop(qe->chan);
02779       /* If appropriate, log that we have a destination channel */
02780       if (qe->chan->cdr)
02781          ast_cdr_setdestchan(qe->chan->cdr, peer->name);
02782       /* Make sure channels are compatible */
02783       res = ast_channel_make_compatible(qe->chan, peer);
02784       if (res < 0) {
02785          ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "SYSCOMPAT", "%s", "");
02786          ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", qe->chan->name, peer->name);
02787          record_abandoned(qe);
02788          ast_hangup(peer);
02789          ao2_ref(member, -1);
02790          return -1;
02791       }
02792 
02793       if (qe->parent->setinterfacevar)
02794             pbx_builtin_setvar_helper(qe->chan, "MEMBERINTERFACE", member->interface);
02795 
02796       /* Begin Monitoring */
02797       if (qe->parent->monfmt && *qe->parent->monfmt) {
02798          if (!qe->parent->montype) {
02799             if (option_debug)
02800                ast_log(LOG_DEBUG, "Starting Monitor as requested.\n");
02801             monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME");
02802             if (pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC") || pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC_ARGS"))
02803                which = qe->chan;
02804             else
02805                which = peer;
02806             if (monitorfilename)
02807                ast_monitor_start(which, qe->parent->monfmt, monitorfilename, NULL, NULL, 1 );
02808             else if (qe->chan->cdr)
02809                ast_monitor_start(which, qe->parent->monfmt, qe->chan->cdr->uniqueid, NULL, NULL, 1 );
02810             else {
02811                /* Last ditch effort -- no CDR, make up something */
02812                snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random());
02813                ast_monitor_start(which, qe->parent->monfmt, tmpid, NULL, NULL, 1 );
02814             }
02815             if (qe->parent->monjoin)
02816                ast_monitor_setjoinfiles(which, 1);
02817          } else {
02818             if (option_debug)
02819                ast_log(LOG_DEBUG, "Starting MixMonitor as requested.\n");
02820             monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME");
02821             if (!monitorfilename) {
02822                if (qe->chan->cdr)
02823                   ast_copy_string(tmpid, qe->chan->cdr->uniqueid, sizeof(tmpid)-1);
02824                else
02825                   snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random());
02826             } else {
02827                ast_copy_string(tmpid2, monitorfilename, sizeof(tmpid2)-1);
02828                for (p = tmpid2; *p ; p++) {
02829                   if (*p == '^' && *(p+1) == '{') {
02830                      *p = '$';
02831                   }
02832                }
02833 
02834                memset(tmpid, 0, sizeof(tmpid));
02835                pbx_substitute_variables_helper(qe->chan, tmpid2, tmpid, sizeof(tmpid) - 1);
02836             }
02837 
02838             monitor_exec = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC");
02839             monitor_options = pbx_builtin_getvar_helper(qe->chan, "MONITOR_OPTIONS");
02840 
02841             if (monitor_exec) {
02842                ast_copy_string(meid2, monitor_exec, sizeof(meid2)-1);
02843                for (p = meid2; *p ; p++) {
02844                   if (*p == '^' && *(p+1) == '{') {
02845                      *p = '$';
02846                   }
02847                }
02848 
02849                memset(meid, 0, sizeof(meid));
02850                pbx_substitute_variables_helper(qe->chan, meid2, meid, sizeof(meid) - 1);
02851             }
02852    
02853             snprintf(tmpid2, sizeof(tmpid2)-1, "%s.%s", tmpid, qe->parent->monfmt);
02854 
02855             mixmonapp = pbx_findapp("MixMonitor");
02856 
02857             if (strchr(tmpid2, '|')) {
02858                ast_log(LOG_WARNING, "monitor-format (in queues.conf) and MONITOR_FILENAME cannot contain a '|'! Not recording.\n");
02859                mixmonapp = NULL;
02860             }
02861 
02862             if (!monitor_options)
02863                monitor_options = "";
02864             
02865             if (strchr(monitor_options, '|')) {
02866                ast_log(LOG_WARNING, "MONITOR_OPTIONS cannot contain a '|'! Not recording.\n");
02867                mixmonapp = NULL;
02868             }
02869 
02870             if (mixmonapp) {
02871                if (!ast_strlen_zero(monitor_exec))
02872                   snprintf(mixmonargs, sizeof(mixmonargs)-1, "%s|b%s|%s", tmpid2, monitor_options, monitor_exec);
02873                else
02874                   snprintf(mixmonargs, sizeof(mixmonargs)-1, "%s|b%s", tmpid2, monitor_options);
02875                   
02876                if (option_debug)
02877                   ast_log(LOG_DEBUG, "Arguments being passed to MixMonitor: %s\n", mixmonargs);
02878                /* We purposely lock the CDR so that pbx_exec does not update the application data */
02879                if (qe->chan->cdr)
02880                   ast_set_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED);
02881                ret = pbx_exec(qe->chan, mixmonapp, mixmonargs);
02882                if (qe->chan->cdr)
02883                   ast_clear_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED);
02884 
02885             } else
02886                ast_log(LOG_WARNING, "Asked to run MixMonitor on this call, but cannot find the MixMonitor app!\n");
02887 
02888          }
02889       }
02890       /* Drop out of the queue at this point, to prepare for next caller */
02891       leave_queue(qe);        
02892       if (!ast_strlen_zero(url) && ast_channel_supports_html(peer)) {
02893          if (option_debug)
02894             ast_log(LOG_DEBUG, "app_queue: sendurl=%s.\n", url);
02895          ast_channel_sendurl(peer, url);
02896       }
02897       if (!ast_strlen_zero(agi)) {
02898          if (option_debug)
02899             ast_log(LOG_DEBUG, "app_queue: agi=%s.\n", agi);
02900          app = pbx_findapp("agi");
02901          if (app) {
02902             agiexec = ast_strdupa(agi);
02903             ret = pbx_exec(qe->chan, app, agiexec);
02904          } else
02905             ast_log(LOG_WARNING, "Asked to execute an AGI on this channel, but could not find application (agi)!\n");
02906       }
02907       ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "CONNECT", "%ld|%s", (long)time(NULL) - qe->start, peer->uniqueid);
02908       if (qe->parent->eventwhencalled)
02909          manager_event(EVENT_FLAG_AGENT, "AgentConnect",
02910                "Queue: %s\r\n"
02911                "Uniqueid: %s\r\n"
02912                "Channel: %s\r\n"
02913                "Member: %s\r\n"
02914                "MemberName: %s\r\n"
02915                "Holdtime: %ld\r\n"
02916                "BridgedChannel: %s\r\n"
02917                "%s",
02918                queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
02919                (long)time(NULL) - qe->start, peer->uniqueid,
02920                qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
02921       ast_copy_string(oldcontext, qe->chan->context, sizeof(oldcontext));
02922       ast_copy_string(oldexten, qe->chan->exten, sizeof(oldexten));
02923       time(&callstart);
02924 
02925       if (member->status == AST_DEVICE_NOT_INUSE)
02926          ast_log(LOG_WARNING, "The device state of this queue member, %s, is still 'Not in Use' when it probably should not be! Please check UPGRADE.txt for correct configuration settings.\n", member->membername);
02927          
02928 
02929       bridge = ast_bridge_call(qe->chan,peer, &bridge_config);
02930 
02931       if (strcasecmp(oldcontext, qe->chan->context) || strcasecmp(oldexten, qe->chan->exten)) {
02932          ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld",
02933             qe->chan->exten, qe->chan->context, (long) (callstart - qe->start),
02934             (long) (time(NULL) - callstart));
02935       } else if (qe->chan->_softhangup) {
02936          ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETECALLER", "%ld|%ld|%d",
02937             (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
02938          if (qe->parent->eventwhencalled)
02939             manager_event(EVENT_FLAG_AGENT, "AgentComplete",
02940                   "Queue: %s\r\n"
02941                   "Uniqueid: %s\r\n"
02942                   "Channel: %s\r\n"
02943                   "Member: %s\r\n"
02944                   "MemberName: %s\r\n"
02945                   "HoldTime: %ld\r\n"
02946                   "TalkTime: %ld\r\n"
02947                   "Reason: caller\r\n"
02948                   "%s",
02949                   queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
02950                   (long)(callstart - qe->start), (long)(time(NULL) - callstart),
02951                   qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
02952       } else {
02953          ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETEAGENT", "%ld|%ld|%d",
02954             (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
02955          if (qe->parent->eventwhencalled)
02956             manager_event(EVENT_FLAG_AGENT, "AgentComplete",
02957                   "Queue: %s\r\n"
02958                   "Uniqueid: %s\r\n"
02959                   "Channel: %s\r\n"
02960                   "MemberName: %s\r\n"
02961                   "HoldTime: %ld\r\n"
02962                   "TalkTime: %ld\r\n"
02963                   "Reason: agent\r\n"
02964                   "%s",
02965                   queuename, qe->chan->uniqueid, peer->name, member->membername, (long)(callstart - qe->start),
02966                   (long)(time(NULL) - callstart),
02967                   qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
02968       }
02969 
02970       if (bridge != AST_PBX_NO_HANGUP_PEER)
02971          ast_hangup(peer);
02972       update_queue(qe->parent, member, callcompletedinsl);
02973       res = bridge ? bridge : 1;
02974       ao2_ref(member, -1);
02975    }
02976 out:
02977    hangupcalls(outgoing, NULL);
02978 
02979    return res;
02980 }
02981 
02982 static int wait_a_bit(struct queue_ent *qe)
02983 {
02984    /* Don't need to hold the lock while we setup the outgoing calls */
02985    int retrywait = qe->parent->retry * 1000;
02986 
02987    int res = ast_waitfordigit(qe->chan, retrywait);
02988    if (res > 0 && !valid_exit(qe, res))
02989       res = 0;
02990 
02991    return res;
02992 }
02993 
02994 static struct member *interface_exists(struct call_queue *q, const char *interface)
02995 {
02996    struct member *mem;
02997    struct ao2_iterator mem_iter;
02998 
02999    if (!q)
03000       return NULL;
03001 
03002    mem_iter = ao2_iterator_init(q->members, 0);
03003    while ((mem = ao2_iterator_next(&mem_iter))) {
03004       if (!strcasecmp(interface, mem->interface))
03005          return mem;
03006       ao2_ref(mem, -1);
03007    }
03008 
03009    return NULL;
03010 }
03011 
03012 
03013 /* Dump all members in a specific queue to the database
03014  *
03015  * <pm_family>/<queuename> = <interface>;<penalty>;<paused>[|...]
03016  *
03017  */
03018 static void dump_queue_members(struct call_queue *pm_queue)
03019 {
03020    struct member *cur_member;
03021    char value[PM_MAX_LEN];
03022    int value_len = 0;
03023    int res;
03024    struct ao2_iterator mem_iter;
03025 
03026    memset(value, 0, sizeof(value));
03027 
03028    if (!pm_queue)
03029       return;
03030 
03031    mem_iter = ao2_iterator_init(pm_queue->members, 0);
03032    while ((cur_member = ao2_iterator_next(&mem_iter))) {
03033       if (!cur_member->dynamic) {
03034          ao2_ref(cur_member, -1);
03035          continue;
03036       }
03037 
03038       res = snprintf(value + value_len, sizeof(value) - value_len, "%s%s;%d;%d;%s",
03039          value_len ? "|" : "", cur_member->interface, cur_member->penalty, cur_member->paused, cur_member->membername);
03040 
03041       ao2_ref(cur_member, -1);
03042 
03043       if (res != strlen(value + value_len)) {
03044          ast_log(LOG_WARNING, "Could not create persistent member string, out of space\n");
03045          break;
03046       }
03047       value_len += res;
03048    }
03049    
03050    if (value_len && !cur_member) {
03051       if (ast_db_put(pm_family, pm_queue->name, value))
03052          ast_log(LOG_WARNING, "failed to create persistent dynamic entry!\n");
03053    } else
03054       /* Delete the entry if the queue is empty or there is an error */
03055       ast_db_del(pm_family, pm_queue->name);
03056 }
03057 
03058 static int remove_from_queue(const char *queuename, const char *interface)
03059 {
03060    struct call_queue *q;
03061    struct member *mem, tmpmem;
03062    int res = RES_NOSUCHQUEUE;
03063 
03064    ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
03065 
03066    AST_LIST_LOCK(&queues);
03067    AST_LIST_TRAVERSE(&queues, q, list) {
03068       ast_mutex_lock(&q->lock);
03069       if (strcmp(q->name, queuename)) {
03070          ast_mutex_unlock(&q->lock);
03071          continue;
03072       }
03073 
03074       if ((mem = ao2_find(q->members, &tmpmem, OBJ_POINTER))) {
03075          /* XXX future changes should beware of this assumption!! */
03076          if(!mem->dynamic) {
03077             res = RES_NOT_DYNAMIC;
03078             ao2_ref(mem, -1);
03079             ast_mutex_unlock(&q->lock);
03080             break;
03081          }
03082          q->membercount--;
03083          manager_event(EVENT_FLAG_AGENT, "QueueMemberRemoved",
03084             "Queue: %s\r\n"
03085             "Location: %s\r\n"
03086             "MemberName: %s\r\n",
03087             q->name, mem->interface, mem->membername);
03088          ao2_unlink(q->members, mem);
03089          ao2_ref(mem, -1);
03090 
03091          if (queue_persistent_members)
03092             dump_queue_members(q);
03093          
03094          res = RES_OKAY;
03095       } else {
03096          res = RES_EXISTS;
03097       }
03098       ast_mutex_unlock(&q->lock);
03099       break;
03100    }
03101 
03102    if (res == RES_OKAY)
03103       remove_from_interfaces(interface);
03104 
03105    AST_LIST_UNLOCK(&queues);
03106 
03107    return res;
03108 }
03109 
03110 
03111 static int add_to_queue(const char *queuename, const char *interface, const char *membername, int penalty, int paused, int dump)
03112 {
03113    struct call_queue *q;
03114    struct member *new_member, *old_member;
03115    int res = RES_NOSUCHQUEUE;
03116 
03117    /* \note Ensure the appropriate realtime queue is loaded.  Note that this
03118     * short-circuits if the queue is already in memory. */
03119    if (!(q = load_realtime_queue(queuename)))
03120       return res;
03121 
03122    AST_LIST_LOCK(&queues);
03123 
03124    ast_mutex_lock(&q->lock);
03125    if ((old_member = interface_exists(q, interface)) == NULL) {
03126       add_to_interfaces(interface);
03127       if ((new_member = create_queue_member(interface, membername, penalty, paused))) {
03128          new_member->dynamic = 1;
03129          ao2_link(q->members, new_member);
03130          q->membercount++;
03131          manager_event(EVENT_FLAG_AGENT, "QueueMemberAdded",
03132             "Queue: %s\r\n"
03133             "Location: %s\r\n"
03134             "MemberName: %s\r\n"
03135             "Membership: %s\r\n"
03136             "Penalty: %d\r\n"
03137             "CallsTaken: %d\r\n"
03138             "LastCall: %d\r\n"
03139             "Status: %d\r\n"
03140             "Paused: %d\r\n",
03141             q->name, new_member->interface, new_member->membername,
03142             "dynamic",
03143             new_member->penalty, new_member->calls, (int) new_member->lastcall,
03144             new_member->status, new_member->paused);
03145          
03146          ao2_ref(new_member, -1);
03147          new_member = NULL;
03148 
03149          if (dump)
03150             dump_queue_members(q);
03151          
03152          res = RES_OKAY;
03153       } else {
03154          res = RES_OUTOFMEMORY;
03155       }
03156    } else {
03157       ao2_ref(old_member, -1);
03158       res = RES_EXISTS;
03159    }
03160    ast_mutex_unlock(&q->lock);
03161    AST_LIST_UNLOCK(&queues);
03162 
03163    return res;
03164 }
03165 
03166 static int set_member_paused(const char *queuename, const char *interface, int paused)
03167 {
03168    int found = 0;
03169    struct call_queue *q;
03170    struct member *mem;
03171 
03172    /* Special event for when all queues are paused - individual events still generated */
03173    /* XXX In all other cases, we use the membername, but since this affects all queues, we cannot */
03174    if (ast_strlen_zero(queuename))
03175       ast_queue_log("NONE", "NONE", interface, (paused ? "PAUSEALL" : "UNPAUSEALL"), "%s", "");
03176 
03177    AST_LIST_LOCK(&queues);
03178    AST_LIST_TRAVERSE(&queues, q, list) {
03179       ast_mutex_lock(&q->lock);
03180       if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
03181          if ((mem = interface_exists(q, interface))) {
03182             found++;
03183             if (mem->paused == paused)
03184                ast_log(LOG_DEBUG, "%spausing already-%spaused queue member %s:%s\n", (paused ? "" : "un"), (paused ? "" : "un"), q->name, interface);
03185             mem->paused = paused;
03186 
03187             if (queue_persistent_members)
03188                dump_queue_members(q);
03189 
03190             if(mem->realtime)
03191                update_realtime_member_field(mem, q->name, "paused", paused ? "1" : "0");
03192 
03193             ast_queue_log(q->name, "NONE", mem->membername, (paused ? "PAUSE" : "UNPAUSE"), "%s", "");
03194 
03195             manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
03196                "Queue: %s\r\n"
03197                "Location: %s\r\n"
03198                "MemberName: %s\r\n"
03199                "Paused: %d\r\n",
03200                   q->name, mem->interface, mem->membername, paused);
03201             ao2_ref(mem, -1);
03202          }
03203       }
03204       ast_mutex_unlock(&q->lock);
03205    }
03206    AST_LIST_UNLOCK(&queues);
03207 
03208    return found ? RESULT_SUCCESS : RESULT_FAILURE;
03209 }
03210 
03211 /* Reload dynamic queue members persisted into the astdb */
03212 static void reload_queue_members(void)
03213 {
03214    char *cur_ptr; 
03215    char *queue_name;
03216    char *member;
03217    char *interface;
03218    char *membername = NULL;
03219    char *penalty_tok;
03220    int penalty = 0;
03221    char *paused_tok;
03222    int paused = 0;
03223    struct ast_db_entry *db_tree;
03224    struct ast_db_entry *entry;
03225    struct call_queue *cur_queue;
03226    char queue_data[PM_MAX_LEN];
03227 
03228    AST_LIST_LOCK(&queues);
03229 
03230    /* Each key in 'pm_family' is the name of a queue */
03231    db_tree = ast_db_gettree(pm_family, NULL);
03232    for (entry = db_tree; entry; entry = entry->next) {
03233 
03234       queue_name = entry->key + strlen(pm_family) + 2;
03235 
03236       AST_LIST_TRAVERSE(&queues, cur_queue, list) {
03237          ast_mutex_lock(&cur_queue->lock);
03238          if (!strcmp(queue_name, cur_queue->name))
03239             break;
03240          ast_mutex_unlock(&cur_queue->lock);
03241       }
03242       
03243       if (!cur_queue)
03244          cur_queue = load_realtime_queue(queue_name);
03245 
03246       if (!cur_queue) {
03247          /* If the queue no longer exists, remove it from the
03248           * database */
03249          ast_log(LOG_WARNING, "Error loading persistent queue: '%s': it does not exist\n", queue_name);
03250          ast_db_del(pm_family, queue_name);
03251          continue;
03252       } else
03253          ast_mutex_unlock(&cur_queue->lock);
03254 
03255       if (ast_db_get(pm_family, queue_name, queue_data, PM_MAX_LEN))
03256          continue;
03257 
03258       cur_ptr = queue_data;
03259       while ((member = strsep(&cur_ptr, "|"))) {
03260          if (ast_strlen_zero(member))
03261             continue;
03262 
03263          interface = strsep(&member, ";");
03264          penalty_tok = strsep(&member, ";");
03265          paused_tok = strsep(&member, ";");
03266          membername = strsep(&member, ";");
03267 
03268          if (!penalty_tok) {
03269             ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (penalty)\n", queue_name);
03270             break;
03271          }
03272          penalty = strtol(penalty_tok, NULL, 10);
03273          if (errno == ERANGE) {
03274             ast_log(LOG_WARNING, "Error converting penalty: %s: Out of range.\n", penalty_tok);
03275             break;
03276          }
03277          
03278          if (!paused_tok) {
03279             ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (paused)\n", queue_name);
03280             break;
03281          }
03282          paused = strtol(paused_tok, NULL, 10);
03283          if ((errno == ERANGE) || paused < 0 || paused > 1) {
03284             ast_log(LOG_WARNING, "Error converting paused: %s: Expected 0 or 1.\n", paused_tok);
03285             break;
03286          }
03287          if (ast_strlen_zero(membername))
03288             membername = interface;
03289 
03290          if (option_debug)
03291             ast_log(LOG_DEBUG, "Reload Members: Queue: %s  Member: %s  Name: %s  Penalty: %d  Paused: %d\n", queue_name, interface, membername, penalty, paused);
03292          
03293          if (add_to_queue(queue_name, interface, membername, penalty, paused, 0) == RES_OUTOFMEMORY) {
03294             ast_log(LOG_ERROR, "Out of Memory when reloading persistent queue member\n");
03295             break;
03296          }
03297       }
03298    }
03299 
03300    AST_LIST_UNLOCK(&queues);
03301    if (db_tree) {
03302       ast_log(LOG_NOTICE, "Queue members successfully reloaded from database.\n");
03303       ast_db_freetree(db_tree);
03304    }
03305 }
03306 
03307 static int pqm_exec(struct ast_channel *chan, void *data)
03308 {
03309    struct ast_module_user *lu;
03310    char *parse;
03311    int priority_jump = 0;
03312    AST_DECLARE_APP_ARGS(args,
03313       AST_APP_ARG(queuename);
03314       AST_APP_ARG(interface);
03315       AST_APP_ARG(options);
03316    );
03317 
03318    if (ast_strlen_zero(data)) {
03319       ast_log(LOG_WARNING, "PauseQueueMember requires an argument ([queuename]|interface[|options])\n");
03320       return -1;
03321    }
03322 
03323    parse = ast_strdupa(data);
03324 
03325    AST_STANDARD_APP_ARGS(args, parse);
03326 
03327    lu = ast_module_user_add(chan);
03328 
03329    if (args.options) {
03330       if (strchr(args.options, 'j'))
03331          priority_jump = 1;
03332    }
03333 
03334    if (ast_strlen_zero(args.interface)) {
03335       ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename]|interface[|options])\n");
03336       ast_module_user_remove(lu);
03337       return -1;
03338    }
03339 
03340    if (set_member_paused(args.queuename, args.interface, 1)) {
03341       ast_log(LOG_WARNING, "Attempt to pause interface %s, not found\n", args.interface);
03342       if (priority_jump || ast_opt_priority_jumping) {
03343          if (ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101)) {
03344             pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND");
03345             ast_module_user_remove(lu);
03346             return 0;
03347          }
03348       }
03349       ast_module_user_remove(lu);
03350       pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND");
03351       return -1;
03352    }
03353 
03354    ast_module_user_remove(lu);
03355    pbx_builtin_setvar_helper(chan, "PQMSTATUS", "PAUSED");
03356 
03357    return 0;
03358 }
03359 
03360 static int upqm_exec(struct ast_channel *chan, void *data)
03361 {
03362    struct ast_module_user *lu;
03363    char *parse;
03364    int priority_jump = 0;
03365    AST_DECLARE_APP_ARGS(args,
03366       AST_APP_ARG(queuename);
03367       AST_APP_ARG(interface);
03368       AST_APP_ARG(options);
03369    );
03370 
03371    if (ast_strlen_zero(data)) {
03372       ast_log(LOG_WARNING, "UnpauseQueueMember requires an argument ([queuename]|interface[|options])\n");
03373       return -1;
03374    }
03375 
03376    parse = ast_strdupa(data);
03377 
03378    AST_STANDARD_APP_ARGS(args, parse);
03379 
03380    lu = ast_module_user_add(chan);
03381 
03382    if (args.options) {
03383       if (strchr(args.options, 'j'))
03384          priority_jump = 1;
03385    }
03386 
03387    if (ast_strlen_zero(args.interface)) {
03388       ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename]|interface[|options])\n");
03389       ast_module_user_remove(lu);
03390       return -1;
03391    }
03392 
03393    if (set_member_paused(args.queuename, args.interface, 0)) {
03394       ast_log(LOG_WARNING, "Attempt to unpause interface %s, not found\n", args.interface);
03395       if (priority_jump || ast_opt_priority_jumping) {
03396          if (ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101)) {
03397             pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND");
03398             ast_module_user_remove(lu);
03399             return 0;
03400          }
03401       }
03402       ast_module_user_remove(lu);
03403       pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND");
03404       return -1;
03405    }
03406 
03407    ast_module_user_remove(lu);
03408    pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "UNPAUSED");
03409 
03410    return 0;
03411 }
03412 
03413 static int rqm_exec(struct ast_channel *chan, void *data)
03414 {
03415    int res=-1;
03416    struct ast_module_user *lu;
03417    char *parse, *temppos = NULL;
03418    int priority_jump = 0;
03419    AST_DECLARE_APP_ARGS(args,
03420       AST_APP_ARG(queuename);
03421       AST_APP_ARG(interface);
03422       AST_APP_ARG(options);
03423    );
03424 
03425 
03426    if (ast_strlen_zero(data)) {
03427       ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename[|interface[|options]])\n");
03428       return -1;
03429    }
03430 
03431    parse = ast_strdupa(data);
03432 
03433    AST_STANDARD_APP_ARGS(args, parse);
03434 
03435    lu = ast_module_user_add(chan);
03436 
03437    if (ast_strlen_zero(args.interface)) {
03438       args.interface = ast_strdupa(chan->name);
03439       temppos = strrchr(args.interface, '-');
03440       if (temppos)
03441          *temppos = '\0';
03442    }
03443 
03444    if (args.options) {
03445       if (strchr(args.options, 'j'))
03446          priority_jump = 1;
03447    }
03448 
03449    switch (remove_from_queue(args.queuename, args.interface)) {
03450    case RES_OKAY:
03451       ast_queue_log(args.queuename, chan->uniqueid, args.interface, "REMOVEMEMBER", "%s", "");
03452       ast_log(LOG_NOTICE, "Removed interface '%s' from queue '%s'\n", args.interface, args.queuename);
03453       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "REMOVED");
03454       res = 0;
03455       break;
03456    case RES_EXISTS:
03457       ast_log(LOG_DEBUG, "Unable to remove interface '%s' from queue '%s': Not there\n", args.interface, args.queuename);
03458       if (priority_jump || ast_opt_priority_jumping)
03459          ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
03460       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTINQUEUE");
03461       res = 0;
03462       break;
03463    case RES_NOSUCHQUEUE:
03464       ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", args.queuename);
03465       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOSUCHQUEUE");
03466       res = 0;
03467       break;
03468    case RES_NOT_DYNAMIC:
03469       ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': '%s' is not a dynamic member\n", args.queuename, args.interface);
03470       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTDYNAMIC");
03471       res = 0;
03472       break;
03473    }
03474 
03475    ast_module_user_remove(lu);
03476 
03477    return res;
03478 }
03479 
03480 static int aqm_exec(struct ast_channel *chan, void *data)
03481 {
03482    int res=-1;
03483    struct ast_module_user *lu;
03484    char *parse, *temppos = NULL;
03485    int priority_jump = 0;
03486    AST_DECLARE_APP_ARGS(args,
03487       AST_APP_ARG(queuename);
03488       AST_APP_ARG(interface);
03489       AST_APP_ARG(penalty);
03490       AST_APP_ARG(options);
03491       AST_APP_ARG(membername);
03492    );
03493    int penalty = 0;
03494 
03495    if (ast_strlen_zero(data)) {
03496       ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename[|[interface]|[penalty][|options][|membername]])\n");
03497       return -1;
03498    }
03499 
03500    parse = ast_strdupa(data);
03501 
03502    AST_STANDARD_APP_ARGS(args, parse);
03503 
03504    lu = ast_module_user_add(chan);
03505 
03506    if (ast_strlen_zero(args.interface)) {
03507       args.interface = ast_strdupa(chan->name);
03508       temppos = strrchr(args.interface, '-');
03509       if (temppos)
03510          *temppos = '\0';
03511    }
03512 
03513    if (!ast_strlen_zero(args.penalty)) {
03514       if ((sscanf(args.penalty, "%d", &penalty) != 1) || penalty < 0) {
03515          ast_log(LOG_WARNING, "Penalty '%s' is invalid, must be an integer >= 0\n", args.penalty);
03516          penalty = 0;
03517       }
03518    }
03519    
03520    if (args.options) {
03521       if (strchr(args.options, 'j'))
03522          priority_jump = 1;
03523    }
03524 
03525    switch (add_to_queue(args.queuename, args.interface, args.membername, penalty, 0, queue_persistent_members)) {
03526    case RES_OKAY:
03527       ast_queue_log(args.queuename, chan->uniqueid, args.interface, "ADDMEMBER", "%s", "");
03528       ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", args.interface, args.queuename);
03529       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "ADDED");
03530       res = 0;
03531       break;
03532    case RES_EXISTS:
03533       ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': Already there\n", args.interface, args.queuename);
03534       if (priority_jump || ast_opt_priority_jumping)
03535          ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
03536       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "MEMBERALREADY");
03537       res = 0;
03538       break;
03539    case RES_NOSUCHQUEUE:
03540       ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", args.queuename);
03541       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "NOSUCHQUEUE");
03542       res = 0;
03543       break;
03544    case RES_OUTOFMEMORY:
03545       ast_log(LOG_ERROR, "Out of memory adding member %s to queue %s\n", args.interface, args.queuename);
03546       break;
03547    }
03548 
03549    ast_module_user_remove(lu);
03550 
03551    return res;
03552 }
03553 
03554 static int ql_exec(struct ast_channel *chan, void *data)
03555 {
03556    struct ast_module_user *u;
03557    char *parse;
03558 
03559    AST_DECLARE_APP_ARGS(args,
03560       AST_APP_ARG(queuename);
03561       AST_APP_ARG(uniqueid);
03562       AST_APP_ARG(membername);
03563       AST_APP_ARG(event);
03564       AST_APP_ARG(params);
03565    );
03566 
03567    if (ast_strlen_zero(data)) {
03568       ast_log(LOG_WARNING, "QueueLog requires arguments (queuename|uniqueid|membername|event[|additionalinfo]\n");
03569       return -1;
03570    }
03571 
03572    u = ast_module_user_add(chan);
03573 
03574    parse = ast_strdupa(data);
03575 
03576    AST_STANDARD_APP_ARGS(args, parse);
03577 
03578    if (ast_strlen_zero(args.queuename) || ast_strlen_zero(args.uniqueid)
03579        || ast_strlen_zero(args.membername) || ast_strlen_zero(args.event)) {
03580       ast_log(LOG_WARNING, "QueueLog requires arguments (queuename|uniqueid|membername|event[|additionalinfo])\n");
03581       ast_module_user_remove(u);
03582       return -1;
03583    }
03584 
03585    ast_queue_log(args.queuename, args.uniqueid, args.membername, args.event, 
03586       "%s", args.params ? args.params : "");
03587 
03588    ast_module_user_remove(u);
03589 
03590    return 0;
03591 }
03592 
03593 static int queue_exec(struct ast_channel *chan, void *data)
03594 {
03595    int res=-1;
03596    int ringing=0;
03597    struct ast_module_user *lu;
03598    const char *user_priority;
03599    const char *max_penalty_str;
03600    int prio;
03601    int max_penalty;
03602    enum queue_result reason = QUEUE_UNKNOWN;
03603    /* whether to exit Queue application after the timeout hits */
03604    int tries = 0;
03605    int noption = 0;
03606    char *parse;
03607    AST_DECLARE_APP_ARGS(args,
03608       AST_APP_ARG(queuename);
03609       AST_APP_ARG(options);
03610       AST_APP_ARG(url);
03611       AST_APP_ARG(announceoverride);
03612       AST_APP_ARG(queuetimeoutstr);
03613       AST_APP_ARG(agi);
03614    );
03615    /* Our queue entry */
03616    struct queue_ent qe;
03617    
03618    if (ast_strlen_zero(data)) {
03619       ast_log(LOG_WARNING, "Queue requires an argument: queuename[|options[|URL[|announceoverride[|timeout[|agi]]]]]\n");
03620       return -1;
03621    }
03622    
03623    parse = ast_strdupa(data);
03624    AST_STANDARD_APP_ARGS(args, parse);
03625 
03626    lu = ast_module_user_add(chan);
03627 
03628    /* Setup our queue entry */
03629    memset(&qe, 0, sizeof(qe));
03630    qe.start = time(NULL);
03631 
03632    /* set the expire time based on the supplied timeout; */
03633    if (args.queuetimeoutstr)
03634       qe.expire = qe.start + atoi(args.queuetimeoutstr);
03635    else
03636       qe.expire = 0;
03637 
03638    /* Get the priority from the variable ${QUEUE_PRIO} */
03639    user_priority = pbx_builtin_getvar_helper(chan, "QUEUE_PRIO");
03640    if (user_priority) {
03641       if (sscanf(user_priority, "%d", &prio) == 1) {
03642          if (option_debug)
03643             ast_log(LOG_DEBUG, "%s: Got priority %d from ${QUEUE_PRIO}.\n",
03644                chan->name, prio);
03645       } else {
03646          ast_log(LOG_WARNING, "${QUEUE_PRIO}: Invalid value (%s), channel %s.\n",
03647             user_priority, chan->name);
03648          prio = 0;
03649       }
03650    } else {
03651       if (option_debug > 2)
03652          ast_log(LOG_DEBUG, "NO QUEUE_PRIO variable found. Using default.\n");
03653       prio = 0;
03654    }
03655 
03656    /* Get the maximum penalty from the variable ${QUEUE_MAX_PENALTY} */
03657    if ((max_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MAX_PENALTY"))) {
03658       if (sscanf(max_penalty_str, "%d", &max_penalty) == 1) {
03659          if (option_debug)
03660             ast_log(LOG_DEBUG, "%s: Got max penalty %d from ${QUEUE_MAX_PENALTY}.\n",
03661                chan->name, max_penalty);
03662       } else {
03663          ast_log(LOG_WARNING, "${QUEUE_MAX_PENALTY}: Invalid value (%s), channel %s.\n",
03664             max_penalty_str, chan->name);
03665          max_penalty = 0;
03666       }
03667    } else {
03668       max_penalty = 0;
03669    }
03670 
03671    if (args.options && (strchr(args.options, 'r')))
03672       ringing = 1;
03673 
03674    if (option_debug)
03675       ast_log(LOG_DEBUG, "queue: %s, options: %s, url: %s, announce: %s, expires: %ld, priority: %d\n",
03676          args.queuename, args.options, args.url, args.announceoverride, (long)qe.expire, prio);
03677 
03678    qe.chan = chan;
03679    qe.prio = prio;
03680    qe.max_penalty = max_penalty;
03681    qe.last_pos_said = 0;
03682    qe.last_pos = 0;
03683    qe.last_periodic_announce_time = time(NULL);
03684    qe.last_periodic_announce_sound = 0;
03685    qe.valid_digits = 0;
03686    if (!join_queue(args.queuename, &qe, &reason)) {
03687       int makeannouncement = 0;
03688 
03689       ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ENTERQUEUE", "%s|%s", S_OR(args.url, ""),
03690          S_OR(chan->cid.cid_num, ""));
03691 check_turns:
03692       if (ringing) {
03693          ast_indicate(chan, AST_CONTROL_RINGING);
03694       } else {
03695          ast_moh_start(chan, qe.moh, NULL);
03696       }
03697 
03698       /* This is the wait loop for callers 2 through maxlen */
03699       res = wait_our_turn(&qe, ringing, &reason);
03700       if (res)
03701          goto stop;
03702 
03703       for (;;) {
03704          /* This is the wait loop for the head caller*/
03705          /* To exit, they may get their call answered; */
03706          /* they may dial a digit from the queue context; */
03707          /* or, they may timeout. */
03708 
03709          enum queue_member_status stat;
03710 
03711          /* Leave if we have exceeded our queuetimeout */
03712          if (qe.expire && (time(NULL) > qe.expire)) {
03713             record_abandoned(&qe);
03714             reason = QUEUE_TIMEOUT;
03715             res = 0;
03716             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
03717             break;
03718          }
03719 
03720          if (makeannouncement) {
03721             /* Make a position announcement, if enabled */
03722             if (qe.parent->announcefrequency && !ringing)
03723                if ((res = say_position(&qe)))
03724                   goto stop;
03725 
03726          }
03727          makeannouncement = 1;
03728 
03729          /* Make a periodic announcement, if enabled */
03730          if (qe.parent->periodicannouncefrequency && !ringing)
03731             if ((res = say_periodic_announcement(&qe)))
03732                goto stop;
03733 
03734          /* Try calling all queue members for 'timeout' seconds */
03735          res = try_calling(&qe, args.options, args.announceoverride, args.url, &tries, &noption, args.agi);
03736          if (res)
03737             goto stop;
03738 
03739          stat = get_member_status(qe.parent, qe.max_penalty);
03740 
03741          /* exit after 'timeout' cycle if 'n' option enabled */
03742          if (noption && tries >= qe.parent->membercount) {
03743             if (option_verbose > 2)
03744                ast_verbose(VERBOSE_PREFIX_3 "Exiting on time-out cycle\n");
03745             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
03746             record_abandoned(&qe);
03747             reason = QUEUE_TIMEOUT;
03748             res = 0;
03749             break;
03750          }
03751 
03752          /* leave the queue if no agents, if enabled */
03753          if (qe.parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) {
03754             record_abandoned(&qe);
03755             reason = QUEUE_LEAVEEMPTY;
03756             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
03757             res = 0;
03758             break;
03759          }
03760 
03761          /* leave the queue if no reachable agents, if enabled */
03762          if ((qe.parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) {
03763             record_abandoned(&qe);
03764             reason = QUEUE_LEAVEUNAVAIL;
03765             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
03766             res = 0;
03767             break;
03768          }
03769 
03770          /* Leave if we have exceeded our queuetimeout */
03771          if (qe.expire && (time(NULL) > qe.expire)) {
03772             record_abandoned(&qe);
03773             reason = QUEUE_TIMEOUT;
03774             res = 0;
03775             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
03776             break;
03777          }
03778 
03779          /* If using dynamic realtime members, we should regenerate the member list for this queue */
03780          update_realtime_members(qe.parent);
03781 
03782          /* OK, we didn't get anybody; wait for 'retry' seconds; may get a digit to exit with */
03783          res = wait_a_bit(&qe);
03784          if (res)
03785             goto stop;
03786 
03787 
03788          /* Since this is a priority queue and
03789           * it is not sure that we are still at the head
03790           * of the queue, go and check for our turn again.
03791           */
03792          if (!is_our_turn(&qe)) {
03793             if (option_debug)
03794                ast_log(LOG_DEBUG, "Darn priorities, going back in queue (%s)!\n",
03795                   qe.chan->name);
03796             goto check_turns;
03797          }
03798       }
03799 
03800 stop:
03801       if (res) {
03802          if (res < 0) {
03803             if (!qe.handled) {
03804                record_abandoned(&qe);
03805                ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ABANDON",
03806                   "%d|%d|%ld", qe.pos, qe.opos,
03807                   (long) time(NULL) - qe.start);
03808             }
03809             res = -1;
03810          } else if (qe.valid_digits) {
03811             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHKEY",
03812                "%s|%d", qe.digits, qe.pos);
03813          }
03814       }
03815 
03816       /* Don't allow return code > 0 */
03817       if (res >= 0 && res != AST_PBX_KEEPALIVE) {
03818          res = 0; 
03819          if (ringing) {
03820             ast_indicate(chan, -1);
03821          } else {
03822             ast_moh_stop(chan);
03823          }        
03824          ast_stopstream(chan);
03825       }
03826       leave_queue(&qe);
03827       if (reason != QUEUE_UNKNOWN)
03828          set_queue_result(chan, reason);
03829    } else {
03830       ast_log(LOG_WARNING, "Unable to join queue '%s'\n", args.queuename);
03831       set_queue_result(chan, reason);
03832       res = 0;
03833    }
03834    ast_module_user_remove(lu);
03835 
03836    return res;
03837 }
03838 
03839 static int queue_function_qac(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
03840 {
03841    int count = 0;
03842    struct call_queue *q;
03843    struct ast_module_user *lu;
03844    struct member *m;
03845    struct ao2_iterator mem_iter;
03846 
03847    buf[0] = '\0';
03848    
03849    if (ast_strlen_zero(data)) {
03850       ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
03851       return -1;
03852    }
03853 
03854    lu = ast_module_user_add(chan);
03855 
03856    if((q = load_realtime_queue(data))) {
03857       ast_mutex_lock(&q->lock);
03858       mem_iter = ao2_iterator_init(q->members, 0);
03859       while ((m = ao2_iterator_next(&mem_iter))) {
03860          /* Count the agents who are logged in and presently answering calls */
03861          if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
03862             count++;
03863          }
03864          ao2_ref(m, -1);
03865       }
03866       ast_mutex_unlock(&q->lock);
03867    } else
03868       ast_log(LOG_WARNING, "queue %s was not found\n", data);
03869 
03870    snprintf(buf, len, "%d", count);
03871    ast_module_user_remove(lu);
03872 
03873    return 0;
03874 }
03875 
03876 static int queue_function_queuewaitingcount(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
03877 {
03878    int count = 0;
03879    struct call_queue *q;
03880    struct ast_module_user *lu;
03881 
03882    buf[0] = '\0';
03883    
03884    if (ast_strlen_zero(data)) {
03885       ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
03886       return -1;
03887    }
03888 
03889    lu = ast_module_user_add(chan);
03890    
03891    AST_LIST_LOCK(&queues);
03892    AST_LIST_TRAVERSE(&queues, q, list) {
03893       if (!strcasecmp(q->name, data)) {
03894          ast_mutex_lock(&q->lock);
03895          break;
03896       }
03897    }
03898    AST_LIST_UNLOCK(&queues);
03899 
03900    if (q) {
03901       count = q->count;
03902       ast_mutex_unlock(&q->lock);
03903    } else
03904       ast_log(LOG_WARNING, "queue %s was not found\n", data);
03905 
03906    snprintf(buf, len, "%d", count);
03907    ast_module_user_remove(lu);
03908    return 0;
03909 }
03910 
03911 static int queue_function_queuememberlist(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
03912 {
03913    struct ast_module_user *u;
03914    struct call_queue *q;
03915    struct member *m;
03916 
03917    /* Ensure an otherwise empty list doesn't return garbage */
03918    buf[0] = '\0';
03919 
03920    if (ast_strlen_zero(data)) {
03921       ast_log(LOG_ERROR, "QUEUE_MEMBER_LIST requires an argument: queuename\n");
03922       return -1;
03923    }
03924    
03925    u = ast_module_user_add(chan);
03926 
03927    AST_LIST_LOCK(&queues);
03928    AST_LIST_TRAVERSE(&queues, q, list) {
03929       if (!strcasecmp(q->name, data)) {
03930          ast_mutex_lock(&q->lock);
03931          break;
03932       }
03933    }
03934    AST_LIST_UNLOCK(&queues);
03935 
03936    if (q) {
03937       int buflen = 0, count = 0;
03938       struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
03939 
03940       while ((m = ao2_iterator_next(&mem_iter))) {
03941          /* strcat() is always faster than printf() */
03942          if (count++) {
03943             strncat(buf + buflen, ",", len - buflen - 1);
03944             buflen++;
03945          }
03946          strncat(buf + buflen, m->membername, len - buflen - 1);
03947          buflen += strlen(m->membername);
03948          /* Safeguard against overflow (negative length) */
03949          if (buflen >= len - 2) {
03950             ao2_ref(m, -1);
03951             ast_log(LOG_WARNING, "Truncating list\n");
03952             break;
03953          }
03954          ao2_ref(m, -1);
03955       }
03956       ast_mutex_unlock(&q->lock);
03957    } else
03958       ast_log(LOG_WARNING, "queue %s was not found\n", data);
03959 
03960    /* We should already be terminated, but let's make sure. */
03961    buf[len - 1] = '\0';
03962    ast_module_user_remove(u);
03963 
03964    return 0;
03965 }
03966 
03967 static struct ast_custom_function queueagentcount_function = {
03968    .name = "QUEUEAGENTCOUNT",
03969    .synopsis = "Count number of agents answering a queue",
03970    .syntax = "QUEUEAGENTCOUNT(<queuename>)",
03971    .desc =
03972 "Returns the number of members currently associated with the specified queue.\n"
03973 "This function is deprecated.  You should use QUEUE_MEMBER_COUNT() instead.\n",
03974    .read = queue_function_qac,
03975 };
03976 
03977 static struct ast_custom_function queuemembercount_function = {
03978    .name = "QUEUE_MEMBER_COUNT",
03979    .synopsis = "Count number of members answering a queue",
03980    .syntax = "QUEUE_MEMBER_COUNT(<queuename>)",
03981    .desc =
03982 "Returns the number of members currently associated with the specified queue.\n",
03983    .read = queue_function_qac,
03984 };
03985 
03986 static struct ast_custom_function queuewaitingcount_function = {
03987    .name = "QUEUE_WAITING_COUNT",
03988    .synopsis = "Count number of calls currently waiting in a queue",
03989    .syntax = "QUEUE_WAITING_COUNT(<queuename>)",
03990    .desc =
03991 "Returns the number of callers currently waiting in the specified queue.\n",
03992    .read = queue_function_queuewaitingcount,
03993 };
03994 
03995 static struct ast_custom_function queuememberlist_function = {
03996    .name = "QUEUE_MEMBER_LIST",
03997    .synopsis = "Returns a list of interfaces on a queue",
03998    .syntax = "QUEUE_MEMBER_LIST(<queuename>)",
03999    .desc =
04000 "Returns a comma-separated list of members associated with the specified queue.\n",
04001    .read = queue_function_queuememberlist,
04002 };
04003 
04004 static int reload_queues(void)
04005 {
04006    struct call_queue *q;
04007    struct ast_config *cfg;
04008    char *cat, *tmp;
04009    struct ast_variable *var;
04010    struct member *cur, *newm;
04011    struct ao2_iterator mem_iter;
04012    int new;
04013    const char *general_val = NULL;
04014    char parse[80];
04015    char *interface;
04016    char *membername = NULL;
04017    int penalty;
04018    AST_DECLARE_APP_ARGS(args,
04019       AST_APP_ARG(interface);
04020       AST_APP_ARG(penalty);
04021       AST_APP_ARG(membername);
04022    );
04023    
04024    if (!(cfg = ast_config_load("queues.conf"))) {
04025       ast_log(LOG_NOTICE, "No call queueing config file (queues.conf), so no call queues\n");
04026       return 0;
04027    }
04028    AST_LIST_LOCK(&queues);
04029    use_weight=0;
04030    /* Mark all non-realtime queues as dead for the moment */
04031    AST_LIST_TRAVERSE(&queues, q, list) {
04032       if(!q->realtime) {
04033          q->dead = 1;
04034          q->found = 0;
04035       }
04036    }
04037 
04038    /* Chug through config file */
04039    cat = NULL;
04040    while ((cat = ast_category_browse(cfg, cat)) ) {
04041       if (!strcasecmp(cat, "general")) {  
04042          /* Initialize global settings */
04043          queue_persistent_members = 0;
04044          if ((general_val = ast_variable_retrieve(cfg, "general", "persistentmembers")))
04045             queue_persistent_members = ast_true(general_val);
04046          autofill_default = 0;
04047          if ((general_val = ast_variable_retrieve(cfg, "general", "autofill")))
04048             autofill_default = ast_true(general_val);
04049          montype_default = 0;
04050          if ((general_val = ast_variable_retrieve(cfg, "general", "monitor-type")))
04051             if (!strcasecmp(general_val, "mixmonitor"))
04052                montype_default = 1;
04053       } else { /* Define queue */
04054          /* Look for an existing one */
04055          AST_LIST_TRAVERSE(&queues, q, list) {
04056             if (!strcmp(q->name, cat))
04057                break;
04058          }
04059          if (!q) {
04060             /* Make one then */
04061             if (!(q = alloc_queue(cat))) {
04062                /* TODO: Handle memory allocation failure */
04063             }
04064             new = 1;
04065          } else
04066             new = 0;
04067          if (q) {
04068             if (!new)
04069                ast_mutex_lock(&q->lock);
04070             /* Check if a queue with this name already exists */
04071             if (q->found) {
04072                ast_log(LOG_WARNING, "Queue '%s' already defined! Skipping!\n", cat);
04073                if(!new)
04074                   ast_mutex_unlock(&q->lock);
04075                continue;
04076             }
04077             /* Re-initialize the queue, and clear statistics */
04078             init_queue(q);
04079             clear_queue(q);
04080             mem_iter = ao2_iterator_init(q->members, 0);
04081             while ((cur = ao2_iterator_next(&mem_iter))) {
04082                if (!cur->dynamic) {
04083                   cur->delme = 1;
04084                }
04085                ao2_ref(cur, -1);
04086             }
04087             for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
04088                if (!strcasecmp(var->name, "member")) {
04089                   struct member tmpmem;
04090 
04091                   /* Add a new member */
04092                   ast_copy_string(parse, var->value, sizeof(parse));
04093                   
04094                   AST_NONSTANDARD_APP_ARGS(args, parse, ',');
04095 
04096                   interface = args.interface;
04097                   if(!ast_strlen_zero(args.penalty)) {
04098                      tmp = args.penalty;
04099                      while (*tmp && *tmp < 33) tmp++;
04100                      penalty = atoi(tmp);
04101                      if (penalty < 0) {
04102                         penalty = 0;
04103                      }
04104                   } else
04105                      penalty = 0;
04106 
04107                   if (!ast_strlen_zero(args.membername)) {
04108                      membername = args.membername;
04109                      while (*membername && *membername < 33) membername++;
04110                   }
04111 
04112                   /* Find the old position in the list */
04113                   ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
04114                   cur = ao2_find(q->members, &tmpmem, OBJ_POINTER | OBJ_UNLINK);
04115 
04116                   newm = create_queue_member(interface, membername, penalty, cur ? cur->paused : 0);
04117                   ao2_link(q->members, newm);
04118                   ao2_ref(newm, -1);
04119                   newm = NULL;
04120 
04121                   if (cur)
04122                      ao2_ref(cur, -1);
04123                   else {
04124                      /* Add them to the master int list if necessary */
04125                      add_to_interfaces(interface);
04126                      q->membercount++;
04127                   }
04128                } else {
04129                   queue_set_param(q, var->name, var->value, var->lineno, 1);
04130                }
04131             }
04132 
04133             /* Free remaining members marked as delme */
04134             mem_iter = ao2_iterator_init(q->members, 0);
04135             while ((cur = ao2_iterator_next(&mem_iter))) {
04136                if (! cur->delme) {
04137                   ao2_ref(cur, -1);
04138                   continue;
04139                }
04140 
04141                remove_from_interfaces(cur->interface);
04142                q->membercount--;
04143                ao2_unlink(q->members, cur);
04144                ao2_ref(cur, -1);
04145             }
04146 
04147             if (q->strategy == QUEUE_STRATEGY_ROUNDROBIN)
04148                rr_dep_warning();
04149 
04150             if (new) {
04151                AST_LIST_INSERT_HEAD(&queues, q, list);
04152             } else
04153                ast_mutex_unlock(&q->lock);
04154          }
04155       }
04156    }
04157    ast_config_destroy(cfg);
04158    AST_LIST_TRAVERSE_SAFE_BEGIN(&queues, q, list) {
04159       if (q->dead) {
04160          AST_LIST_REMOVE_CURRENT(&queues, list);
04161          if (!q->count)
04162             destroy_queue(q);
04163          else
04164             ast_log(LOG_DEBUG, "XXX Leaking a little memory :( XXX\n");
04165       } else {
04166          ast_mutex_lock(&q->lock);
04167          mem_iter = ao2_iterator_init(q->members, 0);
04168          while ((cur = ao2_iterator_next(&mem_iter))) {
04169             if (cur->dynamic)
04170                q->membercount++;
04171             cur->status = ast_device_state(cur->interface);
04172             ao2_ref(cur, -1);
04173          }
04174          ast_mutex_unlock(&q->lock);
04175       }
04176    }
04177    AST_LIST_TRAVERSE_SAFE_END;
04178    AST_LIST_UNLOCK(&queues);
04179    return 1;
04180 }
04181 
04182 static int __queues_show(struct mansession *s, int manager, int fd, int argc, char **argv)
04183 {
04184    struct call_queue *q;
04185    struct queue_ent *qe;
04186    struct member *mem;
04187    int pos, queue_show;
04188    time_t now;
04189    char max_buf[80];
04190    char *max;
04191    size_t max_left;
04192    float sl = 0;
04193    char *term = manager ? "\r\n" : "\n";
04194    struct ao2_iterator mem_iter;
04195 
04196    time(&now);
04197    if (argc == 2)
04198       queue_show = 0;
04199    else if (argc == 3)
04200       queue_show = 1;
04201    else
04202       return RESULT_SHOWUSAGE;
04203 
04204    /* We only want to load realtime queues when a specific queue is asked for. */
04205    if (queue_show)
04206       load_realtime_queue(argv[2]);
04207 
04208    AST_LIST_LOCK(&queues);
04209    if (AST_LIST_EMPTY(&queues)) {
04210       AST_LIST_UNLOCK(&queues);
04211       if (queue_show) {
04212          if (s)
04213             astman_append(s, "No such queue: %s.%s",argv[2], term);
04214          else
04215             ast_cli(fd, "No such queue: %s.%s",argv[2], term);
04216       } else {
04217          if (s)
04218             astman_append(s, "No queues.%s", term);
04219          else
04220             ast_cli(fd, "No queues.%s", term);
04221       }
04222       return RESULT_SUCCESS;
04223    }
04224    AST_LIST_TRAVERSE(&queues, q, list) {
04225       ast_mutex_lock(&q->lock);
04226       if (queue_show) {
04227          if (strcasecmp(q->name, argv[2]) != 0) {
04228             ast_mutex_unlock(&q->lock);
04229             if (!AST_LIST_NEXT(q, list)) {
04230                ast_cli(fd, "No such queue: %s.%s",argv[2], term);
04231                break;
04232             }
04233             continue;
04234          }
04235       }
04236       max_buf[0] = '\0';
04237       max = max_buf;
04238       max_left = sizeof(max_buf);
04239       if (q->maxlen)
04240          ast_build_string(&max, &max_left, "%d", q->maxlen);
04241       else
04242          ast_build_string(&max, &max_left, "unlimited");
04243       sl = 0;
04244       if (q->callscompleted > 0)
04245          sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
04246       if (s)
04247          astman_append(s, "%-12.12s has %d calls (max %s) in '%s' strategy (%ds holdtime), W:%d, C:%d, A:%d, SL:%2.1f%% within %ds%s",
04248             q->name, q->count, max_buf, int2strat(q->strategy), q->holdtime, q->weight,
04249             q->callscompleted, q->callsabandoned,sl,q->servicelevel, term);
04250       else
04251          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",
04252             q->name, q->count, max_buf, int2strat(q->strategy), q->holdtime, q->weight, q->callscompleted, q->callsabandoned,sl,q->servicelevel, term);
04253       if (ao2_container_count(q->members)) {
04254          if (s)
04255             astman_append(s, "   Members: %s", term);
04256          else
04257             ast_cli(fd, "   Members: %s", term);
04258          mem_iter = ao2_iterator_init(q->members, 0);
04259          while ((mem = ao2_iterator_next(&mem_iter))) {
04260             max_buf[0] = '\0';
04261             max = max_buf;
04262             max_left = sizeof(max_buf);
04263             if (mem->penalty)
04264                ast_build_string(&max, &max_left, " with penalty %d", mem->penalty);
04265             if (mem->dynamic)
04266                ast_build_string(&max, &max_left, " (dynamic)");
04267             if (mem->realtime)
04268                ast_build_string(&max, &max_left, " (realtime)");
04269             if (mem->paused)
04270                ast_build_string(&max, &max_left, " (paused)");
04271             ast_build_string(&max, &max_left, " (%s)", devstate2str(mem->status));
04272             if (mem->calls) {
04273                ast_build_string(&max, &max_left, " has taken %d calls (last was %ld secs ago)",
04274                   mem->calls, (long) (time(NULL) - mem->lastcall));
04275             } else
04276                ast_build_string(&max, &max_left, " has taken no calls yet");
04277             if (s)
04278                astman_append(s, "      %s%s%s", mem->membername, max_buf, term);
04279             else
04280                ast_cli(fd, "      %s%s%s", mem->membername, max_buf, term);
04281             ao2_ref(mem, -1);
04282          }
04283       } else if (s)
04284          astman_append(s, "   No Members%s", term);
04285       else  
04286          ast_cli(fd, "   No Members%s", term);
04287       if (q->head) {
04288          pos = 1;
04289          if (s)
04290             astman_append(s, "   Callers: %s", term);
04291          else
04292             ast_cli(fd, "   Callers: %s", term);
04293          for (qe = q->head; qe; qe = qe->next) {
04294             if (s)
04295                astman_append(s, "      %d. %s (wait: %ld:%2.2ld, prio: %d)%s",
04296                   pos++, qe->chan->name, (long) (now - qe->start) / 60,
04297                   (long) (now - qe->start) % 60, qe->prio, term);
04298             else
04299                ast_cli(fd, "      %d. %s (wait: %ld:%2.2ld, prio: %d)%s", pos++,
04300                   qe->chan->name, (long) (now - qe->start) / 60,
04301                   (long) (now - qe->start) % 60, qe->prio, term);
04302          }
04303       } else if (s)
04304          astman_append(s, "   No Callers%s", term);
04305       else
04306          ast_cli(fd, "   No Callers%s", term);
04307       if (s)
04308          astman_append(s, "%s", term);
04309       else
04310          ast_cli(fd, "%s", term);
04311       ast_mutex_unlock(&q->lock);
04312       if (queue_show)
04313          break;
04314    }
04315    AST_LIST_UNLOCK(&queues);
04316    return RESULT_SUCCESS;
04317 }
04318 
04319 static int queue_show(int fd, int argc, char **argv)
04320 {
04321    return __queues_show(NULL, 0, fd, argc, argv);
04322 }
04323 
04324 static char *complete_queue(const char *line, const char *word, int pos, int state)
04325 {
04326    struct call_queue *q;
04327    char *ret = NULL;
04328    int which = 0;
04329    int wordlen = strlen(word);
04330    
04331    AST_LIST_LOCK(&queues);
04332    AST_LIST_TRAVERSE(&queues, q, list) {
04333       if (!strncasecmp(word, q->name, wordlen) && ++which > state) {
04334          ret = ast_strdup(q->name); 
04335          break;
04336       }
04337    }
04338    AST_LIST_UNLOCK(&queues);
04339 
04340    return ret;
04341 }
04342 
04343 static char *complete_queue_show(const char *line, const char *word, int pos, int state)
04344 {
04345    if (pos == 2)
04346       return complete_queue(line, word, pos, state);
04347    return NULL;
04348 }
04349 
04350 /*!\brief callback to display queues status in manager
04351    \addtogroup Group_AMI
04352  */
04353 static int manager_queues_show(struct mansession *s, const struct message *m)
04354 {
04355    char *a[] = { "queue", "show" };
04356 
04357    __queues_show(s, 1, -1, 2, a);
04358    astman_append(s, "\r\n\r\n"); /* Properly terminate Manager output */
04359 
04360    return RESULT_SUCCESS;
04361 }
04362 
04363 /* Dump queue status */
04364 static int manager_queues_status(struct mansession *s, const struct message *m)
04365 {
04366    time_t now;
04367    int pos;
04368    const char *id = astman_get_header(m,"ActionID");
04369    const char *queuefilter = astman_get_header(m,"Queue");
04370    const char *memberfilter = astman_get_header(m,"Member");
04371    char idText[256] = "";
04372    struct call_queue *q;
04373    struct queue_ent *qe;
04374    float sl = 0;
04375    struct member *mem;
04376    struct ao2_iterator mem_iter;
04377 
04378    astman_send_ack(s, m, "Queue status will follow");
04379    time(&now);
04380    AST_LIST_LOCK(&queues);
04381    if (!ast_strlen_zero(id))
04382       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
04383 
04384    AST_LIST_TRAVERSE(&queues, q, list) {
04385       ast_mutex_lock(&q->lock);
04386 
04387       /* List queue properties */
04388       if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) {
04389          sl = ((q->callscompleted > 0) ? 100 * ((float)q->callscompletedinsl / (float)q->callscompleted) : 0);
04390          astman_append(s, "Event: QueueParams\r\n"
04391             "Queue: %s\r\n"
04392             "Max: %d\r\n"
04393             "Calls: %d\r\n"
04394             "Holdtime: %d\r\n"
04395             "Completed: %d\r\n"
04396             "Abandoned: %d\r\n"
04397             "ServiceLevel: %d\r\n"
04398             "ServicelevelPerf: %2.1f\r\n"
04399             "Weight: %d\r\n"
04400             "%s"
04401             "\r\n",
04402             q->name, q->maxlen, q->count, q->holdtime, q->callscompleted,
04403             q->callsabandoned, q->servicelevel, sl, q->weight, idText);
04404          /* List Queue Members */
04405          mem_iter = ao2_iterator_init(q->members, 0);
04406          while ((mem = ao2_iterator_next(&mem_iter))) {
04407             if (ast_strlen_zero(memberfilter) || !strcmp(mem->interface, memberfilter)) {
04408                astman_append(s, "Event: QueueMember\r\n"
04409                   "Queue: %s\r\n"
04410                   "Name: %s\r\n"
04411                   "Location: %s\r\n"
04412                   "Membership: %s\r\n"
04413                   "Penalty: %d\r\n"
04414                   "CallsTaken: %d\r\n"
04415                   "LastCall: %d\r\n"
04416                   "Status: %d\r\n"
04417                   "Paused: %d\r\n"
04418                   "%s"
04419                   "\r\n",
04420                   q->name, mem->membername, mem->interface, mem->dynamic ? "dynamic" : "static",
04421                   mem->penalty, mem->calls, (int)mem->lastcall, mem->status, mem->paused, idText);
04422             }
04423             ao2_ref(mem, -1);
04424          }
04425          /* List Queue Entries */
04426          pos = 1;
04427          for (qe = q->head; qe; qe = qe->next) {
04428             astman_append(s, "Event: QueueEntry\r\n"
04429                "Queue: %s\r\n"
04430                "Position: %d\r\n"
04431                "Channel: %s\r\n"
04432                "CallerID: %s\r\n"
04433                "CallerIDName: %s\r\n"
04434                "Wait: %ld\r\n"
04435                "%s"
04436                "\r\n",
04437                q->name, pos++, qe->chan->name,
04438                S_OR(qe->chan->cid.cid_num, "unknown"),
04439                S_OR(qe->chan->cid.cid_name, "unknown"),
04440                (long) (now - qe->start), idText);
04441          }
04442       }
04443       ast_mutex_unlock(&q->lock);
04444    }
04445 
04446    astman_append(s,
04447       "Event: QueueStatusComplete\r\n"
04448       "%s"
04449       "\r\n",idText);
04450 
04451    AST_LIST_UNLOCK(&queues);
04452 
04453 
04454    return RESULT_SUCCESS;
04455 }
04456 
04457 static int manager_add_queue_member(struct mansession *s, const struct message *m)
04458 {
04459    const char *queuename, *interface, *penalty_s, *paused_s, *membername;
04460    int paused, penalty = 0;
04461 
04462    queuename = astman_get_header(m, "Queue");
04463    interface = astman_get_header(m, "Interface");
04464    penalty_s = astman_get_header(m, "Penalty");
04465    paused_s = astman_get_header(m, "Paused");
04466    membername = astman_get_header(m, "MemberName");
04467 
04468    if (ast_strlen_zero(queuename)) {
04469       astman_send_error(s, m, "'Queue' not specified.");
04470       return 0;
04471    }
04472 
04473    if (ast_strlen_zero(interface)) {
04474       astman_send_error(s, m, "'Interface' not specified.");
04475       return 0;
04476    }
04477 
04478    if (ast_strlen_zero(penalty_s))
04479       penalty = 0;
04480    else if (sscanf(penalty_s, "%d", &penalty) != 1 || penalty < 0)
04481       penalty = 0;
04482 
04483    if (ast_strlen_zero(paused_s))
04484       paused = 0;
04485    else
04486       paused = abs(ast_true(paused_s));
04487 
04488    switch (add_to_queue(queuename, interface, membername, penalty, paused, queue_persistent_members)) {
04489    case RES_OKAY:
04490       ast_queue_log(queuename, "MANAGER", interface, "ADDMEMBER", "%s", "");
04491       astman_send_ack(s, m, "Added interface to queue");
04492       break;
04493    case RES_EXISTS:
04494       astman_send_error(s, m, "Unable to add interface: Already there");
04495       break;
04496    case RES_NOSUCHQUEUE:
04497       astman_send_error(s, m, "Unable to add interface to queue: No such queue");
04498       break;
04499    case RES_OUTOFMEMORY:
04500       astman_send_error(s, m, "Out of memory");
04501       break;
04502    }
04503 
04504    return 0;
04505 }
04506 
04507 static int manager_remove_queue_member(struct mansession *s, const struct message *m)
04508 {
04509    const char *queuename, *interface;
04510 
04511    queuename = astman_get_header(m, "Queue");
04512    interface = astman_get_header(m, "Interface");
04513 
04514    if (ast_strlen_zero(queuename) || ast_strlen_zero(interface)) {
04515       astman_send_error(s, m, "Need 'Queue' and 'Interface' parameters.");
04516       return 0;
04517    }
04518 
04519    switch (remove_from_queue(queuename, interface)) {
04520    case RES_OKAY:
04521       ast_queue_log(queuename, "MANAGER", interface, "REMOVEMEMBER", "%s", "");
04522       astman_send_ack(s, m, "Removed interface from queue");
04523       break;
04524    case RES_EXISTS:
04525       astman_send_error(s, m, "Unable to remove interface: Not there");
04526       break;
04527    case RES_NOSUCHQUEUE:
04528       astman_send_error(s, m, "Unable to remove interface from queue: No such queue");
04529       break;
04530    case RES_OUTOFMEMORY:
04531       astman_send_error(s, m, "Out of memory");
04532       break;
04533    case RES_NOT_DYNAMIC:
04534       astman_send_error(s, m, "Member not dynamic");
04535       break;
04536    }
04537 
04538    return 0;
04539 }
04540 
04541 static int manager_pause_queue_member(struct mansession *s, const struct message *m)
04542 {
04543    const char *queuename, *interface, *paused_s;
04544    int paused;
04545 
04546    interface = astman_get_header(m, "Interface");
04547    paused_s = astman_get_header(m, "Paused");
04548    queuename = astman_get_header(m, "Queue");   /* Optional - if not supplied, pause the given Interface in all queues */
04549 
04550    if (ast_strlen_zero(interface) || ast_strlen_zero(paused_s)) {
04551       astman_send_error(s, m, "Need 'Interface' and 'Paused' parameters.");
04552       return 0;
04553    }
04554 
04555    paused = abs(ast_true(paused_s));
04556 
04557    if (set_member_paused(queuename, interface, paused))
04558       astman_send_error(s, m, "Interface not found");
04559    else
04560       astman_send_ack(s, m, paused ? "Interface paused successfully" : "Interface unpaused successfully");
04561    return 0;
04562 }
04563 
04564 static int handle_queue_add_member(int fd, int argc, char *argv[])
04565 {
04566    char *queuename, *interface, *membername = NULL;
04567    int penalty;
04568 
04569    if ((argc != 6) && (argc != 8) && (argc != 10)) {
04570       return RESULT_SHOWUSAGE;
04571    } else if (strcmp(argv[4], "to")) {
04572       return RESULT_SHOWUSAGE;
04573    } else if ((argc == 8) && strcmp(argv[6], "penalty")) {
04574       return RESULT_SHOWUSAGE;
04575    } else if ((argc == 10) && strcmp(argv[8], "as")) {
04576       return RESULT_SHOWUSAGE;
04577    }
04578 
04579    queuename = argv[5];
04580    interface = argv[3];
04581    if (argc >= 8) {
04582       if (sscanf(argv[7], "%d", &penalty) == 1) {
04583          if (penalty < 0) {
04584             ast_cli(fd, "Penalty must be >= 0\n");
04585             penalty = 0;
04586          }
04587       } else {
04588          ast_cli(fd, "Penalty must be an integer >= 0\n");
04589          penalty = 0;
04590       }
04591    } else {
04592       penalty = 0;
04593    }
04594 
04595    if (argc >= 10) {
04596       membername = argv[9];
04597    }
04598 
04599    switch (add_to_queue(queuename, interface, membername, penalty, 0, queue_persistent_members)) {
04600    case RES_OKAY:
04601       ast_queue_log(queuename, "CLI", interface, "ADDMEMBER", "%s", "");
04602       ast_cli(fd, "Added interface '%s' to queue '%s'\n", interface, queuename);
04603       return RESULT_SUCCESS;
04604    case RES_EXISTS:
04605       ast_cli(fd, "Unable to add interface '%s' to queue '%s': Already there\n", interface, queuename);
04606       return RESULT_FAILURE;
04607    case RES_NOSUCHQUEUE:
04608       ast_cli(fd, "Unable to add interface to queue '%s': No such queue\n", queuename);
04609       return RESULT_FAILURE;
04610    case RES_OUTOFMEMORY:
04611       ast_cli(fd, "Out of memory\n");
04612       return RESULT_FAILURE;
04613    default:
04614       return RESULT_FAILURE;
04615    }
04616 }
04617 
04618 static char *complete_queue_add_member(const char *line, const char *word, int pos, int state)
04619 {
04620    /* 0 - queue; 1 - add; 2 - member; 3 - <interface>; 4 - to; 5 - <queue>; 6 - penalty; 7 - <penalty>; 8 - as; 9 - <membername> */
04621    switch (pos) {
04622    case 3:  /* Don't attempt to complete name of interface (infinite possibilities) */
04623       return NULL;
04624    case 4:  /* only one possible match, "to" */
04625       return state == 0 ? ast_strdup("to") : NULL;
04626    case 5:  /* <queue> */
04627       return complete_queue(line, word, pos, state);
04628    case 6: /* only one possible match, "penalty" */
04629       return state == 0 ? ast_strdup("penalty") : NULL;
04630    case 7:
04631       if (state < 100) {   /* 0-99 */
04632          char *num;
04633          if ((num = ast_malloc(3))) {
04634             sprintf(num, "%d", state);
04635          }
04636          return num;
04637       } else {
04638          return NULL;
04639       }
04640    case 8: /* only one possible match, "as" */
04641       return state == 0 ? ast_strdup("as") : NULL;
04642    case 9:  /* Don't attempt to complete name of member (infinite possibilities) */
04643       return NULL;
04644    default:
04645       return NULL;
04646    }
04647 }
04648 
04649 static int handle_queue_remove_member(int fd, int argc, char *argv[])
04650 {
04651    char *queuename, *interface;
04652 
04653    if (argc != 6) {
04654       return RESULT_SHOWUSAGE;
04655    } else if (strcmp(argv[4], "from")) {
04656       return RESULT_SHOWUSAGE;
04657    }
04658 
04659    queuename = argv[5];
04660    interface = argv[3];
04661 
04662    switch (remove_from_queue(queuename, interface)) {
04663    case RES_OKAY:
04664       ast_queue_log(queuename, "CLI", interface, "REMOVEMEMBER", "%s", "");
04665       ast_cli(fd, "Removed interface '%s' from queue '%s'\n", interface, queuename);
04666       return RESULT_SUCCESS;
04667    case RES_EXISTS:
04668       ast_cli(fd, "Unable to remove interface '%s' from queue '%s': Not there\n", interface, queuename);
04669       return RESULT_FAILURE;
04670    case RES_NOSUCHQUEUE:
04671       ast_cli(fd, "Unable to remove interface from queue '%s': No such queue\n", queuename);
04672       return RESULT_FAILURE;
04673    case RES_OUTOFMEMORY:
04674       ast_cli(fd, "Out of memory\n");
04675       return RESULT_FAILURE;
04676    case RES_NOT_DYNAMIC:
04677       ast_cli(fd, "Member not dynamic\n");
04678       return RESULT_FAILURE;
04679    default:
04680       return RESULT_FAILURE;
04681    }
04682 }
04683 
04684 static char *complete_queue_remove_member(const char *line, const char *word, int pos, int state)
04685 {
04686    int which = 0;
04687    struct call_queue *q;
04688    struct member *m;
04689    struct ao2_iterator mem_iter;
04690 
04691    /* 0 - queue; 1 - remove; 2 - member; 3 - <member>; 4 - from; 5 - <queue> */
04692    if (pos > 5 || pos < 3)
04693       return NULL;
04694    if (pos == 4)  /* only one possible match, 'from' */
04695       return state == 0 ? ast_strdup("from") : NULL;
04696 
04697    if (pos == 5)  /* No need to duplicate code */
04698       return complete_queue(line, word, pos, state);
04699 
04700    /* here is the case for 3, <member> */
04701    if (!AST_LIST_EMPTY(&queues)) { /* XXX unnecessary ? the traverse does that for us */
04702       AST_LIST_TRAVERSE(&queues, q, list) {
04703          ast_mutex_lock(&q->lock);
04704          mem_iter = ao2_iterator_init(q->members, 0);
04705          while ((m = ao2_iterator_next(&mem_iter))) {
04706             if (++which > state) {
04707                char *tmp;
04708                ast_mutex_unlock(&q->lock);
04709                tmp = m->membername;
04710                ao2_ref(m, -1);
04711                return ast_strdup(tmp);
04712             }
04713             ao2_ref(m, -1);
04714          }
04715          ast_mutex_unlock(&q->lock);
04716       }
04717    }
04718 
04719    return NULL;
04720 }
04721 
04722 static char queue_show_usage[] =
04723 "Usage: queue show\n"
04724 "       Provides summary information on a specified queue.\n";
04725 
04726 static char qam_cmd_usage[] =
04727 "Usage: queue add member <channel> to <queue> [penalty <penalty>]\n";
04728 
04729 static char qrm_cmd_usage[] =
04730 "Usage: queue remove member <channel> from <queue>\n";
04731 
04732 static struct ast_cli_entry cli_show_queue_deprecated = {
04733    { "show", "queue", NULL },
04734    queue_show, NULL,
04735    NULL, complete_queue_show };
04736 
04737 static struct ast_cli_entry cli_add_queue_member_deprecated = {
04738    { "add", "queue", "member", NULL },
04739    handle_queue_add_member, NULL,
04740    NULL, complete_queue_add_member };
04741 
04742 static struct ast_cli_entry cli_remove_queue_member_deprecated = {
04743    { "remove", "queue", "member", NULL },
04744    handle_queue_remove_member, NULL,
04745    NULL, complete_queue_remove_member };
04746 
04747 static struct ast_cli_entry cli_queue[] = {
04748    /* Deprecated */
04749    { { "show", "queues", NULL },
04750    queue_show, NULL,
04751    NULL, NULL },
04752 
04753    { { "queue", "show", NULL },
04754    queue_show, "Show status of a specified queue",
04755    queue_show_usage, complete_queue_show, &cli_show_queue_deprecated },
04756 
04757    { { "queue", "add", "member", NULL },
04758    handle_queue_add_member, "Add a channel to a specified queue",
04759    qam_cmd_usage, complete_queue_add_member, &cli_add_queue_member_deprecated },
04760 
04761    { { "queue", "remove", "member", NULL },
04762    handle_queue_remove_member, "Removes a channel from a specified queue",
04763    qrm_cmd_usage, complete_queue_remove_member, &cli_remove_queue_member_deprecated },
04764 };
04765 
04766 static int unload_module(void)
04767 {
04768    int res;
04769 
04770    if (device_state.thread != AST_PTHREADT_NULL) {
04771       device_state.stop = 1;
04772       ast_mutex_lock(&device_state.lock);
04773       ast_cond_signal(&device_state.cond);
04774       ast_mutex_unlock(&device_state.lock);
04775       pthread_join(device_state.thread, NULL);
04776    }
04777 
04778    ast_cli_unregister_multiple(cli_queue, sizeof(cli_queue) / sizeof(struct ast_cli_entry));
04779    res = ast_manager_unregister("QueueStatus");
04780    res |= ast_manager_unregister("Queues");
04781    res |= ast_manager_unregister("QueueStatus");
04782    res |= ast_manager_unregister("QueueAdd");
04783    res |= ast_manager_unregister("QueueRemove");
04784    res |= ast_manager_unregister("QueuePause");
04785    res |= ast_unregister_application(app_aqm);
04786    res |= ast_unregister_application(app_rqm);
04787    res |= ast_unregister_application(app_pqm);
04788    res |= ast_unregister_application(app_upqm);
04789    res |= ast_unregister_application(app_ql);
04790    res |= ast_unregister_application(app);
04791    res |= ast_custom_function_unregister(&queueagentcount_function);
04792    res |= ast_custom_function_unregister(&queuemembercount_function);
04793    res |= ast_custom_function_unregister(&queuememberlist_function);
04794    res |= ast_custom_function_unregister(&queuewaitingcount_function);
04795    ast_devstate_del(statechange_queue, NULL);
04796 
04797    ast_module_user_hangup_all();
04798 
04799    clear_and_free_interfaces();
04800 
04801    return res;
04802 }
04803 
04804 static int load_module(void)
04805 {
04806    int res;
04807 
04808    if (!reload_queues())
04809       return AST_MODULE_LOAD_DECLINE;
04810 
04811    if (queue_persistent_members)
04812       reload_queue_members();
04813 
04814    ast_mutex_init(&device_state.lock);
04815    ast_cond_init(&device_state.cond, NULL);
04816    ast_pthread_create(&device_state.thread, NULL, device_state_thread, NULL);
04817 
04818    ast_cli_register_multiple(cli_queue, sizeof(cli_queue) / sizeof(struct ast_cli_entry));
04819    res = ast_register_application(app, queue_exec, synopsis, descrip);
04820    res |= ast_register_application(app_aqm, aqm_exec, app_aqm_synopsis, app_aqm_descrip);
04821    res |= ast_register_application(app_rqm, rqm_exec, app_rqm_synopsis, app_rqm_descrip);
04822    res |= ast_register_application(app_pqm, pqm_exec, app_pqm_synopsis, app_pqm_descrip);
04823    res |= ast_register_application(app_upqm, upqm_exec, app_upqm_synopsis, app_upqm_descrip);
04824    res |= ast_register_application(app_ql, ql_exec, app_ql_synopsis, app_ql_descrip);
04825    res |= ast_manager_register("Queues", 0, manager_queues_show, "Queues");
04826    res |= ast_manager_register("QueueStatus", 0, manager_queues_status, "Queue Status");
04827    res |= ast_manager_register("QueueAdd", EVENT_FLAG_AGENT, manager_add_queue_member, "Add interface to queue.");
04828    res |= ast_manager_register("QueueRemove", EVENT_FLAG_AGENT, manager_remove_queue_member, "Remove interface from queue.");
04829    res |= ast_manager_register("QueuePause", EVENT_FLAG_AGENT, manager_pause_queue_member, "Makes a queue member temporarily unavailable");
04830    res |= ast_custom_function_register(&queueagentcount_function);
04831    res |= ast_custom_function_register(&queuemembercount_function);
04832    res |= ast_custom_function_register(&queuememberlist_function);
04833    res |= ast_custom_function_register(&queuewaitingcount_function);
04834    res |= ast_devstate_add(statechange_queue, NULL);
04835 
04836    return res;
04837 }
04838 
04839 static int reload(void)
04840 {
04841    reload_queues();
04842    return 0;
04843 }
04844 
04845 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "True Call Queueing",
04846       .load = load_module,
04847       .unload = unload_module,
04848       .reload = reload,
04849           );
04850 

Generated on Fri Sep 25 19:28:04 2009 for Asterisk - the Open Source PBX by  doxygen 1.5.5