Asterisk.org: Developer Documentation (28 Oct 2009)

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