Sat Nov 1 06:28:20 2008

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: 152538 $")
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 /* Please read before modifying this file.
00103  * There are three locks which are regularly used
00104  * throughout this file, the queue list lock, the lock
00105  * for each individual queue, and the interface list lock.
00106  * Please be extra careful to always lock in the following order
00107  * 1) queue list lock
00108  * 2) individual queue lock
00109  * 3) interface list lock
00110  * This order has sort of "evolved" over the lifetime of this
00111  * application, but it is now in place this way, so please adhere
00112  * to this order!
00113  */
00114 
00115 
00116 enum {
00117    QUEUE_STRATEGY_RINGALL = 0,
00118    QUEUE_STRATEGY_ROUNDROBIN,
00119    QUEUE_STRATEGY_LEASTRECENT,
00120    QUEUE_STRATEGY_FEWESTCALLS,
00121    QUEUE_STRATEGY_RANDOM,
00122    QUEUE_STRATEGY_RRMEMORY
00123 };
00124 
00125 static struct strategy {
00126    int strategy;
00127    char *name;
00128 } strategies[] = {
00129    { QUEUE_STRATEGY_RINGALL, "ringall" },
00130    { QUEUE_STRATEGY_ROUNDROBIN, "roundrobin" },
00131    { QUEUE_STRATEGY_LEASTRECENT, "leastrecent" },
00132    { QUEUE_STRATEGY_FEWESTCALLS, "fewestcalls" },
00133    { QUEUE_STRATEGY_RANDOM, "random" },
00134    { QUEUE_STRATEGY_RRMEMORY, "rrmemory" },
00135 };
00136 
00137 #define DEFAULT_RETRY      5
00138 #define DEFAULT_TIMEOUT    15
00139 #define RECHECK         1     /* Recheck every second to see we we're at the top yet */
00140 #define MAX_PERIODIC_ANNOUNCEMENTS 10 /* The maximum periodic announcements we can have */
00141 
00142 #define  RES_OKAY 0     /* Action completed */
00143 #define  RES_EXISTS  (-1)     /* Entry already exists */
00144 #define  RES_OUTOFMEMORY   (-2)     /* Out of memory */
00145 #define  RES_NOSUCHQUEUE   (-3)     /* No such queue */
00146 #define RES_NOT_DYNAMIC (-4)     /* Member is not dynamic */
00147 
00148 static char *app = "Queue";
00149 
00150 static char *synopsis = "Queue a call for a call queue";
00151 
00152 static char *descrip =
00153 "  Queue(queuename[|options[|URL][|announceoverride][|timeout][|AGI]):\n"
00154 "Queues an incoming call in a particular call queue as defined in queues.conf.\n"
00155 "This application will return to the dialplan if the queue does not exist, or\n"
00156 "any of the join options cause the caller to not enter the queue.\n"
00157 "The option string may contain zero or more of the following characters:\n"
00158 "      'd' -- data-quality (modem) call (minimum delay).\n"
00159 "      'h' -- allow callee to hang up by hitting '*', or whatver disconnect sequence\n"
00160 "             defined in the featuremap section in features.conf.\n"
00161 "      'H' -- allow caller to hang up by hitting '*', or whatever disconnect sequence\n"
00162 "             defined in the featuremap section in features.conf.\n"
00163 "      'n' -- no retries on the timeout; will exit this application and \n"
00164 "             go to the next step.\n"
00165 "      'i' -- ignore call forward requests from queue members and do nothing\n"
00166 "             when they are requested.\n"
00167 "      'r' -- ring instead of playing MOH\n"
00168 "      't' -- allow the called user transfer the calling user by pressing '#' or\n"
00169 "             whatever blindxfer sequence defined in the featuremap section in\n"
00170 "             features.conf\n"
00171 "      'T' -- to allow the calling user to transfer the call by pressing '#' or\n"
00172 "             whatever blindxfer sequence defined in the featuremap section in\n"
00173 "             features.conf\n"
00174 "      'w' -- allow the called user to write the conversation to disk via Monitor\n"
00175 "             by pressing the automon sequence defined in the featuremap section in\n"
00176 "             features.conf\n"
00177 "      'W' -- allow the calling user to write the conversation to disk via Monitor\n"
00178 "             by pressing the automon sequence defined in the featuremap section in\n"
00179 "             features.conf\n"
00180 "  In addition to transferring the call, a call may be parked and then picked\n"
00181 "up by another user, by transferring to the parking lot extension. See features.conf.\n"
00182 "  The optional URL will be sent to the called party if the channel supports\n"
00183 "it.\n"
00184 "  The optional AGI parameter will setup an AGI script to be executed on the \n"
00185 "calling party's channel once they are connected to a queue member.\n"
00186 "  The timeout will cause the queue to fail out after a specified number of\n"
00187 "seconds, checked between each queues.conf 'timeout' and 'retry' cycle.\n"
00188 "  This application sets the following channel variable upon completion:\n"
00189 "      QUEUESTATUS    The status of the call as a text string, one of\n"
00190 "             TIMEOUT | FULL | JOINEMPTY | LEAVEEMPTY | JOINUNAVAIL | LEAVEUNAVAIL\n";
00191 
00192 static char *app_aqm = "AddQueueMember" ;
00193 static char *app_aqm_synopsis = "Dynamically adds queue members" ;
00194 static char *app_aqm_descrip =
00195 "   AddQueueMember(queuename[|interface[|penalty[|options[|membername]]]]):\n"
00196 "Dynamically adds interface to an existing queue.\n"
00197 "If the interface is already in the queue and there exists an n+101 priority\n"
00198 "then it will then jump to this priority.  Otherwise it will return an error\n"
00199 "The option string may contain zero or more of the following characters:\n"
00200 "       'j' -- jump to +101 priority when appropriate.\n"
00201 "  This application sets the following channel variable upon completion:\n"
00202 "     AQMSTATUS    The status of the attempt to add a queue member as a \n"
00203 "                     text string, one of\n"
00204 "           ADDED | MEMBERALREADY | NOSUCHQUEUE \n"
00205 "Example: AddQueueMember(techsupport|SIP/3000)\n"
00206 "";
00207 
00208 static char *app_rqm = "RemoveQueueMember" ;
00209 static char *app_rqm_synopsis = "Dynamically removes queue members" ;
00210 static char *app_rqm_descrip =
00211 "   RemoveQueueMember(queuename[|interface[|options]]):\n"
00212 "Dynamically removes interface to an existing queue\n"
00213 "If the interface is NOT in the queue and there exists an n+101 priority\n"
00214 "then it will then jump to this priority.  Otherwise it will return an error\n"
00215 "The option string may contain zero or more of the following characters:\n"
00216 "       'j' -- jump to +101 priority when appropriate.\n"
00217 "  This application sets the following channel variable upon completion:\n"
00218 "     RQMSTATUS      The status of the attempt to remove a queue member as a\n"
00219 "                     text string, one of\n"
00220 "           REMOVED | NOTINQUEUE | NOSUCHQUEUE \n"
00221 "Example: RemoveQueueMember(techsupport|SIP/3000)\n"
00222 "";
00223 
00224 static char *app_pqm = "PauseQueueMember" ;
00225 static char *app_pqm_synopsis = "Pauses a queue member" ;
00226 static char *app_pqm_descrip =
00227 "   PauseQueueMember([queuename]|interface[|options]):\n"
00228 "Pauses (blocks calls for) a queue member.\n"
00229 "The given interface will be paused in the given queue.  This prevents\n"
00230 "any calls from being sent from the queue to the interface until it is\n"
00231 "unpaused with UnpauseQueueMember or the manager interface.  If no\n"
00232 "queuename is given, the interface is paused in every queue it is a\n"
00233 "member of.  If the interface is not in the named queue, or if no queue\n"
00234 "is given and the interface is not in any queue, it will jump to\n"
00235 "priority n+101, if it exists and the appropriate options are set.\n"
00236 "The application will fail if the interface is not found and no extension\n"
00237 "to jump to exists.\n"
00238 "The option string may contain zero or more of the following characters:\n"
00239 "       'j' -- jump to +101 priority when appropriate.\n"
00240 "  This application sets the following channel variable upon completion:\n"
00241 "     PQMSTATUS      The status of the attempt to pause a queue member as a\n"
00242 "                     text string, one of\n"
00243 "           PAUSED | NOTFOUND\n"
00244 "Example: PauseQueueMember(|SIP/3000)\n";
00245 
00246 static char *app_upqm = "UnpauseQueueMember" ;
00247 static char *app_upqm_synopsis = "Unpauses a queue member" ;
00248 static char *app_upqm_descrip =
00249 "   UnpauseQueueMember([queuename]|interface[|options]):\n"
00250 "Unpauses (resumes calls to) a queue member.\n"
00251 "This is the counterpart to PauseQueueMember and operates exactly the\n"
00252 "same way, except it unpauses instead of pausing the given interface.\n"
00253 "The option string may contain zero or more of the following characters:\n"
00254 "       'j' -- jump to +101 priority when appropriate.\n"
00255 "  This application sets the following channel variable upon completion:\n"
00256 "     UPQMSTATUS       The status of the attempt to unpause a queue \n"
00257 "                      member as a text string, one of\n"
00258 "            UNPAUSED | NOTFOUND\n"
00259 "Example: UnpauseQueueMember(|SIP/3000)\n";
00260 
00261 static char *app_ql = "QueueLog" ;
00262 static char *app_ql_synopsis = "Writes to the queue_log" ;
00263 static char *app_ql_descrip =
00264 "   QueueLog(queuename|uniqueid|agent|event[|additionalinfo]):\n"
00265 "Allows you to write your own events into the queue log\n"
00266 "Example: QueueLog(101|${UNIQUEID}|${AGENT}|WENTONBREAK|600)\n";
00267 
00268 /*! \brief Persistent Members astdb family */
00269 static const char *pm_family = "Queue/PersistentMembers";
00270 /* The maximum length of each persistent member queue database entry */
00271 #define PM_MAX_LEN 8192
00272 
00273 /*! \brief queues.conf [general] option */
00274 static int queue_persistent_members = 0;
00275 
00276 /*! \brief queues.conf per-queue weight option */
00277 static int use_weight = 0;
00278 
00279 /*! \brief queues.conf [general] option */
00280 static int autofill_default = 0;
00281 
00282 /*! \brief queues.conf [general] option */
00283 static int montype_default = 0;
00284 
00285 enum queue_result {
00286    QUEUE_UNKNOWN = 0,
00287    QUEUE_TIMEOUT = 1,
00288    QUEUE_JOINEMPTY = 2,
00289    QUEUE_LEAVEEMPTY = 3,
00290    QUEUE_JOINUNAVAIL = 4,
00291    QUEUE_LEAVEUNAVAIL = 5,
00292    QUEUE_FULL = 6,
00293 };
00294 
00295 const struct {
00296    enum queue_result id;
00297    char *text;
00298 } queue_results[] = {
00299    { QUEUE_UNKNOWN, "UNKNOWN" },
00300    { QUEUE_TIMEOUT, "TIMEOUT" },
00301    { QUEUE_JOINEMPTY,"JOINEMPTY" },
00302    { QUEUE_LEAVEEMPTY, "LEAVEEMPTY" },
00303    { QUEUE_JOINUNAVAIL, "JOINUNAVAIL" },
00304    { QUEUE_LEAVEUNAVAIL, "LEAVEUNAVAIL" },
00305    { QUEUE_FULL, "FULL" },
00306 };
00307 
00308 /*! \brief We define a custom "local user" structure because we
00309    use it not only for keeping track of what is in use but
00310    also for keeping track of who we're dialing.
00311 
00312    There are two "links" defined in this structure, q_next and call_next.
00313    q_next links ALL defined callattempt structures into a linked list. call_next is
00314    a link which allows for a subset of the callattempts to be traversed. This subset
00315    is used in wait_for_answer so that irrelevant callattempts are not traversed. This
00316    also is helpful so that queue logs are always accurate in the case where a call to 
00317    a member times out, especially if using the ringall strategy. */
00318 
00319 struct callattempt {
00320    struct callattempt *q_next;
00321    struct callattempt *call_next;
00322    struct ast_channel *chan;
00323    char interface[256];
00324    int stillgoing;
00325    int metric;
00326    int oldstatus;
00327    time_t lastcall;
00328    struct member *member;
00329 };
00330 
00331 
00332 struct queue_ent {
00333    struct call_queue *parent;          /*!< What queue is our parent */
00334    char moh[80];                       /*!< Name of musiconhold to be used */
00335    char announce[80];                  /*!< Announcement to play for member when call is answered */
00336    char context[AST_MAX_CONTEXT];      /*!< Context when user exits queue */
00337    char digits[AST_MAX_EXTENSION];     /*!< Digits entered while in queue */
00338    int valid_digits;        /*!< Digits entered correspond to valid extension. Exited */
00339    int pos;                            /*!< Where we are in the queue */
00340    int prio;                           /*!< Our priority */
00341    int last_pos_said;                  /*!< Last position we told the user */
00342    time_t last_periodic_announce_time; /*!< The last time we played a periodic announcement */
00343    int last_periodic_announce_sound;   /*!< The last periodic announcement we made */
00344    time_t last_pos;                    /*!< Last time we told the user their position */
00345    int opos;                           /*!< Where we started in the queue */
00346    int handled;                        /*!< Whether our call was handled */
00347    int pending;                        /*!< Non-zero if we are attempting to call a member */
00348    int max_penalty;                    /*!< Limit the members that can take this call to this penalty or lower */
00349    time_t start;                       /*!< When we started holding */
00350    time_t expire;                      /*!< When this entry should expire (time out of queue) */
00351    struct ast_channel *chan;           /*!< Our channel */
00352    struct queue_ent *next;             /*!< The next queue entry */
00353 };
00354 
00355 struct member {
00356    char interface[80];                 /*!< Technology/Location */
00357    char membername[80];                /*!< Member name to use in queue logs */
00358    int penalty;                        /*!< Are we a last resort? */
00359    int calls;                          /*!< Number of calls serviced by this member */
00360    int dynamic;                        /*!< Are we dynamically added? */
00361    int realtime;                       /*!< Is this member realtime? */
00362    int status;                         /*!< Status of queue member */
00363    int paused;                         /*!< Are we paused (not accepting calls)? */
00364    time_t lastcall;                    /*!< When last successful call was hungup */
00365    unsigned int dead:1;                /*!< Used to detect members deleted in realtime */
00366    unsigned int delme:1;               /*!< Flag to delete entry on reload */
00367 };
00368 
00369 struct member_interface {
00370    char interface[80];
00371    AST_LIST_ENTRY(member_interface) list;    /*!< Next call queue */
00372 };
00373 
00374 static AST_LIST_HEAD_STATIC(interfaces, member_interface);
00375 
00376 /* values used in multi-bit flags in call_queue */
00377 #define QUEUE_EMPTY_NORMAL 1
00378 #define QUEUE_EMPTY_STRICT 2
00379 #define ANNOUNCEHOLDTIME_ALWAYS 1
00380 #define ANNOUNCEHOLDTIME_ONCE 2
00381 #define QUEUE_EVENT_VARIABLES 3
00382 
00383 struct call_queue {
00384    ast_mutex_t lock; 
00385    char name[80];                      /*!< Name */
00386    char moh[80];                       /*!< Music On Hold class to be used */
00387    char announce[80];                  /*!< Announcement to play when call is answered */
00388    char context[AST_MAX_CONTEXT];      /*!< Exit context */
00389    unsigned int monjoin:1;
00390    unsigned int dead:1;
00391    unsigned int joinempty:2;
00392    unsigned int eventwhencalled:2;
00393    unsigned int leavewhenempty:2;
00394    unsigned int ringinuse:1;
00395    unsigned int setinterfacevar:1;
00396    unsigned int reportholdtime:1;
00397    unsigned int wrapped:1;
00398    unsigned int timeoutrestart:1;
00399    unsigned int announceholdtime:2;
00400    int strategy:4;
00401    unsigned int maskmemberstatus:1;
00402    unsigned int realtime:1;
00403    unsigned int found:1;
00404    int announcefrequency;              /*!< How often to announce their position */
00405    int periodicannouncefrequency;      /*!< How often to play periodic announcement */
00406    int roundingseconds;                /*!< How many seconds do we round to? */
00407    int holdtime;                       /*!< Current avg holdtime, based on an exponential average */
00408    int callscompleted;                 /*!< Number of queue calls completed */
00409    int callsabandoned;                 /*!< Number of queue calls abandoned */
00410    int servicelevel;                   /*!< seconds setting for servicelevel*/
00411    int callscompletedinsl;             /*!< Number of calls answered with servicelevel*/
00412    char monfmt[8];                     /*!< Format to use when recording calls */
00413    int montype;                        /*!< Monitor type  Monitor vs. MixMonitor */
00414    char sound_next[80];                /*!< Sound file: "Your call is now first in line" (def. queue-youarenext) */
00415    char sound_thereare[80];            /*!< Sound file: "There are currently" (def. queue-thereare) */
00416    char sound_calls[80];               /*!< Sound file: "calls waiting to speak to a representative." (def. queue-callswaiting)*/
00417    char sound_holdtime[80];            /*!< Sound file: "The current estimated total holdtime is" (def. queue-holdtime) */
00418    char sound_minutes[80];             /*!< Sound file: "minutes." (def. queue-minutes) */
00419    char sound_lessthan[80];            /*!< Sound file: "less-than" (def. queue-lessthan) */
00420    char sound_seconds[80];             /*!< Sound file: "seconds." (def. queue-seconds) */
00421    char sound_thanks[80];              /*!< Sound file: "Thank you for your patience." (def. queue-thankyou) */
00422    char sound_reporthold[80];          /*!< Sound file: "Hold time" (def. queue-reporthold) */
00423    char sound_periodicannounce[MAX_PERIODIC_ANNOUNCEMENTS][80];/*!< Sound files: Custom announce, no default */
00424 
00425    int count;                          /*!< How many entries */
00426    int maxlen;                         /*!< Max number of entries */
00427    int wrapuptime;                     /*!< Wrapup Time */
00428 
00429    int retry;                          /*!< Retry calling everyone after this amount of time */
00430    int timeout;                        /*!< How long to wait for an answer */
00431    int weight;                         /*!< Respective weight */
00432    int autopause;                      /*!< Auto pause queue members if they fail to answer */
00433 
00434    /* Queue strategy things */
00435    int rrpos;                          /*!< Round Robin - position */
00436    int memberdelay;                    /*!< Seconds to delay connecting member to caller */
00437    int autofill;                       /*!< Ignore the head call status and ring an available agent */
00438    
00439    struct ao2_container *members;             /*!< Head of the list of members */
00440    /*! 
00441     * \brief Number of members _logged in_
00442     * \note There will be members in the members container that are not logged
00443     *       in, so this can not simply be replaced with ao2_container_count(). 
00444     */
00445    int membercount;
00446    struct queue_ent *head;             /*!< Head of the list of callers */
00447    AST_LIST_ENTRY(call_queue) list;    /*!< Next call queue */
00448 };
00449 
00450 static AST_LIST_HEAD_STATIC(queues, call_queue);
00451 
00452 static int set_member_paused(const char *queuename, const char *interface, int paused);
00453 
00454 static void queue_transfer_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan); 
00455 
00456 static void rr_dep_warning(void)
00457 {
00458    static unsigned int warned = 0;
00459 
00460    if (!warned) {
00461       ast_log(LOG_NOTICE, "The 'roundrobin' queue strategy is deprecated. Please use the 'rrmemory' strategy instead.\n");
00462       warned = 1;
00463    }
00464 }
00465 
00466 static void monjoin_dep_warning(void)
00467 {
00468    static unsigned int warned = 0;
00469    if (!warned) {
00470       ast_log(LOG_NOTICE, "The 'monitor-join' queue option is deprecated. Please use monitor-type=mixmonitor instead.\n");
00471       warned = 1;
00472    }
00473 }
00474 /*! \brief sets the QUEUESTATUS channel variable */
00475 static void set_queue_result(struct ast_channel *chan, enum queue_result res)
00476 {
00477    int i;
00478 
00479    for (i = 0; i < sizeof(queue_results) / sizeof(queue_results[0]); i++) {
00480       if (queue_results[i].id == res) {
00481          pbx_builtin_setvar_helper(chan, "QUEUESTATUS", queue_results[i].text);
00482          return;
00483       }
00484    }
00485 }
00486 
00487 static char *int2strat(int strategy)
00488 {
00489    int x;
00490 
00491    for (x = 0; x < sizeof(strategies) / sizeof(strategies[0]); x++) {
00492       if (strategy == strategies[x].strategy)
00493          return strategies[x].name;
00494    }
00495 
00496    return "<unknown>";
00497 }
00498 
00499 static int strat2int(const char *strategy)
00500 {
00501    int x;
00502 
00503    for (x = 0; x < sizeof(strategies) / sizeof(strategies[0]); x++) {
00504       if (!strcasecmp(strategy, strategies[x].name))
00505          return strategies[x].strategy;
00506    }
00507 
00508    return -1;
00509 }
00510 
00511 /*! \brief Insert the 'new' entry after the 'prev' entry of queue 'q' */
00512 static inline void insert_entry(struct call_queue *q, struct queue_ent *prev, struct queue_ent *new, int *pos)
00513 {
00514    struct queue_ent *cur;
00515 
00516    if (!q || !new)
00517       return;
00518    if (prev) {
00519       cur = prev->next;
00520       prev->next = new;
00521    } else {
00522       cur = q->head;
00523       q->head = new;
00524    }
00525    new->next = cur;
00526    new->parent = q;
00527    new->pos = ++(*pos);
00528    new->opos = *pos;
00529 }
00530 
00531 enum queue_member_status {
00532    QUEUE_NO_MEMBERS,
00533    QUEUE_NO_REACHABLE_MEMBERS,
00534    QUEUE_NORMAL
00535 };
00536 
00537 /*! \brief Check if members are available
00538  *
00539  * This function checks to see if members are available to be called. If any member
00540  * is available, the function immediately returns QUEUE_NORMAL. If no members are available,
00541  * the appropriate reason why is returned
00542  */
00543 static enum queue_member_status get_member_status(struct call_queue *q, int max_penalty)
00544 {
00545    struct member *member;
00546    struct ao2_iterator mem_iter;
00547    enum queue_member_status result = QUEUE_NO_MEMBERS;
00548 
00549    ast_mutex_lock(&q->lock);
00550    mem_iter = ao2_iterator_init(q->members, 0);
00551    while ((member = ao2_iterator_next(&mem_iter))) {
00552       if (max_penalty && (member->penalty > max_penalty)) {
00553          ao2_ref(member, -1);
00554          continue;
00555       }
00556 
00557       if (member->paused) {
00558          ao2_ref(member, -1);
00559          continue;
00560       }
00561 
00562       switch (member->status) {
00563       case AST_DEVICE_INVALID:
00564          /* nothing to do */
00565          ao2_ref(member, -1);
00566          break;
00567       case AST_DEVICE_UNAVAILABLE:
00568          result = QUEUE_NO_REACHABLE_MEMBERS;
00569          ao2_ref(member, -1);
00570          break;
00571       default:
00572          ast_mutex_unlock(&q->lock);
00573          ao2_ref(member, -1);
00574          return QUEUE_NORMAL;
00575       }
00576    }
00577 
00578    ast_mutex_unlock(&q->lock);
00579    return result;
00580 }
00581 
00582 struct statechange {
00583    AST_LIST_ENTRY(statechange) entry;
00584    int state;
00585    char dev[0];
00586 };
00587 
00588 static int update_status(const char *interface, const int status)
00589 {
00590    struct member *cur;
00591    struct ao2_iterator mem_iter;
00592    struct call_queue *q;
00593 
00594    AST_LIST_LOCK(&queues);
00595    AST_LIST_TRAVERSE(&queues, q, list) {
00596       ast_mutex_lock(&q->lock);
00597       mem_iter = ao2_iterator_init(q->members, 0);
00598       while ((cur = ao2_iterator_next(&mem_iter))) {
00599          char *tmp_interface;
00600          char *slash_pos;
00601          tmp_interface = ast_strdupa(cur->interface);
00602          if ((slash_pos = strchr(tmp_interface, '/')))
00603             if ((slash_pos = strchr(slash_pos + 1, '/')))
00604                *slash_pos = '\0';
00605 
00606          if (strcasecmp(interface, tmp_interface)) {
00607             ao2_ref(cur, -1);
00608             continue;
00609          }
00610 
00611          if (cur->status != status) {
00612             cur->status = status;
00613             if (q->maskmemberstatus) {
00614                ao2_ref(cur, -1);
00615                continue;
00616             }
00617 
00618             manager_event(EVENT_FLAG_AGENT, "QueueMemberStatus",
00619                "Queue: %s\r\n"
00620                "Location: %s\r\n"
00621                "MemberName: %s\r\n"
00622                "Membership: %s\r\n"
00623                "Penalty: %d\r\n"
00624                "CallsTaken: %d\r\n"
00625                "LastCall: %d\r\n"
00626                "Status: %d\r\n"
00627                "Paused: %d\r\n",
00628                q->name, cur->interface, cur->membername, cur->dynamic ? "dynamic" : cur->realtime ? "realtime" : "static",
00629                cur->penalty, cur->calls, (int)cur->lastcall, cur->status, cur->paused);
00630          }
00631          ao2_ref(cur, -1);
00632       }
00633       ast_mutex_unlock(&q->lock);
00634    }
00635    AST_LIST_UNLOCK(&queues);
00636 
00637    return 0;
00638 }
00639 
00640 /*! \brief set a member's status based on device state of that member's interface*/
00641 static void *handle_statechange(struct statechange *sc)
00642 {
00643    struct member_interface *curint;
00644    char *loc;
00645    char *technology;
00646 
00647    technology = ast_strdupa(sc->dev);
00648    loc = strchr(technology, '/');
00649    if (loc) {
00650       *loc++ = '\0';
00651    } else {
00652       return NULL;
00653    }
00654 
00655    AST_LIST_LOCK(&interfaces);
00656    AST_LIST_TRAVERSE(&interfaces, curint, list) {
00657       char *interface;
00658       char *slash_pos;
00659       interface = ast_strdupa(curint->interface);
00660       if ((slash_pos = strchr(interface, '/')))
00661          if ((slash_pos = strchr(slash_pos + 1, '/')))
00662             *slash_pos = '\0';
00663 
00664       if (!strcasecmp(interface, sc->dev))
00665          break;
00666    }
00667    AST_LIST_UNLOCK(&interfaces);
00668 
00669    if (!curint) {
00670       if (option_debug > 2)
00671          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));
00672       return NULL;
00673    }
00674 
00675    if (option_debug)
00676       ast_log(LOG_DEBUG, "Device '%s/%s' changed to state '%d' (%s)\n", technology, loc, sc->state, devstate2str(sc->state));
00677 
00678    update_status(sc->dev, sc->state);
00679 
00680    return NULL;
00681 }
00682 
00683 /*!
00684  * \brief Data used by the device state thread
00685  */
00686 static struct {
00687    /*! Set to 1 to stop the thread */
00688    unsigned int stop:1;
00689    /*! The device state monitoring thread */
00690    pthread_t thread;
00691    /*! Lock for the state change queue */
00692    ast_mutex_t lock;
00693    /*! Condition for the state change queue */
00694    ast_cond_t cond;
00695    /*! Queue of state changes */
00696    AST_LIST_HEAD_NOLOCK(, statechange) state_change_q;
00697 } device_state = {
00698    .thread = AST_PTHREADT_NULL,
00699 };
00700 
00701 /*! \brief Consumer of the statechange queue */
00702 static void *device_state_thread(void *data)
00703 {
00704    struct statechange *sc = NULL;
00705 
00706    while (!device_state.stop) {
00707       ast_mutex_lock(&device_state.lock);
00708       if (!(sc = AST_LIST_REMOVE_HEAD(&device_state.state_change_q, entry))) {
00709          ast_cond_wait(&device_state.cond, &device_state.lock);
00710          sc = AST_LIST_REMOVE_HEAD(&device_state.state_change_q, entry);
00711       }
00712       ast_mutex_unlock(&device_state.lock);
00713 
00714       /* Check to see if we were woken up to see the request to stop */
00715       if (device_state.stop)
00716          break;
00717 
00718       if (!sc)
00719          continue;
00720 
00721       handle_statechange(sc);
00722 
00723       free(sc);
00724       sc = NULL;
00725    }
00726 
00727    if (sc)
00728       free(sc);
00729 
00730    while ((sc = AST_LIST_REMOVE_HEAD(&device_state.state_change_q, entry)))
00731       free(sc);
00732 
00733    return NULL;
00734 }
00735 /*! \brief Producer of the statechange queue */
00736 static int statechange_queue(const char *dev, int state, void *ign)
00737 {
00738    struct statechange *sc;
00739 
00740    if (!(sc = ast_calloc(1, sizeof(*sc) + strlen(dev) + 1)))
00741       return 0;
00742 
00743    sc->state = state;
00744    strcpy(sc->dev, dev);
00745 
00746    ast_mutex_lock(&device_state.lock);
00747    AST_LIST_INSERT_TAIL(&device_state.state_change_q, sc, entry);
00748    ast_cond_signal(&device_state.cond);
00749    ast_mutex_unlock(&device_state.lock);
00750 
00751    return 0;
00752 }
00753 /*! \brief allocate space for new queue member and set fields based on parameters passed */
00754 static struct member *create_queue_member(const char *interface, const char *membername, int penalty, int paused)
00755 {
00756    struct member *cur;
00757    
00758    if ((cur = ao2_alloc(sizeof(*cur), NULL))) {
00759       cur->penalty = penalty;
00760       cur->paused = paused;
00761       ast_copy_string(cur->interface, interface, sizeof(cur->interface));
00762       if (!ast_strlen_zero(membername))
00763          ast_copy_string(cur->membername, membername, sizeof(cur->membername));
00764       else
00765          ast_copy_string(cur->membername, interface, sizeof(cur->membername));
00766       if (!strchr(cur->interface, '/'))
00767          ast_log(LOG_WARNING, "No location at interface '%s'\n", interface);
00768       cur->status = ast_device_state(interface);
00769    }
00770 
00771    return cur;
00772 }
00773 
00774 static struct call_queue *alloc_queue(const char *queuename)
00775 {
00776    struct call_queue *q;
00777 
00778    if ((q = ast_calloc(1, sizeof(*q)))) {
00779       ast_mutex_init(&q->lock);
00780       ast_copy_string(q->name, queuename, sizeof(q->name));
00781    }
00782    return q;
00783 }
00784 
00785 static int compress_char(const char c)
00786 {
00787    if (c < 32)
00788       return 0;
00789    else if (c > 96)
00790       return c - 64;
00791    else
00792       return c - 32;
00793 }
00794 
00795 static int member_hash_fn(const void *obj, const int flags)
00796 {
00797    const struct member *mem = obj;
00798    const char *chname = strchr(mem->interface, '/');
00799    int ret = 0, i;
00800    if (!chname)
00801       chname = mem->interface;
00802    for (i = 0; i < 5 && chname[i]; i++)
00803       ret += compress_char(chname[i]) << (i * 6);
00804    return ret;
00805 }
00806 
00807 static int member_cmp_fn(void *obj1, void *obj2, int flags)
00808 {
00809    struct member *mem1 = obj1, *mem2 = obj2;
00810    return strcmp(mem1->interface, mem2->interface) ? 0 : CMP_MATCH | CMP_STOP;
00811 }
00812 
00813 static void init_queue(struct call_queue *q)
00814 {
00815    int i;
00816 
00817    q->dead = 0;
00818    q->retry = DEFAULT_RETRY;
00819    q->timeout = -1;
00820    q->maxlen = 0;
00821    q->announcefrequency = 0;
00822    q->announceholdtime = 0;
00823    q->roundingseconds = 0; /* Default - don't announce seconds */
00824    q->servicelevel = 0;
00825    q->ringinuse = 1;
00826    q->setinterfacevar = 0;
00827    q->autofill = autofill_default;
00828    q->montype = montype_default;
00829    q->moh[0] = '\0';
00830    q->announce[0] = '\0';
00831    q->context[0] = '\0';
00832    q->monfmt[0] = '\0';
00833    q->periodicannouncefrequency = 0;
00834    q->reportholdtime = 0;
00835    q->monjoin = 0;
00836    q->wrapuptime = 0;
00837    q->joinempty = 0;
00838    q->leavewhenempty = 0;
00839    q->memberdelay = 0;
00840    q->maskmemberstatus = 0;
00841    q->eventwhencalled = 0;
00842    q->weight = 0;
00843    q->timeoutrestart = 0;
00844    if (!q->members)
00845       q->members = ao2_container_alloc(37, member_hash_fn, member_cmp_fn);
00846    q->membercount = 0;
00847    q->found = 1;
00848    ast_copy_string(q->sound_next, "queue-youarenext", sizeof(q->sound_next));
00849    ast_copy_string(q->sound_thereare, "queue-thereare", sizeof(q->sound_thereare));
00850    ast_copy_string(q->sound_calls, "queue-callswaiting", sizeof(q->sound_calls));
00851    ast_copy_string(q->sound_holdtime, "queue-holdtime", sizeof(q->sound_holdtime));
00852    ast_copy_string(q->sound_minutes, "queue-minutes", sizeof(q->sound_minutes));
00853    ast_copy_string(q->sound_seconds, "queue-seconds", sizeof(q->sound_seconds));
00854    ast_copy_string(q->sound_thanks, "queue-thankyou", sizeof(q->sound_thanks));
00855    ast_copy_string(q->sound_lessthan, "queue-less-than", sizeof(q->sound_lessthan));
00856    ast_copy_string(q->sound_reporthold, "queue-reporthold", sizeof(q->sound_reporthold));
00857    ast_copy_string(q->sound_periodicannounce[0], "queue-periodic-announce", sizeof(q->sound_periodicannounce[0]));
00858    for (i = 1; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) {
00859       q->sound_periodicannounce[i][0]='\0';
00860    }
00861 }
00862 
00863 static void clear_queue(struct call_queue *q)
00864 {
00865    q->holdtime = 0;
00866    q->callscompleted = 0;
00867    q->callsabandoned = 0;
00868    q->callscompletedinsl = 0;
00869    q->wrapuptime = 0;
00870 }
00871 
00872 static int add_to_interfaces(const char *