2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2006, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
21 * \brief True call queues with optional send URL on answer
23 * \author Mark Spencer <markster@digium.com>
25 * \arg Config in \ref Config_qu queues.conf
27 * \par Development notes
28 * \note 2004-11-25: Persistent Dynamic Members added by:
29 * NetNation Communications (www.netnation.com)
30 * Kevin Lindsay <kevinl@netnation.com>
32 * Each dynamic agent in each queue is now stored in the astdb.
33 * When asterisk is restarted, each agent will be automatically
34 * readded into their recorded queues. This feature can be
35 * configured with the 'persistent_members=<1|0>' setting in the
36 * '[general]' category in queues.conf. The default is on.
38 * \note 2004-06-04: Priorities in queues added by inAccess Networks (work funded by Hellas On Line (HOL) www.hol.gr).
40 * \note These features added by David C. Troy <dave@toad.net>:
41 * - Per-queue holdtime calculation
42 * - Estimated holdtime announcement
43 * - Position announcement
44 * - Abandoned/completed call counters
45 * - Failout timer passed as optional app parameter
46 * - Optional monitoring of calls, started when call is answered
48 * Patch Version 1.07 2003-12-24 01
50 * Added servicelevel statistic by Michiel Betel <michiel@betel.nl>
51 * Added Priority jumping code for adding and removing queue members by Jonathan Stanton <asterisk@doilooklikeicare.com>
53 * Fixed to work with CVS as of 2004-02-25 and released as 1.07a
54 * by Matthew Enger <m.enger@xi.com.au>
56 * \ingroup applications
61 ASTERISK_FILE_VERSION(__FILE__
, "$Revision$")
70 #include <sys/signal.h>
71 #include <netinet/in.h>
73 #include "asterisk/lock.h"
74 #include "asterisk/file.h"
75 #include "asterisk/logger.h"
76 #include "asterisk/channel.h"
77 #include "asterisk/pbx.h"
78 #include "asterisk/options.h"
79 #include "asterisk/app.h"
80 #include "asterisk/linkedlists.h"
81 #include "asterisk/module.h"
82 #include "asterisk/translate.h"
83 #include "asterisk/say.h"
84 #include "asterisk/features.h"
85 #include "asterisk/musiconhold.h"
86 #include "asterisk/cli.h"
87 #include "asterisk/manager.h"
88 #include "asterisk/config.h"
89 #include "asterisk/monitor.h"
90 #include "asterisk/utils.h"
91 #include "asterisk/causes.h"
92 #include "asterisk/astdb.h"
93 #include "asterisk/devicestate.h"
94 #include "asterisk/stringfields.h"
95 #include "asterisk/astobj2.h"
98 QUEUE_STRATEGY_RINGALL
= 0,
99 QUEUE_STRATEGY_ROUNDROBIN
,
100 QUEUE_STRATEGY_LEASTRECENT
,
101 QUEUE_STRATEGY_FEWESTCALLS
,
102 QUEUE_STRATEGY_RANDOM
,
103 QUEUE_STRATEGY_RRMEMORY
106 static struct strategy
{
110 { QUEUE_STRATEGY_RINGALL
, "ringall" },
111 { QUEUE_STRATEGY_ROUNDROBIN
, "roundrobin" },
112 { QUEUE_STRATEGY_LEASTRECENT
, "leastrecent" },
113 { QUEUE_STRATEGY_FEWESTCALLS
, "fewestcalls" },
114 { QUEUE_STRATEGY_RANDOM
, "random" },
115 { QUEUE_STRATEGY_RRMEMORY
, "rrmemory" },
118 #define DEFAULT_RETRY 5
119 #define DEFAULT_TIMEOUT 15
120 #define RECHECK 1 /* Recheck every second to see we we're at the top yet */
121 #define MAX_PERIODIC_ANNOUNCEMENTS 10 /* The maximum periodic announcements we can have */
123 #define RES_OKAY 0 /* Action completed */
124 #define RES_EXISTS (-1) /* Entry already exists */
125 #define RES_OUTOFMEMORY (-2) /* Out of memory */
126 #define RES_NOSUCHQUEUE (-3) /* No such queue */
127 #define RES_NOT_DYNAMIC (-4) /* Member is not dynamic */
129 static char *app
= "Queue";
131 static char *synopsis
= "Queue a call for a call queue";
133 static char *descrip
=
134 " Queue(queuename[|options[|URL][|announceoverride][|timeout][|AGI]):\n"
135 "Queues an incoming call in a particular call queue as defined in queues.conf.\n"
136 "This application will return to the dialplan if the queue does not exist, or\n"
137 "any of the join options cause the caller to not enter the queue.\n"
138 "The option string may contain zero or more of the following characters:\n"
139 " 'd' -- data-quality (modem) call (minimum delay).\n"
140 " 'h' -- allow callee to hang up by hitting *.\n"
141 " 'H' -- allow caller to hang up by hitting *.\n"
142 " 'n' -- no retries on the timeout; will exit this application and \n"
143 " go to the next step.\n"
144 " 'i' -- ignore call forward requests from queue members and do nothing\n"
145 " when they are requested.\n"
146 " 'r' -- ring instead of playing MOH\n"
147 " 't' -- allow the called user transfer the calling user\n"
148 " 'T' -- to allow the calling user to transfer the call.\n"
149 " 'w' -- allow the called user to write the conversation to disk via Monitor\n"
150 " 'W' -- allow the calling user to write the conversation to disk via Monitor\n"
151 " In addition to transferring the call, a call may be parked and then picked\n"
152 "up by another user.\n"
153 " The optional URL will be sent to the called party if the channel supports\n"
155 " The optional AGI parameter will setup an AGI script to be executed on the \n"
156 "calling party's channel once they are connected to a queue member.\n"
157 " The timeout will cause the queue to fail out after a specified number of\n"
158 "seconds, checked between each queues.conf 'timeout' and 'retry' cycle.\n"
159 " This application sets the following channel variable upon completion:\n"
160 " QUEUESTATUS The status of the call as a text string, one of\n"
161 " TIMEOUT | FULL | JOINEMPTY | LEAVEEMPTY | JOINUNAVAIL | LEAVEUNAVAIL\n";
163 static char *app_aqm
= "AddQueueMember" ;
164 static char *app_aqm_synopsis
= "Dynamically adds queue members" ;
165 static char *app_aqm_descrip
=
166 " AddQueueMember(queuename[|interface[|penalty[|options[|membername]]]]):\n"
167 "Dynamically adds interface to an existing queue.\n"
168 "If the interface is already in the queue and there exists an n+101 priority\n"
169 "then it will then jump to this priority. Otherwise it will return an error\n"
170 "The option string may contain zero or more of the following characters:\n"
171 " 'j' -- jump to +101 priority when appropriate.\n"
172 " This application sets the following channel variable upon completion:\n"
173 " AQMSTATUS The status of the attempt to add a queue member as a \n"
174 " text string, one of\n"
175 " ADDED | MEMBERALREADY | NOSUCHQUEUE \n"
176 "Example: AddQueueMember(techsupport|SIP/3000)\n"
179 static char *app_rqm
= "RemoveQueueMember" ;
180 static char *app_rqm_synopsis
= "Dynamically removes queue members" ;
181 static char *app_rqm_descrip
=
182 " RemoveQueueMember(queuename[|interface[|options]]):\n"
183 "Dynamically removes interface to an existing queue\n"
184 "If the interface is NOT in the queue and there exists an n+101 priority\n"
185 "then it will then jump to this priority. Otherwise it will return an error\n"
186 "The option string may contain zero or more of the following characters:\n"
187 " 'j' -- jump to +101 priority when appropriate.\n"
188 " This application sets the following channel variable upon completion:\n"
189 " RQMSTATUS The status of the attempt to remove a queue member as a\n"
190 " text string, one of\n"
191 " REMOVED | NOTINQUEUE | NOSUCHQUEUE \n"
192 "Example: RemoveQueueMember(techsupport|SIP/3000)\n"
195 static char *app_pqm
= "PauseQueueMember" ;
196 static char *app_pqm_synopsis
= "Pauses a queue member" ;
197 static char *app_pqm_descrip
=
198 " PauseQueueMember([queuename]|interface[|options]):\n"
199 "Pauses (blocks calls for) a queue member.\n"
200 "The given interface will be paused in the given queue. This prevents\n"
201 "any calls from being sent from the queue to the interface until it is\n"
202 "unpaused with UnpauseQueueMember or the manager interface. If no\n"
203 "queuename is given, the interface is paused in every queue it is a\n"
204 "member of. If the interface is not in the named queue, or if no queue\n"
205 "is given and the interface is not in any queue, it will jump to\n"
206 "priority n+101, if it exists and the appropriate options are set.\n"
207 "The application will fail if the interface is not found and no extension\n"
208 "to jump to exists.\n"
209 "The option string may contain zero or more of the following characters:\n"
210 " 'j' -- jump to +101 priority when appropriate.\n"
211 " This application sets the following channel variable upon completion:\n"
212 " PQMSTATUS The status of the attempt to pause a queue member as a\n"
213 " text string, one of\n"
214 " PAUSED | NOTFOUND\n"
215 "Example: PauseQueueMember(|SIP/3000)\n";
217 static char *app_upqm
= "UnpauseQueueMember" ;
218 static char *app_upqm_synopsis
= "Unpauses a queue member" ;
219 static char *app_upqm_descrip
=
220 " UnpauseQueueMember([queuename]|interface[|options]):\n"
221 "Unpauses (resumes calls to) a queue member.\n"
222 "This is the counterpart to PauseQueueMember and operates exactly the\n"
223 "same way, except it unpauses instead of pausing the given interface.\n"
224 "The option string may contain zero or more of the following characters:\n"
225 " 'j' -- jump to +101 priority when appropriate.\n"
226 " This application sets the following channel variable upon completion:\n"
227 " UPQMSTATUS The status of the attempt to unpause a queue \n"
228 " member as a text string, one of\n"
229 " UNPAUSED | NOTFOUND\n"
230 "Example: UnpauseQueueMember(|SIP/3000)\n";
232 static char *app_ql
= "QueueLog" ;
233 static char *app_ql_synopsis
= "Writes to the queue_log" ;
234 static char *app_ql_descrip
=
235 " QueueLog(queuename|uniqueid|agent|event[|additionalinfo]):\n"
236 "Allows you to write your own events into the queue log\n"
237 "Example: QueueLog(101|${UNIQUEID}|${AGENT}|WENTONBREAK|600)\n";
239 /*! \brief Persistent Members astdb family */
240 static const char *pm_family
= "Queue/PersistentMembers";
241 /* The maximum length of each persistent member queue database entry */
242 #define PM_MAX_LEN 8192
244 /*! \brief queues.conf [general] option */
245 static int queue_persistent_members
= 0;
247 /*! \brief queues.conf per-queue weight option */
248 static int use_weight
= 0;
250 /*! \brief queues.conf [general] option */
251 static int autofill_default
= 0;
253 /*! \brief queues.conf [general] option */
254 static int montype_default
= 0;
260 QUEUE_LEAVEEMPTY
= 3,
261 QUEUE_JOINUNAVAIL
= 4,
262 QUEUE_LEAVEUNAVAIL
= 5,
267 enum queue_result id
;
269 } queue_results
[] = {
270 { QUEUE_UNKNOWN
, "UNKNOWN" },
271 { QUEUE_TIMEOUT
, "TIMEOUT" },
272 { QUEUE_JOINEMPTY
,"JOINEMPTY" },
273 { QUEUE_LEAVEEMPTY
, "LEAVEEMPTY" },
274 { QUEUE_JOINUNAVAIL
, "JOINUNAVAIL" },
275 { QUEUE_LEAVEUNAVAIL
, "LEAVEUNAVAIL" },
276 { QUEUE_FULL
, "FULL" },
279 /*! \brief We define a custom "local user" structure because we
280 use it not only for keeping track of what is in use but
281 also for keeping track of who we're dialing. */
284 struct callattempt
*q_next
;
285 struct ast_channel
*chan
;
291 struct member
*member
;
296 struct call_queue
*parent
; /*!< What queue is our parent */
297 char moh
[80]; /*!< Name of musiconhold to be used */
298 char announce
[80]; /*!< Announcement to play for member when call is answered */
299 char context
[AST_MAX_CONTEXT
]; /*!< Context when user exits queue */
300 char digits
[AST_MAX_EXTENSION
]; /*!< Digits entered while in queue */
301 int valid_digits
; /*!< Digits entered correspond to valid extension. Exited */
302 int pos
; /*!< Where we are in the queue */
303 int prio
; /*!< Our priority */
304 int last_pos_said
; /*!< Last position we told the user */
305 time_t last_periodic_announce_time
; /*!< The last time we played a periodic announcement */
306 int last_periodic_announce_sound
; /*!< The last periodic announcement we made */
307 time_t last_pos
; /*!< Last time we told the user their position */
308 int opos
; /*!< Where we started in the queue */
309 int handled
; /*!< Whether our call was handled */
310 int max_penalty
; /*!< Limit the members that can take this call to this penalty or lower */
311 time_t start
; /*!< When we started holding */
312 time_t expire
; /*!< When this entry should expire (time out of queue) */
313 struct ast_channel
*chan
; /*!< Our channel */
314 struct queue_ent
*next
; /*!< The next queue entry */
318 char interface
[80]; /*!< Technology/Location */
319 char membername
[80]; /*!< Member name to use in queue logs */
320 int penalty
; /*!< Are we a last resort? */
321 int calls
; /*!< Number of calls serviced by this member */
322 int dynamic
; /*!< Are we dynamically added? */
323 int realtime
; /*!< Is this member realtime? */
324 int status
; /*!< Status of queue member */
325 int paused
; /*!< Are we paused (not accepting calls)? */
326 time_t lastcall
; /*!< When last successful call was hungup */
327 unsigned int dead
:1; /*!< Used to detect members deleted in realtime */
328 unsigned int delme
:1; /*!< Flag to delete entry on reload */
331 struct member_interface
{
333 AST_LIST_ENTRY(member_interface
) list
; /*!< Next call queue */
336 static AST_LIST_HEAD_STATIC(interfaces
, member_interface
);
338 /* values used in multi-bit flags in call_queue */
339 #define QUEUE_EMPTY_NORMAL 1
340 #define QUEUE_EMPTY_STRICT 2
341 #define ANNOUNCEHOLDTIME_ALWAYS 1
342 #define ANNOUNCEHOLDTIME_ONCE 2
343 #define QUEUE_EVENT_VARIABLES 3
347 char name
[80]; /*!< Name */
348 char moh
[80]; /*!< Music On Hold class to be used */
349 char announce
[80]; /*!< Announcement to play when call is answered */
350 char context
[AST_MAX_CONTEXT
]; /*!< Exit context */
351 unsigned int monjoin
:1;
353 unsigned int joinempty
:2;
354 unsigned int eventwhencalled
:2;
355 unsigned int leavewhenempty
:2;
356 unsigned int ringinuse
:1;
357 unsigned int setinterfacevar
:1;
358 unsigned int reportholdtime
:1;
359 unsigned int wrapped
:1;
360 unsigned int timeoutrestart
:1;
361 unsigned int announceholdtime
:2;
363 unsigned int maskmemberstatus
:1;
364 unsigned int realtime
:1;
365 unsigned int found
:1;
366 int announcefrequency
; /*!< How often to announce their position */
367 int periodicannouncefrequency
; /*!< How often to play periodic announcement */
368 int roundingseconds
; /*!< How many seconds do we round to? */
369 int holdtime
; /*!< Current avg holdtime, based on recursive boxcar filter */
370 int callscompleted
; /*!< Number of queue calls completed */
371 int callsabandoned
; /*!< Number of queue calls abandoned */
372 int servicelevel
; /*!< seconds setting for servicelevel*/
373 int callscompletedinsl
; /*!< Number of calls answered with servicelevel*/
374 char monfmt
[8]; /*!< Format to use when recording calls */
375 int montype
; /*!< Monitor type Monitor vs. MixMonitor */
376 char sound_next
[80]; /*!< Sound file: "Your call is now first in line" (def. queue-youarenext) */
377 char sound_thereare
[80]; /*!< Sound file: "There are currently" (def. queue-thereare) */
378 char sound_calls
[80]; /*!< Sound file: "calls waiting to speak to a representative." (def. queue-callswaiting)*/
379 char sound_holdtime
[80]; /*!< Sound file: "The current estimated total holdtime is" (def. queue-holdtime) */
380 char sound_minutes
[80]; /*!< Sound file: "minutes." (def. queue-minutes) */
381 char sound_lessthan
[80]; /*!< Sound file: "less-than" (def. queue-lessthan) */
382 char sound_seconds
[80]; /*!< Sound file: "seconds." (def. queue-seconds) */
383 char sound_thanks
[80]; /*!< Sound file: "Thank you for your patience." (def. queue-thankyou) */
384 char sound_reporthold
[80]; /*!< Sound file: "Hold time" (def. queue-reporthold) */
385 char sound_periodicannounce
[MAX_PERIODIC_ANNOUNCEMENTS
][80];/*!< Sound files: Custom announce, no default */
387 int count
; /*!< How many entries */
388 int maxlen
; /*!< Max number of entries */
389 int wrapuptime
; /*!< Wrapup Time */
391 int retry
; /*!< Retry calling everyone after this amount of time */
392 int timeout
; /*!< How long to wait for an answer */
393 int weight
; /*!< Respective weight */
394 int autopause
; /*!< Auto pause queue members if they fail to answer */
396 /* Queue strategy things */
397 int rrpos
; /*!< Round Robin - position */
398 int memberdelay
; /*!< Seconds to delay connecting member to caller */
399 int autofill
; /*!< Ignore the head call status and ring an available agent */
401 struct ao2_container
*members
; /*!< Head of the list of members */
403 * \brief Number of members _logged in_
404 * \note There will be members in the members container that are not logged
405 * in, so this can not simply be replaced with ao2_container_count().
408 struct queue_ent
*head
; /*!< Head of the list of callers */
409 AST_LIST_ENTRY(call_queue
) list
; /*!< Next call queue */
412 static AST_LIST_HEAD_STATIC(queues
, call_queue
);
414 static int set_member_paused(const char *queuename
, const char *interface
, int paused
);
416 static void rr_dep_warning(void)
418 static unsigned int warned
= 0;
421 ast_log(LOG_NOTICE
, "The 'roundrobin' queue strategy is deprecated. Please use the 'rrmemory' strategy instead.\n");
426 static void monjoin_dep_warning(void)
428 static unsigned int warned
= 0;
430 ast_log(LOG_NOTICE
, "The 'monitor-join' queue option is deprecated. Please use monitor-type=mixmonitor instead.\n");
435 static void set_queue_result(struct ast_channel
*chan
, enum queue_result res
)
439 for (i
= 0; i
< sizeof(queue_results
) / sizeof(queue_results
[0]); i
++) {
440 if (queue_results
[i
].id
== res
) {
441 pbx_builtin_setvar_helper(chan
, "QUEUESTATUS", queue_results
[i
].text
);
447 static char *int2strat(int strategy
)
451 for (x
= 0; x
< sizeof(strategies
) / sizeof(strategies
[0]); x
++) {
452 if (strategy
== strategies
[x
].strategy
)
453 return strategies
[x
].name
;
459 static int strat2int(const char *strategy
)
463 for (x
= 0; x
< sizeof(strategies
) / sizeof(strategies
[0]); x
++) {
464 if (!strcasecmp(strategy
, strategies
[x
].name
))
465 return strategies
[x
].strategy
;
471 /*! \brief Insert the 'new' entry after the 'prev' entry of queue 'q' */
472 static inline void insert_entry(struct call_queue
*q
, struct queue_ent
*prev
, struct queue_ent
*new, int *pos
)
474 struct queue_ent
*cur
;
491 enum queue_member_status
{
493 QUEUE_NO_REACHABLE_MEMBERS
,
497 static enum queue_member_status
get_member_status(struct call_queue
*q
, int max_penalty
)
499 struct member
*member
;
500 struct ao2_iterator mem_iter
;
501 enum queue_member_status result
= QUEUE_NO_MEMBERS
;
503 ast_mutex_lock(&q
->lock
);
504 mem_iter
= ao2_iterator_init(q
->members
, 0);
505 while ((member
= ao2_iterator_next(&mem_iter
))) {
506 if (max_penalty
&& (member
->penalty
> max_penalty
)) {
511 if (member
->paused
) {
516 switch (member
->status
) {
517 case AST_DEVICE_INVALID
:
521 case AST_DEVICE_UNAVAILABLE
:
522 result
= QUEUE_NO_REACHABLE_MEMBERS
;
526 ast_mutex_unlock(&q
->lock
);
532 ast_mutex_unlock(&q
->lock
);
537 AST_LIST_ENTRY(statechange
) entry
;
542 static void *handle_statechange(struct statechange
*sc
)
544 struct call_queue
*q
;
546 struct ao2_iterator mem_iter
;
547 struct member_interface
*curint
;
551 technology
= ast_strdupa(sc
->dev
);
552 loc
= strchr(technology
, '/');
559 AST_LIST_LOCK(&interfaces
);
560 AST_LIST_TRAVERSE(&interfaces
, curint
, list
) {
563 interface
= ast_strdupa(curint
->interface
);
564 if ((slash_pos
= strchr(interface
, '/')))
565 if ((slash_pos
= strchr(slash_pos
+ 1, '/')))
568 if (!strcasecmp(interface
, sc
->dev
))
571 AST_LIST_UNLOCK(&interfaces
);
574 if (option_debug
> 2)
575 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
));
580 ast_log(LOG_DEBUG
, "Device '%s/%s' changed to state '%d' (%s)\n", technology
, loc
, sc
->state
, devstate2str(sc
->state
));
581 AST_LIST_LOCK(&queues
);
582 AST_LIST_TRAVERSE(&queues
, q
, list
) {
583 ast_mutex_lock(&q
->lock
);
584 mem_iter
= ao2_iterator_init(q
->members
, 0);
585 while ((cur
= ao2_iterator_next(&mem_iter
))) {
588 interface
= ast_strdupa(cur
->interface
);
589 if ((slash_pos
= strchr(interface
, '/')))
590 if ((slash_pos
= strchr(slash_pos
+ 1, '/')))
593 if (strcasecmp(sc
->dev
, interface
)) {
598 if (cur
->status
!= sc
->state
) {
599 cur
->status
= sc
->state
;
600 if (q
->maskmemberstatus
) {
605 manager_event(EVENT_FLAG_AGENT
, "QueueMemberStatus",
615 q
->name
, cur
->interface
, cur
->membername
, cur
->dynamic
? "dynamic" : cur
->realtime
? "realtime" : "static",
616 cur
->penalty
, cur
->calls
, (int)cur
->lastcall
, cur
->status
, cur
->paused
);
620 ast_mutex_unlock(&q
->lock
);
622 AST_LIST_UNLOCK(&queues
);
628 * \brief Data used by the device state thread
631 /*! Set to 1 to stop the thread */
633 /*! The device state monitoring thread */
635 /*! Lock for the state change queue */
637 /*! Condition for the state change queue */
639 /*! Queue of state changes */
640 AST_LIST_HEAD_NOLOCK(, statechange
) state_change_q
;
642 .thread
= AST_PTHREADT_NULL
,
645 static void *device_state_thread(void *data
)
647 struct statechange
*sc
= NULL
;
649 while (!device_state
.stop
) {
650 ast_mutex_lock(&device_state
.lock
);
651 if (!(sc
= AST_LIST_REMOVE_HEAD(&device_state
.state_change_q
, entry
))) {
652 ast_cond_wait(&device_state
.cond
, &device_state
.lock
);
653 sc
= AST_LIST_REMOVE_HEAD(&device_state
.state_change_q
, entry
);
655 ast_mutex_unlock(&device_state
.lock
);
657 /* Check to see if we were woken up to see the request to stop */
658 if (device_state
.stop
)
664 handle_statechange(sc
);
673 while ((sc
= AST_LIST_REMOVE_HEAD(&device_state
.state_change_q
, entry
)))
679 static int statechange_queue(const char *dev
, int state
, void *ign
)
681 struct statechange
*sc
;
683 if (!(sc
= ast_calloc(1, sizeof(*sc
) + strlen(dev
) + 1)))
687 strcpy(sc
->dev
, dev
);
689 ast_mutex_lock(&device_state
.lock
);
690 AST_LIST_INSERT_TAIL(&device_state
.state_change_q
, sc
, entry
);
691 ast_cond_signal(&device_state
.cond
);
692 ast_mutex_unlock(&device_state
.lock
);
697 static struct member
*create_queue_member(const char *interface
, const char *membername
, int penalty
, int paused
)
701 if ((cur
= ao2_alloc(sizeof(*cur
), NULL
))) {
702 cur
->penalty
= penalty
;
703 cur
->paused
= paused
;
704 ast_copy_string(cur
->interface
, interface
, sizeof(cur
->interface
));
705 if(!ast_strlen_zero(membername
))
706 ast_copy_string(cur
->membername
, membername
, sizeof(cur
->membername
));
708 ast_copy_string(cur
->membername
, interface
, sizeof(cur
->membername
));
709 if (!strchr(cur
->interface
, '/'))
710 ast_log(LOG_WARNING
, "No location at interface '%s'\n", interface
);
711 cur
->status
= ast_device_state(interface
);
717 static struct call_queue
*alloc_queue(const char *queuename
)
719 struct call_queue
*q
;
721 if ((q
= ast_calloc(1, sizeof(*q
)))) {
722 ast_mutex_init(&q
->lock
);
723 ast_copy_string(q
->name
, queuename
, sizeof(q
->name
));
728 static int compress_char(const char c
)
738 static int member_hash_fn(const void *obj
, const int flags
)
740 const struct member
*mem
= obj
;
741 const char *chname
= strchr(mem
->interface
, '/');
744 chname
= mem
->interface
;
745 for (i
= 0; i
< 5 && chname
[i
]; i
++)
746 ret
+= compress_char(chname
[i
]) << (i
* 6);
750 static int member_cmp_fn(void *obj1
, void *obj2
, int flags
)
752 struct member
*mem1
= obj1
, *mem2
= obj2
;
753 return strcmp(mem1
->interface
, mem2
->interface
) ? 0 : CMP_MATCH
;
756 static void init_queue(struct call_queue
*q
)
761 q
->retry
= DEFAULT_RETRY
;
764 q
->announcefrequency
= 0;
765 q
->announceholdtime
= 0;
766 q
->roundingseconds
= 0; /* Default - don't announce seconds */
769 q
->setinterfacevar
= 0;
770 q
->autofill
= autofill_default
;
771 q
->montype
= montype_default
;
773 q
->announce
[0] = '\0';
774 q
->context
[0] = '\0';
776 q
->periodicannouncefrequency
= 0;
778 q
->members
= ao2_container_alloc(37, member_hash_fn
, member_cmp_fn
);
781 ast_copy_string(q
->sound_next
, "queue-youarenext", sizeof(q
->sound_next
));
782 ast_copy_string(q
->sound_thereare
, "queue-thereare", sizeof(q
->sound_thereare
));
783 ast_copy_string(q
->sound_calls
, "queue-callswaiting", sizeof(q
->sound_calls
));
784 ast_copy_string(q
->sound_holdtime
, "queue-holdtime", sizeof(q
->sound_holdtime
));
785 ast_copy_string(q
->sound_minutes
, "queue-minutes", sizeof(q
->sound_minutes
));
786 ast_copy_string(q
->sound_seconds
, "queue-seconds", sizeof(q
->sound_seconds
));
787 ast_copy_string(q
->sound_thanks
, "queue-thankyou", sizeof(q
->sound_thanks
));
788 ast_copy_string(q
->sound_lessthan
, "queue-less-than", sizeof(q
->sound_lessthan
));
789 ast_copy_string(q
->sound_reporthold
, "queue-reporthold", sizeof(q
->sound_reporthold
));
790 ast_copy_string(q
->sound_periodicannounce
[0], "queue-periodic-announce", sizeof(q
->sound_periodicannounce
[0]));
791 for (i
= 1; i
< MAX_PERIODIC_ANNOUNCEMENTS
; i
++) {
792 q
->sound_periodicannounce
[i
][0]='\0';
796 static void clear_queue(struct call_queue
*q
)
799 q
->callscompleted
= 0;
800 q
->callsabandoned
= 0;
801 q
->callscompletedinsl
= 0;
805 static int add_to_interfaces(const char *interface
)
807 struct member_interface
*curint
;
809 AST_LIST_LOCK(&interfaces
);
810 AST_LIST_TRAVERSE(&interfaces
, curint
, list
) {
811 if (!strcasecmp(curint
->interface
, interface
))
816 AST_LIST_UNLOCK(&interfaces
);
821 ast_log(LOG_DEBUG
, "Adding %s to the list of interfaces that make up all of our queue members.\n", interface
);
823 if ((curint
= ast_calloc(1, sizeof(*curint
)))) {
824 ast_copy_string(curint
->interface
, interface
, sizeof(curint
->interface
));
825 AST_LIST_INSERT_HEAD(&interfaces
, curint
, list
);
827 AST_LIST_UNLOCK(&interfaces
);
832 static int interface_exists_global(const char *interface
)
834 struct call_queue
*q
;
835 struct member
*mem
, tmpmem
;
838 ast_copy_string(tmpmem
.interface
, interface
, sizeof(tmpmem
.interface
));
840 AST_LIST_LOCK(&queues
);
841 AST_LIST_TRAVERSE(&queues
, q
, list
) {
842 ast_mutex_lock(&q
->lock
);
843 if ((mem
= ao2_find(q
->members
, &tmpmem
, OBJ_POINTER
))) {
847 ast_mutex_unlock(&q
->lock
);
851 AST_LIST_UNLOCK(&queues
);
856 static int remove_from_interfaces(const char *interface
)
858 struct member_interface
*curint
;
860 AST_LIST_LOCK(&interfaces
);
861 AST_LIST_TRAVERSE_SAFE_BEGIN(&interfaces
, curint
, list
) {
862 if (!strcasecmp(curint
->interface
, interface
)) {
863 if (!interface_exists_global(interface
)) {
865 ast_log(LOG_DEBUG
, "Removing %s from the list of interfaces that make up all of our queue members.\n", interface
);
866 AST_LIST_REMOVE_CURRENT(&interfaces
, list
);
872 AST_LIST_TRAVERSE_SAFE_END
;
873 AST_LIST_UNLOCK(&interfaces
);
878 static void clear_and_free_interfaces(void)
880 struct member_interface
*curint
;
882 AST_LIST_LOCK(&interfaces
);
883 while ((curint
= AST_LIST_REMOVE_HEAD(&interfaces
, list
)))
885 AST_LIST_UNLOCK(&interfaces
);
888 /*! \brief Configure a queue parameter.
890 For error reporting, line number is passed for .conf static configuration.
891 For Realtime queues, linenum is -1.
892 The failunknown flag is set for config files (and static realtime) to show
893 errors for unknown parameters. It is cleared for dynamic realtime to allow
894 extra fields in the tables. */
895 static void queue_set_param(struct call_queue
*q
, const char *param
, const char *val
, int linenum
, int failunknown
)
897 if (!strcasecmp(param
, "musicclass") ||
898 !strcasecmp(param
, "music") || !strcasecmp(param
, "musiconhold")) {
899 ast_copy_string(q
->moh
, val
, sizeof(q
->moh
));
900 } else if (!strcasecmp(param
, "announce")) {
901 ast_copy_string(q
->announce
, val
, sizeof(q
->announce
));
902 } else if (!strcasecmp(param
, "context")) {
903 ast_copy_string(q
->context
, val
, sizeof(q
->context
));
904 } else if (!strcasecmp(param
, "timeout")) {
905 q
->timeout
= atoi(val
);
907 q
->timeout
= DEFAULT_TIMEOUT
;
908 } else if (!strcasecmp(param
, "ringinuse")) {
909 q
->ringinuse
= ast_true(val
);
910 } else if (!strcasecmp(param
, "setinterfacevar")) {
911 q
->setinterfacevar
= ast_true(val
);
912 } else if (!strcasecmp(param
, "monitor-join")) {
913 monjoin_dep_warning();
914 q
->monjoin
= ast_true(val
);
915 } else if (!strcasecmp(param
, "monitor-format")) {
916 ast_copy_string(q
->monfmt
, val
, sizeof(q
->monfmt
));
917 } else if (!strcasecmp(param
, "queue-youarenext")) {
918 ast_copy_string(q
->sound_next
, val
, sizeof(q
->sound_next
));
919 } else if (!strcasecmp(param
, "queue-thereare")) {
920 ast_copy_string(q
->sound_thereare
, val
, sizeof(q
->sound_thereare
));
921 } else if (!strcasecmp(param
, "queue-callswaiting")) {
922 ast_copy_string(q
->sound_calls
, val
, sizeof(q
->sound_calls
));
923 } else if (!strcasecmp(param
, "queue-holdtime")) {
924 ast_copy_string(q
->sound_holdtime
, val
, sizeof(q
->sound_holdtime
));
925 } else if (!strcasecmp(param
, "queue-minutes")) {
926 ast_copy_string(q
->sound_minutes
, val
, sizeof(q
->sound_minutes
));
927 } else if (!strcasecmp(param
, "queue-seconds")) {
928 ast_copy_string(q
->sound_seconds
, val
, sizeof(q
->sound_seconds
));
929 } else if (!strcasecmp(param
, "queue-lessthan")) {
930 ast_copy_string(q
->sound_lessthan
, val
, sizeof(q
->sound_lessthan
));
931 } else if (!strcasecmp(param
, "queue-thankyou")) {
932 ast_copy_string(q
->sound_thanks
, val
, sizeof(q
->sound_thanks
));
933 } else if (!strcasecmp(param
, "queue-reporthold")) {
934 ast_copy_string(q
->sound_reporthold
, val
, sizeof(q
->sound_reporthold
));
935 } else if (!strcasecmp(param
, "announce-frequency")) {
936 q
->announcefrequency
= atoi(val
);
937 } else if (!strcasecmp(param
, "announce-round-seconds")) {
938 q
->roundingseconds
= atoi(val
);
939 if (q
->roundingseconds
>60 || q
->roundingseconds
<0) {
941 ast_log(LOG_WARNING
, "'%s' isn't a valid value for %s "
942 "using 0 instead for queue '%s' at line %d of queues.conf\n",
943 val
, param
, q
->name
, linenum
);
945 ast_log(LOG_WARNING
, "'%s' isn't a valid value for %s "
946 "using 0 instead for queue '%s'\n", val
, param
, q
->name
);
948 q
->roundingseconds
=0;
950 } else if (!strcasecmp(param
, "announce-holdtime")) {
951 if (!strcasecmp(val
, "once"))
952 q
->announceholdtime
= ANNOUNCEHOLDTIME_ONCE
;
953 else if (ast_true(val
))
954 q
->announceholdtime
= ANNOUNCEHOLDTIME_ALWAYS
;
956 q
->announceholdtime
= 0;
957 } else if (!strcasecmp(param
, "periodic-announce")) {
958 if (strchr(val
, '|')) {
959 char *s
, *buf
= ast_strdupa(val
);
962 while ((s
= strsep(&buf
, "|"))) {
963 ast_copy_string(q
->sound_periodicannounce
[i
], s
, sizeof(q
->sound_periodicannounce
[i
]));
965 if (i
== MAX_PERIODIC_ANNOUNCEMENTS
)
969 ast_copy_string(q
->sound_periodicannounce
[0], val
, sizeof(q
->sound_periodicannounce
[0]));
971 } else if (!strcasecmp(param
, "periodic-announce-frequency")) {
972 q
->periodicannouncefrequency
= atoi(val
);
973 } else if (!strcasecmp(param
, "retry")) {
974 q
->retry
= atoi(val
);
976 q
->retry
= DEFAULT_RETRY
;
977 } else if (!strcasecmp(param
, "wrapuptime")) {
978 q
->wrapuptime
= atoi(val
);
979 } else if (!strcasecmp(param
, "autofill")) {
980 q
->autofill
= ast_true(val
);
981 } else if (!strcasecmp(param
, "monitor-type")) {
982 if (!strcasecmp(val
, "mixmonitor"))
984 } else if (!strcasecmp(param
, "autopause")) {
985 q
->autopause
= ast_true(val
);
986 } else if (!strcasecmp(param
, "maxlen")) {
987 q
->maxlen
= atoi(val
);
990 } else if (!strcasecmp(param
, "servicelevel")) {
991 q
->servicelevel
= atoi(val
);
992 } else if (!strcasecmp(param
, "strategy")) {
993 q
->strategy
= strat2int(val
);
994 if (q
->strategy
< 0) {
995 ast_log(LOG_WARNING
, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
997 q
->strategy
= QUEUE_STRATEGY_RINGALL
;
999 } else if (!strcasecmp(param
, "joinempty")) {
1000 if (!strcasecmp(val
, "strict"))
1001 q
->joinempty
= QUEUE_EMPTY_STRICT
;
1002 else if (ast_true(val
))
1003 q
->joinempty
= QUEUE_EMPTY_NORMAL
;
1006 } else if (!strcasecmp(param
, "leavewhenempty")) {
1007 if (!strcasecmp(val
, "strict"))
1008 q
->leavewhenempty
= QUEUE_EMPTY_STRICT
;
1009 else if (ast_true(val
))
1010 q
->leavewhenempty
= QUEUE_EMPTY_NORMAL
;
1012 q
->leavewhenempty
= 0;
1013 } else if (!strcasecmp(param
, "eventmemberstatus")) {
1014 q
->maskmemberstatus
= !ast_true(val
);
1015 } else if (!strcasecmp(param
, "eventwhencalled")) {
1016 if (!strcasecmp(val
, "vars")) {
1017 q
->eventwhencalled
= QUEUE_EVENT_VARIABLES
;
1019 q
->eventwhencalled
= ast_true(val
);
1021 } else if (!strcasecmp(param
, "reportholdtime")) {
1022 q
->reportholdtime
= ast_true(val
);
1023 } else if (!strcasecmp(param
, "memberdelay")) {
1024 q
->memberdelay
= atoi(val
);
1025 } else if (!strcasecmp(param
, "weight")) {
1026 q
->weight
= atoi(val
);
1029 /* With Realtime queues, if the last queue using weights is deleted in realtime,
1030 we will not see any effect on use_weight until next reload. */
1031 } else if (!strcasecmp(param
, "timeoutrestart")) {
1032 q
->timeoutrestart
= ast_true(val
);
1033 } else if (failunknown
) {
1035 ast_log(LOG_WARNING
, "Unknown keyword in queue '%s': %s at line %d of queues.conf\n",
1036 q
->name
, param
, linenum
);
1038 ast_log(LOG_WARNING
, "Unknown keyword in queue '%s': %s\n", q
->name
, param
);
1043 static void rt_handle_member_record(struct call_queue
*q
, char *interface
, const char *membername
, const char *penalty_str
, const char *paused_str
)
1045 struct member
*m
, tmpmem
;
1050 penalty
= atoi(penalty_str
);
1056 paused
= atoi(paused_str
);
1061 /* Find the member, or the place to put a new one. */
1062 ast_copy_string(tmpmem
.interface
, interface
, sizeof(tmpmem
.interface
));
1063 m
= ao2_find(q
->members
, &tmpmem
, OBJ_POINTER
);
1065 /* Create a new one if not found, else update penalty */
1067 if ((m
= create_queue_member(interface
, membername
, penalty
, paused
))) {
1070 add_to_interfaces(interface
);
1071 ao2_link(q
->members
, m
);
1075 m
->dead
= 0; /* Do not delete this one. */
1078 m
->penalty
= penalty
;
1083 static void free_members(struct call_queue
*q
, int all
)
1085 /* Free non-dynamic members */
1087 struct ao2_iterator mem_iter
= ao2_iterator_init(q
->members
, 0);
1089 while ((cur
= ao2_iterator_next(&mem_iter
))) {
1090 if (all
|| !cur
->dynamic
) {
1091 ao2_unlink(q
->members
, cur
);
1092 remove_from_interfaces(cur
->interface
);
1099 static void destroy_queue(struct call_queue
*q
)
1102 ast_mutex_destroy(&q
->lock
);
1103 ao2_ref(q
->members
, -1);
1107 /*!\brief Reload a single queue via realtime.
1108 \return Return the queue, or NULL if it doesn't exist.
1109 \note Should be called with the global qlock locked. */
1110 static struct call_queue
*find_queue_by_name_rt(const char *queuename
, struct ast_variable
*queue_vars
, struct ast_config
*member_config
)
1112 struct ast_variable
*v
;
1113 struct call_queue
*q
;
1115 struct ao2_iterator mem_iter
;
1116 char *interface
= NULL
;
1117 char *tmp
, *tmp_name
;
1118 char tmpbuf
[64]; /* Must be longer than the longest queue param name. */
1120 /* Find the queue in the in-core list (we will create a new one if not found). */
1121 AST_LIST_TRAVERSE(&queues
, q
, list
) {
1122 if (!strcasecmp(q
->name
, queuename
))
1126 /* Static queues override realtime. */
1128 ast_mutex_lock(&q
->lock
);
1131 ast_mutex_unlock(&q
->lock
);
1134 ast_log(LOG_WARNING
, "Static queue '%s' already exists. Not loading from realtime\n", q
->name
);
1135 ast_mutex_unlock(&q
->lock
);
1139 } else if (!member_config
)
1140 /* Not found in the list, and it's not realtime ... */
1143 /* Check if queue is defined in realtime. */
1145 /* Delete queue from in-core list if it has been deleted in realtime. */
1147 /*! \note Hmm, can't seem to distinguish a DB failure from a not
1148 found condition... So we might delete an in-core queue
1149 in case of DB failure. */
1150 ast_log(LOG_DEBUG
, "Queue %s not found in realtime.\n", queuename
);
1153 /* Delete if unused (else will be deleted when last caller leaves). */
1156 AST_LIST_REMOVE(&queues
, q
, list
);
1157 ast_mutex_unlock(&q
->lock
);
1160 ast_mutex_unlock(&q
->lock
);
1165 /* Create a new queue if an in-core entry does not exist yet. */
1167 if (!(q
= alloc_queue(queuename
)))
1169 ast_mutex_lock(&q
->lock
);
1172 init_queue(q
); /* Ensure defaults for all parameters not set explicitly. */
1173 AST_LIST_INSERT_HEAD(&queues
, q
, list
);
1176 memset(tmpbuf
, 0, sizeof(tmpbuf
));
1177 for (v
= queue_vars
; v
; v
= v
->next
) {
1178 /* Convert to dashes `-' from underscores `_' as the latter are more SQL friendly. */
1179 if ((tmp
= strchr(v
->name
, '_'))) {
1180 ast_copy_string(tmpbuf
, v
->name
, sizeof(tmpbuf
));
1183 while ((tmp
= strchr(tmp
, '_')))
1187 queue_set_param(q
, tmp_name
, v
->value
, -1, 0);
1190 if (q
->strategy
== QUEUE_STRATEGY_ROUNDROBIN
)
1193 /* Temporarily set realtime members dead so we can detect deleted ones.
1194 * Also set the membercount correctly for realtime*/
1195 mem_iter
= ao2_iterator_init(q
->members
, 0);
1196 while ((m
= ao2_iterator_next(&mem_iter
))) {
1203 while ((interface
= ast_category_browse(member_config
, interface
))) {
1204 rt_handle_member_record(q
, interface
,
1205 ast_variable_retrieve(member_config
, interface
, "membername"),
1206 ast_variable_retrieve(member_config
, interface
, "penalty"),
1207 ast_variable_retrieve(member_config
, interface
, "paused"));
1210 /* Delete all realtime members that have been deleted in DB. */
1211 mem_iter
= ao2_iterator_init(q
->members
, 0);
1212 while ((m
= ao2_iterator_next(&mem_iter
))) {
1214 ao2_unlink(q
->members
, m
);
1215 ast_mutex_unlock(&q
->lock
);
1216 remove_from_interfaces(m
->interface
);
1217 ast_mutex_lock(&q
->lock
);
1223 ast_mutex_unlock(&q
->lock
);
1228 static int update_realtime_member_field(struct member
*mem
, const char *queue_name
, const char *field
, const char *value
)
1230 struct ast_variable
*var
;
1233 if(!(var
= ast_load_realtime("queue_members", "interface", mem
->interface
, "queue_name", queue_name
, NULL
)))
1236 if(!strcmp(var
->name
, "uniqueid"))
1240 if(var
&& !ast_strlen_zero(var
->value
)) {
1241 if ((ast_update_realtime("queue_members", "uniqueid", var
->value
, field
, value
, NULL
)) > -1)
1247 static void update_realtime_members(struct call_queue
*q
)
1249 struct ast_config
*member_config
= NULL
;
1251 char *interface
= NULL
;
1252 struct ao2_iterator mem_iter
;
1254 member_config
= ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", q
->name
, NULL
);
1255 if (!member_config
) {
1256 /*This queue doesn't have realtime members*/
1257 if (option_debug
> 2)
1258 ast_log(LOG_DEBUG
, "Queue %s has no realtime members defined. No need for update\n", q
->name
);
1262 ast_mutex_lock(&q
->lock
);
1264 /* Temporarily set realtime members dead so we can detect deleted ones.*/
1265 mem_iter
= ao2_iterator_init(q
->members
, 0);
1266 while ((m
= ao2_iterator_next(&mem_iter
))) {
1272 while ((interface
= ast_category_browse(member_config
, interface
))) {
1273 rt_handle_member_record(q
, interface
,
1274 S_OR(ast_variable_retrieve(member_config
, interface
, "membername"), interface
),
1275 ast_variable_retrieve(member_config
, interface
, "penalty"),
1276 ast_variable_retrieve(member_config
, interface
, "paused"));
1279 /* Delete all realtime members that have been deleted in DB. */
1280 mem_iter
= ao2_iterator_init(q
->members
, 0);
1281 while ((m
= ao2_iterator_next(&mem_iter
))) {
1283 ao2_unlink(q
->members
, m
);
1284 ast_mutex_unlock(&q
->lock
);
1285 remove_from_interfaces(m
->interface
);
1286 ast_mutex_lock(&q
->lock
);
1291 ast_mutex_unlock(&q
->lock
);
1294 static struct call_queue
*load_realtime_queue(const char *queuename
)
1296 struct ast_variable
*queue_vars
;
1297 struct ast_config
*member_config
= NULL
;
1298 struct call_queue
*q
;
1300 /* Find the queue in the in-core list first. */
1301 AST_LIST_LOCK(&queues
);
1302 AST_LIST_TRAVERSE(&queues
, q
, list
) {
1303 if (!strcasecmp(q
->name
, queuename
)) {
1307 AST_LIST_UNLOCK(&queues
);
1309 if (!q
|| q
->realtime
) {
1310 /*! \note Load from realtime before taking the global qlock, to avoid blocking all
1311 queue operations while waiting for the DB.
1313 This will be two separate database transactions, so we might
1314 see queue parameters as they were before another process
1315 changed the queue and member list as it was after the change.
1316 Thus we might see an empty member list when a queue is
1317 deleted. In practise, this is unlikely to cause a problem. */
1319 queue_vars
= ast_load_realtime("queues", "name", queuename
, NULL
);
1321 member_config
= ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", queuename
, NULL
);
1322 if (!member_config
) {
1323 ast_log(LOG_ERROR
, "no queue_members defined in your config (extconfig.conf).\n");
1328 AST_LIST_LOCK(&queues
);
1330 q
= find_queue_by_name_rt(queuename
, queue_vars
, member_config
);
1332 ast_config_destroy(member_config
);
1334 ast_variables_destroy(queue_vars
);
1336 AST_LIST_UNLOCK(&queues
);
1338 update_realtime_members(q
);
1343 static int join_queue(char *queuename
, struct queue_ent
*qe
, enum queue_result
*reason
)
1345 struct call_queue
*q
;
1346 struct queue_ent
*cur
, *prev
= NULL
;
1350 enum queue_member_status stat
;
1352 if (!(q
= load_realtime_queue(queuename
)))
1355 AST_LIST_LOCK(&queues
);
1356 ast_mutex_lock(&q
->lock
);
1358 /* This is our one */
1359 stat
= get_member_status(q
, qe
->max_penalty
);
1360 if (!q
->joinempty
&& (stat
== QUEUE_NO_MEMBERS
))
1361 *reason
= QUEUE_JOINEMPTY
;
1362 else if ((q
->joinempty
== QUEUE_EMPTY_STRICT
) && (stat
== QUEUE_NO_REACHABLE_MEMBERS
))
1363 *reason
= QUEUE_JOINUNAVAIL
;
1364 else if (q
->maxlen
&& (q
->count
>= q
->maxlen
))
1365 *reason
= QUEUE_FULL
;
1367 /* There's space for us, put us at the right position inside
1369 * Take into account the priority of the calling user */
1374 /* We have higher priority than the current user, enter
1375 * before him, after all the other users with priority
1376 * higher or equal to our priority. */
1377 if ((!inserted
) && (qe
->prio
> cur
->prio
)) {
1378 insert_entry(q
, prev
, qe
, &pos
);
1385 /* No luck, join at the end of the queue */
1387 insert_entry(q
, prev
, qe
, &pos
);
1388 ast_copy_string(qe
->moh
, q
->moh
, sizeof(qe
->moh
));
1389 ast_copy_string(qe
->announce
, q
->announce
, sizeof(qe
->announce
));
1390 ast_copy_string(qe
->context
, q
->context
, sizeof(qe
->context
));
1393 manager_event(EVENT_FLAG_CALL
, "Join",
1394 "Channel: %s\r\nCallerID: %s\r\nCallerIDName: %s\r\nQueue: %s\r\nPosition: %d\r\nCount: %d\r\nUniqueid: %s\r\n",
1396 S_OR(qe
->chan
->cid
.cid_num
, "unknown"), /* XXX somewhere else it is <unknown> */
1397 S_OR(qe
->chan
->cid
.cid_name
, "unknown"),
1398 q
->name
, qe
->pos
, q
->count
, qe
->chan
->uniqueid
);
1400 ast_log(LOG_DEBUG
, "Queue '%s' Join, Channel '%s', Position '%d'\n", q
->name
, qe
->chan
->name
, qe
->pos
);
1402 ast_mutex_unlock(&q
->lock
);
1403 AST_LIST_UNLOCK(&queues
);
1408 static int play_file(struct ast_channel
*chan
, char *filename
)
1412 ast_stopstream(chan
);
1414 res
= ast_streamfile(chan
, filename
, chan
->language
);
1416 res
= ast_waitstream(chan
, AST_DIGIT_ANY
);
1418 ast_stopstream(chan
);
1423 static int valid_exit(struct queue_ent
*qe
, char digit
)
1425 int digitlen
= strlen(qe
->digits
);
1427 /* Prevent possible buffer overflow */
1428 if (digitlen
< sizeof(qe
->digits
) - 2) {
1429 qe
->digits
[digitlen
] = digit
;
1430 qe
->digits
[digitlen
+ 1] = '\0';
1432 qe
->digits
[0] = '\0';
1436 /* If there's no context to goto, short-circuit */
1437 if (ast_strlen_zero(qe
->context
))
1440 /* If the extension is bad, then reset the digits to blank */
1441 if (!ast_canmatch_extension(qe
->chan
, qe
->context
, qe
->digits
, 1, qe
->chan
->cid
.cid_num
)) {
1442 qe
->digits
[0] = '\0';
1446 /* We have an exact match */
1447 if (!ast_goto_if_exists(qe
->chan
, qe
->context
, qe
->digits
, 1)) {
1448 qe
->valid_digits
= 1;
1449 /* Return 1 on a successful goto */
1456 static int say_position(struct queue_ent
*qe
)
1458 int res
= 0, avgholdmins
, avgholdsecs
;
1461 /* Check to see if this is ludicrous -- if we just announced position, don't do it again*/
1463 if ((now
- qe
->last_pos
) < 15)
1466 /* If either our position has changed, or we are over the freq timer, say position */
1467 if ((qe
->last_pos_said
== qe
->pos
) && ((now
- qe
->last_pos
) < qe
->parent
->announcefrequency
))
1470 ast_moh_stop(qe
->chan
);
1471 /* Say we're next, if we are */
1473 res
= play_file(qe
->chan
, qe
->parent
->sound_next
);
1479 res
= play_file(qe
->chan
, qe
->parent
->sound_thereare
);
1482 res
= ast_say_number(qe
->chan
, qe
->pos
, AST_DIGIT_ANY
, qe
->chan
->language
, (char *) NULL
); /* Needs gender */
1485 res
= play_file(qe
->chan
, qe
->parent
->sound_calls
);
1489 /* Round hold time to nearest minute */
1490 avgholdmins
= abs(((qe
->parent
->holdtime
+ 30) - (now
- qe
->start
)) / 60);
1492 /* If they have specified a rounding then round the seconds as well */
1493 if (qe
->parent
->roundingseconds
) {
1494 avgholdsecs
= (abs(((qe
->parent
->holdtime
+ 30) - (now
- qe
->start
))) - 60 * avgholdmins
) / qe
->parent
->roundingseconds
;
1495 avgholdsecs
*= qe
->parent
->roundingseconds
;
1500 if (option_verbose
> 2)
1501 ast_verbose(VERBOSE_PREFIX_3
"Hold time for %s is %d minutes %d seconds\n", qe
->parent
->name
, avgholdmins
, avgholdsecs
);
1503 /* If the hold time is >1 min, if it's enabled, and if it's not
1504 supposed to be only once and we have already said it, say it */
1505 if ((avgholdmins
+avgholdsecs
) > 0 && (qe
->parent
->announceholdtime
) &&
1506 (!(qe
->parent
->announceholdtime
== ANNOUNCEHOLDTIME_ONCE
) && qe
->last_pos
)) {
1507 res
= play_file(qe
->chan
, qe
->parent
->sound_holdtime
);
1511 if (avgholdmins
> 0) {
1512 if (avgholdmins
< 2) {
1513 res
= play_file(qe
->chan
, qe
->parent
->sound_lessthan
);
1517 res
= ast_say_number(qe
->chan
, 2, AST_DIGIT_ANY
, qe
->chan
->language
, NULL
);
1521 res
= ast_say_number(qe
->chan
, avgholdmins
, AST_DIGIT_ANY
, qe
->chan
->language
, NULL
);
1526 res
= play_file(qe
->chan
, qe
->parent
->sound_minutes
);
1530 if (avgholdsecs
>0) {
1531 res
= ast_say_number(qe
->chan
, avgholdsecs
, AST_DIGIT_ANY
, qe
->chan
->language
, NULL
);
1535 res
= play_file(qe
->chan
, qe
->parent
->sound_seconds
);
1543 if (option_verbose
> 2)
1544 ast_verbose(VERBOSE_PREFIX_3
"Told %s in %s their queue position (which was %d)\n",
1545 qe
->chan
->name
, qe
->parent
->name
, qe
->pos
);
1546 res
= play_file(qe
->chan
, qe
->parent
->sound_thanks
);
1549 if ((res
> 0 && !valid_exit(qe
, res
)) || res
< 0)
1552 /* Set our last_pos indicators */
1554 qe
->last_pos_said
= qe
->pos
;
1556 /* Don't restart music on hold if we're about to exit the caller from the queue */
1558 ast_moh_start(qe
->chan
, qe
->moh
, NULL
);
1563 static void recalc_holdtime(struct queue_ent
*qe
, int newholdtime
)
1567 /* Calculate holdtime using a recursive boxcar filter */
1568 /* Thanks to SRT for this contribution */
1569 /* 2^2 (4) is the filter coefficient; a higher exponent would give old entries more weight */
1571 ast_mutex_lock(&qe
->parent
->lock
);
1572 oldvalue
= qe
->parent
->holdtime
;
1573 qe
->parent
->holdtime
= (((oldvalue
<< 2) - oldvalue
) + newholdtime
) >> 2;
1574 ast_mutex_unlock(&qe
->parent
->lock
);
1578 static void leave_queue(struct queue_ent
*qe
)
1580 struct call_queue
*q
;
1581 struct queue_ent
*cur
, *prev
= NULL
;
1584 if (!(q
= qe
->parent
))
1586 ast_mutex_lock(&q
->lock
);
1589 for (cur
= q
->head
; cur
; cur
= cur
->next
) {
1593 /* Take us out of the queue */
1594 manager_event(EVENT_FLAG_CALL
, "Leave",
1595 "Channel: %s\r\nQueue: %s\r\nCount: %d\r\nUniqueid: %s\r\n",
1596 qe
->chan
->name
, q
->name
, q
->count
, qe
->chan
->uniqueid
);
1598 ast_log(LOG_DEBUG
, "Queue '%s' Leave, Channel '%s'\n", q
->name
, qe
->chan
->name
);
1599 /* Take us out of the queue */
1601 prev
->next
= cur
->next
;
1603 q
->head
= cur
->next
;
1605 /* Renumber the people after us in the queue based on a new count */
1610 ast_mutex_unlock(&q
->lock
);
1612 if (q
->dead
&& !q
->count
) {
1613 /* It's dead and nobody is in it, so kill it */
1614 AST_LIST_LOCK(&queues
);
1615 AST_LIST_REMOVE(&queues
, q
, list
);
1616 AST_LIST_UNLOCK(&queues
);
1621 /* Hang up a list of outgoing calls */
1622 static void hangupcalls(struct callattempt
*outgoing
, struct ast_channel
*exception
)
1624 struct callattempt
*oo
;
1627 /* Hangup any existing lines we have open */
1628 if (outgoing
->chan
&& (outgoing
->chan
!= exception
))
1629 ast_hangup(outgoing
->chan
);
1631 outgoing
= outgoing
->q_next
;
1633 ao2_ref(oo
->member
, -1);
1638 static int update_status(struct call_queue
*q
, struct member
*member
, int status
)
1641 struct ao2_iterator mem_iter
;
1643 /* Since a reload could have taken place, we have to traverse the list to
1644 be sure it's still valid */
1645 ast_mutex_lock(&q
->lock
);
1646 mem_iter
= ao2_iterator_init(q
->members
, 0);
1647 while ((cur
= ao2_iterator_next(&mem_iter
))) {
1648 if (member
!= cur
) {
1653 cur
->status
= status
;
1654 if (!q
->maskmemberstatus
) {
1655 manager_event(EVENT_FLAG_AGENT
, "QueueMemberStatus",
1658 "MemberName: %s\r\n"
1659 "Membership: %s\r\n"
1661 "CallsTaken: %d\r\n"
1665 q
->name
, cur
->interface
, cur
->membername
, cur
->dynamic
? "dynamic" : cur
->realtime
? "realtime": "static",
1666 cur
->penalty
, cur
->calls
, (int)cur
->lastcall
, cur
->status
, cur
->paused
);
1670 ast_mutex_unlock(&q
->lock
);
1674 static int update_dial_status(struct call_queue
*q
, struct member
*member
, int status
)
1676 if (status
== AST_CAUSE_BUSY
)
1677 status
= AST_DEVICE_BUSY
;
1678 else if (status
== AST_CAUSE_UNREGISTERED
)
1679 status
= AST_DEVICE_UNAVAILABLE
;
1680 else if (status
== AST_CAUSE_NOSUCHDRIVER
)
1681 status
= AST_DEVICE_INVALID
;
1683 status
= AST_DEVICE_UNKNOWN
;
1684 return update_status(q
, member
, status
);
1687 /* traverse all defined queues which have calls waiting and contain this member
1688 return 0 if no other queue has precedence (higher weight) or 1 if found */
1689 static int compare_weight(struct call_queue
*rq
, struct member
*member
)
1691 struct call_queue
*q
;
1695 /* &qlock and &rq->lock already set by try_calling()
1696 * to solve deadlock */
1697 AST_LIST_TRAVERSE(&queues
, q
, list
) {
1698 if (q
== rq
) /* don't check myself, could deadlock */
1700 ast_mutex_lock(&q
->lock
);
1701 if (q
->count
&& q
->members
) {
1702 if ((mem
= ao2_find(q
->members
, member
, OBJ_POINTER
))) {
1703 ast_log(LOG_DEBUG
, "Found matching member %s in queue '%s'\n", mem
->interface
, q
->name
);
1704 if (q
->weight
> rq
->weight
) {
1705 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
);
1711 ast_mutex_unlock(&q
->lock
);
1718 /*! \brief common hangup actions */
1719 static void do_hang(struct callattempt
*o
)
1722 ast_hangup(o
->chan
);
1726 static char *vars2manager(struct ast_channel
*chan
, char *vars
, size_t len
)
1728 char *tmp
= alloca(len
);
1730 if (pbx_builtin_serialize_variables(chan
, tmp
, len
)) {
1733 /* convert "\n" to "\nVariable: " */
1734 strcpy(vars
, "Variable: ");
1736 for (i
= 0, j
= 10; (i
< len
- 1) && (j
< len
- 1); i
++, j
++) {
1739 if (tmp
[i
+ 1] == '\0')
1741 if (tmp
[i
] == '\n') {
1745 ast_copy_string(&(vars
[j
]), "Variable: ", len
- j
);
1755 /* there are no channel variables; leave it blank */
1761 static int ring_entry(struct queue_ent
*qe
, struct callattempt
*tmp
, int *busies
)
1768 /* on entry here, we know that tmp->chan == NULL */
1769 if (qe
->parent
->wrapuptime
&& (time(NULL
) - tmp
->lastcall
< qe
->parent
->wrapuptime
)) {
1771 ast_log(LOG_DEBUG
, "Wrapuptime not yet expired for %s\n", tmp
->interface
);
1773 ast_cdr_busy(qe
->chan
->cdr
);
1774 tmp
->stillgoing
= 0;
1779 if (!qe
->parent
->ringinuse
&& (tmp
->member
->status
!= AST_DEVICE_NOT_INUSE
) && (tmp
->member
->status
!= AST_DEVICE_UNKNOWN
)) {
1781 ast_log(LOG_DEBUG
, "%s in use, can't receive call\n", tmp
->interface
);
1783 ast_cdr_busy(qe
->chan
->cdr
);
1784 tmp
->stillgoing
= 0;
1788 if (tmp
->member
->paused
) {
1790 ast_log(LOG_DEBUG
, "%s paused, can't receive call\n", tmp
->interface
);
1792 ast_cdr_busy(qe
->chan
->cdr
);
1793 tmp
->stillgoing
= 0;
1796 if (use_weight
&& compare_weight(qe
->parent
,tmp
->member
)) {
1797 ast_log(LOG_DEBUG
, "Priority queue delaying call to %s:%s\n", qe
->parent
->name
, tmp
->interface
);
1799 ast_cdr_busy(qe
->chan
->cdr
);
1800 tmp
->stillgoing
= 0;
1805 ast_copy_string(tech
, tmp
->interface
, sizeof(tech
));
1806 if ((location
= strchr(tech
, '/')))
1811 /* Request the peer */
1812 tmp
->chan
= ast_request(tech
, qe
->chan
->nativeformats
, location
, &status
);
1813 if (!tmp
->chan
) { /* If we can't, just go on to the next call */
1815 ast_cdr_busy(qe
->chan
->cdr
);
1816 tmp
->stillgoing
= 0;
1817 update_dial_status(qe
->parent
, tmp
->member
, status
);
1819 ast_mutex_lock(&qe
->parent
->lock
);
1820 qe
->parent
->rrpos
++;
1821 ast_mutex_unlock(&qe
->parent
->lock
);
1825 } else if (status
!= tmp
->oldstatus
)
1826 update_dial_status(qe
->parent
, tmp
->member
, status
);
1828 tmp
->chan
->appl
= "AppQueue";
1829 tmp
->chan
->data
= "(Outgoing Line)";
1830 tmp
->chan
->whentohangup
= 0;
1831 if (tmp
->chan
->cid
.cid_num
)
1832 free(tmp
->chan
->cid
.cid_num
);
1833 tmp
->chan
->cid
.cid_num
= ast_strdup(qe
->chan
->cid
.cid_num
);
1834 if (tmp
->chan
->cid
.cid_name
)
1835 free(tmp
->chan
->cid
.cid_name
);
1836 tmp
->chan
->cid
.cid_name
= ast_strdup(qe
->chan
->cid
.cid_name
);
1837 if (tmp
->chan
->cid
.cid_ani
)
1838 free(tmp
->chan
->cid
.cid_ani
);
1839 tmp
->chan
->cid
.cid_ani
= ast_strdup(qe
->chan
->cid
.cid_ani
);
1841 /* Inherit specially named variables from parent channel */
1842 ast_channel_inherit_variables(qe
->chan
, tmp
->chan
);
1844 /* Presense of ADSI CPE on outgoing channel follows ours */
1845 tmp
->chan
->adsicpe
= qe
->chan
->adsicpe
;
1847 /* Place the call, but don't wait on the answer */
1848 if ((res
= ast_call(tmp
->chan
, location
, 0))) {
1849 /* Again, keep going even if there's an error */
1851 ast_log(LOG_DEBUG
, "ast call on peer returned %d\n", res
);
1852 if (option_verbose
> 2)
1853 ast_verbose(VERBOSE_PREFIX_3
"Couldn't call %s\n", tmp
->interface
);
1857 } else if (qe
->parent
->eventwhencalled
) {
1860 manager_event(EVENT_FLAG_AGENT
, "AgentCalled",
1861 "AgentCalled: %s\r\n"
1863 "ChannelCalling: %s\r\n"
1865 "CallerIDName: %s\r\n"
1870 tmp
->interface
, tmp
->member
->membername
, qe
->chan
->name
,
1871 tmp
->chan
->cid
.cid_num
? tmp
->chan
->cid
.cid_num
: "unknown",
1872 tmp
->chan
->cid
.cid_name
? tmp
->chan
->cid
.cid_name
: "unknown",
1873 qe
->chan
->context
, qe
->chan
->exten
, qe
->chan
->priority
,
1874 qe
->parent
->eventwhencalled
== QUEUE_EVENT_VARIABLES
? vars2manager(qe
->chan
, vars
, sizeof(vars
)) : "");
1875 if (option_verbose
> 2)
1876 ast_verbose(VERBOSE_PREFIX_3
"Called %s\n", tmp
->interface
);
1882 /*! \brief find the entry with the best metric, or NULL */
1883 static struct callattempt
*find_best(struct callattempt
*outgoing
)
1885 struct callattempt
*best
= NULL
, *cur
;
1887 for (cur
= outgoing
; cur
; cur
= cur
->q_next
) {
1888 if (cur
->stillgoing
&& /* Not already done */
1889 !cur
->chan
&& /* Isn't already going */
1890 (!best
|| cur
->metric
< best
->metric
)) { /* We haven't found one yet, or it's better */
1898 static int ring_one(struct queue_ent
*qe
, struct callattempt
*outgoing
, int *busies
)
1903 struct callattempt
*best
= find_best(outgoing
);
1906 ast_log(LOG_DEBUG
, "Nobody left to try ringing in queue\n");
1909 if (qe
->parent
->strategy
== QUEUE_STRATEGY_RINGALL
) {
1910 struct callattempt
*cur
;
1911 /* Ring everyone who shares this best metric (for ringall) */
1912 for (cur
= outgoing
; cur
; cur
= cur
->q_next
) {
1913 if (cur
->stillgoing
&& !cur
->chan
&& cur
->metric
<= best
->metric
) {
1915 ast_log(LOG_DEBUG
, "(Parallel) Trying '%s' with metric %d\n", cur
->interface
, cur
->metric
);
1916 ring_entry(qe
, cur
, busies
);
1920 /* Ring just the best channel */
1922 ast_log(LOG_DEBUG
, "Trying '%s' with metric %d\n", best
->interface
, best
->metric
);
1923 ring_entry(qe
, best
, busies
);
1925 if (best
->chan
) /* break out with result = 1 */
1932 static int store_next(struct queue_ent
*qe
, struct callattempt
*outgoing
)
1934 struct callattempt
*best
= find_best(outgoing
);
1937 /* Ring just the best channel */
1939 ast_log(LOG_DEBUG
, "Next is '%s' with metric %d\n", best
->interface
, best
->metric
);
1940 qe
->parent
->rrpos
= best
->metric
% 1000;
1942 /* Just increment rrpos */
1943 if (qe
->parent
->wrapped
) {
1944 /* No more channels, start over */
1945 qe
->parent
->rrpos
= 0;
1947 /* Prioritize next entry */
1948 qe
->parent
->rrpos
++;
1951 qe
->parent
->wrapped
= 0;
1956 static int say_periodic_announcement(struct queue_ent
*qe
)
1961 /* Get the current time */
1964 /* Check to see if it is time to announce */
1965 if ((now
- qe
->last_periodic_announce_time
) < qe
->parent
->periodicannouncefrequency
)
1968 /* Stop the music on hold so we can play our own file */
1969 ast_moh_stop(qe
->chan
);
1971 if (option_verbose
> 2)
1972 ast_verbose(VERBOSE_PREFIX_3
"Playing periodic announcement\n");
1974 /* Check to make sure we have a sound file. If not, reset to the first sound file */
1975 if (qe
->last_periodic_announce_sound
>= MAX_PERIODIC_ANNOUNCEMENTS
|| !strlen(qe
->parent
->sound_periodicannounce
[qe
->last_periodic_announce_sound
])) {
1976 qe
->last_periodic_announce_sound
= 0;
1979 /* play the announcement */
1980 res
= play_file(qe
->chan
, qe
->parent
->sound_periodicannounce
[qe
->last_periodic_announce_sound
]);
1982 if ((res
> 0 && !valid_exit(qe
, res
)) || res
< 0)
1985 /* Resume Music on Hold if the caller is going to stay in the queue */
1987 ast_moh_start(qe
->chan
, qe
->moh
, NULL
);
1989 /* update last_periodic_announce_time */
1990 qe
->last_periodic_announce_time
= now
;
1992 /* Update the current periodic announcement to the next announcement */
1993 qe
->last_periodic_announce_sound
++;
1998 static void record_abandoned(struct queue_ent
*qe
)
2000 ast_mutex_lock(&qe
->parent
->lock
);
2001 manager_event(EVENT_FLAG_AGENT
, "QueueCallerAbandon",
2005 "OriginalPosition: %d\r\n"
2007 qe
->parent
->name
, qe
->chan
->uniqueid
, qe
->pos
, qe
->opos
, (int)(time(NULL
) - qe
->start
));
2009 qe
->parent
->callsabandoned
++;
2010 ast_mutex_unlock(&qe
->parent
->lock
);
2013 /*! \brief RNA == Ring No Answer. Common code that is executed when we try a queue member and they don't answer. */
2014 static void rna(int rnatime
, struct queue_ent
*qe
, char *interface
, char *membername
)
2016 if (option_verbose
> 2)
2017 ast_verbose( VERBOSE_PREFIX_3
"Nobody picked up in %d ms\n", rnatime
);
2018 ast_queue_log(qe
->parent
->name
, qe
->chan
->uniqueid
, membername
, "RINGNOANSWER", "%d", rnatime
);
2019 if (qe
->parent
->autopause
) {
2020 if (!set_member_paused(qe
->parent
->name
, interface
, 1)) {
2021 if (option_verbose
> 2)
2022 ast_verbose( VERBOSE_PREFIX_3
"Auto-Pausing Queue Member %s in queue %s since they failed to answer.\n", interface
, qe
->parent
->name
);
2024 if (option_verbose
> 2)
2025 ast_verbose( VERBOSE_PREFIX_3
"Failed to pause Queue Member %s in queue %s!\n", interface
, qe
->parent
->name
);
2031 #define AST_MAX_WATCHERS 256
2033 static struct callattempt
*wait_for_answer(struct queue_ent
*qe
, struct callattempt
*outgoing
, int *to
, char *digit
, int prebusies
, int caller_disconnect
, int forwardsallowed
)
2035 char *queue
= qe
->parent
->name
;
2036 struct callattempt
*o
;
2038 int sentringing
= 0;
2039 int numbusies
= prebusies
;
2043 struct ast_frame
*f
;
2044 struct callattempt
*peer
= NULL
;
2045 struct ast_channel
*winner
;
2046 struct ast_channel
*in
= qe
->chan
;
2048 char membername
[80] = "";
2052 starttime
= (long) time(NULL
);
2054 while (*to
&& !peer
) {
2055 int numlines
, retry
, pos
= 1;
2056 struct ast_channel
*watchers
[AST_MAX_WATCHERS
];
2059 for (retry
= 0; retry
< 2; retry
++) {
2061 for (o
= outgoing
; o
; o
= o
->q_next
) { /* Keep track of important channels */
2062 if (o
->stillgoing
) { /* Keep track of important channels */
2065 watchers
[pos
++] = o
->chan
;
2069 if (pos
> 1 /* found */ || !stillgoing
/* nobody listening */ ||
2070 (qe
->parent
->strategy
!= QUEUE_STRATEGY_RINGALL
) /* ring would not be delivered */)
2072 /* On "ringall" strategy we only move to the next penalty level
2073 when *all* ringing phones are done in the current penalty level */
2074 ring_one(qe
, outgoing
, &numbusies
);
2077 if (pos
== 1 /* not found */) {
2078 if (numlines
== (numbusies
+ numnochan
)) {
2079 ast_log(LOG_DEBUG
, "Everyone is busy at this time\n");
2081 ast_log(LOG_NOTICE
, "No one is answering queue '%s' (%d/%d/%d)\n", queue
, numlines
, numbusies
, numnochan
);
2086 winner
= ast_waitfor_n(watchers
, pos
, to
);
2087 for (o
= outgoing
; o
; o
= o
->q_next
) {
2088 if (o
->stillgoing
&& (o
->chan
) && (o
->chan
->_state
== AST_STATE_UP
)) {
2090 if (option_verbose
> 2)
2091 ast_verbose( VERBOSE_PREFIX_3
"%s answered %s\n", o
->chan
->name
, in
->name
);
2094 } else if (o
->chan
&& (o
->chan
== winner
)) {
2096 ast_copy_string(on
, o
->member
->interface
, sizeof(on
));
2097 ast_copy_string(membername
, o
->member
->membername
, sizeof(membername
));
2099 if (!ast_strlen_zero(o
->chan
->call_forward
) && !forwardsallowed
) {
2100 if (option_verbose
> 2)
2101 ast_verbose(VERBOSE_PREFIX_3
"Forwarding %s to '%s' prevented.\n", in
->name
, o
->chan
->call_forward
);
2106 } else if (!ast_strlen_zero(o
->chan
->call_forward
)) {
2111 ast_copy_string(tmpchan
, o
->chan
->call_forward
, sizeof(tmpchan
));
2112 if ((stuff
= strchr(tmpchan
, '/'))) {
2116 snprintf(tmpchan
, sizeof(tmpchan
), "%s@%s", o
->chan
->call_forward
, o
->chan
->context
);
2120 /* Before processing channel, go ahead and check for forwarding */
2121 if (option_verbose
> 2)
2122 ast_verbose(VERBOSE_PREFIX_3
"Now forwarding %s to '%s/%s' (thanks to %s)\n", in
->name
, tech
, stuff
, o
->chan
->name
);
2123 /* Setup parameters */
2124 o
->chan
= ast_request(tech
, in
->nativeformats
, stuff
, &status
);
2125 if (status
!= o
->oldstatus
)
2126 update_dial_status(qe
->parent
, o
->member
, status
);
2128 ast_log(LOG_NOTICE
, "Unable to create local channel for call forward to '%s/%s'\n", tech
, stuff
);
2132 ast_channel_inherit_variables(in
, o
->chan
);
2133 if (o
->chan
->cid
.cid_num
)
2134 free(o
->chan
->cid
.cid_num
);
2135 o
->chan
->cid
.cid_num
= ast_strdup(in
->cid
.cid_num
);
2137 if (o
->chan
->cid
.cid_name
)
2138 free(o
->chan
->cid
.cid_name
);
2139 o
->chan
->cid
.cid_name
= ast_strdup(in
->cid
.cid_name
);
2141 ast_string_field_set(o
->chan
, accountcode
, in
->accountcode
);
2142 o
->chan
->cdrflags
= in
->cdrflags
;
2144 if (in
->cid
.cid_ani
) {
2145 if (o
->chan
->cid
.cid_ani
)
2146 free(o
->chan
->cid
.cid_ani
);
2147 o
->chan
->cid
.cid_ani
= ast_strdup(in
->cid
.cid_ani
);
2149 if (o
->chan
->cid
.cid_rdnis
)
2150 free(o
->chan
->cid
.cid_rdnis
);
2151 o
->chan
->cid
.cid_rdnis
= ast_strdup(S_OR(in
->macroexten
, in
->exten
));
2152 if (ast_call(o
->chan
, tmpchan
, 0)) {
2153 ast_log(LOG_NOTICE
, "Failed to dial on local channel for call forward to '%s'\n", tmpchan
);
2158 /* Hangup the original channel now, in case we needed it */
2162 f
= ast_read(winner
);
2164 if (f
->frametype
== AST_FRAME_CONTROL
) {
2165 switch (f
->subclass
) {
2166 case AST_CONTROL_ANSWER
:
2167 /* This is our guy if someone answered. */
2169 if (option_verbose
> 2)
2170 ast_verbose( VERBOSE_PREFIX_3
"%s answered %s\n", o
->chan
->name
, in
->name
);
2174 case AST_CONTROL_BUSY
:
2175 if (option_verbose
> 2)
2176 ast_verbose( VERBOSE_PREFIX_3
"%s is busy\n", o
->chan
->name
);
2178 ast_cdr_busy(in
->cdr
);
2180 endtime
= (long)time(NULL
);
2181 endtime
-= starttime
;
2182 rna(endtime
*1000, qe
, on
, membername
);
2183 if (qe
->parent
->strategy
!= QUEUE_STRATEGY_RINGALL
) {
2184 if (qe
->parent
->timeoutrestart
)
2186 ring_one(qe
, outgoing
, &numbusies
);
2190 case AST_CONTROL_CONGESTION
:
2191 if (option_verbose
> 2)
2192 ast_verbose( VERBOSE_PREFIX_3
"%s is circuit-busy\n", o
->chan
->name
);
2194 ast_cdr_busy(in
->cdr
);
2195 endtime
= (long)time(NULL
);
2196 endtime
-= starttime
;
2197 rna(endtime
*1000, qe
, on
, membername
);
2199 if (qe
->parent
->strategy
!= QUEUE_STRATEGY_RINGALL
) {
2200 if (qe
->parent
->timeoutrestart
)
2202 ring_one(qe
, outgoing
, &numbusies
);
2206 case AST_CONTROL_RINGING
:
2207 if (option_verbose
> 2)
2208 ast_verbose( VERBOSE_PREFIX_3
"%s is ringing\n", o
->chan
->name
);
2211 ast_indicate(in
, AST_CONTROL_RINGING
);
2216 case AST_CONTROL_OFFHOOK
:
2217 /* Ignore going off hook */
2220 ast_log(LOG_DEBUG
, "Dunno what to do with control type %d\n", f
->subclass
);
2225 endtime
= (long) time(NULL
) - starttime
;
2226 rna(endtime
* 1000, qe
, on
, membername
);
2228 if (qe
->parent
->strategy
!= QUEUE_STRATEGY_RINGALL
) {
2229 if (qe
->parent
->timeoutrestart
)
2231 ring_one(qe
, outgoing
, &numbusies
);
2238 if (!f
|| ((f
->frametype
== AST_FRAME_CONTROL
) && (f
->subclass
== AST_CONTROL_HANGUP
))) {
2245 if ((f
->frametype
== AST_FRAME_DTMF
) && caller_disconnect
&& (f
->subclass
== '*')) {
2246 if (option_verbose
> 3)
2247 ast_verbose(VERBOSE_PREFIX_3
"User hit %c to disconnect call.\n", f
->subclass
);
2252 if ((f
->frametype
== AST_FRAME_DTMF
) && valid_exit(qe
, f
->subclass
)) {
2253 if (option_verbose
> 3)
2254 ast_verbose(VERBOSE_PREFIX_3
"User pressed digit: %c\n", f
->subclass
);
2256 *digit
= f
->subclass
;
2263 rna(orig
, qe
, on
, membername
);
2269 static int is_our_turn(struct queue_ent
*qe
)
2271 struct queue_ent
*ch
;
2277 if (!qe
->parent
->autofill
) {
2278 /* Atomically read the parent head -- does not need a lock */
2279 ch
= qe
->parent
->head
;
2280 /* If we are now at the top of the head, break out */
2283 ast_log(LOG_DEBUG
, "It's our turn (%s).\n", qe
->chan
->name
);
2287 ast_log(LOG_DEBUG
, "It's not our turn (%s).\n", qe
->chan
->name
);
2292 /* This needs a lock. How many members are available to be served? */
2293 ast_mutex_lock(&qe
->parent
->lock
);
2295 ch
= qe
->parent
->head
;
2297 if (qe
->parent
->strategy
== QUEUE_STRATEGY_RINGALL
) {
2299 ast_log(LOG_DEBUG
, "Even though there are %d available members, the strategy is ringall so only the head call is allowed in\n", avl
);
2302 struct ao2_iterator mem_iter
= ao2_iterator_init(qe
->parent
->members
, 0);
2303 while ((cur
= ao2_iterator_next(&mem_iter
))) {
2304 switch (cur
->status
) {
2305 case AST_DEVICE_NOT_INUSE
:
2306 case AST_DEVICE_UNKNOWN
:
2316 ast_log(LOG_DEBUG
, "There are %d available members.\n", avl
);
2318 while ((idx
< avl
) && (ch
) && (ch
!= qe
)) {
2323 /* If the queue entry is within avl [the number of available members] calls from the top ... */
2324 if (ch
&& idx
< avl
) {
2326 ast_log(LOG_DEBUG
, "It's our turn (%s).\n", qe
->chan
->name
);
2330 ast_log(LOG_DEBUG
, "It's not our turn (%s).\n", qe
->chan
->name
);
2334 ast_mutex_unlock(&qe
->parent
->lock
);
2340 static int wait_our_turn(struct queue_ent
*qe
, int ringing
, enum queue_result
*reason
)
2344 /* This is the holding pen for callers 2 through maxlen */
2346 enum queue_member_status stat
;
2348 if (is_our_turn(qe
))
2351 /* If we have timed out, break out */
2352 if (qe
->expire
&& (time(NULL
) > qe
->expire
)) {
2353 *reason
= QUEUE_TIMEOUT
;
2357 stat
= get_member_status(qe
->parent
, qe
->max_penalty
);
2359 /* leave the queue if no agents, if enabled */
2360 if (qe
->parent
->leavewhenempty
&& (stat
== QUEUE_NO_MEMBERS
)) {
2361 *reason
= QUEUE_LEAVEEMPTY
;
2362 ast_queue_log(qe
->parent
->name
, qe
->chan
->uniqueid
, "NONE", "EXITEMPTY", "%d|%d|%ld", qe
->pos
, qe
->opos
, (long)time(NULL
) - qe
->start
);
2367 /* leave the queue if no reachable agents, if enabled */
2368 if ((qe
->parent
->leavewhenempty
== QUEUE_EMPTY_STRICT
) && (stat
== QUEUE_NO_REACHABLE_MEMBERS
)) {
2369 *reason
= QUEUE_LEAVEUNAVAIL
;
2370 ast_queue_log(qe
->parent
->name
, qe
->chan
->uniqueid
, "NONE", "EXITEMPTY", "%d|%d|%ld", qe
->pos
, qe
->opos
, (long)time(NULL
) - qe
->start
);
2375 /* Make a position announcement, if enabled */
2376 if (qe
->parent
->announcefrequency
&& !ringing
&&
2377 (res
= say_position(qe
)))
2380 /* Make a periodic announcement, if enabled */
2381 if (qe
->parent
->periodicannouncefrequency
&& !ringing
&&
2382 (res
= say_periodic_announcement(qe
)))
2385 /* Wait a second before checking again */
2386 if ((res
= ast_waitfordigit(qe
->chan
, RECHECK
* 1000))) {
2387 if (res
> 0 && !valid_exit(qe
, res
))
2397 static int update_queue(struct call_queue
*q
, struct member
*member
, int callcompletedinsl
)
2399 ast_mutex_lock(&q
->lock
);
2400 time(&member
->lastcall
);
2402 q
->callscompleted
++;
2403 if (callcompletedinsl
)
2404 q
->callscompletedinsl
++;
2405 ast_mutex_unlock(&q
->lock
);
2409 static int calc_metric(struct call_queue
*q
, struct member
*mem
, int pos
, struct queue_ent
*qe
, struct callattempt
*tmp
)
2411 if (qe
->max_penalty
&& (mem
->penalty
> qe
->max_penalty
))
2414 switch (q
->strategy
) {
2415 case QUEUE_STRATEGY_RINGALL
:
2416 /* Everyone equal, except for penalty */
2417 tmp
->metric
= mem
->penalty
* 1000000;
2419 case QUEUE_STRATEGY_ROUNDROBIN
:
2422 /* No more channels, start over */
2425 /* Prioritize next entry */
2431 case QUEUE_STRATEGY_RRMEMORY
:
2432 if (pos
< q
->rrpos
) {
2433 tmp
->metric
= 1000 + pos
;
2436 /* Indicate there is another priority */
2440 tmp
->metric
+= mem
->penalty
* 1000000;
2442 case QUEUE_STRATEGY_RANDOM
:
2443 tmp
->metric
= ast_random() % 1000;
2444 tmp
->metric
+= mem
->penalty
* 1000000;
2446 case QUEUE_STRATEGY_FEWESTCALLS
:
2447 tmp
->metric
= mem
->calls
;
2448 tmp
->metric
+= mem
->penalty
* 1000000;
2450 case QUEUE_STRATEGY_LEASTRECENT
:
2454 tmp
->metric
= 1000000 - (time(NULL
) - mem
->lastcall
);
2455 tmp
->metric
+= mem
->penalty
* 1000000;
2458 ast_log(LOG_WARNING
, "Can't calculate metric for unknown strategy %d\n", q
->strategy
);
2464 static int try_calling(struct queue_ent
*qe
, const char *options
, char *announceoverride
, const char *url
, int *tries
, int *noption
, const char *agi
)
2467 struct callattempt
*outgoing
= NULL
; /* the list of calls we are building */
2469 char oldexten
[AST_MAX_EXTENSION
]="";
2470 char oldcontext
[AST_MAX_CONTEXT
]="";
2471 char queuename
[256]="";
2472 struct ast_channel
*peer
;
2473 struct ast_channel
*which
;
2474 struct callattempt
*lpeer
;
2475 struct member
*member
;
2476 struct ast_app
*app
;
2477 int res
= 0, bridge
= 0;
2480 char *announce
= NULL
;
2483 time_t now
= time(NULL
);
2484 struct ast_bridge_config bridge_config
;
2485 char nondataquality
= 1;
2486 char *agiexec
= NULL
;
2488 const char *monitorfilename
;
2489 const char *monitor_exec
;
2490 const char *monitor_options
;
2491 char tmpid
[256], tmpid2
[256];
2492 char meid
[1024], meid2
[1024];
2493 char mixmonargs
[1512];
2494 struct ast_app
*mixmonapp
= NULL
;
2497 int forwardsallowed
= 1;
2498 int callcompletedinsl
;
2499 struct ao2_iterator memi
;
2501 memset(&bridge_config
, 0, sizeof(bridge_config
));
2504 for (; options
&& *options
; options
++)
2507 ast_set_flag(&(bridge_config
.features_callee
), AST_FEATURE_REDIRECT
);
2510 ast_set_flag(&(bridge_config
.features_caller
), AST_FEATURE_REDIRECT
);
2513 ast_set_flag(&(bridge_config
.features_callee
), AST_FEATURE_AUTOMON
);
2516 ast_set_flag(&(bridge_config
.features_caller
), AST_FEATURE_AUTOMON
);
2522 ast_set_flag(&(bridge_config
.features_callee
), AST_FEATURE_DISCONNECT
);
2525 ast_set_flag(&(bridge_config
.features_caller
), AST_FEATURE_DISCONNECT
);
2528 if (qe
->parent
->strategy
== QUEUE_STRATEGY_ROUNDROBIN
|| qe
->parent
->strategy
== QUEUE_STRATEGY_RRMEMORY
)
2531 *tries
= qe
->parent
->membercount
;
2535 forwardsallowed
= 0;
2539 /* Hold the lock while we setup the outgoing calls */
2541 AST_LIST_LOCK(&queues
);
2542 ast_mutex_lock(&qe
->parent
->lock
);
2544 ast_log(LOG_DEBUG
, "%s is trying to call a queue member.\n",
2546 ast_copy_string(queuename
, qe
->parent
->name
, sizeof(queuename
));
2547 if (!ast_strlen_zero(qe
->announce
))
2548 announce
= qe
->announce
;
2549 if (!ast_strlen_zero(announceoverride
))
2550 announce
= announceoverride
;
2552 memi
= ao2_iterator_init(qe
->parent
->members
, 0);
2553 while ((cur
= ao2_iterator_next(&memi
))) {
2554 struct callattempt
*tmp
= ast_calloc(1, sizeof(*tmp
));
2558 ast_mutex_unlock(&qe
->parent
->lock
);
2560 AST_LIST_UNLOCK(&queues
);
2563 tmp
->stillgoing
= -1;
2565 tmp
->oldstatus
= cur
->status
;
2566 tmp
->lastcall
= cur
->lastcall
;
2567 ast_copy_string(tmp
->interface
, cur
->interface
, sizeof(tmp
->interface
));
2568 /* Special case: If we ring everyone, go ahead and ring them, otherwise
2569 just calculate their metric for the appropriate strategy */
2570 if (!calc_metric(qe
->parent
, cur
, x
++, qe
, tmp
)) {
2571 /* Put them in the list of outgoing thingies... We're ready now.
2572 XXX If we're forcibly removed, these outgoing calls won't get
2574 tmp
->q_next
= outgoing
;
2576 /* If this line is up, don't try anybody else */
2577 if (outgoing
->chan
&& (outgoing
->chan
->_state
== AST_STATE_UP
))
2584 if (qe
->expire
&& (!qe
->parent
->timeout
|| (qe
->expire
- now
) <= qe
->parent
->timeout
))
2585 to
= (qe
->expire
- now
) * 1000;
2587 to
= (qe
->parent
->timeout
) ? qe
->parent
->timeout
* 1000 : -1;
2588 ring_one(qe
, outgoing
, &numbusies
);
2589 ast_mutex_unlock(&qe
->parent
->lock
);
2591 AST_LIST_UNLOCK(&queues
);
2592 lpeer
= wait_for_answer(qe
, outgoing
, &to
, &digit
, numbusies
, ast_test_flag(&(bridge_config
.features_caller
), AST_FEATURE_DISCONNECT
), forwardsallowed
);
2593 ast_mutex_lock(&qe
->parent
->lock
);
2594 if (qe
->parent
->strategy
== QUEUE_STRATEGY_RRMEMORY
) {
2595 store_next(qe
, outgoing
);
2597 ast_mutex_unlock(&qe
->parent
->lock
);
2598 peer
= lpeer
? lpeer
->chan
: NULL
;
2601 /* Must gotten hung up */
2604 /* User exited by pressing a digit */
2607 if (option_debug
&& res
== -1)
2608 ast_log(LOG_DEBUG
, "%s: Nobody answered.\n", qe
->chan
->name
);
2609 } else { /* peer is valid */
2610 /* Ah ha! Someone answered within the desired timeframe. Of course after this
2611 we will always return with -1 so that it is hung up properly after the
2614 if (!strcmp(qe
->chan
->tech
->type
, "Zap"))
2615 ast_channel_setoption(qe
->chan
, AST_OPTION_TONE_VERIFY
, &nondataquality
, sizeof(nondataquality
), 0);
2616 if (!strcmp(peer
->tech
->type
, "Zap"))
2617 ast_channel_setoption(peer
, AST_OPTION_TONE_VERIFY
, &nondataquality
, sizeof(nondataquality
), 0);
2618 /* Update parameters for the queue */
2620 recalc_holdtime(qe
, (now
- qe
->start
));
2621 ast_mutex_lock(&qe
->parent
->lock
);
2622 callcompletedinsl
= ((now
- qe
->start
) <= qe
->parent
->servicelevel
);
2623 ast_mutex_unlock(&qe
->parent
->lock
);
2624 member
= lpeer
->member
;
2625 hangupcalls(outgoing
, peer
);
2627 if (announce
|| qe
->parent
->reportholdtime
|| qe
->parent
->memberdelay
) {
2630 res2
= ast_autoservice_start(qe
->chan
);
2632 if (qe
->parent
->memberdelay
) {
2633 ast_log(LOG_NOTICE
, "Delaying member connect for %d seconds\n", qe
->parent
->memberdelay
);
2634 res2
|= ast_safe_sleep(peer
, qe
->parent
->memberdelay
* 1000);
2636 if (!res2
&& announce
) {
2637 play_file(peer
, announce
);
2639 if (!res2
&& qe
->parent
->reportholdtime
) {
2640 if (!play_file(peer
, qe
->parent
->sound_reporthold
)) {
2644 holdtime
= abs((now
- qe
->start
) / 60);
2646 play_file(peer
, qe
->parent
->sound_lessthan
);
2647 ast_say_number(peer
, 2, AST_DIGIT_ANY
, peer
->language
, NULL
);
2649 ast_say_number(peer
, holdtime
, AST_DIGIT_ANY
, peer
->language
, NULL
);
2650 play_file(peer
, qe
->parent
->sound_minutes
);
2654 res2
|= ast_autoservice_stop(qe
->chan
);
2655 if (peer
->_softhangup
) {
2656 /* Agent must have hung up */
2657 ast_log(LOG_WARNING
, "Agent on %s hungup on the customer.\n", peer
->name
);
2658 ast_queue_log(queuename
, qe
->chan
->uniqueid
, member
->membername
, "AGENTDUMP", "%s", "");
2659 record_abandoned(qe
);
2660 if (qe
->parent
->eventwhencalled
)
2661 manager_event(EVENT_FLAG_AGENT
, "AgentDump",
2666 "MemberName: %s\r\n"
2668 queuename
, qe
->chan
->uniqueid
, peer
->name
, member
->interface
, member
->membername
,
2669 qe
->parent
->eventwhencalled
== QUEUE_EVENT_VARIABLES
? vars2manager(qe
->chan
, vars
, sizeof(vars
)) : "");
2673 /* Caller must have hung up just before being connected*/
2674 ast_log(LOG_NOTICE
, "Caller was about to talk to agent on %s but the caller hungup.\n", peer
->name
);
2675 ast_queue_log(queuename
, qe
->chan
->uniqueid
, member
->membername
, "ABANDON", "%d|%d|%ld", qe
->pos
, qe
->opos
, (long)time(NULL
) - qe
->start
);
2676 record_abandoned(qe
);
2681 /* Stop music on hold */
2682 ast_moh_stop(qe
->chan
);
2683 /* If appropriate, log that we have a destination channel */
2685 ast_cdr_setdestchan(qe
->chan
->cdr
, peer
->name
);
2686 /* Make sure channels are compatible */
2687 res
= ast_channel_make_compatible(qe
->chan
, peer
);
2689 ast_queue_log(queuename
, qe
->chan
->uniqueid
, member
->membername
, "SYSCOMPAT", "%s", "");
2690 ast_log(LOG_WARNING
, "Had to drop call because I couldn't make %s compatible with %s\n", qe
->chan
->name
, peer
->name
);
2691 record_abandoned(qe
);
2696 if (qe
->parent
->setinterfacevar
)
2697 pbx_builtin_setvar_helper(qe
->chan
, "MEMBERINTERFACE", member
->interface
);
2699 /* Begin Monitoring */
2700 if (qe
->parent
->monfmt
&& *qe
->parent
->monfmt
) {
2701 if (!qe
->parent
->montype
) {
2703 ast_log(LOG_DEBUG
, "Starting Monitor as requested.\n");
2704 monitorfilename
= pbx_builtin_getvar_helper(qe
->chan
, "MONITOR_FILENAME");
2705 if (pbx_builtin_getvar_helper(qe
->chan
, "MONITOR_EXEC") || pbx_builtin_getvar_helper(qe
->chan
, "MONITOR_EXEC_ARGS"))
2709 if (monitorfilename
)
2710 ast_monitor_start(which
, qe
->parent
->monfmt
, monitorfilename
, 1 );
2711 else if (qe
->chan
->cdr
)
2712 ast_monitor_start(which
, qe
->parent
->monfmt
, qe
->chan
->cdr
->uniqueid
, 1 );
2714 /* Last ditch effort -- no CDR, make up something */
2715 snprintf(tmpid
, sizeof(tmpid
), "chan-%lx", ast_random());
2716 ast_monitor_start(which
, qe
->parent
->monfmt
, tmpid
, 1 );
2718 if (qe
->parent
->monjoin
)
2719 ast_monitor_setjoinfiles(which
, 1);
2722 ast_log(LOG_DEBUG
, "Starting MixMonitor as requested.\n");
2723 monitorfilename
= pbx_builtin_getvar_helper(qe
->chan
, "MONITOR_FILENAME");
2724 if (!monitorfilename
) {
2726 ast_copy_string(tmpid
, qe
->chan
->cdr
->uniqueid
, sizeof(tmpid
)-1);
2728 snprintf(tmpid
, sizeof(tmpid
), "chan-%lx", ast_random());
2730 ast_copy_string(tmpid2
, monitorfilename
, sizeof(tmpid2
)-1);
2731 for (p
= tmpid2
; *p
; p
++) {
2732 if (*p
== '^' && *(p
+1) == '{') {
2737 memset(tmpid
, 0, sizeof(tmpid
));
2738 pbx_substitute_variables_helper(qe
->chan
, tmpid2
, tmpid
, sizeof(tmpid
) - 1);
2741 monitor_exec
= pbx_builtin_getvar_helper(qe
->chan
, "MONITOR_EXEC");
2742 monitor_options
= pbx_builtin_getvar_helper(qe
->chan
, "MONITOR_OPTIONS");
2745 ast_copy_string(meid2
, monitor_exec
, sizeof(meid2
)-1);
2746 for (p
= meid2
; *p
; p
++) {
2747 if (*p
== '^' && *(p
+1) == '{') {
2752 memset(meid
, 0, sizeof(meid
));
2753 pbx_substitute_variables_helper(qe
->chan
, meid2
, meid
, sizeof(meid
) - 1);
2756 snprintf(tmpid2
, sizeof(tmpid2
)-1, "%s.%s", tmpid
, qe
->parent
->monfmt
);
2758 mixmonapp
= pbx_findapp("MixMonitor");
2760 if (strchr(tmpid2
, '|')) {
2761 ast_log(LOG_WARNING
, "monitor-format (in queues.conf) and MONITOR_FILENAME cannot contain a '|'! Not recording.\n");
2765 if (!monitor_options
)
2766 monitor_options
= "";
2768 if (strchr(monitor_options
, '|')) {
2769 ast_log(LOG_WARNING
, "MONITOR_OPTIONS cannot contain a '|'! Not recording.\n");
2774 if (!ast_strlen_zero(monitor_exec
))
2775 snprintf(mixmonargs
, sizeof(mixmonargs
)-1, "%s|b%s|%s", tmpid2
, monitor_options
, monitor_exec
);
2777 snprintf(mixmonargs
, sizeof(mixmonargs
)-1, "%s|b%s", tmpid2
, monitor_options
);
2780 ast_log(LOG_DEBUG
, "Arguments being passed to MixMonitor: %s\n", mixmonargs
);
2781 /* We purposely lock the CDR so that pbx_exec does not update the application data */
2783 ast_set_flag(qe
->chan
->cdr
, AST_CDR_FLAG_LOCKED
);
2784 ret
= pbx_exec(qe
->chan
, mixmonapp
, mixmonargs
);
2786 ast_clear_flag(qe
->chan
->cdr
, AST_CDR_FLAG_LOCKED
);
2789 ast_log(LOG_WARNING
, "Asked to run MixMonitor on this call, but cannot find the MixMonitor app!\n");
2793 /* Drop out of the queue at this point, to prepare for next caller */
2795 if (!ast_strlen_zero(url
) && ast_channel_supports_html(peer
)) {
2797 ast_log(LOG_DEBUG
, "app_queue: sendurl=%s.\n", url
);
2798 ast_channel_sendurl(peer
, url
);
2800 if (!ast_strlen_zero(agi
)) {
2802 ast_log(LOG_DEBUG
, "app_queue: agi=%s.\n", agi
);
2803 app
= pbx_findapp("agi");
2805 agiexec
= ast_strdupa(agi
);
2806 ret
= pbx_exec(qe
->chan
, app
, agiexec
);
2808 ast_log(LOG_WARNING
, "Asked to execute an AGI on this channel, but could not find application (agi)!\n");
2810 ast_queue_log(queuename
, qe
->chan
->uniqueid
, member
->membername
, "CONNECT", "%ld|%s", (long)time(NULL
) - qe
->start
, peer
->uniqueid
);
2811 if (qe
->parent
->eventwhencalled
)
2812 manager_event(EVENT_FLAG_AGENT
, "AgentConnect",
2817 "MemberName: %s\r\n"
2819 "BridgedChannel: %s\r\n"
2821 queuename
, qe
->chan
->uniqueid
, peer
->name
, member
->interface
, member
->membername
,
2822 (long)time(NULL
) - qe
->start
, peer
->uniqueid
,
2823 qe
->parent
->eventwhencalled
== QUEUE_EVENT_VARIABLES
? vars2manager(qe
->chan
, vars
, sizeof(vars
)) : "");
2824 ast_copy_string(oldcontext
, qe
->chan
->context
, sizeof(oldcontext
));
2825 ast_copy_string(oldexten
, qe
->chan
->exten
, sizeof(oldexten
));
2828 if (member
->status
== AST_DEVICE_NOT_INUSE
)
2829 ast_log(LOG_WARNING
, "The device state of this queue member, %s, is still 'Not in Use' when it probably should not be! Please check UPGRADE.txt for correct configuration settings.\n", member
->membername
);
2832 bridge
= ast_bridge_call(qe
->chan
,peer
, &bridge_config
);
2834 if (strcasecmp(oldcontext
, qe
->chan
->context
) || strcasecmp(oldexten
, qe
->chan
->exten
)) {
2835 ast_queue_log(queuename
, qe
->chan
->uniqueid
, member
->membername
, "TRANSFER", "%s|%s|%ld|%ld",
2836 qe
->chan
->exten
, qe
->chan
->context
, (long) (callstart
- qe
->start
),
2837 (long) (time(NULL
) - callstart
));
2838 } else if (qe
->chan
->_softhangup
) {
2839 ast_queue_log(queuename
, qe
->chan
->uniqueid
, member
->membername
, "COMPLETECALLER", "%ld|%ld|%d",
2840 (long) (callstart
- qe
->start
), (long) (time(NULL
) - callstart
), qe
->opos
);
2841 if (qe
->parent
->eventwhencalled
)
2842 manager_event(EVENT_FLAG_AGENT
, "AgentComplete",
2847 "MemberName: %s\r\n"
2850 "Reason: caller\r\n"
2852 queuename
, qe
->chan
->uniqueid
, peer
->name
, member
->interface
, member
->membername
,
2853 (long)(callstart
- qe
->start
), (long)(time(NULL
) - callstart
),
2854 qe
->parent
->eventwhencalled
== QUEUE_EVENT_VARIABLES
? vars2manager(qe
->chan
, vars
, sizeof(vars
)) : "");
2856 ast_queue_log(queuename
, qe
->chan
->uniqueid
, member
->membername
, "COMPLETEAGENT", "%ld|%ld|%d",
2857 (long) (callstart
- qe
->start
), (long) (time(NULL
) - callstart
), qe
->opos
);
2858 if (qe
->parent
->eventwhencalled
)
2859 manager_event(EVENT_FLAG_AGENT
, "AgentComplete",
2863 "MemberName: %s\r\n"
2868 queuename
, qe
->chan
->uniqueid
, peer
->name
, member
->membername
, (long)(callstart
- qe
->start
),
2869 (long)(time(NULL
) - callstart
),
2870 qe
->parent
->eventwhencalled
== QUEUE_EVENT_VARIABLES
? vars2manager(qe
->chan
, vars
, sizeof(vars
)) : "");
2873 if (bridge
!= AST_PBX_NO_HANGUP_PEER
)
2875 update_queue(qe
->parent
, member
, callcompletedinsl
);
2876 res
= bridge
? bridge
: 1;
2879 hangupcalls(outgoing
, NULL
);
2884 static int wait_a_bit(struct queue_ent
*qe
)
2886 /* Don't need to hold the lock while we setup the outgoing calls */
2887 int retrywait
= qe
->parent
->retry
* 1000;
2889 int res
= ast_waitfordigit(qe
->chan
, retrywait
);
2890 if (res
> 0 && !valid_exit(qe
, res
))
2896 static struct member
*interface_exists(struct call_queue
*q
, const char *interface
)
2899 struct ao2_iterator mem_iter
;
2904 mem_iter
= ao2_iterator_init(q
->members
, 0);
2905 while ((mem
= ao2_iterator_next(&mem_iter
))) {
2906 if (!strcasecmp(interface
, mem
->interface
))
2915 /* Dump all members in a specific queue to the database
2917 * <pm_family>/<queuename> = <interface>;<penalty>;<paused>[|...]
2920 static void dump_queue_members(struct call_queue
*pm_queue
)
2922 struct member
*cur_member
;
2923 char value
[PM_MAX_LEN
];
2926 struct ao2_iterator mem_iter
;
2928 memset(value
, 0, sizeof(value
));
2933 mem_iter
= ao2_iterator_init(pm_queue
->members
, 0);
2934 while ((cur_member
= ao2_iterator_next(&mem_iter
))) {
2935 if (!cur_member
->dynamic
) {
2936 ao2_ref(cur_member
, -1);
2940 res
= snprintf(value
+ value_len
, sizeof(value
) - value_len
, "%s%s;%d;%d;%s",
2941 value_len
? "|" : "", cur_member
->interface
, cur_member
->penalty
, cur_member
->paused
, cur_member
->membername
);
2943 ao2_ref(cur_member
, -1);
2945 if (res
!= strlen(value
+ value_len
)) {
2946 ast_log(LOG_WARNING
, "Could not create persistent member string, out of space\n");
2952 if (value_len
&& !cur_member
) {
2953 if (ast_db_put(pm_family
, pm_queue
->name
, value
))
2954 ast_log(LOG_WARNING
, "failed to create persistent dynamic entry!\n");
2956 /* Delete the entry if the queue is empty or there is an error */
2957 ast_db_del(pm_family
, pm_queue
->name
);
2960 static int remove_from_queue(const char *queuename
, const char *interface
)
2962 struct call_queue
*q
;
2963 struct member
*mem
, tmpmem
;
2964 int res
= RES_NOSUCHQUEUE
;
2966 ast_copy_string(tmpmem
.interface
, interface
, sizeof(tmpmem
.interface
));
2968 AST_LIST_LOCK(&queues
);
2969 AST_LIST_TRAVERSE(&queues
, q
, list
) {
2970 ast_mutex_lock(&q
->lock
);
2971 if (strcmp(q
->name
, queuename
)) {
2972 ast_mutex_unlock(&q
->lock
);
2976 if ((mem
= ao2_find(q
->members
, &tmpmem
, OBJ_POINTER
))) {
2977 /* XXX future changes should beware of this assumption!! */
2979 res
= RES_NOT_DYNAMIC
;
2981 ast_mutex_unlock(&q
->lock
);
2985 manager_event(EVENT_FLAG_AGENT
, "QueueMemberRemoved",
2988 "MemberName: %s\r\n",
2989 q
->name
, mem
->interface
, mem
->membername
);
2990 ao2_unlink(q
->members
, mem
);
2993 if (queue_persistent_members
)
2994 dump_queue_members(q
);
3000 ast_mutex_unlock(&q
->lock
);
3004 if (res
== RES_OKAY
)
3005 remove_from_interfaces(interface
);
3007 AST_LIST_UNLOCK(&queues
);
3013 static int add_to_queue(const char *queuename
, const char *interface
, const char *membername
, int penalty
, int paused
, int dump
)
3015 struct call_queue
*q
;
3016 struct member
*new_member
, *old_member
;
3017 int res
= RES_NOSUCHQUEUE
;
3019 /* \note Ensure the appropriate realtime queue is loaded. Note that this
3020 * short-circuits if the queue is already in memory. */
3021 if (!(q
= load_realtime_queue(queuename
)))
3024 AST_LIST_LOCK(&queues
);
3026 ast_mutex_lock(&q
->lock
);
3027 if ((old_member
= interface_exists(q
, interface
)) == NULL
) {
3028 add_to_interfaces(interface
);
3029 if ((new_member
= create_queue_member(interface
, membername
, penalty
, paused
))) {
3030 new_member
->dynamic
= 1;
3031 ao2_link(q
->members
, new_member
);
3033 manager_event(EVENT_FLAG_AGENT
, "QueueMemberAdded",
3036 "MemberName: %s\r\n"
3037 "Membership: %s\r\n"
3039 "CallsTaken: %d\r\n"
3043 q
->name
, new_member
->interface
, new_member
->membername
,
3045 new_member
->penalty
, new_member
->calls
, (int) new_member
->lastcall
,
3046 new_member
->status
, new_member
->paused
);
3049 dump_queue_members(q
);
3053 res
= RES_OUTOFMEMORY
;
3056 ao2_ref(old_member
, -1);
3059 ast_mutex_unlock(&q
->lock
);
3060 AST_LIST_UNLOCK(&queues
);
3065 static int set_member_paused(const char *queuename
, const char *interface
, int paused
)
3068 struct call_queue
*q
;
3071 /* Special event for when all queues are paused - individual events still generated */
3072 /* XXX In all other cases, we use the membername, but since this affects all queues, we cannot */
3073 if (ast_strlen_zero(queuename
))
3074 ast_queue_log("NONE", "NONE", interface
, (paused
? "PAUSEALL" : "UNPAUSEALL"), "%s", "");
3076 AST_LIST_LOCK(&queues
);
3077 AST_LIST_TRAVERSE(&queues
, q
, list
) {
3078 ast_mutex_lock(&q
->lock
);
3079 if (ast_strlen_zero(queuename
) || !strcasecmp(q
->name
, queuename
)) {
3080 if ((mem
= interface_exists(q
, interface
))) {
3082 if (mem
->paused
== paused
)
3083 ast_log(LOG_DEBUG
, "%spausing already-%spaused queue member %s:%s\n", (paused
? "" : "un"), (paused
? "" : "un"), q
->name
, interface
);
3084 mem
->paused
= paused
;
3086 if (queue_persistent_members
)
3087 dump_queue_members(q
);
3090 update_realtime_member_field(mem
, q
->name
, "paused", paused
? "1" : "0");
3092 ast_queue_log(q
->name
, "NONE", mem
->membername
, (paused
? "PAUSE" : "UNPAUSE"), "%s", "");
3094 manager_event(EVENT_FLAG_AGENT
, "QueueMemberPaused",
3097 "MemberName: %s\r\n"
3099 q
->name
, mem
->interface
, mem
->membername
, paused
);
3103 ast_mutex_unlock(&q
->lock
);
3105 AST_LIST_UNLOCK(&queues
);
3107 return found
? RESULT_SUCCESS
: RESULT_FAILURE
;
3110 /* Reload dynamic queue members persisted into the astdb */
3111 static void reload_queue_members(void)
3117 char *membername
= NULL
;
3122 struct ast_db_entry
*db_tree
;
3123 struct ast_db_entry
*entry
;
3124 struct call_queue
*cur_queue
;
3125 char queue_data
[PM_MAX_LEN
];
3127 AST_LIST_LOCK(&queues
);
3129 /* Each key in 'pm_family' is the name of a queue */
3130 db_tree
= ast_db_gettree(pm_family
, NULL
);
3131 for (entry
= db_tree
; entry
; entry
= entry
->next
) {
3133 queue_name
= entry
->key
+ strlen(pm_family
) + 2;
3135 AST_LIST_TRAVERSE(&queues
, cur_queue
, list
) {
3136 ast_mutex_lock(&cur_queue
->lock
);
3137 if (!strcmp(queue_name
, cur_queue
->name
))
3139 ast_mutex_unlock(&cur_queue
->lock
);
3143 cur_queue
= load_realtime_queue(queue_name
);
3146 /* If the queue no longer exists, remove it from the
3148 ast_log(LOG_WARNING
, "Error loading persistent queue: '%s': it does not exist\n", queue_name
);
3149 ast_db_del(pm_family
, queue_name
);
3152 ast_mutex_unlock(&cur_queue
->lock
);
3154 if (ast_db_get(pm_family
, queue_name
, queue_data
, PM_MAX_LEN
))
3157 cur_ptr
= queue_data
;
3158 while ((member
= strsep(&cur_ptr
, "|"))) {
3159 if (ast_strlen_zero(member
))
3162 interface
= strsep(&member
, ";");
3163 penalty_tok
= strsep(&member
, ";");
3164 paused_tok
= strsep(&member
, ";");
3165 membername
= strsep(&member
, ";");
3168 ast_log(LOG_WARNING
, "Error parsing persistent member string for '%s' (penalty)\n", queue_name
);
3171 penalty
= strtol(penalty_tok
, NULL
, 10);
3172 if (errno
== ERANGE
) {
3173 ast_log(LOG_WARNING
, "Error converting penalty: %s: Out of range.\n", penalty_tok
);
3178 ast_log(LOG_WARNING
, "Error parsing persistent member string for '%s' (paused)\n", queue_name
);
3181 paused
= strtol(paused_tok
, NULL
, 10);
3182 if ((errno
== ERANGE
) || paused
< 0 || paused
> 1) {
3183 ast_log(LOG_WARNING
, "Error converting paused: %s: Expected 0 or 1.\n", paused_tok
);
3186 if (ast_strlen_zero(membername
))
3187 membername
= interface
;
3190 ast_log(LOG_DEBUG
, "Reload Members: Queue: %s Member: %s Name: %s Penalty: %d Paused: %d\n", queue_name
, interface
, membername
, penalty
, paused
);
3192 if (add_to_queue(queue_name
, interface
, membername
, penalty
, paused
, 0) == RES_OUTOFMEMORY
) {
3193 ast_log(LOG_ERROR
, "Out of Memory when reloading persistent queue member\n");
3199 AST_LIST_UNLOCK(&queues
);
3201 ast_log(LOG_NOTICE
, "Queue members successfully reloaded from database.\n");
3202 ast_db_freetree(db_tree
);
3206 static int pqm_exec(struct ast_channel
*chan
, void *data
)
3208 struct ast_module_user
*lu
;
3210 int priority_jump
= 0;
3211 AST_DECLARE_APP_ARGS(args
,
3212 AST_APP_ARG(queuename
);
3213 AST_APP_ARG(interface
);
3214 AST_APP_ARG(options
);
3217 if (ast_strlen_zero(data
)) {
3218 ast_log(LOG_WARNING
, "PauseQueueMember requires an argument ([queuename]|interface[|options])\n");
3222 parse
= ast_strdupa(data
);
3224 AST_STANDARD_APP_ARGS(args
, parse
);
3226 lu
= ast_module_user_add(chan
);
3229 if (strchr(args
.options
, 'j'))
3233 if (ast_strlen_zero(args
.interface
)) {
3234 ast_log(LOG_WARNING
, "Missing interface argument to PauseQueueMember ([queuename]|interface[|options])\n");
3235 ast_module_user_remove(lu
);
3239 if (set_member_paused(args
.queuename
, args
.interface
, 1)) {
3240 ast_log(LOG_WARNING
, "Attempt to pause interface %s, not found\n", args
.interface
);
3241 if (priority_jump
|| ast_opt_priority_jumping
) {
3242 if (ast_goto_if_exists(chan
, chan
->context
, chan
->exten
, chan
->priority
+ 101)) {
3243 pbx_builtin_setvar_helper(chan
, "PQMSTATUS", "NOTFOUND");
3244 ast_module_user_remove(lu
);
3248 ast_module_user_remove(lu
);
3249 pbx_builtin_setvar_helper(chan
, "PQMSTATUS", "NOTFOUND");
3253 ast_module_user_remove(lu
);
3254 pbx_builtin_setvar_helper(chan
, "PQMSTATUS", "PAUSED");
3259 static int upqm_exec(struct ast_channel
*chan
, void *data
)
3261 struct ast_module_user
*lu
;
3263 int priority_jump
= 0;
3264 AST_DECLARE_APP_ARGS(args
,
3265 AST_APP_ARG(queuename
);
3266 AST_APP_ARG(interface
);
3267 AST_APP_ARG(options
);
3270 if (ast_strlen_zero(data
)) {
3271 ast_log(LOG_WARNING
, "UnpauseQueueMember requires an argument ([queuename]|interface[|options])\n");
3275 parse
= ast_strdupa(data
);
3277 AST_STANDARD_APP_ARGS(args
, parse
);
3279 lu
= ast_module_user_add(chan
);
3282 if (strchr(args
.options
, 'j'))
3286 if (ast_strlen_zero(args
.interface
)) {
3287 ast_log(LOG_WARNING
, "Missing interface argument to PauseQueueMember ([queuename]|interface[|options])\n");
3288 ast_module_user_remove(lu
);
3292 if (set_member_paused(args
.queuename
, args
.interface
, 0)) {
3293 ast_log(LOG_WARNING
, "Attempt to unpause interface %s, not found\n", args
.interface
);
3294 if (priority_jump
|| ast_opt_priority_jumping
) {
3295 if (ast_goto_if_exists(chan
, chan
->context
, chan
->exten
, chan
->priority
+ 101)) {
3296 pbx_builtin_setvar_helper(chan
, "UPQMSTATUS", "NOTFOUND");
3297 ast_module_user_remove(lu
);
3301 ast_module_user_remove(lu
);
3302 pbx_builtin_setvar_helper(chan
, "UPQMSTATUS", "NOTFOUND");
3306 ast_module_user_remove(lu
);
3307 pbx_builtin_setvar_helper(chan
, "UPQMSTATUS", "UNPAUSED");
3312 static int rqm_exec(struct ast_channel
*chan
, void *data
)
3315 struct ast_module_user
*lu
;
3316 char *parse
, *temppos
= NULL
;
3317 int priority_jump
= 0;
3318 AST_DECLARE_APP_ARGS(args
,
3319 AST_APP_ARG(queuename
);
3320 AST_APP_ARG(interface
);
3321 AST_APP_ARG(options
);
3325 if (ast_strlen_zero(data
)) {
3326 ast_log(LOG_WARNING
, "RemoveQueueMember requires an argument (queuename[|interface[|options]])\n");
3330 parse
= ast_strdupa(data
);
3332 AST_STANDARD_APP_ARGS(args
, parse
);
3334 lu
= ast_module_user_add(chan
);
3336 if (ast_strlen_zero(args
.interface
)) {
3337 args
.interface
= ast_strdupa(chan
->name
);
3338 temppos
= strrchr(args
.interface
, '-');
3344 if (strchr(args
.options
, 'j'))
3348 switch (remove_from_queue(args
.queuename
, args
.interface
)) {
3350 ast_queue_log(args
.queuename
, chan
->uniqueid
, args
.interface
, "REMOVEMEMBER", "%s", "");
3351 ast_log(LOG_NOTICE
, "Removed interface '%s' from queue '%s'\n", args
.interface
, args
.queuename
);
3352 pbx_builtin_setvar_helper(chan
, "RQMSTATUS", "REMOVED");
3356 ast_log(LOG_DEBUG
, "Unable to remove interface '%s' from queue '%s': Not there\n", args
.interface
, args
.queuename
);
3357 if (priority_jump
|| ast_opt_priority_jumping
)
3358 ast_goto_if_exists(chan
, chan
->context
, chan
->exten
, chan
->priority
+ 101);
3359 pbx_builtin_setvar_helper(chan
, "RQMSTATUS", "NOTINQUEUE");
3362 case RES_NOSUCHQUEUE
:
3363 ast_log(LOG_WARNING
, "Unable to remove interface from queue '%s': No such queue\n", args
.queuename
);
3364 pbx_builtin_setvar_helper(chan
, "RQMSTATUS", "NOSUCHQUEUE");
3367 case RES_NOT_DYNAMIC
:
3368 ast_log(LOG_WARNING
, "Unable to remove interface from queue '%s': '%s' is not a dynamic member\n", args
.queuename
, args
.interface
);
3369 pbx_builtin_setvar_helper(chan
, "RQMSTATUS", "NOTDYNAMIC");
3374 ast_module_user_remove(lu
);
3379 static int aqm_exec(struct ast_channel
*chan
, void *data
)
3382 struct ast_module_user
*lu
;
3383 char *parse
, *temppos
= NULL
;
3384 int priority_jump
= 0;
3385 AST_DECLARE_APP_ARGS(args
,
3386 AST_APP_ARG(queuename
);
3387 AST_APP_ARG(interface
);
3388 AST_APP_ARG(penalty
);
3389 AST_APP_ARG(options
);
3390 AST_APP_ARG(membername
);
3394 if (ast_strlen_zero(data
)) {
3395 ast_log(LOG_WARNING
, "AddQueueMember requires an argument (queuename[|[interface]|[penalty][|options][|membername]])\n");
3399 parse
= ast_strdupa(data
);
3401 AST_STANDARD_APP_ARGS(args
, parse
);
3403 lu
= ast_module_user_add(chan
);
3405 if (ast_strlen_zero(args
.interface
)) {
3406 args
.interface
= ast_strdupa(chan
->name
);
3407 temppos
= strrchr(args
.interface
, '-');
3412 if (!ast_strlen_zero(args
.penalty
)) {
3413 if ((sscanf(args
.penalty
, "%d", &penalty
) != 1) || penalty
< 0) {
3414 ast_log(LOG_WARNING
, "Penalty '%s' is invalid, must be an integer >= 0\n", args
.penalty
);
3420 if (strchr(args
.options
, 'j'))
3424 switch (add_to_queue(args
.queuename
, args
.interface
, args
.membername
, penalty
, 0, queue_persistent_members
)) {
3426 ast_queue_log(args
.queuename
, chan
->uniqueid
, args
.interface
, "ADDMEMBER", "%s", "");
3427 ast_log(LOG_NOTICE
, "Added interface '%s' to queue '%s'\n", args
.interface
, args
.queuename
);
3428 pbx_builtin_setvar_helper(chan
, "AQMSTATUS", "ADDED");
3432 ast_log(LOG_WARNING
, "Unable to add interface '%s' to queue '%s': Already there\n", args
.interface
, args
.queuename
);
3433 if (priority_jump
|| ast_opt_priority_jumping
)
3434 ast_goto_if_exists(chan
, chan
->context
, chan
->exten
, chan
->priority
+ 101);
3435 pbx_builtin_setvar_helper(chan
, "AQMSTATUS", "MEMBERALREADY");
3438 case RES_NOSUCHQUEUE
:
3439 ast_log(LOG_WARNING
, "Unable to add interface to queue '%s': No such queue\n", args
.queuename
);
3440 pbx_builtin_setvar_helper(chan
, "AQMSTATUS", "NOSUCHQUEUE");
3443 case RES_OUTOFMEMORY
:
3444 ast_log(LOG_ERROR
, "Out of memory adding member %s to queue %s\n", args
.interface
, args
.queuename
);
3448 ast_module_user_remove(lu
);
3453 static int ql_exec(struct ast_channel
*chan
, void *data
)
3455 struct ast_module_user
*u
;
3458 AST_DECLARE_APP_ARGS(args
,
3459 AST_APP_ARG(queuename
);
3460 AST_APP_ARG(uniqueid
);
3461 AST_APP_ARG(membername
);
3463 AST_APP_ARG(params
);
3466 if (ast_strlen_zero(data
)) {
3467 ast_log(LOG_WARNING
, "QueueLog requires arguments (queuename|uniqueid|membername|event[|additionalinfo]\n");
3471 u
= ast_module_user_add(chan
);
3473 parse
= ast_strdupa(data
);
3475 AST_STANDARD_APP_ARGS(args
, parse
);
3477 if (ast_strlen_zero(args
.queuename
) || ast_strlen_zero(args
.uniqueid
)
3478 || ast_strlen_zero(args
.membername
) || ast_strlen_zero(args
.event
)) {
3479 ast_log(LOG_WARNING
, "QueueLog requires arguments (queuename|uniqueid|membername|event[|additionalinfo])\n");
3480 ast_module_user_remove(u
);
3484 ast_queue_log(args
.queuename
, args
.uniqueid
, args
.membername
, args
.event
,
3485 "%s", args
.params
? args
.params
: "");
3487 ast_module_user_remove(u
);
3492 static int queue_exec(struct ast_channel
*chan
, void *data
)
3496 struct ast_module_user
*lu
;
3497 const char *user_priority
;
3498 const char *max_penalty_str
;
3501 enum queue_result reason
= QUEUE_UNKNOWN
;
3502 /* whether to exit Queue application after the timeout hits */
3506 AST_DECLARE_APP_ARGS(args
,
3507 AST_APP_ARG(queuename
);
3508 AST_APP_ARG(options
);
3510 AST_APP_ARG(announceoverride
);
3511 AST_APP_ARG(queuetimeoutstr
);
3514 /* Our queue entry */
3515 struct queue_ent qe
;
3517 if (ast_strlen_zero(data
)) {
3518 ast_log(LOG_WARNING
, "Queue requires an argument: queuename[|options[|URL[|announceoverride[|timeout[|agi]]]]]\n");
3522 parse
= ast_strdupa(data
);
3523 AST_STANDARD_APP_ARGS(args
, parse
);
3525 lu
= ast_module_user_add(chan
);
3527 /* Setup our queue entry */
3528 memset(&qe
, 0, sizeof(qe
));
3529 qe
.start
= time(NULL
);
3531 /* set the expire time based on the supplied timeout; */
3532 if (args
.queuetimeoutstr
)
3533 qe
.expire
= qe
.start
+ atoi(args
.queuetimeoutstr
);
3537 /* Get the priority from the variable ${QUEUE_PRIO} */
3538 user_priority
= pbx_builtin_getvar_helper(chan
, "QUEUE_PRIO");
3539 if (user_priority
) {
3540 if (sscanf(user_priority
, "%d", &prio
) == 1) {
3542 ast_log(LOG_DEBUG
, "%s: Got priority %d from ${QUEUE_PRIO}.\n",
3545 ast_log(LOG_WARNING
, "${QUEUE_PRIO}: Invalid value (%s), channel %s.\n",
3546 user_priority
, chan
->name
);
3550 if (option_debug
> 2)
3551 ast_log(LOG_DEBUG
, "NO QUEUE_PRIO variable found. Using default.\n");
3555 /* Get the maximum penalty from the variable ${QUEUE_MAX_PENALTY} */
3556 if ((max_penalty_str
= pbx_builtin_getvar_helper(chan
, "QUEUE_MAX_PENALTY"))) {
3557 if (sscanf(max_penalty_str
, "%d", &max_penalty
) == 1) {
3559 ast_log(LOG_DEBUG
, "%s: Got max penalty %d from ${QUEUE_MAX_PENALTY}.\n",
3560 chan
->name
, max_penalty
);
3562 ast_log(LOG_WARNING
, "${QUEUE_MAX_PENALTY}: Invalid value (%s), channel %s.\n",
3563 max_penalty_str
, chan
->name
);
3570 if (args
.options
&& (strchr(args
.options
, 'r')))
3574 ast_log(LOG_DEBUG
, "queue: %s, options: %s, url: %s, announce: %s, expires: %ld, priority: %d\n",
3575 args
.queuename
, args
.options
, args
.url
, args
.announceoverride
, (long)qe
.expire
, prio
);
3579 qe
.max_penalty
= max_penalty
;
3580 qe
.last_pos_said
= 0;
3582 qe
.last_periodic_announce_time
= time(NULL
);
3583 qe
.last_periodic_announce_sound
= 0;
3584 qe
.valid_digits
= 0;
3585 if (!join_queue(args
.queuename
, &qe
, &reason
)) {
3586 int makeannouncement
= 0;
3588 ast_queue_log(args
.queuename
, chan
->uniqueid
, "NONE", "ENTERQUEUE", "%s|%s", S_OR(args
.url
, ""),
3589 S_OR(chan
->cid
.cid_num
, ""));
3592 ast_indicate(chan
, AST_CONTROL_RINGING
);
3594 ast_moh_start(chan
, qe
.moh
, NULL
);
3597 /* This is the wait loop for callers 2 through maxlen */
3598 res
= wait_our_turn(&qe
, ringing
, &reason
);
3603 /* This is the wait loop for the head caller*/
3604 /* To exit, they may get their call answered; */
3605 /* they may dial a digit from the queue context; */
3606 /* or, they may timeout. */
3608 enum queue_member_status stat
;
3610 /* Leave if we have exceeded our queuetimeout */
3611 if (qe
.expire
&& (time(NULL
) > qe
.expire
)) {
3612 record_abandoned(&qe
);
3613 reason
= QUEUE_TIMEOUT
;
3615 ast_queue_log(args
.queuename
, chan
->uniqueid
, "NONE", "EXITWITHTIMEOUT", "%d", qe
.pos
);
3619 if (makeannouncement
) {
3620 /* Make a position announcement, if enabled */
3621 if (qe
.parent
->announcefrequency
&& !ringing
)
3622 if ((res
= say_position(&qe
)))
3626 makeannouncement
= 1;
3628 /* Make a periodic announcement, if enabled */
3629 if (qe
.parent
->periodicannouncefrequency
&& !ringing
)
3630 if ((res
= say_periodic_announcement(&qe
)))
3633 /* Try calling all queue members for 'timeout' seconds */
3634 res
= try_calling(&qe
, args
.options
, args
.announceoverride
, args
.url
, &tries
, &noption
, args
.agi
);
3638 stat
= get_member_status(qe
.parent
, qe
.max_penalty
);
3640 /* exit after 'timeout' cycle if 'n' option enabled */
3641 if (noption
&& tries
>= qe
.parent
->membercount
) {
3642 if (option_verbose
> 2)
3643 ast_verbose(VERBOSE_PREFIX_3
"Exiting on time-out cycle\n");
3644 ast_queue_log(args
.queuename
, chan
->uniqueid
, "NONE", "EXITWITHTIMEOUT", "%d", qe
.pos
);
3645 record_abandoned(&qe
);
3646 reason
= QUEUE_TIMEOUT
;
3651 /* leave the queue if no agents, if enabled */
3652 if (qe
.parent
->leavewhenempty
&& (stat
== QUEUE_NO_MEMBERS
)) {
3653 record_abandoned(&qe
);
3654 reason
= QUEUE_LEAVEEMPTY
;
3655 ast_queue_log(args
.queuename
, chan
->uniqueid
, "NONE", "EXITEMPTY", "%d|%d|%ld", qe
.pos
, qe
.opos
, (long)(time(NULL
) - qe
.start
));
3660 /* leave the queue if no reachable agents, if enabled */
3661 if ((qe
.parent
->leavewhenempty
== QUEUE_EMPTY_STRICT
) && (stat
== QUEUE_NO_REACHABLE_MEMBERS
)) {
3662 record_abandoned(&qe
);
3663 reason
= QUEUE_LEAVEUNAVAIL
;
3664 ast_queue_log(args
.queuename
, chan
->uniqueid
, "NONE", "EXITEMPTY", "%d|%d|%ld", qe
.pos
, qe
.opos
, (long)(time(NULL
) - qe
.start
));
3669 /* Leave if we have exceeded our queuetimeout */
3670 if (qe
.expire
&& (time(NULL
) > qe
.expire
)) {
3671 record_abandoned(&qe
);
3672 reason
= QUEUE_TIMEOUT
;
3674 ast_queue_log(args
.queuename
, chan
->uniqueid
, "NONE", "EXITWITHTIMEOUT", "%d", qe
.pos
);
3678 /* If using dynamic realtime members, we should regenerate the member list for this queue */
3679 update_realtime_members(qe
.parent
);
3681 /* OK, we didn't get anybody; wait for 'retry' seconds; may get a digit to exit with */
3682 res
= wait_a_bit(&qe
);
3687 /* Since this is a priority queue and
3688 * it is not sure that we are still at the head
3689 * of the queue, go and check for our turn again.
3691 if (!is_our_turn(&qe
)) {
3693 ast_log(LOG_DEBUG
, "Darn priorities, going back in queue (%s)!\n",
3703 record_abandoned(&qe
);
3704 ast_queue_log(args
.queuename
, chan
->uniqueid
, "NONE", "ABANDON",
3705 "%d|%d|%ld", qe
.pos
, qe
.opos
,
3706 (long) time(NULL
) - qe
.start
);
3709 } else if (qe
.valid_digits
) {
3710 ast_queue_log(args
.queuename
, chan
->uniqueid
, "NONE", "EXITWITHKEY",
3711 "%s|%d", qe
.digits
, qe
.pos
);
3715 /* Don't allow return code > 0 */
3716 if (res
>= 0 && res
!= AST_PBX_KEEPALIVE
) {
3719 ast_indicate(chan
, -1);
3723 ast_stopstream(chan
);
3726 if (reason
!= QUEUE_UNKNOWN
)
3727 set_queue_result(chan
, reason
);
3729 ast_log(LOG_WARNING
, "Unable to join queue '%s'\n", args
.queuename
);
3730 set_queue_result(chan
, reason
);
3733 ast_module_user_remove(lu
);
3738 static int queue_function_qac(struct ast_channel
*chan
, char *cmd
, char *data
, char *buf
, size_t len
)
3741 struct call_queue
*q
;
3742 struct ast_module_user
*lu
;
3744 struct ao2_iterator mem_iter
;
3748 if (ast_strlen_zero(data
)) {
3749 ast_log(LOG_ERROR
, "%s requires an argument: queuename\n", cmd
);
3753 lu
= ast_module_user_add(chan
);
3755 AST_LIST_LOCK(&queues
);
3756 AST_LIST_TRAVERSE(&queues
, q
, list
) {
3757 if (!strcasecmp(q
->name
, data
)) {
3758 ast_mutex_lock(&q
->lock
);
3762 AST_LIST_UNLOCK(&queues
);
3765 mem_iter
= ao2_iterator_init(q
->members
, 0);
3766 while ((m
= ao2_iterator_next(&mem_iter
))) {
3767 /* Count the agents who are logged in and presently answering calls */
3768 if ((m
->status
!= AST_DEVICE_UNAVAILABLE
) && (m
->status
!= AST_DEVICE_INVALID
)) {
3773 ast_mutex_unlock(&q
->lock
);
3775 ast_log(LOG_WARNING
, "queue %s was not found\n", data
);
3777 snprintf(buf
, len
, "%d", count
);
3778 ast_module_user_remove(lu
);
3783 static int queue_function_queuewaitingcount(struct ast_channel
*chan
, char *cmd
, char *data
, char *buf
, size_t len
)
3786 struct call_queue
*q
;
3787 struct ast_module_user
*lu
;
3791 if (ast_strlen_zero(data
)) {
3792 ast_log(LOG_ERROR
, "%s requires an argument: queuename\n", cmd
);
3796 lu
= ast_module_user_add(chan
);
3798 AST_LIST_LOCK(&queues
);
3799 AST_LIST_TRAVERSE(&queues
, q
, list
) {
3800 if (!strcasecmp(q
->name
, data
)) {
3801 ast_mutex_lock(&q
->lock
);
3805 AST_LIST_UNLOCK(&queues
);
3809 ast_mutex_unlock(&q
->lock
);
3811 ast_log(LOG_WARNING
, "queue %s was not found\n", data
);
3813 snprintf(buf
, len
, "%d", count
);
3814 ast_module_user_remove(lu
);
3818 static int queue_function_queuememberlist(struct ast_channel
*chan
, char *cmd
, char *data
, char *buf
, size_t len
)
3820 struct ast_module_user
*u
;
3821 struct call_queue
*q
;
3824 /* Ensure an otherwise empty list doesn't return garbage */
3827 if (ast_strlen_zero(data
)) {
3828 ast_log(LOG_ERROR
, "QUEUE_MEMBER_LIST requires an argument: queuename\n");
3832 u
= ast_module_user_add(chan
);
3834 AST_LIST_LOCK(&queues
);
3835 AST_LIST_TRAVERSE(&queues
, q
, list
) {
3836 if (!strcasecmp(q
->name
, data
)) {
3837 ast_mutex_lock(&q
->lock
);
3841 AST_LIST_UNLOCK(&queues
);
3844 int buflen
= 0, count
= 0;
3845 struct ao2_iterator mem_iter
= ao2_iterator_init(q
->members
, 0);
3847 while ((m
= ao2_iterator_next(&mem_iter
))) {
3848 /* strcat() is always faster than printf() */
3850 strncat(buf
+ buflen
, ",", len
- buflen
- 1);
3853 strncat(buf
+ buflen
, m
->membername
, len
- buflen
- 1);
3854 buflen
+= strlen(m
->membername
);
3855 /* Safeguard against overflow (negative length) */
3856 if (buflen
>= len
- 2) {
3858 ast_log(LOG_WARNING
, "Truncating list\n");
3863 ast_mutex_unlock(&q
->lock
);
3865 ast_log(LOG_WARNING
, "queue %s was not found\n", data
);
3867 /* We should already be terminated, but let's make sure. */
3868 buf
[len
- 1] = '\0';
3869 ast_module_user_remove(u
);
3874 static struct ast_custom_function queueagentcount_function
= {
3875 .name
= "QUEUEAGENTCOUNT",
3876 .synopsis
= "Count number of agents answering a queue",
3877 .syntax
= "QUEUEAGENTCOUNT(<queuename>)",
3879 "Returns the number of members currently associated with the specified queue.\n"
3880 "This function is deprecated. You should use QUEUE_MEMBER_COUNT() instead.\n",
3881 .read
= queue_function_qac
,
3884 static struct ast_custom_function queuemembercount_function
= {
3885 .name
= "QUEUE_MEMBER_COUNT",
3886 .synopsis
= "Count number of members answering a queue",
3887 .syntax
= "QUEUE_MEMBER_COUNT(<queuename>)",
3889 "Returns the number of members currently associated with the specified queue.\n",
3890 .read
= queue_function_qac
,
3893 static struct ast_custom_function queuewaitingcount_function
= {
3894 .name
= "QUEUE_WAITING_COUNT",
3895 .synopsis
= "Count number of calls currently waiting in a queue",
3896 .syntax
= "QUEUE_WAITING_COUNT(<queuename>)",
3898 "Returns the number of callers currently waiting in the specified queue.\n",
3899 .read
= queue_function_queuewaitingcount
,
3902 static struct ast_custom_function queuememberlist_function
= {
3903 .name
= "QUEUE_MEMBER_LIST",
3904 .synopsis
= "Returns a list of interfaces on a queue",
3905 .syntax
= "QUEUE_MEMBER_LIST(<queuename>)",
3907 "Returns a comma-separated list of members associated with the specified queue.\n",
3908 .read
= queue_function_queuememberlist
,
3911 static int reload_queues(void)
3913 struct call_queue
*q
;
3914 struct ast_config
*cfg
;
3916 struct ast_variable
*var
;
3917 struct member
*cur
, *newm
;
3918 struct ao2_iterator mem_iter
;
3920 const char *general_val
= NULL
;
3923 char *membername
= NULL
;
3925 AST_DECLARE_APP_ARGS(args
,
3926 AST_APP_ARG(interface
);
3927 AST_APP_ARG(penalty
);
3928 AST_APP_ARG(membername
);
3931 if (!(cfg
= ast_config_load("queues.conf"))) {
3932 ast_log(LOG_NOTICE
, "No call queueing config file (queues.conf), so no call queues\n");
3935 AST_LIST_LOCK(&queues
);
3937 /* Mark all non-realtime queues as dead for the moment */
3938 AST_LIST_TRAVERSE(&queues
, q
, list
) {
3945 /* Chug through config file */
3947 while ((cat
= ast_category_browse(cfg
, cat
)) ) {
3948 if (!strcasecmp(cat
, "general")) {
3949 /* Initialize global settings */
3950 queue_persistent_members
= 0;
3951 if ((general_val
= ast_variable_retrieve(cfg
, "general", "persistentmembers")))
3952 queue_persistent_members
= ast_true(general_val
);
3953 autofill_default
= 0;
3954 if ((general_val
= ast_variable_retrieve(cfg
, "general", "autofill")))
3955 autofill_default
= ast_true(general_val
);
3956 montype_default
= 0;
3957 if ((general_val
= ast_variable_retrieve(cfg
, "general", "monitor-type")))
3958 if (!strcasecmp(general_val
, "mixmonitor"))
3959 montype_default
= 1;
3960 } else { /* Define queue */
3961 /* Look for an existing one */
3962 AST_LIST_TRAVERSE(&queues
, q
, list
) {
3963 if (!strcmp(q
->name
, cat
))
3968 if (!(q
= alloc_queue(cat
))) {
3969 /* TODO: Handle memory allocation failure */
3976 ast_mutex_lock(&q
->lock
);
3977 /* Check if a queue with this name already exists */
3979 ast_log(LOG_WARNING
, "Queue '%s' already defined! Skipping!\n", cat
);
3981 ast_mutex_unlock(&q
->lock
);
3984 /* Re-initialize the queue, and clear statistics */
3987 mem_iter
= ao2_iterator_init(q
->members
, 0);
3988 while ((cur
= ao2_iterator_next(&mem_iter
))) {
3989 if (!cur
->dynamic
) {
3994 for (var
= ast_variable_browse(cfg
, cat
); var
; var
= var
->next
) {
3995 if (!strcasecmp(var
->name
, "member")) {
3996 struct member tmpmem
;
3998 /* Add a new member */
3999 ast_copy_string(parse
, var
->value
, sizeof(parse
));
4001 AST_NONSTANDARD_APP_ARGS(args
, parse
, ',');
4003 interface
= args
.interface
;
4004 if(!ast_strlen_zero(args
.penalty
)) {
4006 while (*tmp
&& *tmp
< 33) tmp
++;
4007 penalty
= atoi(tmp
);
4014 if (!ast_strlen_zero(args
.membername
)) {
4015 membername
= args
.membername
;
4016 while (*membername
&& *membername
< 33) membername
++;
4019 /* Find the old position in the list */
4020 ast_copy_string(tmpmem
.interface
, interface
, sizeof(tmpmem
.interface
));
4021 cur
= ao2_find(q
->members
, &tmpmem
, OBJ_POINTER
| OBJ_UNLINK
);
4023 newm
= create_queue_member(interface
, membername
, penalty
, cur
? cur
->paused
: 0);
4024 ao2_link(q
->members
, newm
);
4029 /* Add them to the master int list if necessary */
4030 add_to_interfaces(interface
);
4034 queue_set_param(q
, var
->name
, var
->value
, var
->lineno
, 1);
4038 /* Free remaining members marked as delme */
4039 mem_iter
= ao2_iterator_init(q
->members
, 0);
4040 while ((cur
= ao2_iterator_next(&mem_iter
))) {
4046 remove_from_interfaces(cur
->interface
);
4048 ao2_unlink(q
->members
, cur
);
4052 if (q
->strategy
== QUEUE_STRATEGY_ROUNDROBIN
)
4056 AST_LIST_INSERT_HEAD(&queues
, q
, list
);
4058 ast_mutex_unlock(&q
->lock
);
4062 ast_config_destroy(cfg
);
4063 AST_LIST_TRAVERSE_SAFE_BEGIN(&queues
, q
, list
) {
4065 AST_LIST_REMOVE_CURRENT(&queues
, list
);
4069 ast_log(LOG_DEBUG
, "XXX Leaking a little memory :( XXX\n");
4071 ast_mutex_lock(&q
->lock
);
4072 mem_iter
= ao2_iterator_init(q
->members
, 0);
4073 while ((cur
= ao2_iterator_next(&mem_iter
))) {
4076 cur
->status
= ast_device_state(cur
->interface
);
4079 ast_mutex_unlock(&q
->lock
);
4082 AST_LIST_TRAVERSE_SAFE_END
;
4083 AST_LIST_UNLOCK(&queues
);
4087 static int __queues_show(struct mansession
*s
, int manager
, int fd
, int argc
, char **argv
)
4089 struct call_queue
*q
;
4090 struct queue_ent
*qe
;
4092 int pos
, queue_show
;
4098 char *term
= manager
? "\r\n" : "\n";
4099 struct ao2_iterator mem_iter
;
4107 return RESULT_SHOWUSAGE
;
4109 /* We only want to load realtime queues when a specific queue is asked for. */
4111 load_realtime_queue(argv
[2]);
4113 AST_LIST_LOCK(&queues
);
4114 if (AST_LIST_EMPTY(&queues
)) {
4115 AST_LIST_UNLOCK(&queues
);
4118 astman_append(s
, "No such queue: %s.%s",argv
[2], term
);
4120 ast_cli(fd
, "No such queue: %s.%s",argv
[2], term
);
4123 astman_append(s
, "No queues.%s", term
);
4125 ast_cli(fd
, "No queues.%s", term
);
4127 return RESULT_SUCCESS
;
4129 AST_LIST_TRAVERSE(&queues
, q
, list
) {
4130 ast_mutex_lock(&q
->lock
);
4132 if (strcasecmp(q
->name
, argv
[2]) != 0) {
4133 ast_mutex_unlock(&q
->lock
);
4134 if (!AST_LIST_NEXT(q
, list
)) {
4135 ast_cli(fd
, "No such queue: %s.%s",argv
[2], term
);
4143 max_left
= sizeof(max_buf
);
4145 ast_build_string(&max
, &max_left
, "%d", q
->maxlen
);
4147 ast_build_string(&max
, &max_left
, "unlimited");
4149 if (q
->callscompleted
> 0)
4150 sl
= 100 * ((float) q
->callscompletedinsl
/ (float) q
->callscompleted
);
4152 astman_append(s
, "%-12.12s has %d calls (max %s) in '%s' strategy (%ds holdtime), W:%d, C:%d, A:%d, SL:%2.1f%% within %ds%s",
4153 q
->name
, q
->count
, max_buf
, int2strat(q
->strategy
), q
->holdtime
, q
->weight
,
4154 q
->callscompleted
, q
->callsabandoned
,sl
,q
->servicelevel
, term
);
4156 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",
4157 q
->name
, q
->count
, max_buf
, int2strat(q
->strategy
), q
->holdtime
, q
->weight
, q
->callscompleted
, q
->callsabandoned
,sl
,q
->servicelevel
, term
);
4158 if (ao2_container_count(q
->members
)) {
4160 astman_append(s
, " Members: %s", term
);
4162 ast_cli(fd
, " Members: %s", term
);
4163 mem_iter
= ao2_iterator_init(q
->members
, 0);
4164 while ((mem
= ao2_iterator_next(&mem_iter
))) {
4167 max_left
= sizeof(max_buf
);
4169 ast_build_string(&max
, &max_left
, " with penalty %d", mem
->penalty
);
4171 ast_build_string(&max
, &max_left
, " (dynamic)");
4173 ast_build_string(&max
, &max_left
, " (realtime)");
4175 ast_build_string(&max
, &max_left
, " (paused)");
4176 ast_build_string(&max
, &max_left
, " (%s)", devstate2str(mem
->status
));
4178 ast_build_string(&max
, &max_left
, " has taken %d calls (last was %ld secs ago)",
4179 mem
->calls
, (long) (time(NULL
) - mem
->lastcall
));
4181 ast_build_string(&max
, &max_left
, " has taken no calls yet");
4183 astman_append(s
, " %s%s%s", mem
->membername
, max_buf
, term
);
4185 ast_cli(fd
, " %s%s%s", mem
->membername
, max_buf
, term
);
4189 astman_append(s
, " No Members%s", term
);
4191 ast_cli(fd
, " No Members%s", term
);
4195 astman_append(s
, " Callers: %s", term
);
4197 ast_cli(fd
, " Callers: %s", term
);
4198 for (qe
= q
->head
; qe
; qe
= qe
->next
) {
4200 astman_append(s
, " %d. %s (wait: %ld:%2.2ld, prio: %d)%s",
4201 pos
++, qe
->chan
->name
, (long) (now
- qe
->start
) / 60,
4202 (long) (now
- qe
->start
) % 60, qe
->prio
, term
);
4204 ast_cli(fd
, " %d. %s (wait: %ld:%2.2ld, prio: %d)%s", pos
++,
4205 qe
->chan
->name
, (long) (now
- qe
->start
) / 60,
4206 (long) (now
- qe
->start
) % 60, qe
->prio
, term
);
4209 astman_append(s
, " No Callers%s", term
);
4211 ast_cli(fd
, " No Callers%s", term
);
4213 astman_append(s
, "%s", term
);
4215 ast_cli(fd
, "%s", term
);
4216 ast_mutex_unlock(&q
->lock
);
4220 AST_LIST_UNLOCK(&queues
);
4221 return RESULT_SUCCESS
;
4224 static int queue_show(int fd
, int argc
, char **argv
)
4226 return __queues_show(NULL
, 0, fd
, argc
, argv
);
4229 static char *complete_queue(const char *line
, const char *word
, int pos
, int state
)
4231 struct call_queue
*q
;
4234 int wordlen
= strlen(word
);
4236 AST_LIST_LOCK(&queues
);
4237 AST_LIST_TRAVERSE(&queues
, q
, list
) {
4238 if (!strncasecmp(word
, q
->name
, wordlen
) && ++which
> state
) {
4239 ret
= ast_strdup(q
->name
);
4243 AST_LIST_UNLOCK(&queues
);
4248 static char *complete_queue_show(const char *line
, const char *word
, int pos
, int state
)
4251 return complete_queue(line
, word
, pos
, state
);
4255 /*!\brief callback to display queues status in manager
4256 \addtogroup Group_AMI
4258 static int manager_queues_show(struct mansession
*s
, const struct message
*m
)
4260 char *a
[] = { "queue", "show" };
4262 __queues_show(s
, 1, -1, 2, a
);
4263 astman_append(s
, "\r\n\r\n"); /* Properly terminate Manager output */
4265 return RESULT_SUCCESS
;
4268 /* Dump queue status */
4269 static int manager_queues_status(struct mansession
*s
, const struct message
*m
)
4273 const char *id
= astman_get_header(m
,"ActionID");
4274 const char *queuefilter
= astman_get_header(m
,"Queue");
4275 const char *memberfilter
= astman_get_header(m
,"Member");
4276 char idText
[256] = "";
4277 struct call_queue
*q
;
4278 struct queue_ent
*qe
;
4281 struct ao2_iterator mem_iter
;
4283 astman_send_ack(s
, m
, "Queue status will follow");
4285 AST_LIST_LOCK(&queues
);
4286 if (!ast_strlen_zero(id
))
4287 snprintf(idText
, sizeof(idText
), "ActionID: %s\r\n", id
);
4289 AST_LIST_TRAVERSE(&queues
, q
, list
) {
4290 ast_mutex_lock(&q
->lock
);
4292 /* List queue properties */
4293 if (ast_strlen_zero(queuefilter
) || !strcmp(q
->name
, queuefilter
)) {
4294 sl
= ((q
->callscompleted
> 0) ? 100 * ((float)q
->callscompletedinsl
/ (float)q
->callscompleted
) : 0);
4295 astman_append(s
, "Event: QueueParams\r\n"
4302 "ServiceLevel: %d\r\n"
4303 "ServicelevelPerf: %2.1f\r\n"
4307 q
->name
, q
->maxlen
, q
->count
, q
->holdtime
, q
->callscompleted
,
4308 q
->callsabandoned
, q
->servicelevel
, sl
, q
->weight
, idText
);
4309 /* List Queue Members */
4310 mem_iter
= ao2_iterator_init(q
->members
, 0);
4311 while ((mem
= ao2_iterator_next(&mem_iter
))) {
4312 if (ast_strlen_zero(memberfilter
) || !strcmp(mem
->interface
, memberfilter
)) {
4313 astman_append(s
, "Event: QueueMember\r\n"
4317 "Membership: %s\r\n"
4319 "CallsTaken: %d\r\n"
4325 q
->name
, mem
->membername
, mem
->interface
, mem
->dynamic
? "dynamic" : "static",
4326 mem
->penalty
, mem
->calls
, (int)mem
->lastcall
, mem
->status
, mem
->paused
, idText
);
4330 /* List Queue Entries */
4332 for (qe
= q
->head
; qe
; qe
= qe
->next
) {
4333 astman_append(s
, "Event: QueueEntry\r\n"
4338 "CallerIDName: %s\r\n"
4342 q
->name
, pos
++, qe
->chan
->name
,
4343 S_OR(qe
->chan
->cid
.cid_num
, "unknown"),
4344 S_OR(qe
->chan
->cid
.cid_name
, "unknown"),
4345 (long) (now
- qe
->start
), idText
);
4348 ast_mutex_unlock(&q
->lock
);
4352 "Event: QueueStatusComplete\r\n"
4356 AST_LIST_UNLOCK(&queues
);
4359 return RESULT_SUCCESS
;
4362 static int manager_add_queue_member(struct mansession
*s
, const struct message
*m
)
4364 const char *queuename
, *interface
, *penalty_s
, *paused_s
, *membername
;
4365 int paused
, penalty
= 0;
4367 queuename
= astman_get_header(m
, "Queue");
4368 interface
= astman_get_header(m
, "Interface");
4369 penalty_s
= astman_get_header(m
, "Penalty");
4370 paused_s
= astman_get_header(m
, "Paused");
4371 membername
= astman_get_header(m
, "MemberName");
4373 if (ast_strlen_zero(queuename
)) {
4374 astman_send_error(s
, m
, "'Queue' not specified.");
4378 if (ast_strlen_zero(interface
)) {
4379 astman_send_error(s
, m
, "'Interface' not specified.");
4383 if (ast_strlen_zero(penalty_s
))
4385 else if (sscanf(penalty_s
, "%d", &penalty
) != 1)
4388 if (ast_strlen_zero(paused_s
))
4391 paused
= abs(ast_true(paused_s
));
4393 switch (add_to_queue(queuename
, interface
, membername
, penalty
, paused
, queue_persistent_members
)) {
4395 ast_queue_log(queuename
, "MANAGER", interface
, "ADDMEMBER", "%s", "");
4396 astman_send_ack(s
, m
, "Added interface to queue");
4399 astman_send_error(s
, m
, "Unable to add interface: Already there");
4401 case RES_NOSUCHQUEUE
:
4402 astman_send_error(s
, m
, "Unable to add interface to queue: No such queue");
4404 case RES_OUTOFMEMORY
:
4405 astman_send_error(s
, m
, "Out of memory");
4412 static int manager_remove_queue_member(struct mansession
*s
, const struct message
*m
)
4414 const char *queuename
, *interface
;
4416 queuename
= astman_get_header(m
, "Queue");
4417 interface
= astman_get_header(m
, "Interface");
4419 if (ast_strlen_zero(queuename
) || ast_strlen_zero(interface
)) {
4420 astman_send_error(s
, m
, "Need 'Queue' and 'Interface' parameters.");
4424 switch (remove_from_queue(queuename
, interface
)) {
4426 ast_queue_log(queuename
, "MANAGER", interface
, "REMOVEMEMBER", "%s", "");
4427 astman_send_ack(s
, m
, "Removed interface from queue");
4430 astman_send_error(s
, m
, "Unable to remove interface: Not there");
4432 case RES_NOSUCHQUEUE
:
4433 astman_send_error(s
, m
, "Unable to remove interface from queue: No such queue");
4435 case RES_OUTOFMEMORY
:
4436 astman_send_error(s
, m
, "Out of memory");
4438 case RES_NOT_DYNAMIC
:
4439 astman_send_error(s
, m
, "Member not dynamic");
4446 static int manager_pause_queue_member(struct mansession
*s
, const struct message
*m
)
4448 const char *queuename
, *interface
, *paused_s
;
4451 interface
= astman_get_header(m
, "Interface");
4452 paused_s
= astman_get_header(m
, "Paused");
4453 queuename
= astman_get_header(m
, "Queue"); /* Optional - if not supplied, pause the given Interface in all queues */
4455 if (ast_strlen_zero(interface
) || ast_strlen_zero(paused_s
)) {
4456 astman_send_error(s
, m
, "Need 'Interface' and 'Paused' parameters.");
4460 paused
= abs(ast_true(paused_s
));
4462 if (set_member_paused(queuename
, interface
, paused
))
4463 astman_send_error(s
, m
, "Interface not found");
4465 astman_send_ack(s
, m
, paused
? "Interface paused successfully" : "Interface unpaused successfully");
4469 static int handle_queue_add_member(int fd
, int argc
, char *argv
[])
4471 char *queuename
, *interface
, *membername
= NULL
;
4474 if ((argc
!= 6) && (argc
!= 8) && (argc
!= 10)) {
4475 return RESULT_SHOWUSAGE
;
4476 } else if (strcmp(argv
[4], "to")) {
4477 return RESULT_SHOWUSAGE
;
4478 } else if ((argc
== 8) && strcmp(argv
[6], "penalty")) {
4479 return RESULT_SHOWUSAGE
;
4480 } else if ((argc
== 10) && strcmp(argv
[8], "as")) {
4481 return RESULT_SHOWUSAGE
;
4484 queuename
= argv
[5];
4485 interface
= argv
[3];
4487 if (sscanf(argv
[7], "%d", &penalty
) == 1) {
4489 ast_cli(fd
, "Penalty must be >= 0\n");
4493 ast_cli(fd
, "Penalty must be an integer >= 0\n");
4501 membername
= argv
[9];
4504 switch (add_to_queue(queuename
, interface
, membername
, penalty
, 0, queue_persistent_members
)) {
4506 ast_queue_log(queuename
, "CLI", interface
, "ADDMEMBER", "%s", "");
4507 ast_cli(fd
, "Added interface '%s' to queue '%s'\n", interface
, queuename
);
4508 return RESULT_SUCCESS
;
4510 ast_cli(fd
, "Unable to add interface '%s' to queue '%s': Already there\n", interface
, queuename
);
4511 return RESULT_FAILURE
;
4512 case RES_NOSUCHQUEUE
:
4513 ast_cli(fd
, "Unable to add interface to queue '%s': No such queue\n", queuename
);
4514 return RESULT_FAILURE
;
4515 case RES_OUTOFMEMORY
:
4516 ast_cli(fd
, "Out of memory\n");
4517 return RESULT_FAILURE
;
4519 return RESULT_FAILURE
;
4523 static char *complete_queue_add_member(const char *line
, const char *word
, int pos
, int state
)
4525 /* 0 - queue; 1 - add; 2 - member; 3 - <interface>; 4 - to; 5 - <queue>; 6 - penalty; 7 - <penalty>; 8 - as; 9 - <membername> */
4527 case 3: /* Don't attempt to complete name of interface (infinite possibilities) */
4529 case 4: /* only one possible match, "to" */
4530 return state
== 0 ? ast_strdup("to") : NULL
;
4531 case 5: /* <queue> */
4532 return complete_queue(line
, word
, pos
, state
);
4533 case 6: /* only one possible match, "penalty" */
4534 return state
== 0 ? ast_strdup("penalty") : NULL
;
4536 if (state
< 100) { /* 0-99 */
4538 if ((num
= ast_malloc(3))) {
4539 sprintf(num
, "%d", state
);
4545 case 8: /* only one possible match, "as" */
4546 return state
== 0 ? ast_strdup("as") : NULL
;
4547 case 9: /* Don't attempt to complete name of member (infinite possibilities) */
4554 static int handle_queue_remove_member(int fd
, int argc
, char *argv
[])
4556 char *queuename
, *interface
;
4559 return RESULT_SHOWUSAGE
;
4560 } else if (strcmp(argv
[4], "from")) {
4561 return RESULT_SHOWUSAGE
;
4564 queuename
= argv
[5];
4565 interface
= argv
[3];
4567 switch (remove_from_queue(queuename
, interface
)) {
4569 ast_queue_log(queuename
, "CLI", interface
, "REMOVEMEMBER", "%s", "");
4570 ast_cli(fd
, "Removed interface '%s' from queue '%s'\n", interface
, queuename
);
4571 return RESULT_SUCCESS
;
4573 ast_cli(fd
, "Unable to remove interface '%s' from queue '%s': Not there\n", interface
, queuename
);
4574 return RESULT_FAILURE
;
4575 case RES_NOSUCHQUEUE
:
4576 ast_cli(fd
, "Unable to remove interface from queue '%s': No such queue\n", queuename
);
4577 return RESULT_FAILURE
;
4578 case RES_OUTOFMEMORY
:
4579 ast_cli(fd
, "Out of memory\n");
4580 return RESULT_FAILURE
;
4581 case RES_NOT_DYNAMIC
:
4582 ast_cli(fd
, "Member not dynamic\n");
4583 return RESULT_FAILURE
;
4585 return RESULT_FAILURE
;
4589 static char *complete_queue_remove_member(const char *line
, const char *word
, int pos
, int state
)
4592 struct call_queue
*q
;
4594 struct ao2_iterator mem_iter
;
4596 /* 0 - queue; 1 - remove; 2 - member; 3 - <member>; 4 - from; 5 - <queue> */
4597 if (pos
> 5 || pos
< 3)
4599 if (pos
== 4) /* only one possible match, 'from' */
4600 return state
== 0 ? ast_strdup("from") : NULL
;
4602 if (pos
== 5) /* No need to duplicate code */
4603 return complete_queue(line
, word
, pos
, state
);
4605 /* here is the case for 3, <member> */
4606 if (!AST_LIST_EMPTY(&queues
)) { /* XXX unnecessary ? the traverse does that for us */
4607 AST_LIST_TRAVERSE(&queues
, q
, list
) {
4608 ast_mutex_lock(&q
->lock
);
4609 mem_iter
= ao2_iterator_init(q
->members
, 0);
4610 while ((m
= ao2_iterator_next(&mem_iter
))) {
4611 if (++which
> state
) {
4613 ast_mutex_unlock(&q
->lock
);
4614 tmp
= m
->membername
;
4616 return ast_strdup(tmp
);
4620 ast_mutex_unlock(&q
->lock
);
4627 static char queue_show_usage
[] =
4628 "Usage: queue show\n"
4629 " Provides summary information on a specified queue.\n";
4631 static char qam_cmd_usage
[] =
4632 "Usage: queue add member <channel> to <queue> [penalty <penalty>]\n";
4634 static char qrm_cmd_usage
[] =
4635 "Usage: queue remove member <channel> from <queue>\n";
4637 static struct ast_cli_entry cli_show_queue_deprecated
= {
4638 { "show", "queue", NULL
},
4640 NULL
, complete_queue_show
};
4642 static struct ast_cli_entry cli_add_queue_member_deprecated
= {
4643 { "add", "queue", "member", NULL
},
4644 handle_queue_add_member
, NULL
,
4645 NULL
, complete_queue_add_member
};
4647 static struct ast_cli_entry cli_remove_queue_member_deprecated
= {
4648 { "remove", "queue", "member", NULL
},
4649 handle_queue_remove_member
, NULL
,
4650 NULL
, complete_queue_remove_member
};
4652 static struct ast_cli_entry cli_queue
[] = {
4654 { { "show", "queues", NULL
},
4658 { { "queue", "show", NULL
},
4659 queue_show
, "Show status of a specified queue",
4660 queue_show_usage
, complete_queue_show
, &cli_show_queue_deprecated
},
4662 { { "queue", "add", "member", NULL
},
4663 handle_queue_add_member
, "Add a channel to a specified queue",
4664 qam_cmd_usage
, complete_queue_add_member
, &cli_add_queue_member_deprecated
},
4666 { { "queue", "remove", "member", NULL
},
4667 handle_queue_remove_member
, "Removes a channel from a specified queue",
4668 qrm_cmd_usage
, complete_queue_remove_member
, &cli_remove_queue_member_deprecated
},
4671 static int unload_module(void)
4675 if (device_state
.thread
!= AST_PTHREADT_NULL
) {
4676 device_state
.stop
= 1;
4677 ast_mutex_lock(&device_state
.lock
);
4678 ast_cond_signal(&device_state
.cond
);
4679 ast_mutex_unlock(&device_state
.lock
);
4680 pthread_join(device_state
.thread
, NULL
);
4683 ast_cli_unregister_multiple(cli_queue
, sizeof(cli_queue
) / sizeof(struct ast_cli_entry
));
4684 res
= ast_manager_unregister("QueueStatus");
4685 res
|= ast_manager_unregister("Queues");
4686 res
|= ast_manager_unregister("QueueStatus");
4687 res
|= ast_manager_unregister("QueueAdd");
4688 res
|= ast_manager_unregister("QueueRemove");
4689 res
|= ast_manager_unregister("QueuePause");
4690 res
|= ast_unregister_application(app_aqm
);
4691 res
|= ast_unregister_application(app_rqm
);
4692 res
|= ast_unregister_application(app_pqm
);
4693 res
|= ast_unregister_application(app_upqm
);
4694 res
|= ast_unregister_application(app_ql
);
4695 res
|= ast_unregister_application(app
);
4696 res
|= ast_custom_function_unregister(&queueagentcount_function
);
4697 res
|= ast_custom_function_unregister(&queuemembercount_function
);
4698 res
|= ast_custom_function_unregister(&queuememberlist_function
);
4699 res
|= ast_custom_function_unregister(&queuewaitingcount_function
);
4700 ast_devstate_del(statechange_queue
, NULL
);
4702 ast_module_user_hangup_all();
4704 clear_and_free_interfaces();
4709 static int load_module(void)
4713 if (!reload_queues())
4714 return AST_MODULE_LOAD_DECLINE
;
4716 if (queue_persistent_members
)
4717 reload_queue_members();
4719 ast_mutex_init(&device_state
.lock
);
4720 ast_cond_init(&device_state
.cond
, NULL
);
4721 ast_pthread_create(&device_state
.thread
, NULL
, device_state_thread
, NULL
);
4723 ast_cli_register_multiple(cli_queue
, sizeof(cli_queue
) / sizeof(struct ast_cli_entry
));
4724 res
= ast_register_application(app
, queue_exec
, synopsis
, descrip
);
4725 res
|= ast_register_application(app_aqm
, aqm_exec
, app_aqm_synopsis
, app_aqm_descrip
);
4726 res
|= ast_register_application(app_rqm
, rqm_exec
, app_rqm_synopsis
, app_rqm_descrip
);
4727 res
|= ast_register_application(app_pqm
, pqm_exec
, app_pqm_synopsis
, app_pqm_descrip
);
4728 res
|= ast_register_application(app_upqm
, upqm_exec
, app_upqm_synopsis
, app_upqm_descrip
);
4729 res
|= ast_register_application(app_ql
, ql_exec
, app_ql_synopsis
, app_ql_descrip
);
4730 res
|= ast_manager_register("Queues", 0, manager_queues_show
, "Queues");
4731 res
|= ast_manager_register("QueueStatus", 0, manager_queues_status
, "Queue Status");
4732 res
|= ast_manager_register("QueueAdd", EVENT_FLAG_AGENT
, manager_add_queue_member
, "Add interface to queue.");
4733 res
|= ast_manager_register("QueueRemove", EVENT_FLAG_AGENT
, manager_remove_queue_member
, "Remove interface from queue.");
4734 res
|= ast_manager_register("QueuePause", EVENT_FLAG_AGENT
, manager_pause_queue_member
, "Makes a queue member temporarily unavailable");
4735 res
|= ast_custom_function_register(&queueagentcount_function
);
4736 res
|= ast_custom_function_register(&queuemembercount_function
);
4737 res
|= ast_custom_function_register(&queuememberlist_function
);
4738 res
|= ast_custom_function_register(&queuewaitingcount_function
);
4739 res
|= ast_devstate_add(statechange_queue
, NULL
);
4744 static int reload(void)
4750 AST_MODULE_INFO(ASTERISK_GPL_KEY
, AST_MODFLAG_DEFAULT
, "True Call Queueing",
4751 .load
= load_module
,
4752 .unload
= unload_module
,