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"
97 QUEUE_STRATEGY_RINGALL
= 0,
98 QUEUE_STRATEGY_ROUNDROBIN
,
99 QUEUE_STRATEGY_LEASTRECENT
,
100 QUEUE_STRATEGY_FEWESTCALLS
,
101 QUEUE_STRATEGY_RANDOM
,
102 QUEUE_STRATEGY_RRMEMORY
105 static struct strategy
{
109 { QUEUE_STRATEGY_RINGALL
, "ringall" },
110 { QUEUE_STRATEGY_ROUNDROBIN
, "roundrobin" },
111 { QUEUE_STRATEGY_LEASTRECENT
, "leastrecent" },
112 { QUEUE_STRATEGY_FEWESTCALLS
, "fewestcalls" },
113 { QUEUE_STRATEGY_RANDOM
, "random" },
114 { QUEUE_STRATEGY_RRMEMORY
, "rrmemory" },
117 #define DEFAULT_RETRY 5
118 #define DEFAULT_TIMEOUT 15
119 #define RECHECK 1 /* Recheck every second to see we we're at the top yet */
120 #define MAX_PERIODIC_ANNOUNCEMENTS 10 /* The maximum periodic announcements we can have */
122 #define RES_OKAY 0 /* Action completed */
123 #define RES_EXISTS (-1) /* Entry already exists */
124 #define RES_OUTOFMEMORY (-2) /* Out of memory */
125 #define RES_NOSUCHQUEUE (-3) /* No such queue */
127 static char *app
= "Queue";
129 static char *synopsis
= "Queue a call for a call queue";
131 static char *descrip
=
132 " Queue(queuename[|options[|URL][|announceoverride][|timeout][|AGI]):\n"
133 "Queues an incoming call in a particular call queue as defined in queues.conf.\n"
134 "This application will return to the dialplan if the queue does not exist, or\n"
135 "any of the join options cause the caller to not enter the queue.\n"
136 "The option string may contain zero or more of the following characters:\n"
137 " 'd' -- data-quality (modem) call (minimum delay).\n"
138 " 'h' -- allow callee to hang up by hitting *.\n"
139 " 'H' -- allow caller to hang up by hitting *.\n"
140 " 'n' -- no retries on the timeout; will exit this application and \n"
141 " go to the next step.\n"
142 " 'i' -- ignore call forward requests from queue members and do nothing\n"
143 " when they are requested.\n"
144 " 'r' -- ring instead of playing MOH\n"
145 " 't' -- allow the called user transfer the calling user\n"
146 " 'T' -- to allow the calling user to transfer the call.\n"
147 " 'w' -- allow the called user to write the conversation to disk via Monitor\n"
148 " 'W' -- allow the calling user to write the conversation to disk via Monitor\n"
149 " In addition to transferring the call, a call may be parked and then picked\n"
150 "up by another user.\n"
151 " The optional URL will be sent to the called party if the channel supports\n"
153 " The optional AGI parameter will setup an AGI script to be executed on the \n"
154 "calling party's channel once they are connected to a queue member.\n"
155 " The timeout will cause the queue to fail out after a specified number of\n"
156 "seconds, checked between each queues.conf 'timeout' and 'retry' cycle.\n"
157 " This application sets the following channel variable upon completion:\n"
158 " QUEUESTATUS The status of the call as a text string, one of\n"
159 " TIMEOUT | FULL | JOINEMPTY | LEAVEEMPTY | JOINUNAVAIL | LEAVEUNAVAIL\n";
161 static char *app_aqm
= "AddQueueMember" ;
162 static char *app_aqm_synopsis
= "Dynamically adds queue members" ;
163 static char *app_aqm_descrip
=
164 " AddQueueMember(queuename[|interface[|penalty[|options]]]):\n"
165 "Dynamically adds interface to an existing queue.\n"
166 "If the interface is already in the queue and there exists an n+101 priority\n"
167 "then it will then jump to this priority. Otherwise it will return an error\n"
168 "The option string may contain zero or more of the following characters:\n"
169 " 'j' -- jump to +101 priority when appropriate.\n"
170 " This application sets the following channel variable upon completion:\n"
171 " AQMSTATUS The status of the attempt to add a queue member as a \n"
172 " text string, one of\n"
173 " ADDED | MEMBERALREADY | NOSUCHQUEUE \n"
174 "Example: AddQueueMember(techsupport|SIP/3000)\n"
177 static char *app_rqm
= "RemoveQueueMember" ;
178 static char *app_rqm_synopsis
= "Dynamically removes queue members" ;
179 static char *app_rqm_descrip
=
180 " RemoveQueueMember(queuename[|interface[|options]]):\n"
181 "Dynamically removes interface to an existing queue\n"
182 "If the interface is NOT in the queue and there exists an n+101 priority\n"
183 "then it will then jump to this priority. Otherwise it will return an error\n"
184 "The option string may contain zero or more of the following characters:\n"
185 " 'j' -- jump to +101 priority when appropriate.\n"
186 " This application sets the following channel variable upon completion:\n"
187 " RQMSTATUS The status of the attempt to remove a queue member as a\n"
188 " text string, one of\n"
189 " REMOVED | NOTINQUEUE | NOSUCHQUEUE \n"
190 "Example: RemoveQueueMember(techsupport|SIP/3000)\n"
193 static char *app_pqm
= "PauseQueueMember" ;
194 static char *app_pqm_synopsis
= "Pauses a queue member" ;
195 static char *app_pqm_descrip
=
196 " PauseQueueMember([queuename]|interface[|options]):\n"
197 "Pauses (blocks calls for) a queue member.\n"
198 "The given interface will be paused in the given queue. This prevents\n"
199 "any calls from being sent from the queue to the interface until it is\n"
200 "unpaused with UnpauseQueueMember or the manager interface. If no\n"
201 "queuename is given, the interface is paused in every queue it is a\n"
202 "member of. If the interface is not in the named queue, or if no queue\n"
203 "is given and the interface is not in any queue, it will jump to\n"
204 "priority n+101, if it exists and the appropriate options are set.\n"
205 "The application will fail if the interface is not found and no extension\n"
206 "to jump to exists.\n"
207 "The option string may contain zero or more of the following characters:\n"
208 " 'j' -- jump to +101 priority when appropriate.\n"
209 " This application sets the following channel variable upon completion:\n"
210 " PQMSTATUS The status of the attempt to pause a queue member as a\n"
211 " text string, one of\n"
212 " PAUSED | NOTFOUND\n"
213 "Example: PauseQueueMember(|SIP/3000)\n";
215 static char *app_upqm
= "UnpauseQueueMember" ;
216 static char *app_upqm_synopsis
= "Unpauses a queue member" ;
217 static char *app_upqm_descrip
=
218 " UnpauseQueueMember([queuename]|interface[|options]):\n"
219 "Unpauses (resumes calls to) a queue member.\n"
220 "This is the counterpart to PauseQueueMember and operates exactly the\n"
221 "same way, except it unpauses instead of pausing the given interface.\n"
222 "The option string may contain zero or more of the following characters:\n"
223 " 'j' -- jump to +101 priority when appropriate.\n"
224 " This application sets the following channel variable upon completion:\n"
225 " UPQMSTATUS The status of the attempt to unpause a queue \n"
226 " member as a text string, one of\n"
227 " UNPAUSED | NOTFOUND\n"
228 "Example: UnpauseQueueMember(|SIP/3000)\n";
230 static char *app_ql
= "QueueLog" ;
231 static char *app_ql_synopsis
= "Writes to the queue_log" ;
232 static char *app_ql_descrip
=
233 " QueueLog(queuename|uniqueid|agent|event[|additionalinfo]):\n"
234 "Allows you to write your own events into the queue log\n"
235 "Example: QueueLog(101|${UNIQUEID}|${AGENT}|WENTONBREAK|600)\n";
237 /*! \brief Persistent Members astdb family */
238 static const char *pm_family
= "/Queue/PersistentMembers";
239 /* The maximum length of each persistent member queue database entry */
240 #define PM_MAX_LEN 8192
242 /*! \brief queues.conf [general] option */
243 static int queue_persistent_members
= 0;
245 /*! \brief queues.conf per-queue weight option */
246 static int use_weight
= 0;
248 /*! \brief queues.conf [general] option */
249 static int autofill_default
= 0;
251 /*! \brief queues.conf [general] option */
252 static int montype_default
= 0;
258 QUEUE_LEAVEEMPTY
= 3,
259 QUEUE_JOINUNAVAIL
= 4,
260 QUEUE_LEAVEUNAVAIL
= 5,
265 enum queue_result id
;
267 } queue_results
[] = {
268 { QUEUE_UNKNOWN
, "UNKNOWN" },
269 { QUEUE_TIMEOUT
, "TIMEOUT" },
270 { QUEUE_JOINEMPTY
,"JOINEMPTY" },
271 { QUEUE_LEAVEEMPTY
, "LEAVEEMPTY" },
272 { QUEUE_JOINUNAVAIL
, "JOINUNAVAIL" },
273 { QUEUE_LEAVEUNAVAIL
, "LEAVEUNAVAIL" },
274 { QUEUE_FULL
, "FULL" },
277 /*! \brief We define a custom "local user" structure because we
278 use it not only for keeping track of what is in use but
279 also for keeping track of who we're dialing. */
282 struct callattempt
*q_next
;
283 struct ast_channel
*chan
;
289 struct member
*member
;
294 struct call_queue
*parent
; /*!< What queue is our parent */
295 char moh
[80]; /*!< Name of musiconhold to be used */
296 char announce
[80]; /*!< Announcement to play for member when call is answered */
297 char context
[AST_MAX_CONTEXT
]; /*!< Context when user exits queue */
298 char digits
[AST_MAX_EXTENSION
]; /*!< Digits entered while in queue */
299 int pos
; /*!< Where we are in the queue */
300 int prio
; /*!< Our priority */
301 int last_pos_said
; /*!< Last position we told the user */
302 time_t last_periodic_announce_time
; /*!< The last time we played a periodic announcement */
303 int last_periodic_announce_sound
; /*!< The last periodic announcement we made */
304 time_t last_pos
; /*!< Last time we told the user their position */
305 int opos
; /*!< Where we started in the queue */
306 int handled
; /*!< Whether our call was handled */
307 int max_penalty
; /*!< Limit the members that can take this call to this penalty or lower */
308 time_t start
; /*!< When we started holding */
309 time_t expire
; /*!< When this entry should expire (time out of queue) */
310 struct ast_channel
*chan
; /*!< Our channel */
311 struct queue_ent
*next
; /*!< The next queue entry */
315 char interface
[80]; /*!< Technology/Location */
316 char membername
[80]; /*!< Member name to use in queue logs */
317 int penalty
; /*!< Are we a last resort? */
318 int calls
; /*!< Number of calls serviced by this member */
319 int dynamic
; /*!< Are we dynamically added? */
320 int status
; /*!< Status of queue member */
321 int paused
; /*!< Are we paused (not accepting calls)? */
322 time_t lastcall
; /*!< When last successful call was hungup */
323 unsigned int dead
:1; /*!< Used to detect members deleted in realtime */
324 unsigned int delme
:1; /*!< Flag to delete entry on reload */
325 struct member
*next
; /*!< Next member */
328 struct member_interface
{
330 AST_LIST_ENTRY(member_interface
) list
; /*!< Next call queue */
333 static AST_LIST_HEAD_STATIC(interfaces
, member_interface
);
335 /* values used in multi-bit flags in call_queue */
336 #define QUEUE_EMPTY_NORMAL 1
337 #define QUEUE_EMPTY_STRICT 2
338 #define ANNOUNCEHOLDTIME_ALWAYS 1
339 #define ANNOUNCEHOLDTIME_ONCE 2
340 #define QUEUE_EVENT_VARIABLES 3
344 char name
[80]; /*!< Name */
345 char moh
[80]; /*!< Music On Hold class to be used */
346 char announce
[80]; /*!< Announcement to play when call is answered */
347 char context
[AST_MAX_CONTEXT
]; /*!< Exit context */
348 unsigned int monjoin
:1;
350 unsigned int joinempty
:2;
351 unsigned int eventwhencalled
:2;
352 unsigned int leavewhenempty
:2;
353 unsigned int ringinuse
:1;
354 unsigned int setinterfacevar
:1;
355 unsigned int reportholdtime
:1;
356 unsigned int wrapped
:1;
357 unsigned int timeoutrestart
:1;
358 unsigned int announceholdtime
:2;
359 unsigned int strategy
:3;
360 unsigned int maskmemberstatus
:1;
361 unsigned int realtime
:1;
362 int announcefrequency
; /*!< How often to announce their position */
363 int periodicannouncefrequency
; /*!< How often to play periodic announcement */
364 int roundingseconds
; /*!< How many seconds do we round to? */
365 int holdtime
; /*!< Current avg holdtime, based on recursive boxcar filter */
366 int callscompleted
; /*!< Number of queue calls completed */
367 int callsabandoned
; /*!< Number of queue calls abandoned */
368 int servicelevel
; /*!< seconds setting for servicelevel*/
369 int callscompletedinsl
; /*!< Number of calls answered with servicelevel*/
370 char monfmt
[8]; /*!< Format to use when recording calls */
371 int montype
; /*!< Monitor type Monitor vs. MixMonitor */
372 char sound_next
[80]; /*!< Sound file: "Your call is now first in line" (def. queue-youarenext) */
373 char sound_thereare
[80]; /*!< Sound file: "There are currently" (def. queue-thereare) */
374 char sound_calls
[80]; /*!< Sound file: "calls waiting to speak to a representative." (def. queue-callswaiting)*/
375 char sound_holdtime
[80]; /*!< Sound file: "The current estimated total holdtime is" (def. queue-holdtime) */
376 char sound_minutes
[80]; /*!< Sound file: "minutes." (def. queue-minutes) */
377 char sound_lessthan
[80]; /*!< Sound file: "less-than" (def. queue-lessthan) */
378 char sound_seconds
[80]; /*!< Sound file: "seconds." (def. queue-seconds) */
379 char sound_thanks
[80]; /*!< Sound file: "Thank you for your patience." (def. queue-thankyou) */
380 char sound_reporthold
[80]; /*!< Sound file: "Hold time" (def. queue-reporthold) */
381 char sound_periodicannounce
[MAX_PERIODIC_ANNOUNCEMENTS
][80];/*!< Sound files: Custom announce, no default */
383 int count
; /*!< How many entries */
384 int maxlen
; /*!< Max number of entries */
385 int wrapuptime
; /*!< Wrapup Time */
387 int retry
; /*!< Retry calling everyone after this amount of time */
388 int timeout
; /*!< How long to wait for an answer */
389 int weight
; /*!< Respective weight */
390 int autopause
; /*!< Auto pause queue members if they fail to answer */
392 /* Queue strategy things */
393 int rrpos
; /*!< Round Robin - position */
394 int memberdelay
; /*!< Seconds to delay connecting member to caller */
395 int autofill
; /*!< Ignore the head call status and ring an available agent */
397 struct member
*members
; /*!< Head of the list of members */
398 struct queue_ent
*head
; /*!< Head of the list of callers */
399 AST_LIST_ENTRY(call_queue
) list
; /*!< Next call queue */
402 static AST_LIST_HEAD_STATIC(queues
, call_queue
);
404 static int set_member_paused(char *queuename
, char *interface
, int paused
);
406 static void rr_dep_warning(void)
408 static unsigned int warned
= 0;
411 ast_log(LOG_NOTICE
, "The 'roundrobin' queue strategy is deprecated. Please use the 'rrmemory' strategy instead.\n");
416 static void set_queue_result(struct ast_channel
*chan
, enum queue_result res
)
420 for (i
= 0; i
< sizeof(queue_results
) / sizeof(queue_results
[0]); i
++) {
421 if (queue_results
[i
].id
== res
) {
422 pbx_builtin_setvar_helper(chan
, "QUEUESTATUS", queue_results
[i
].text
);
428 static char *int2strat(int strategy
)
432 for (x
= 0; x
< sizeof(strategies
) / sizeof(strategies
[0]); x
++) {
433 if (strategy
== strategies
[x
].strategy
)
434 return strategies
[x
].name
;
440 static int strat2int(const char *strategy
)
444 for (x
= 0; x
< sizeof(strategies
) / sizeof(strategies
[0]); x
++) {
445 if (!strcasecmp(strategy
, strategies
[x
].name
))
446 return strategies
[x
].strategy
;
452 /*! \brief Insert the 'new' entry after the 'prev' entry of queue 'q' */
453 static inline void insert_entry(struct call_queue
*q
, struct queue_ent
*prev
, struct queue_ent
*new, int *pos
)
455 struct queue_ent
*cur
;
472 enum queue_member_status
{
474 QUEUE_NO_REACHABLE_MEMBERS
,
478 static enum queue_member_status
get_member_status(struct call_queue
*q
, int max_penalty
)
480 struct member
*member
;
481 enum queue_member_status result
= QUEUE_NO_MEMBERS
;
483 ast_mutex_lock(&q
->lock
);
484 for (member
= q
->members
; member
; member
= member
->next
) {
485 if (max_penalty
&& (member
->penalty
> max_penalty
))
488 if (member
->paused
) continue;
490 switch (member
->status
) {
491 case AST_DEVICE_INVALID
:
494 case AST_DEVICE_UNAVAILABLE
:
495 result
= QUEUE_NO_REACHABLE_MEMBERS
;
498 ast_mutex_unlock(&q
->lock
);
503 ast_mutex_unlock(&q
->lock
);
512 static void *changethread(void *data
)
514 struct call_queue
*q
;
515 struct statechange
*sc
= data
;
517 struct member_interface
*curint
;
521 technology
= ast_strdupa(sc
->dev
);
522 loc
= strchr(technology
, '/');
530 AST_LIST_LOCK(&interfaces
);
531 AST_LIST_TRAVERSE(&interfaces
, curint
, list
) {
534 interface
= ast_strdupa(curint
->interface
);
535 if ((slash_pos
= strchr(interface
, '/')))
536 if ((slash_pos
= strchr(slash_pos
+ 1, '/')))
539 if (!strcasecmp(interface
, sc
->dev
))
542 AST_LIST_UNLOCK(&interfaces
);
546 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
));
552 ast_log(LOG_DEBUG
, "Device '%s/%s' changed to state '%d' (%s)\n", technology
, loc
, sc
->state
, devstate2str(sc
->state
));
553 AST_LIST_LOCK(&queues
);
554 AST_LIST_TRAVERSE(&queues
, q
, list
) {
555 ast_mutex_lock(&q
->lock
);
556 for (cur
= q
->members
; cur
; cur
= cur
->next
) {
559 interface
= ast_strdupa(cur
->interface
);
560 if ((slash_pos
= strchr(interface
, '/')))
561 if ((slash_pos
= strchr(slash_pos
+ 1, '/')))
564 if (strcasecmp(sc
->dev
, interface
))
567 if (cur
->status
!= sc
->state
) {
568 cur
->status
= sc
->state
;
569 if (q
->maskmemberstatus
)
572 manager_event(EVENT_FLAG_AGENT
, "QueueMemberStatus",
582 q
->name
, cur
->interface
, cur
->membername
, cur
->dynamic
? "dynamic" : "static",
583 cur
->penalty
, cur
->calls
, (int)cur
->lastcall
, cur
->status
, cur
->paused
);
586 ast_mutex_unlock(&q
->lock
);
588 AST_LIST_UNLOCK(&queues
);
593 static int statechange_queue(const char *dev
, int state
, void *ign
)
595 /* Avoid potential for deadlocks by spawning a new thread to handle
597 struct statechange
*sc
;
601 if (!(sc
= ast_calloc(1, sizeof(*sc
) + strlen(dev
) + 1)))
605 strcpy(sc
->dev
, dev
);
606 pthread_attr_init(&attr
);
607 pthread_attr_setdetachstate(&attr
, PTHREAD_CREATE_DETACHED
);
608 if (ast_pthread_create_background(&t
, &attr
, changethread
, sc
)) {
609 ast_log(LOG_WARNING
, "Failed to create update thread!\n");
616 static struct member
*create_queue_member(char *interface
, const char *membername
, int penalty
, int paused
)
620 if ((cur
= ast_calloc(1, sizeof(*cur
)))) {
621 cur
->penalty
= penalty
;
622 cur
->paused
= paused
;
623 ast_copy_string(cur
->interface
, interface
, sizeof(cur
->interface
));
624 ast_copy_string(cur
->membername
, membername
, sizeof(cur
->membername
));
625 if (!strchr(cur
->interface
, '/'))
626 ast_log(LOG_WARNING
, "No location at interface '%s'\n", interface
);
627 cur
->status
= ast_device_state(interface
);
633 static struct call_queue
*alloc_queue(const char *queuename
)
635 struct call_queue
*q
;
637 if ((q
= ast_calloc(1, sizeof(*q
)))) {
638 ast_mutex_init(&q
->lock
);
639 ast_copy_string(q
->name
, queuename
, sizeof(q
->name
));
644 static void init_queue(struct call_queue
*q
)
649 q
->retry
= DEFAULT_RETRY
;
652 q
->announcefrequency
= 0;
653 q
->announceholdtime
= 0;
654 q
->roundingseconds
= 0; /* Default - don't announce seconds */
657 q
->setinterfacevar
= 0;
658 q
->autofill
= autofill_default
;
659 q
->montype
= montype_default
;
661 q
->announce
[0] = '\0';
662 q
->context
[0] = '\0';
664 q
->periodicannouncefrequency
= 0;
665 ast_copy_string(q
->sound_next
, "queue-youarenext", sizeof(q
->sound_next
));
666 ast_copy_string(q
->sound_thereare
, "queue-thereare", sizeof(q
->sound_thereare
));
667 ast_copy_string(q
->sound_calls
, "queue-callswaiting", sizeof(q
->sound_calls
));
668 ast_copy_string(q
->sound_holdtime
, "queue-holdtime", sizeof(q
->sound_holdtime
));
669 ast_copy_string(q
->sound_minutes
, "queue-minutes", sizeof(q
->sound_minutes
));
670 ast_copy_string(q
->sound_seconds
, "queue-seconds", sizeof(q
->sound_seconds
));
671 ast_copy_string(q
->sound_thanks
, "queue-thankyou", sizeof(q
->sound_thanks
));
672 ast_copy_string(q
->sound_lessthan
, "queue-less-than", sizeof(q
->sound_lessthan
));
673 ast_copy_string(q
->sound_reporthold
, "queue-reporthold", sizeof(q
->sound_reporthold
));
674 ast_copy_string(q
->sound_periodicannounce
[0], "queue-periodic-announce", sizeof(q
->sound_periodicannounce
[0]));
675 for (i
= 1; i
< MAX_PERIODIC_ANNOUNCEMENTS
; i
++) {
676 q
->sound_periodicannounce
[i
][0]='\0';
680 static void clear_queue(struct call_queue
*q
)
683 q
->callscompleted
= 0;
684 q
->callsabandoned
= 0;
685 q
->callscompletedinsl
= 0;
689 static int add_to_interfaces(char *interface
)
691 struct member_interface
*curint
;
693 AST_LIST_LOCK(&interfaces
);
694 AST_LIST_TRAVERSE(&interfaces
, curint
, list
) {
695 if (!strcasecmp(curint
->interface
, interface
))
700 AST_LIST_UNLOCK(&interfaces
);
705 ast_log(LOG_DEBUG
, "Adding %s to the list of interfaces that make up all of our queue members.\n", interface
);
707 if ((curint
= ast_calloc(1, sizeof(*curint
)))) {
708 ast_copy_string(curint
->interface
, interface
, sizeof(curint
->interface
));
709 AST_LIST_INSERT_HEAD(&interfaces
, curint
, list
);
711 AST_LIST_UNLOCK(&interfaces
);
716 static int interface_exists_global(char *interface
)
718 struct call_queue
*q
;
722 AST_LIST_LOCK(&queues
);
723 AST_LIST_TRAVERSE(&queues
, q
, list
) {
724 ast_mutex_lock(&q
->lock
);
725 for (mem
= q
->members
; mem
&& !ret
; mem
= mem
->next
) {
726 if (!strcasecmp(interface
, mem
->interface
))
729 ast_mutex_unlock(&q
->lock
);
733 AST_LIST_UNLOCK(&queues
);
738 static int remove_from_interfaces(char *interface
)
740 struct member_interface
*curint
;
742 AST_LIST_LOCK(&interfaces
);
743 AST_LIST_TRAVERSE_SAFE_BEGIN(&interfaces
, curint
, list
) {
744 if (!strcasecmp(curint
->interface
, interface
)) {
745 if (!interface_exists_global(interface
)) {
747 ast_log(LOG_DEBUG
, "Removing %s from the list of interfaces that make up all of our queue members.\n", interface
);
748 AST_LIST_REMOVE_CURRENT(&interfaces
, list
);
754 AST_LIST_TRAVERSE_SAFE_END
;
755 AST_LIST_UNLOCK(&interfaces
);
760 static void clear_and_free_interfaces(void)
762 struct member_interface
*curint
;
764 AST_LIST_LOCK(&interfaces
);
765 while ((curint
= AST_LIST_REMOVE_HEAD(&interfaces
, list
)))
767 AST_LIST_UNLOCK(&interfaces
);
770 /*! \brief Configure a queue parameter.
772 For error reporting, line number is passed for .conf static configuration.
773 For Realtime queues, linenum is -1.
774 The failunknown flag is set for config files (and static realtime) to show
775 errors for unknown parameters. It is cleared for dynamic realtime to allow
776 extra fields in the tables. */
777 static void queue_set_param(struct call_queue
*q
, const char *param
, const char *val
, int linenum
, int failunknown
)
779 if (!strcasecmp(param
, "musicclass") ||
780 !strcasecmp(param
, "music") || !strcasecmp(param
, "musiconhold")) {
781 ast_copy_string(q
->moh
, val
, sizeof(q
->moh
));
782 } else if (!strcasecmp(param
, "announce")) {
783 ast_copy_string(q
->announce
, val
, sizeof(q
->announce
));
784 } else if (!strcasecmp(param
, "context")) {
785 ast_copy_string(q
->context
, val
, sizeof(q
->context
));
786 } else if (!strcasecmp(param
, "timeout")) {
787 q
->timeout
= atoi(val
);
789 q
->timeout
= DEFAULT_TIMEOUT
;
790 } else if (!strcasecmp(param
, "ringinuse")) {
791 q
->ringinuse
= ast_true(val
);
792 } else if (!strcasecmp(param
, "setinterfacevar")) {
793 q
->setinterfacevar
= ast_true(val
);
794 } else if (!strcasecmp(param
, "monitor-join")) {
795 q
->monjoin
= ast_true(val
);
796 } else if (!strcasecmp(param
, "monitor-format")) {
797 ast_copy_string(q
->monfmt
, val
, sizeof(q
->monfmt
));
798 } else if (!strcasecmp(param
, "queue-youarenext")) {
799 ast_copy_string(q
->sound_next
, val
, sizeof(q
->sound_next
));
800 } else if (!strcasecmp(param
, "queue-thereare")) {
801 ast_copy_string(q
->sound_thereare
, val
, sizeof(q
->sound_thereare
));
802 } else if (!strcasecmp(param
, "queue-callswaiting")) {
803 ast_copy_string(q
->sound_calls
, val
, sizeof(q
->sound_calls
));
804 } else if (!strcasecmp(param
, "queue-holdtime")) {
805 ast_copy_string(q
->sound_holdtime
, val
, sizeof(q
->sound_holdtime
));
806 } else if (!strcasecmp(param
, "queue-minutes")) {
807 ast_copy_string(q
->sound_minutes
, val
, sizeof(q
->sound_minutes
));
808 } else if (!strcasecmp(param
, "queue-seconds")) {
809 ast_copy_string(q
->sound_seconds
, val
, sizeof(q
->sound_seconds
));
810 } else if (!strcasecmp(param
, "queue-lessthan")) {
811 ast_copy_string(q
->sound_lessthan
, val
, sizeof(q
->sound_lessthan
));
812 } else if (!strcasecmp(param
, "queue-thankyou")) {
813 ast_copy_string(q
->sound_thanks
, val
, sizeof(q
->sound_thanks
));
814 } else if (!strcasecmp(param
, "queue-reporthold")) {
815 ast_copy_string(q
->sound_reporthold
, val
, sizeof(q
->sound_reporthold
));
816 } else if (!strcasecmp(param
, "announce-frequency")) {
817 q
->announcefrequency
= atoi(val
);
818 } else if (!strcasecmp(param
, "announce-round-seconds")) {
819 q
->roundingseconds
= atoi(val
);
820 if (q
->roundingseconds
>60 || q
->roundingseconds
<0) {
822 ast_log(LOG_WARNING
, "'%s' isn't a valid value for %s "
823 "using 0 instead for queue '%s' at line %d of queues.conf\n",
824 val
, param
, q
->name
, linenum
);
826 ast_log(LOG_WARNING
, "'%s' isn't a valid value for %s "
827 "using 0 instead for queue '%s'\n", val
, param
, q
->name
);
829 q
->roundingseconds
=0;
831 } else if (!strcasecmp(param
, "announce-holdtime")) {
832 if (!strcasecmp(val
, "once"))
833 q
->announceholdtime
= ANNOUNCEHOLDTIME_ONCE
;
834 else if (ast_true(val
))
835 q
->announceholdtime
= ANNOUNCEHOLDTIME_ALWAYS
;
837 q
->announceholdtime
= 0;
838 } else if (!strcasecmp(param
, "periodic-announce")) {
839 if (strchr(val
, '|')) {
840 char *s
, *buf
= ast_strdupa(val
);
843 while ((s
= strsep(&buf
, "|"))) {
844 ast_copy_string(q
->sound_periodicannounce
[i
], s
, sizeof(q
->sound_periodicannounce
[i
]));
846 if (i
== MAX_PERIODIC_ANNOUNCEMENTS
)
850 ast_copy_string(q
->sound_periodicannounce
[0], val
, sizeof(q
->sound_periodicannounce
[0]));
852 } else if (!strcasecmp(param
, "periodic-announce-frequency")) {
853 q
->periodicannouncefrequency
= atoi(val
);
854 } else if (!strcasecmp(param
, "retry")) {
855 q
->retry
= atoi(val
);
857 q
->retry
= DEFAULT_RETRY
;
858 } else if (!strcasecmp(param
, "wrapuptime")) {
859 q
->wrapuptime
= atoi(val
);
860 } else if (!strcasecmp(param
, "autofill")) {
861 q
->autofill
= ast_true(val
);
862 } else if (!strcasecmp(param
, "monitor-type")) {
863 if (!strcasecmp(val
, "mixmonitor"))
865 } else if (!strcasecmp(param
, "autopause")) {
866 q
->autopause
= ast_true(val
);
867 } else if (!strcasecmp(param
, "maxlen")) {
868 q
->maxlen
= atoi(val
);
871 } else if (!strcasecmp(param
, "servicelevel")) {
872 q
->servicelevel
= atoi(val
);
873 } else if (!strcasecmp(param
, "strategy")) {
874 q
->strategy
= strat2int(val
);
875 if (q
->strategy
< 0) {
876 ast_log(LOG_WARNING
, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
878 q
->strategy
= QUEUE_STRATEGY_RINGALL
;
880 } else if (!strcasecmp(param
, "joinempty")) {
881 if (!strcasecmp(val
, "strict"))
882 q
->joinempty
= QUEUE_EMPTY_STRICT
;
883 else if (ast_true(val
))
884 q
->joinempty
= QUEUE_EMPTY_NORMAL
;
887 } else if (!strcasecmp(param
, "leavewhenempty")) {
888 if (!strcasecmp(val
, "strict"))
889 q
->leavewhenempty
= QUEUE_EMPTY_STRICT
;
890 else if (ast_true(val
))
891 q
->leavewhenempty
= QUEUE_EMPTY_NORMAL
;
893 q
->leavewhenempty
= 0;
894 } else if (!strcasecmp(param
, "eventmemberstatus")) {
895 q
->maskmemberstatus
= !ast_true(val
);
896 } else if (!strcasecmp(param
, "eventwhencalled")) {
897 if (!strcasecmp(val
, "vars")) {
898 q
->eventwhencalled
= QUEUE_EVENT_VARIABLES
;
900 q
->eventwhencalled
= ast_true(val
);
902 } else if (!strcasecmp(param
, "reportholdtime")) {
903 q
->reportholdtime
= ast_true(val
);
904 } else if (!strcasecmp(param
, "memberdelay")) {
905 q
->memberdelay
= atoi(val
);
906 } else if (!strcasecmp(param
, "weight")) {
907 q
->weight
= atoi(val
);
910 /* With Realtime queues, if the last queue using weights is deleted in realtime,
911 we will not see any effect on use_weight until next reload. */
912 } else if (!strcasecmp(param
, "timeoutrestart")) {
913 q
->timeoutrestart
= ast_true(val
);
914 } else if (failunknown
) {
916 ast_log(LOG_WARNING
, "Unknown keyword in queue '%s': %s at line %d of queues.conf\n",
917 q
->name
, param
, linenum
);
919 ast_log(LOG_WARNING
, "Unknown keyword in queue '%s': %s\n", q
->name
, param
);
924 static void rt_handle_member_record(struct call_queue
*q
, char *interface
, const char *membername
, const char *penalty_str
)
926 struct member
*m
, *prev_m
;
930 penalty
= atoi(penalty_str
);
935 /* Find the member, or the place to put a new one. */
936 for (m
= q
->members
, prev_m
= NULL
;
937 m
&& strcmp(m
->interface
, interface
);
938 prev_m
= m
, m
= m
->next
);
940 /* Create a new one if not found, else update penalty */
942 if ((m
= create_queue_member(interface
, membername
, penalty
, 0))) {
944 add_to_interfaces(interface
);
952 m
->dead
= 0; /* Do not delete this one. */
953 m
->penalty
= penalty
;
957 static void free_members(struct call_queue
*q
, int all
)
959 /* Free non-dynamic members */
960 struct member
*curm
, *next
, *prev
= NULL
;
962 for (curm
= q
->members
; curm
; curm
= next
) {
964 if (all
|| !curm
->dynamic
) {
969 remove_from_interfaces(curm
->interface
);
976 static void destroy_queue(struct call_queue
*q
)
979 ast_mutex_destroy(&q
->lock
);
983 /*!\brief Reload a single queue via realtime.
984 \return Return the queue, or NULL if it doesn't exist.
985 \note Should be called with the global qlock locked. */
986 static struct call_queue
*find_queue_by_name_rt(const char *queuename
, struct ast_variable
*queue_vars
, struct ast_config
*member_config
)
988 struct ast_variable
*v
;
989 struct call_queue
*q
;
990 struct member
*m
, *prev_m
, *next_m
;
991 char *interface
= NULL
;
992 char *tmp
, *tmp_name
;
993 char tmpbuf
[64]; /* Must be longer than the longest queue param name. */
995 /* Find the queue in the in-core list (we will create a new one if not found). */
996 AST_LIST_TRAVERSE(&queues
, q
, list
) {
997 if (!strcasecmp(q
->name
, queuename
))
1001 /* Static queues override realtime. */
1003 ast_mutex_lock(&q
->lock
);
1006 ast_mutex_unlock(&q
->lock
);
1009 ast_mutex_unlock(&q
->lock
);
1013 } else if (!member_config
)
1014 /* Not found in the list, and it's not realtime ... */
1017 /* Check if queue is defined in realtime. */
1019 /* Delete queue from in-core list if it has been deleted in realtime. */
1021 /*! \note Hmm, can't seem to distinguish a DB failure from a not
1022 found condition... So we might delete an in-core queue
1023 in case of DB failure. */
1024 ast_log(LOG_DEBUG
, "Queue %s not found in realtime.\n", queuename
);
1027 /* Delete if unused (else will be deleted when last caller leaves). */
1030 AST_LIST_REMOVE(&queues
, q
, list
);
1031 ast_mutex_unlock(&q
->lock
);
1034 ast_mutex_unlock(&q
->lock
);
1039 /* Create a new queue if an in-core entry does not exist yet. */
1041 if (!(q
= alloc_queue(queuename
)))
1043 ast_mutex_lock(&q
->lock
);
1046 AST_LIST_INSERT_HEAD(&queues
, q
, list
);
1048 init_queue(q
); /* Ensure defaults for all parameters not set explicitly. */
1050 memset(tmpbuf
, 0, sizeof(tmpbuf
));
1051 for (v
= queue_vars
; v
; v
= v
->next
) {
1052 /* Convert to dashes `-' from underscores `_' as the latter are more SQL friendly. */
1053 if ((tmp
= strchr(v
->name
, '_'))) {
1054 ast_copy_string(tmpbuf
, v
->name
, sizeof(tmpbuf
));
1057 while ((tmp
= strchr(tmp
, '_')))
1061 queue_set_param(q
, tmp_name
, v
->value
, -1, 0);
1064 if (q
->strategy
== QUEUE_STRATEGY_ROUNDROBIN
)
1067 /* Temporarily set non-dynamic members dead so we can detect deleted ones. */
1068 for (m
= q
->members
; m
; m
= m
->next
) {
1073 while ((interface
= ast_category_browse(member_config
, interface
))) {
1074 rt_handle_member_record(q
, interface
,
1075 S_OR(ast_variable_retrieve(member_config
, interface
, "membername"), interface
),
1076 ast_variable_retrieve(member_config
, interface
, "penalty"));
1079 /* Delete all realtime members that have been deleted in DB. */
1086 prev_m
->next
= next_m
;
1088 q
->members
= next_m
;
1090 remove_from_interfaces(m
->interface
);
1098 ast_mutex_unlock(&q
->lock
);
1103 static struct call_queue
*load_realtime_queue(char *queuename
)
1105 struct ast_variable
*queue_vars
;
1106 struct ast_config
*member_config
= NULL
;
1107 struct call_queue
*q
;
1109 /* Find the queue in the in-core list first. */
1110 AST_LIST_LOCK(&queues
);
1111 AST_LIST_TRAVERSE(&queues
, q
, list
) {
1112 if (!strcasecmp(q
->name
, queuename
)) {
1116 AST_LIST_UNLOCK(&queues
);
1118 if (!q
|| q
->realtime
) {
1119 /*! \note Load from realtime before taking the global qlock, to avoid blocking all
1120 queue operations while waiting for the DB.
1122 This will be two separate database transactions, so we might
1123 see queue parameters as they were before another process
1124 changed the queue and member list as it was after the change.
1125 Thus we might see an empty member list when a queue is
1126 deleted. In practise, this is unlikely to cause a problem. */
1128 queue_vars
= ast_load_realtime("queues", "name", queuename
, NULL
);
1130 member_config
= ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", queuename
, NULL
);
1131 if (!member_config
) {
1132 ast_log(LOG_ERROR
, "no queue_members defined in your config (extconfig.conf).\n");
1137 AST_LIST_LOCK(&queues
);
1139 q
= find_queue_by_name_rt(queuename
, queue_vars
, member_config
);
1141 ast_config_destroy(member_config
);
1143 ast_variables_destroy(queue_vars
);
1145 AST_LIST_UNLOCK(&queues
);
1150 static int join_queue(char *queuename
, struct queue_ent
*qe
, enum queue_result
*reason
)
1152 struct call_queue
*q
;
1153 struct queue_ent
*cur
, *prev
= NULL
;
1157 enum queue_member_status stat
;
1159 if (!(q
= load_realtime_queue(queuename
)))
1162 AST_LIST_LOCK(&queues
);
1163 ast_mutex_lock(&q
->lock
);
1165 /* This is our one */
1166 stat
= get_member_status(q
, qe
->max_penalty
);
1167 if (!q
->joinempty
&& (stat
== QUEUE_NO_MEMBERS
))
1168 *reason
= QUEUE_JOINEMPTY
;
1169 else if ((q
->joinempty
== QUEUE_EMPTY_STRICT
) && (stat
== QUEUE_NO_REACHABLE_MEMBERS
))
1170 *reason
= QUEUE_JOINUNAVAIL
;
1171 else if (q
->maxlen
&& (q
->count
>= q
->maxlen
))
1172 *reason
= QUEUE_FULL
;
1174 /* There's space for us, put us at the right position inside
1176 * Take into account the priority of the calling user */
1181 /* We have higher priority than the current user, enter
1182 * before him, after all the other users with priority
1183 * higher or equal to our priority. */
1184 if ((!inserted
) && (qe
->prio
> cur
->prio
)) {
1185 insert_entry(q
, prev
, qe
, &pos
);
1192 /* No luck, join at the end of the queue */
1194 insert_entry(q
, prev
, qe
, &pos
);
1195 ast_copy_string(qe
->moh
, q
->moh
, sizeof(qe
->moh
));
1196 ast_copy_string(qe
->announce
, q
->announce
, sizeof(qe
->announce
));
1197 ast_copy_string(qe
->context
, q
->context
, sizeof(qe
->context
));
1200 manager_event(EVENT_FLAG_CALL
, "Join",
1201 "Channel: %s\r\nCallerID: %s\r\nCallerIDName: %s\r\nQueue: %s\r\nPosition: %d\r\nCount: %d\r\nUniqueid: %s\r\n",
1203 S_OR(qe
->chan
->cid
.cid_num
, "unknown"), /* XXX somewhere else it is <unknown> */
1204 S_OR(qe
->chan
->cid
.cid_name
, "unknown"),
1205 q
->name
, qe
->pos
, q
->count
, qe
->chan
->uniqueid
);
1207 ast_log(LOG_DEBUG
, "Queue '%s' Join, Channel '%s', Position '%d'\n", q
->name
, qe
->chan
->name
, qe
->pos
);
1209 ast_mutex_unlock(&q
->lock
);
1210 AST_LIST_UNLOCK(&queues
);
1215 static int play_file(struct ast_channel
*chan
, char *filename
)
1219 ast_stopstream(chan
);
1220 res
= ast_streamfile(chan
, filename
, chan
->language
);
1222 res
= ast_waitstream(chan
, AST_DIGIT_ANY
);
1223 ast_stopstream(chan
);
1228 static int valid_exit(struct queue_ent
*qe
, char digit
)
1230 int digitlen
= strlen(qe
->digits
);
1232 /* Prevent possible buffer overflow */
1233 if (digitlen
< sizeof(qe
->digits
) - 2) {
1234 qe
->digits
[digitlen
] = digit
;
1235 qe
->digits
[digitlen
+ 1] = '\0';
1237 qe
->digits
[0] = '\0';
1241 /* If there's no context to goto, short-circuit */
1242 if (ast_strlen_zero(qe
->context
))
1245 /* If the extension is bad, then reset the digits to blank */
1246 if (!ast_canmatch_extension(qe
->chan
, qe
->context
, qe
->digits
, 1, qe
->chan
->cid
.cid_num
)) {
1247 qe
->digits
[0] = '\0';
1251 /* We have an exact match */
1252 if (!ast_goto_if_exists(qe
->chan
, qe
->context
, qe
->digits
, 1)) {
1253 /* Return 1 on a successful goto */
1260 static int say_position(struct queue_ent
*qe
)
1262 int res
= 0, avgholdmins
, avgholdsecs
;
1265 /* Check to see if this is ludicrous -- if we just announced position, don't do it again*/
1267 if ((now
- qe
->last_pos
) < 15)
1270 /* If either our position has changed, or we are over the freq timer, say position */
1271 if ((qe
->last_pos_said
== qe
->pos
) && ((now
- qe
->last_pos
) < qe
->parent
->announcefrequency
))
1274 ast_moh_stop(qe
->chan
);
1275 /* Say we're next, if we are */
1277 res
= play_file(qe
->chan
, qe
->parent
->sound_next
);
1278 if (res
&& valid_exit(qe
, res
))
1283 res
= play_file(qe
->chan
, qe
->parent
->sound_thereare
);
1284 if (res
&& valid_exit(qe
, res
))
1286 res
= ast_say_number(qe
->chan
, qe
->pos
, AST_DIGIT_ANY
, qe
->chan
->language
, (char *) NULL
); /* Needs gender */
1287 if (res
&& valid_exit(qe
, res
))
1289 res
= play_file(qe
->chan
, qe
->parent
->sound_calls
);
1290 if (res
&& valid_exit(qe
, res
))
1293 /* Round hold time to nearest minute */
1294 avgholdmins
= abs(((qe
->parent
->holdtime
+ 30) - (now
- qe
->start
)) / 60);
1296 /* If they have specified a rounding then round the seconds as well */
1297 if (qe
->parent
->roundingseconds
) {
1298 avgholdsecs
= (abs(((qe
->parent
->holdtime
+ 30) - (now
- qe
->start
))) - 60 * avgholdmins
) / qe
->parent
->roundingseconds
;
1299 avgholdsecs
*= qe
->parent
->roundingseconds
;
1304 if (option_verbose
> 2)
1305 ast_verbose(VERBOSE_PREFIX_3
"Hold time for %s is %d minutes %d seconds\n", qe
->parent
->name
, avgholdmins
, avgholdsecs
);
1307 /* If the hold time is >1 min, if it's enabled, and if it's not
1308 supposed to be only once and we have already said it, say it */
1309 if ((avgholdmins
+avgholdsecs
) > 0 && (qe
->parent
->announceholdtime
) &&
1310 (!(qe
->parent
->announceholdtime
== ANNOUNCEHOLDTIME_ONCE
) && qe
->last_pos
)) {
1311 res
= play_file(qe
->chan
, qe
->parent
->sound_holdtime
);
1312 if (res
&& valid_exit(qe
, res
))
1315 if (avgholdmins
> 0) {
1316 if (avgholdmins
< 2) {
1317 res
= play_file(qe
->chan
, qe
->parent
->sound_lessthan
);
1318 if (res
&& valid_exit(qe
, res
))
1321 res
= ast_say_number(qe
->chan
, 2, AST_DIGIT_ANY
, qe
->chan
->language
, NULL
);
1322 if (res
&& valid_exit(qe
, res
))
1325 res
= ast_say_number(qe
->chan
, avgholdmins
, AST_DIGIT_ANY
, qe
->chan
->language
, NULL
);
1326 if (res
&& valid_exit(qe
, res
))
1330 res
= play_file(qe
->chan
, qe
->parent
->sound_minutes
);
1331 if (res
&& valid_exit(qe
, res
))
1334 if (avgholdsecs
>0) {
1335 res
= ast_say_number(qe
->chan
, avgholdsecs
, AST_DIGIT_ANY
, qe
->chan
->language
, NULL
);
1336 if (res
&& valid_exit(qe
, res
))
1339 res
= play_file(qe
->chan
, qe
->parent
->sound_seconds
);
1340 if (res
&& valid_exit(qe
, res
))
1347 if (option_verbose
> 2)
1348 ast_verbose(VERBOSE_PREFIX_3
"Told %s in %s their queue position (which was %d)\n",
1349 qe
->chan
->name
, qe
->parent
->name
, qe
->pos
);
1350 res
= play_file(qe
->chan
, qe
->parent
->sound_thanks
);
1351 if (res
&& !valid_exit(qe
, res
))
1355 /* Set our last_pos indicators */
1357 qe
->last_pos_said
= qe
->pos
;
1359 /* Don't restart music on hold if we're about to exit the caller from the queue */
1361 ast_moh_start(qe
->chan
, qe
->moh
, NULL
);
1366 static void recalc_holdtime(struct queue_ent
*qe
)
1368 int oldvalue
, newvalue
;
1370 /* Calculate holdtime using a recursive boxcar filter */
1371 /* Thanks to SRT for this contribution */
1372 /* 2^2 (4) is the filter coefficient; a higher exponent would give old entries more weight */
1374 newvalue
= time(NULL
) - qe
->start
;
1376 ast_mutex_lock(&qe
->parent
->lock
);
1377 if (newvalue
<= qe
->parent
->servicelevel
)
1378 qe
->parent
->callscompletedinsl
++;
1379 oldvalue
= qe
->parent
->holdtime
;
1380 qe
->parent
->holdtime
= (((oldvalue
<< 2) - oldvalue
) + newvalue
) >> 2;
1381 ast_mutex_unlock(&qe
->parent
->lock
);
1385 static void leave_queue(struct queue_ent
*qe
)
1387 struct call_queue
*q
;
1388 struct queue_ent
*cur
, *prev
= NULL
;
1391 if (!(q
= qe
->parent
))
1393 ast_mutex_lock(&q
->lock
);
1396 for (cur
= q
->head
; cur
; cur
= cur
->next
) {
1400 /* Take us out of the queue */
1401 manager_event(EVENT_FLAG_CALL
, "Leave",
1402 "Channel: %s\r\nQueue: %s\r\nCount: %d\r\nUniqueid: %s\r\n",
1403 qe
->chan
->name
, q
->name
, q
->count
, qe
->chan
->uniqueid
);
1405 ast_log(LOG_DEBUG
, "Queue '%s' Leave, Channel '%s'\n", q
->name
, qe
->chan
->name
);
1406 /* Take us out of the queue */
1408 prev
->next
= cur
->next
;
1410 q
->head
= cur
->next
;
1412 /* Renumber the people after us in the queue based on a new count */
1417 ast_mutex_unlock(&q
->lock
);
1419 if (q
->dead
&& !q
->count
) {
1420 /* It's dead and nobody is in it, so kill it */
1421 AST_LIST_LOCK(&queues
);
1422 AST_LIST_REMOVE(&queues
, q
, list
);
1423 AST_LIST_UNLOCK(&queues
);
1428 /* Hang up a list of outgoing calls */
1429 static void hangupcalls(struct callattempt
*outgoing
, struct ast_channel
*exception
)
1431 struct callattempt
*oo
;
1434 /* Hangup any existing lines we have open */
1435 if (outgoing
->chan
&& (outgoing
->chan
!= exception
))
1436 ast_hangup(outgoing
->chan
);
1438 outgoing
= outgoing
->q_next
;
1443 static int update_status(struct call_queue
*q
, struct member
*member
, int status
)
1447 /* Since a reload could have taken place, we have to traverse the list to
1448 be sure it's still valid */
1449 ast_mutex_lock(&q
->lock
);
1450 for (cur
= q
->members
; cur
; cur
= cur
->next
) {
1454 cur
->status
= status
;
1455 if (!q
->maskmemberstatus
) {
1456 manager_event(EVENT_FLAG_AGENT
, "QueueMemberStatus",
1459 "MemberName: %s\r\n"
1460 "Membership: %s\r\n"
1462 "CallsTaken: %d\r\n"
1466 q
->name
, cur
->interface
, cur
->membername
, cur
->dynamic
? "dynamic" : "static",
1467 cur
->penalty
, cur
->calls
, (int)cur
->lastcall
, cur
->status
, cur
->paused
);
1470 ast_mutex_unlock(&q
->lock
);
1474 static int update_dial_status(struct call_queue
*q
, struct member
*member
, int status
)
1476 if (status
== AST_CAUSE_BUSY
)
1477 status
= AST_DEVICE_BUSY
;
1478 else if (status
== AST_CAUSE_UNREGISTERED
)
1479 status
= AST_DEVICE_UNAVAILABLE
;
1480 else if (status
== AST_CAUSE_NOSUCHDRIVER
)
1481 status
= AST_DEVICE_INVALID
;
1483 status
= AST_DEVICE_UNKNOWN
;
1484 return update_status(q
, member
, status
);
1487 /* traverse all defined queues which have calls waiting and contain this member
1488 return 0 if no other queue has precedence (higher weight) or 1 if found */
1489 static int compare_weight(struct call_queue
*rq
, struct member
*member
)
1491 struct call_queue
*q
;
1495 /* &qlock and &rq->lock already set by try_calling()
1496 * to solve deadlock */
1497 AST_LIST_TRAVERSE(&queues
, q
, list
) {
1498 if (q
== rq
) /* don't check myself, could deadlock */
1500 ast_mutex_lock(&q
->lock
);
1501 if (q
->count
&& q
->members
) {
1502 for (mem
= q
->members
; mem
; mem
= mem
->next
) {
1503 if (strcmp(mem
->interface
, member
->interface
))
1506 ast_log(LOG_DEBUG
, "Found matching member %s in queue '%s'\n", mem
->interface
, q
->name
);
1507 if (q
->weight
> rq
->weight
) {
1508 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
);
1514 ast_mutex_unlock(&q
->lock
);
1521 /*! \brief common hangup actions */
1522 static void do_hang(struct callattempt
*o
)
1525 ast_hangup(o
->chan
);
1529 static char *vars2manager(struct ast_channel
*chan
, char *vars
, size_t len
)
1531 char *tmp
= alloca(len
);
1533 if (pbx_builtin_serialize_variables(chan
, tmp
, len
)) {
1536 /* convert "\n" to "\nVariable: " */
1537 strcpy(vars
, "Variable: ");
1539 for (i
= 0, j
= 10; (i
< len
- 1) && (j
< len
- 1); i
++, j
++) {
1542 if (tmp
[i
+ 1] == '\0')
1544 if (tmp
[i
] == '\n') {
1548 ast_copy_string(&(vars
[j
]), "Variable: ", len
- j
);
1558 /* there are no channel variables; leave it blank */
1564 static int ring_entry(struct queue_ent
*qe
, struct callattempt
*tmp
, int *busies
)
1571 /* on entry here, we know that tmp->chan == NULL */
1572 if (qe
->parent
->wrapuptime
&& (time(NULL
) - tmp
->lastcall
< qe
->parent
->wrapuptime
)) {
1574 ast_log(LOG_DEBUG
, "Wrapuptime not yet expired for %s\n", tmp
->interface
);
1576 ast_cdr_busy(qe
->chan
->cdr
);
1577 tmp
->stillgoing
= 0;
1582 if (!qe
->parent
->ringinuse
&& (tmp
->member
->status
!= AST_DEVICE_NOT_INUSE
) && (tmp
->member
->status
!= AST_DEVICE_UNKNOWN
)) {
1584 ast_log(LOG_DEBUG
, "%s in use, can't receive call\n", tmp
->interface
);
1586 ast_cdr_busy(qe
->chan
->cdr
);
1587 tmp
->stillgoing
= 0;
1591 if (tmp
->member
->paused
) {
1593 ast_log(LOG_DEBUG
, "%s paused, can't receive call\n", tmp
->interface
);
1595 ast_cdr_busy(qe
->chan
->cdr
);
1596 tmp
->stillgoing
= 0;
1599 if (use_weight
&& compare_weight(qe
->parent
,tmp
->member
)) {
1600 ast_log(LOG_DEBUG
, "Priority queue delaying call to %s:%s\n", qe
->parent
->name
, tmp
->interface
);
1602 ast_cdr_busy(qe
->chan
->cdr
);
1603 tmp
->stillgoing
= 0;
1608 ast_copy_string(tech
, tmp
->interface
, sizeof(tech
));
1609 if ((location
= strchr(tech
, '/')))
1614 /* Request the peer */
1615 tmp
->chan
= ast_request(tech
, qe
->chan
->nativeformats
, location
, &status
);
1616 if (!tmp
->chan
) { /* If we can't, just go on to the next call */
1618 ast_cdr_busy(qe
->chan
->cdr
);
1619 tmp
->stillgoing
= 0;
1620 update_dial_status(qe
->parent
, tmp
->member
, status
);
1622 ast_mutex_lock(&qe
->parent
->lock
);
1623 qe
->parent
->rrpos
++;
1624 ast_mutex_unlock(&qe
->parent
->lock
);
1628 } else if (status
!= tmp
->oldstatus
)
1629 update_dial_status(qe
->parent
, tmp
->member
, status
);
1631 tmp
->chan
->appl
= "AppQueue";
1632 tmp
->chan
->data
= "(Outgoing Line)";
1633 tmp
->chan
->whentohangup
= 0;
1634 if (tmp
->chan
->cid
.cid_num
)
1635 free(tmp
->chan
->cid
.cid_num
);
1636 tmp
->chan
->cid
.cid_num
= ast_strdup(qe
->chan
->cid
.cid_num
);
1637 if (tmp
->chan
->cid
.cid_name
)
1638 free(tmp
->chan
->cid
.cid_name
);
1639 tmp
->chan
->cid
.cid_name
= ast_strdup(qe
->chan
->cid
.cid_name
);
1640 if (tmp
->chan
->cid
.cid_ani
)
1641 free(tmp
->chan
->cid
.cid_ani
);
1642 tmp
->chan
->cid
.cid_ani
= ast_strdup(qe
->chan
->cid
.cid_ani
);
1644 /* Inherit specially named variables from parent channel */
1645 ast_channel_inherit_variables(qe
->chan
, tmp
->chan
);
1647 /* Presense of ADSI CPE on outgoing channel follows ours */
1648 tmp
->chan
->adsicpe
= qe
->chan
->adsicpe
;
1650 /* Place the call, but don't wait on the answer */
1651 if ((res
= ast_call(tmp
->chan
, location
, 0))) {
1652 /* Again, keep going even if there's an error */
1654 ast_log(LOG_DEBUG
, "ast call on peer returned %d\n", res
);
1655 if (option_verbose
> 2)
1656 ast_verbose(VERBOSE_PREFIX_3
"Couldn't call %s\n", tmp
->interface
);
1660 } else if (qe
->parent
->eventwhencalled
) {
1663 manager_event(EVENT_FLAG_AGENT
, "AgentCalled",
1664 "AgentCalled: %s\r\n"
1665 "ChannelCalling: %s\r\n"
1667 "CallerIDName: %s\r\n"
1672 tmp
->interface
, qe
->chan
->name
,
1673 tmp
->chan
->cid
.cid_num
? tmp
->chan
->cid
.cid_num
: "unknown",
1674 tmp
->chan
->cid
.cid_name
? tmp
->chan
->cid
.cid_name
: "unknown",
1675 qe
->chan
->context
, qe
->chan
->exten
, qe
->chan
->priority
,
1676 qe
->parent
->eventwhencalled
== QUEUE_EVENT_VARIABLES
? vars2manager(qe
->chan
, vars
, sizeof(vars
)) : "");
1677 if (option_verbose
> 2)
1678 ast_verbose(VERBOSE_PREFIX_3
"Called %s\n", tmp
->interface
);
1684 /*! \brief find the entry with the best metric, or NULL */
1685 static struct callattempt
*find_best(struct callattempt
*outgoing
)
1687 struct callattempt
*best
= NULL
, *cur
;
1689 for (cur
= outgoing
; cur
; cur
= cur
->q_next
) {
1690 if (cur
->stillgoing
&& /* Not already done */
1691 !cur
->chan
&& /* Isn't already going */
1692 (!best
|| cur
->metric
< best
->metric
)) { /* We haven't found one yet, or it's better */
1700 static int ring_one(struct queue_ent
*qe
, struct callattempt
*outgoing
, int *busies
)
1705 struct callattempt
*best
= find_best(outgoing
);
1708 ast_log(LOG_DEBUG
, "Nobody left to try ringing in queue\n");
1711 if (qe
->parent
->strategy
== QUEUE_STRATEGY_RINGALL
) {
1712 struct callattempt
*cur
;
1713 /* Ring everyone who shares this best metric (for ringall) */
1714 for (cur
= outgoing
; cur
; cur
= cur
->q_next
) {
1715 if (cur
->stillgoing
&& !cur
->chan
&& cur
->metric
<= best
->metric
) {
1717 ast_log(LOG_DEBUG
, "(Parallel) Trying '%s' with metric %d\n", cur
->interface
, cur
->metric
);
1718 ring_entry(qe
, cur
, busies
);
1722 /* Ring just the best channel */
1724 ast_log(LOG_DEBUG
, "Trying '%s' with metric %d\n", best
->interface
, best
->metric
);
1725 ring_entry(qe
, best
, busies
);
1727 if (best
->chan
) /* break out with result = 1 */
1734 static int store_next(struct queue_ent
*qe
, struct callattempt
*outgoing
)
1736 struct callattempt
*best
= find_best(outgoing
);
1739 /* Ring just the best channel */
1741 ast_log(LOG_DEBUG
, "Next is '%s' with metric %d\n", best
->interface
, best
->metric
);
1742 qe
->parent
->rrpos
= best
->metric
% 1000;
1744 /* Just increment rrpos */
1745 if (qe
->parent
->wrapped
) {
1746 /* No more channels, start over */
1747 qe
->parent
->rrpos
= 0;
1749 /* Prioritize next entry */
1750 qe
->parent
->rrpos
++;
1753 qe
->parent
->wrapped
= 0;
1758 static int background_file(struct queue_ent
*qe
, struct ast_channel
*chan
, char *filename
)
1762 ast_stopstream(chan
);
1763 res
= ast_streamfile(chan
, filename
, chan
->language
);
1766 /* Wait for a keypress */
1767 res
= ast_waitstream(chan
, AST_DIGIT_ANY
);
1768 if (res
< 0 || !valid_exit(qe
, res
))
1772 ast_stopstream(chan
);
1778 static int say_periodic_announcement(struct queue_ent
*qe
)
1783 /* Get the current time */
1786 /* Check to see if it is time to announce */
1787 if ((now
- qe
->last_periodic_announce_time
) < qe
->parent
->periodicannouncefrequency
)
1790 /* Stop the music on hold so we can play our own file */
1791 ast_moh_stop(qe
->chan
);
1793 if (option_verbose
> 2)
1794 ast_verbose(VERBOSE_PREFIX_3
"Playing periodic announcement\n");
1796 /* Check to make sure we have a sound file. If not, reset to the first sound file */
1797 if (qe
->last_periodic_announce_sound
>= MAX_PERIODIC_ANNOUNCEMENTS
|| !strlen(qe
->parent
->sound_periodicannounce
[qe
->last_periodic_announce_sound
])) {
1798 qe
->last_periodic_announce_sound
= 0;
1801 /* play the announcement */
1802 res
= background_file(qe
, qe
->chan
, qe
->parent
->sound_periodicannounce
[qe
->last_periodic_announce_sound
]);
1804 /* Resume Music on Hold if the caller is going to stay in the queue */
1806 ast_moh_start(qe
->chan
, qe
->moh
, NULL
);
1808 /* update last_periodic_announce_time */
1809 qe
->last_periodic_announce_time
= now
;
1811 /* Update the current periodic announcement to the next announcement */
1812 qe
->last_periodic_announce_sound
++;
1817 static void record_abandoned(struct queue_ent
*qe
)
1819 ast_mutex_lock(&qe
->parent
->lock
);
1820 manager_event(EVENT_FLAG_AGENT
, "QueueCallerAbandon",
1824 "OriginalPosition: %d\r\n"
1826 qe
->parent
->name
, qe
->chan
->uniqueid
, qe
->pos
, qe
->opos
, (int)(time(NULL
) - qe
->start
));
1828 qe
->parent
->callsabandoned
++;
1829 ast_mutex_unlock(&qe
->parent
->lock
);
1832 /*! \brief RNA == Ring No Answer. Common code that is executed when we try a queue member and they don't answer. */
1833 static void rna(int rnatime
, struct queue_ent
*qe
, char *interface
, char *membername
)
1835 if (option_verbose
> 2)
1836 ast_verbose( VERBOSE_PREFIX_3
"Nobody picked up in %d ms\n", rnatime
);
1837 ast_queue_log(qe
->parent
->name
, qe
->chan
->uniqueid
, membername
, "RINGNOANSWER", "%d", rnatime
);
1838 if (qe
->parent
->autopause
) {
1839 if (!set_member_paused(qe
->parent
->name
, interface
, 1)) {
1840 if (option_verbose
> 2)
1841 ast_verbose( VERBOSE_PREFIX_3
"Auto-Pausing Queue Member %s in queue %s since they failed to answer.\n", interface
, qe
->parent
->name
);
1843 if (option_verbose
> 2)
1844 ast_verbose( VERBOSE_PREFIX_3
"Failed to pause Queue Member %s in queue %s!\n", interface
, qe
->parent
->name
);
1850 #define AST_MAX_WATCHERS 256
1852 static struct callattempt
*wait_for_answer(struct queue_ent
*qe
, struct callattempt
*outgoing
, int *to
, char *digit
, int prebusies
, int caller_disconnect
, int forwardsallowed
)
1854 char *queue
= qe
->parent
->name
;
1855 struct callattempt
*o
;
1857 int sentringing
= 0;
1858 int numbusies
= prebusies
;
1862 struct ast_frame
*f
;
1863 struct callattempt
*peer
= NULL
;
1864 struct ast_channel
*winner
;
1865 struct ast_channel
*in
= qe
->chan
;
1867 char membername
[80] = "";
1871 starttime
= (long) time(NULL
);
1873 while (*to
&& !peer
) {
1874 int numlines
, retry
, pos
= 1;
1875 struct ast_channel
*watchers
[AST_MAX_WATCHERS
];
1878 for (retry
= 0; retry
< 2; retry
++) {
1880 for (o
= outgoing
; o
; o
= o
->q_next
) { /* Keep track of important channels */
1881 if (o
->stillgoing
) { /* Keep track of important channels */
1884 watchers
[pos
++] = o
->chan
;
1888 if (pos
> 1 /* found */ || !stillgoing
/* nobody listening */ ||
1889 (qe
->parent
->strategy
!= QUEUE_STRATEGY_RINGALL
) /* ring would not be delivered */)
1891 /* On "ringall" strategy we only move to the next penalty level
1892 when *all* ringing phones are done in the current penalty level */
1893 ring_one(qe
, outgoing
, &numbusies
);
1896 if (pos
== 1 /* not found */) {
1897 if (numlines
== (numbusies
+ numnochan
)) {
1898 ast_log(LOG_DEBUG
, "Everyone is busy at this time\n");
1900 ast_log(LOG_NOTICE
, "No one is answering queue '%s' (%d/%d/%d)\n", queue
, numlines
, numbusies
, numnochan
);
1905 winner
= ast_waitfor_n(watchers
, pos
, to
);
1906 for (o
= outgoing
; o
; o
= o
->q_next
) {
1907 if (o
->stillgoing
&& (o
->chan
) && (o
->chan
->_state
== AST_STATE_UP
)) {
1909 if (option_verbose
> 2)
1910 ast_verbose( VERBOSE_PREFIX_3
"%s answered %s\n", o
->chan
->name
, in
->name
);
1913 } else if (o
->chan
&& (o
->chan
== winner
)) {
1915 ast_copy_string(on
, o
->member
->interface
, sizeof(on
));
1916 ast_copy_string(membername
, o
->member
->membername
, sizeof(membername
));
1918 if (!ast_strlen_zero(o
->chan
->call_forward
) && !forwardsallowed
) {
1919 if (option_verbose
> 2)
1920 ast_verbose(VERBOSE_PREFIX_3
"Forwarding %s to '%s' prevented.\n", in
->name
, o
->chan
->call_forward
);
1921 winner
= o
->chan
= NULL
;
1922 } else if (!ast_strlen_zero(o
->chan
->call_forward
)) {
1927 ast_copy_string(tmpchan
, o
->chan
->call_forward
, sizeof(tmpchan
));
1928 if ((stuff
= strchr(tmpchan
, '/'))) {
1932 snprintf(tmpchan
, sizeof(tmpchan
), "%s@%s", o
->chan
->call_forward
, o
->chan
->context
);
1936 /* Before processing channel, go ahead and check for forwarding */
1937 if (option_verbose
> 2)
1938 ast_verbose(VERBOSE_PREFIX_3
"Now forwarding %s to '%s/%s' (thanks to %s)\n", in
->name
, tech
, stuff
, o
->chan
->name
);
1939 /* Setup parameters */
1940 o
->chan
= ast_request(tech
, in
->nativeformats
, stuff
, &status
);
1941 if (status
!= o
->oldstatus
)
1942 update_dial_status(qe
->parent
, o
->member
, status
);
1944 ast_log(LOG_NOTICE
, "Unable to create local channel for call forward to '%s/%s'\n", tech
, stuff
);
1948 ast_channel_inherit_variables(in
, o
->chan
);
1949 if (o
->chan
->cid
.cid_num
)
1950 free(o
->chan
->cid
.cid_num
);
1951 o
->chan
->cid
.cid_num
= ast_strdup(in
->cid
.cid_num
);
1953 if (o
->chan
->cid
.cid_name
)
1954 free(o
->chan
->cid
.cid_name
);
1955 o
->chan
->cid
.cid_name
= ast_strdup(in
->cid
.cid_name
);
1957 ast_string_field_set(o
->chan
, accountcode
, in
->accountcode
);
1958 o
->chan
->cdrflags
= in
->cdrflags
;
1960 if (in
->cid
.cid_ani
) {
1961 if (o
->chan
->cid
.cid_ani
)
1962 free(o
->chan
->cid
.cid_ani
);
1963 o
->chan
->cid
.cid_ani
= ast_strdup(in
->cid
.cid_ani
);
1965 if (o
->chan
->cid
.cid_rdnis
)
1966 free(o
->chan
->cid
.cid_rdnis
);
1967 o
->chan
->cid
.cid_rdnis
= ast_strdup(S_OR(in
->macroexten
, in
->exten
));
1968 if (ast_call(o
->chan
, tmpchan
, 0)) {
1969 ast_log(LOG_NOTICE
, "Failed to dial on local channel for call forward to '%s'\n", tmpchan
);
1974 /* Hangup the original channel now, in case we needed it */
1978 f
= ast_read(winner
);
1980 if (f
->frametype
== AST_FRAME_CONTROL
) {
1981 switch (f
->subclass
) {
1982 case AST_CONTROL_ANSWER
:
1983 /* This is our guy if someone answered. */
1985 if (option_verbose
> 2)
1986 ast_verbose( VERBOSE_PREFIX_3
"%s answered %s\n", o
->chan
->name
, in
->name
);
1990 case AST_CONTROL_BUSY
:
1991 if (option_verbose
> 2)
1992 ast_verbose( VERBOSE_PREFIX_3
"%s is busy\n", o
->chan
->name
);
1994 ast_cdr_busy(in
->cdr
);
1996 endtime
= (long)time(NULL
);
1997 endtime
-= starttime
;
1998 rna(endtime
*1000, qe
, on
, membername
);
1999 if (qe
->parent
->strategy
!= QUEUE_STRATEGY_RINGALL
) {
2000 if (qe
->parent
->timeoutrestart
)
2002 ring_one(qe
, outgoing
, &numbusies
);
2006 case AST_CONTROL_CONGESTION
:
2007 if (option_verbose
> 2)
2008 ast_verbose( VERBOSE_PREFIX_3
"%s is circuit-busy\n", o
->chan
->name
);
2010 ast_cdr_busy(in
->cdr
);
2011 endtime
= (long)time(NULL
);
2012 endtime
-= starttime
;
2013 rna(endtime
*1000, qe
, on
, membername
);
2015 if (qe
->parent
->strategy
!= QUEUE_STRATEGY_RINGALL
) {
2016 if (qe
->parent
->timeoutrestart
)
2018 ring_one(qe
, outgoing
, &numbusies
);
2022 case AST_CONTROL_RINGING
:
2023 if (option_verbose
> 2)
2024 ast_verbose( VERBOSE_PREFIX_3
"%s is ringing\n", o
->chan
->name
);
2027 ast_indicate(in
, AST_CONTROL_RINGING
);
2032 case AST_CONTROL_OFFHOOK
:
2033 /* Ignore going off hook */
2036 ast_log(LOG_DEBUG
, "Dunno what to do with control type %d\n", f
->subclass
);
2041 endtime
= (long) time(NULL
) - starttime
;
2042 rna(endtime
* 1000, qe
, on
, membername
);
2044 if (qe
->parent
->strategy
!= QUEUE_STRATEGY_RINGALL
) {
2045 if (qe
->parent
->timeoutrestart
)
2047 ring_one(qe
, outgoing
, &numbusies
);
2054 if (!f
|| ((f
->frametype
== AST_FRAME_CONTROL
) && (f
->subclass
== AST_CONTROL_HANGUP
))) {
2061 if ((f
->frametype
== AST_FRAME_DTMF
) && caller_disconnect
&& (f
->subclass
== '*')) {
2062 if (option_verbose
> 3)
2063 ast_verbose(VERBOSE_PREFIX_3
"User hit %c to disconnect call.\n", f
->subclass
);
2068 if ((f
->frametype
== AST_FRAME_DTMF
) && (f
->subclass
!= '*') && valid_exit(qe
, f
->subclass
)) {
2069 if (option_verbose
> 3)
2070 ast_verbose(VERBOSE_PREFIX_3
"User pressed digit: %c\n", f
->subclass
);
2072 *digit
= f
->subclass
;
2079 rna(orig
, qe
, on
, membername
);
2085 static int is_our_turn(struct queue_ent
*qe
)
2087 struct queue_ent
*ch
;
2093 if (!qe
->parent
->autofill
) {
2094 /* Atomically read the parent head -- does not need a lock */
2095 ch
= qe
->parent
->head
;
2096 /* If we are now at the top of the head, break out */
2099 ast_log(LOG_DEBUG
, "It's our turn (%s).\n", qe
->chan
->name
);
2103 ast_log(LOG_DEBUG
, "It's not our turn (%s).\n", qe
->chan
->name
);
2108 /* This needs a lock. How many members are available to be served? */
2109 ast_mutex_lock(&qe
->parent
->lock
);
2111 ch
= qe
->parent
->head
;
2113 if (qe
->parent
->strategy
== QUEUE_STRATEGY_RINGALL
) {
2115 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
);
2118 for (cur
= qe
->parent
->members
; cur
; cur
= cur
->next
) {
2119 switch (cur
->status
) {
2120 case AST_DEVICE_NOT_INUSE
:
2121 case AST_DEVICE_UNKNOWN
:
2129 ast_log(LOG_DEBUG
, "There are %d available members.\n", avl
);
2131 while ((idx
< avl
) && (ch
) && (ch
!= qe
)) {
2136 /* If the queue entry is within avl [the number of available members] calls from the top ... */
2137 if (ch
&& idx
< avl
) {
2139 ast_log(LOG_DEBUG
, "It's our turn (%s).\n", qe
->chan
->name
);
2143 ast_log(LOG_DEBUG
, "It's not our turn (%s).\n", qe
->chan
->name
);
2147 ast_mutex_unlock(&qe
->parent
->lock
);
2153 static int wait_our_turn(struct queue_ent
*qe
, int ringing
, enum queue_result
*reason
)
2157 /* This is the holding pen for callers 2 through maxlen */
2159 enum queue_member_status stat
;
2161 if (is_our_turn(qe
))
2164 /* If we have timed out, break out */
2165 if (qe
->expire
&& (time(NULL
) > qe
->expire
)) {
2166 *reason
= QUEUE_TIMEOUT
;
2170 stat
= get_member_status(qe
->parent
, qe
->max_penalty
);
2172 /* leave the queue if no agents, if enabled */
2173 if (qe
->parent
->leavewhenempty
&& (stat
== QUEUE_NO_MEMBERS
)) {
2174 *reason
= QUEUE_LEAVEEMPTY
;
2175 ast_queue_log(qe
->parent
->name
, qe
->chan
->uniqueid
, "NONE", "EXITEMPTY", "%d|%d|%ld", qe
->pos
, qe
->opos
, (long)time(NULL
) - qe
->start
);
2180 /* leave the queue if no reachable agents, if enabled */
2181 if ((qe
->parent
->leavewhenempty
== QUEUE_EMPTY_STRICT
) && (stat
== QUEUE_NO_REACHABLE_MEMBERS
)) {
2182 *reason
= QUEUE_LEAVEUNAVAIL
;
2183 ast_queue_log(qe
->parent
->name
, qe
->chan
->uniqueid
, "NONE", "EXITEMPTY", "%d|%d|%ld", qe
->pos
, qe
->opos
, (long)time(NULL
) - qe
->start
);
2188 /* Make a position announcement, if enabled */
2189 if (qe
->parent
->announcefrequency
&& !ringing
&&
2190 (res
= say_position(qe
)))
2193 /* Make a periodic announcement, if enabled */
2194 if (qe
->parent
->periodicannouncefrequency
&& !ringing
&&
2195 (res
= say_periodic_announcement(qe
)))
2198 /* Wait a second before checking again */
2199 if ((res
= ast_waitfordigit(qe
->chan
, RECHECK
* 1000)))
2206 static int update_queue(struct call_queue
*q
, struct member
*member
)
2210 /* Since a reload could have taken place, we have to traverse the list to
2211 be sure it's still valid */
2212 ast_mutex_lock(&q
->lock
);
2215 if (member
== cur
) {
2216 time(&cur
->lastcall
);
2222 q
->callscompleted
++;
2223 ast_mutex_unlock(&q
->lock
);
2227 static int calc_metric(struct call_queue
*q
, struct member
*mem
, int pos
, struct queue_ent
*qe
, struct callattempt
*tmp
)
2229 if (qe
->max_penalty
&& (mem
->penalty
> qe
->max_penalty
))
2232 switch (q
->strategy
) {
2233 case QUEUE_STRATEGY_RINGALL
:
2234 /* Everyone equal, except for penalty */
2235 tmp
->metric
= mem
->penalty
* 1000000;
2237 case QUEUE_STRATEGY_ROUNDROBIN
:
2240 /* No more channels, start over */
2243 /* Prioritize next entry */
2249 case QUEUE_STRATEGY_RRMEMORY
:
2250 if (pos
< q
->rrpos
) {
2251 tmp
->metric
= 1000 + pos
;
2254 /* Indicate there is another priority */
2258 tmp
->metric
+= mem
->penalty
* 1000000;
2260 case QUEUE_STRATEGY_RANDOM
:
2261 tmp
->metric
= ast_random() % 1000;
2262 tmp
->metric
+= mem
->penalty
* 1000000;
2264 case QUEUE_STRATEGY_FEWESTCALLS
:
2265 tmp
->metric
= mem
->calls
;
2266 tmp
->metric
+= mem
->penalty
* 1000000;
2268 case QUEUE_STRATEGY_LEASTRECENT
:
2272 tmp
->metric
= 1000000 - (time(NULL
) - mem
->lastcall
);
2273 tmp
->metric
+= mem
->penalty
* 1000000;
2276 ast_log(LOG_WARNING
, "Can't calculate metric for unknown strategy %d\n", q
->strategy
);
2282 static int try_calling(struct queue_ent
*qe
, const char *options
, char *announceoverride
, const char *url
, int *go_on
, const char *agi
)
2285 struct callattempt
*outgoing
= NULL
; /* the list of calls we are building */
2287 char oldexten
[AST_MAX_EXTENSION
]="";
2288 char oldcontext
[AST_MAX_CONTEXT
]="";
2289 char queuename
[256]="";
2290 struct ast_channel
*peer
;
2291 struct ast_channel
*which
;
2292 struct callattempt
*lpeer
;
2293 struct member
*member
;
2294 struct ast_app
*app
;
2295 int res
= 0, bridge
= 0;
2298 char *announce
= NULL
;
2301 time_t now
= time(NULL
);
2302 struct ast_bridge_config bridge_config
;
2303 char nondataquality
= 1;
2304 char *agiexec
= NULL
;
2306 const char *monitorfilename
;
2307 const char *monitor_exec
;
2308 const char *monitor_options
;
2309 char tmpid
[256], tmpid2
[256];
2310 char meid
[1024], meid2
[1024];
2311 char mixmonargs
[1512];
2312 struct ast_app
*mixmonapp
= NULL
;
2315 int forwardsallowed
= 1;
2317 memset(&bridge_config
, 0, sizeof(bridge_config
));
2320 for (; options
&& *options
; options
++)
2323 ast_set_flag(&(bridge_config
.features_callee
), AST_FEATURE_REDIRECT
);
2326 ast_set_flag(&(bridge_config
.features_caller
), AST_FEATURE_REDIRECT
);
2329 ast_set_flag(&(bridge_config
.features_callee
), AST_FEATURE_AUTOMON
);
2332 ast_set_flag(&(bridge_config
.features_caller
), AST_FEATURE_AUTOMON
);
2338 ast_set_flag(&(bridge_config
.features_callee
), AST_FEATURE_DISCONNECT
);
2341 ast_set_flag(&(bridge_config
.features_caller
), AST_FEATURE_DISCONNECT
);
2344 if ((now
- qe
->start
>= qe
->parent
->timeout
))
2348 forwardsallowed
= 0;
2352 /* Hold the lock while we setup the outgoing calls */
2354 AST_LIST_LOCK(&queues
);
2355 ast_mutex_lock(&qe
->parent
->lock
);
2357 ast_log(LOG_DEBUG
, "%s is trying to call a queue member.\n",
2359 ast_copy_string(queuename
, qe
->parent
->name
, sizeof(queuename
));
2360 cur
= qe
->parent
->members
;
2361 if (!ast_strlen_zero(qe
->announce
))
2362 announce
= qe
->announce
;
2363 if (!ast_strlen_zero(announceoverride
))
2364 announce
= announceoverride
;
2366 for (; cur
; cur
= cur
->next
) {
2367 struct callattempt
*tmp
= ast_calloc(1, sizeof(*tmp
));
2370 ast_mutex_unlock(&qe
->parent
->lock
);
2372 AST_LIST_UNLOCK(&queues
);
2375 tmp
->stillgoing
= -1;
2376 tmp
->member
= cur
; /* Never directly dereference! Could change on reload */
2377 tmp
->oldstatus
= cur
->status
;
2378 tmp
->lastcall
= cur
->lastcall
;
2379 ast_copy_string(tmp
->interface
, cur
->interface
, sizeof(tmp
->interface
));
2380 /* Special case: If we ring everyone, go ahead and ring them, otherwise
2381 just calculate their metric for the appropriate strategy */
2382 if (!calc_metric(qe
->parent
, cur
, x
++, qe
, tmp
)) {
2383 /* Put them in the list of outgoing thingies... We're ready now.
2384 XXX If we're forcibly removed, these outgoing calls won't get
2386 tmp
->q_next
= outgoing
;
2388 /* If this line is up, don't try anybody else */
2389 if (outgoing
->chan
&& (outgoing
->chan
->_state
== AST_STATE_UP
))
2395 if (qe
->expire
&& (!qe
->parent
->timeout
|| (qe
->expire
- now
) <= qe
->parent
->timeout
))
2396 to
= (qe
->expire
- now
) * 1000;
2398 to
= (qe
->parent
->timeout
) ? qe
->parent
->timeout
* 1000 : -1;
2399 ring_one(qe
, outgoing
, &numbusies
);
2400 ast_mutex_unlock(&qe
->parent
->lock
);
2402 AST_LIST_UNLOCK(&queues
);
2403 lpeer
= wait_for_answer(qe
, outgoing
, &to
, &digit
, numbusies
, ast_test_flag(&(bridge_config
.features_caller
), AST_FEATURE_DISCONNECT
), forwardsallowed
);
2404 ast_mutex_lock(&qe
->parent
->lock
);
2405 if (qe
->parent
->strategy
== QUEUE_STRATEGY_RRMEMORY
) {
2406 store_next(qe
, outgoing
);
2408 ast_mutex_unlock(&qe
->parent
->lock
);
2409 peer
= lpeer
? lpeer
->chan
: NULL
;
2412 /* Must gotten hung up */
2418 ast_log(LOG_DEBUG
, "%s: Nobody answered.\n", qe
->chan
->name
);
2419 } else { /* peer is valid */
2420 /* Ah ha! Someone answered within the desired timeframe. Of course after this
2421 we will always return with -1 so that it is hung up properly after the
2424 if (!strcmp(qe
->chan
->tech
->type
, "Zap"))
2425 ast_channel_setoption(qe
->chan
, AST_OPTION_TONE_VERIFY
, &nondataquality
, sizeof(nondataquality
), 0);
2426 if (!strcmp(peer
->tech
->type
, "Zap"))
2427 ast_channel_setoption(peer
, AST_OPTION_TONE_VERIFY
, &nondataquality
, sizeof(nondataquality
), 0);
2428 /* Update parameters for the queue */
2429 recalc_holdtime(qe
);
2430 member
= lpeer
->member
;
2431 hangupcalls(outgoing
, peer
);
2433 if (announce
|| qe
->parent
->reportholdtime
|| qe
->parent
->memberdelay
) {
2436 res2
= ast_autoservice_start(qe
->chan
);
2438 if (qe
->parent
->memberdelay
) {
2439 ast_log(LOG_NOTICE
, "Delaying member connect for %d seconds\n", qe
->parent
->memberdelay
);
2440 res2
|= ast_safe_sleep(peer
, qe
->parent
->memberdelay
* 1000);
2442 if (!res2
&& announce
) {
2443 if (play_file(peer
, announce
))
2444 ast_log(LOG_WARNING
, "Announcement file '%s' is unavailable, continuing anyway...\n", announce
);
2446 if (!res2
&& qe
->parent
->reportholdtime
) {
2447 if (!play_file(peer
, qe
->parent
->sound_reporthold
)) {
2451 holdtime
= abs((now
- qe
->start
) / 60);
2453 play_file(peer
, qe
->parent
->sound_lessthan
);
2454 ast_say_number(peer
, 2, AST_DIGIT_ANY
, peer
->language
, NULL
);
2456 ast_say_number(peer
, holdtime
, AST_DIGIT_ANY
, peer
->language
, NULL
);
2457 play_file(peer
, qe
->parent
->sound_minutes
);
2461 res2
|= ast_autoservice_stop(qe
->chan
);
2462 if (peer
->_softhangup
) {
2463 /* Agent must have hung up */
2464 ast_log(LOG_WARNING
, "Agent on %s hungup on the customer. They're going to be pissed.\n", peer
->name
);
2465 ast_queue_log(queuename
, qe
->chan
->uniqueid
, member
->membername
, "AGENTDUMP", "%s", "");
2466 record_abandoned(qe
);
2467 if (qe
->parent
->eventwhencalled
)
2468 manager_event(EVENT_FLAG_AGENT
, "AgentDump",
2473 "MemberName: %s\r\n"
2475 queuename
, qe
->chan
->uniqueid
, peer
->name
, member
->interface
, member
->membername
,
2476 qe
->parent
->eventwhencalled
== QUEUE_EVENT_VARIABLES
? vars2manager(qe
->chan
, vars
, sizeof(vars
)) : "");
2480 /* Caller must have hung up just before being connected*/
2481 ast_log(LOG_NOTICE
, "Caller was about to talk to agent on %s but the caller hungup.\n", peer
->name
);
2482 ast_queue_log(queuename
, qe
->chan
->uniqueid
, member
->membername
, "ABANDON", "%d|%d|%ld", qe
->pos
, qe
->opos
, (long)time(NULL
) - qe
->start
);
2483 record_abandoned(qe
);
2488 /* Stop music on hold */
2489 ast_moh_stop(qe
->chan
);
2490 /* If appropriate, log that we have a destination channel */
2492 ast_cdr_setdestchan(qe
->chan
->cdr
, peer
->name
);
2493 /* Make sure channels are compatible */
2494 res
= ast_channel_make_compatible(qe
->chan
, peer
);
2496 ast_queue_log(queuename
, qe
->chan
->uniqueid
, member
->membername
, "SYSCOMPAT", "%s", "");
2497 ast_log(LOG_WARNING
, "Had to drop call because I couldn't make %s compatible with %s\n", qe
->chan
->name
, peer
->name
);
2498 record_abandoned(qe
);
2502 /* Begin Monitoring */
2503 if (qe
->parent
->monfmt
&& *qe
->parent
->monfmt
) {
2504 if (!qe
->parent
->montype
) {
2506 ast_log(LOG_DEBUG
, "Starting Monitor as requested.\n");
2507 monitorfilename
= pbx_builtin_getvar_helper(qe
->chan
, "MONITOR_FILENAME");
2508 if (pbx_builtin_getvar_helper(qe
->chan
, "MONITOR_EXEC") || pbx_builtin_getvar_helper(qe
->chan
, "MONITOR_EXEC_ARGS"))
2512 if (monitorfilename
)
2513 ast_monitor_start(which
, qe
->parent
->monfmt
, monitorfilename
, 1 );
2514 else if (qe
->chan
->cdr
)
2515 ast_monitor_start(which
, qe
->parent
->monfmt
, qe
->chan
->cdr
->uniqueid
, 1 );
2517 /* Last ditch effort -- no CDR, make up something */
2518 snprintf(tmpid
, sizeof(tmpid
), "chan-%lx", ast_random());
2519 ast_monitor_start(which
, qe
->parent
->monfmt
, tmpid
, 1 );
2521 if (qe
->parent
->monjoin
)
2522 ast_monitor_setjoinfiles(which
, 1);
2525 ast_log(LOG_DEBUG
, "Starting MixMonitor as requested.\n");
2526 monitorfilename
= pbx_builtin_getvar_helper(qe
->chan
, "MONITOR_FILENAME");
2527 if (!monitorfilename
) {
2529 ast_copy_string(tmpid
, qe
->chan
->cdr
->uniqueid
, sizeof(tmpid
)-1);
2531 snprintf(tmpid
, sizeof(tmpid
), "chan-%lx", ast_random());
2533 ast_copy_string(tmpid2
, monitorfilename
, sizeof(tmpid2
)-1);
2534 for (p
= tmpid2
; *p
; p
++) {
2535 if (*p
== '^' && *(p
+1) == '{') {
2540 memset(tmpid
, 0, sizeof(tmpid
));
2541 pbx_substitute_variables_helper(qe
->chan
, tmpid2
, tmpid
, sizeof(tmpid
) - 1);
2544 monitor_exec
= pbx_builtin_getvar_helper(qe
->chan
, "MONITOR_EXEC");
2545 monitor_options
= pbx_builtin_getvar_helper(qe
->chan
, "MONITOR_OPTIONS");
2548 ast_copy_string(meid2
, monitor_exec
, sizeof(meid2
)-1);
2549 for (p
= meid2
; *p
; p
++) {
2550 if (*p
== '^' && *(p
+1) == '{') {
2555 memset(meid
, 0, sizeof(meid
));
2556 pbx_substitute_variables_helper(qe
->chan
, meid2
, meid
, sizeof(meid
) - 1);
2559 snprintf(tmpid2
, sizeof(tmpid2
)-1, "%s.%s", tmpid
, qe
->parent
->monfmt
);
2561 mixmonapp
= pbx_findapp("MixMonitor");
2563 if (strchr(tmpid2
, '|')) {
2564 ast_log(LOG_WARNING
, "monitor-format (in queues.conf) and MONITOR_FILENAME cannot contain a '|'! Not recording.\n");
2568 if (!monitor_options
)
2569 monitor_options
= "";
2571 if (strchr(monitor_options
, '|')) {
2572 ast_log(LOG_WARNING
, "MONITOR_OPTIONS cannot contain a '|'! Not recording.\n");
2577 if (!ast_strlen_zero(monitor_exec
) && !ast_strlen_zero(monitor_options
))
2578 snprintf(mixmonargs
, sizeof(mixmonargs
)-1, "%s|b%s|%s", tmpid2
, monitor_options
, monitor_exec
);
2580 snprintf(mixmonargs
, sizeof(mixmonargs
)-1, "%s|b%s", tmpid2
, monitor_options
);
2583 ast_log(LOG_DEBUG
, "Arguments being passed to MixMonitor: %s\n", mixmonargs
);
2585 ret
= pbx_exec(qe
->chan
, mixmonapp
, mixmonargs
);
2588 ast_log(LOG_WARNING
, "Asked to run MixMonitor on this call, but cannot find the MixMonitor app!\n");
2592 /* Drop out of the queue at this point, to prepare for next caller */
2594 if (!ast_strlen_zero(url
) && ast_channel_supports_html(peer
)) {
2596 ast_log(LOG_DEBUG
, "app_queue: sendurl=%s.\n", url
);
2597 ast_channel_sendurl(peer
, url
);
2599 if (qe
->parent
->setinterfacevar
)
2600 pbx_builtin_setvar_helper(qe
->chan
, "MEMBERINTERFACE", member
->interface
);
2601 if (!ast_strlen_zero(agi
)) {
2603 ast_log(LOG_DEBUG
, "app_queue: agi=%s.\n", agi
);
2604 app
= pbx_findapp("agi");
2606 agiexec
= ast_strdupa(agi
);
2607 ret
= pbx_exec(qe
->chan
, app
, agiexec
);
2609 ast_log(LOG_WARNING
, "Asked to execute an AGI on this channel, but could not find application (agi)!\n");
2611 ast_queue_log(queuename
, qe
->chan
->uniqueid
, member
->membername
, "CONNECT", "%ld|%s", (long)time(NULL
) - qe
->start
, peer
->uniqueid
);
2612 if (qe
->parent
->eventwhencalled
)
2613 manager_event(EVENT_FLAG_AGENT
, "AgentConnect",
2618 "MemberName: %s\r\n"
2620 "BridgedChannel: %s\r\n"
2622 queuename
, qe
->chan
->uniqueid
, peer
->name
, member
->interface
, member
->membername
,
2623 (long)time(NULL
) - qe
->start
, peer
->uniqueid
,
2624 qe
->parent
->eventwhencalled
== QUEUE_EVENT_VARIABLES
? vars2manager(qe
->chan
, vars
, sizeof(vars
)) : "");
2625 ast_copy_string(oldcontext
, qe
->chan
->context
, sizeof(oldcontext
));
2626 ast_copy_string(oldexten
, qe
->chan
->exten
, sizeof(oldexten
));
2629 bridge
= ast_bridge_call(qe
->chan
,peer
, &bridge_config
);
2631 if (strcasecmp(oldcontext
, qe
->chan
->context
) || strcasecmp(oldexten
, qe
->chan
->exten
)) {
2632 ast_queue_log(queuename
, qe
->chan
->uniqueid
, member
->membername
, "TRANSFER", "%s|%s|%ld|%ld",
2633 qe
->chan
->exten
, qe
->chan
->context
, (long) (callstart
- qe
->start
),
2634 (long) (time(NULL
) - callstart
));
2635 } else if (qe
->chan
->_softhangup
) {
2636 ast_queue_log(queuename
, qe
->chan
->uniqueid
, peer
->name
, "COMPLETECALLER", "%ld|%ld",
2637 (long) (callstart
- qe
->start
), (long) (time(NULL
) - callstart
));
2638 if (qe
->parent
->eventwhencalled
)
2639 manager_event(EVENT_FLAG_AGENT
, "AgentComplete",
2644 "MemberName: %s\r\n"
2647 "Reason: caller\r\n"
2649 queuename
, qe
->chan
->uniqueid
, peer
->name
, member
->interface
, member
->membername
,
2650 (long)(callstart
- qe
->start
), (long)(time(NULL
) - callstart
),
2651 qe
->parent
->eventwhencalled
== QUEUE_EVENT_VARIABLES
? vars2manager(qe
->chan
, vars
, sizeof(vars
)) : "");
2653 ast_queue_log(queuename
, qe
->chan
->uniqueid
, member
->membername
, "COMPLETEAGENT", "%ld|%ld",
2654 (long) (callstart
- qe
->start
), (long) (time(NULL
) - callstart
));
2655 if (qe
->parent
->eventwhencalled
)
2656 manager_event(EVENT_FLAG_AGENT
, "AgentComplete",
2660 "MemberName: %s\r\n"
2665 queuename
, qe
->chan
->uniqueid
, peer
->name
, member
->membername
, (long)(callstart
- qe
->start
),
2666 (long)(time(NULL
) - callstart
),
2667 qe
->parent
->eventwhencalled
== QUEUE_EVENT_VARIABLES
? vars2manager(qe
->chan
, vars
, sizeof(vars
)) : "");
2670 if (bridge
!= AST_PBX_NO_HANGUP_PEER
)
2672 update_queue(qe
->parent
, member
);
2673 res
= bridge
? bridge
: 1;
2676 hangupcalls(outgoing
, NULL
);
2681 static int wait_a_bit(struct queue_ent
*qe
)
2683 /* Don't need to hold the lock while we setup the outgoing calls */
2684 int retrywait
= qe
->parent
->retry
* 1000;
2686 return ast_waitfordigit(qe
->chan
, retrywait
);
2689 static struct member
*interface_exists(struct call_queue
*q
, char *interface
)
2696 for (mem
= q
->members
; mem
; mem
= mem
->next
) {
2697 if (!strcasecmp(interface
, mem
->interface
))
2705 /* Dump all members in a specific queue to the database
2707 * <pm_family>/<queuename> = <interface>;<penalty>;<paused>[|...]
2710 static void dump_queue_members(struct call_queue
*pm_queue
)
2712 struct member
*cur_member
;
2713 char value
[PM_MAX_LEN
];
2717 memset(value
, 0, sizeof(value
));
2722 for (cur_member
= pm_queue
->members
; cur_member
; cur_member
= cur_member
->next
) {
2723 if (!cur_member
->dynamic
)
2726 res
= snprintf(value
+ value_len
, sizeof(value
) - value_len
, "%s;%d;%d;%s%s",
2727 cur_member
->interface
, cur_member
->penalty
, cur_member
->paused
, cur_member
->membername
,
2728 cur_member
->next
? "|" : "");
2729 if (res
!= strlen(value
+ value_len
)) {
2730 ast_log(LOG_WARNING
, "Could not create persistent member string, out of space\n");
2736 if (value_len
&& !cur_member
) {
2737 if (ast_db_put(pm_family
, pm_queue
->name
, value
))
2738 ast_log(LOG_WARNING
, "failed to create persistent dynamic entry!\n");
2740 /* Delete the entry if the queue is empty or there is an error */
2741 ast_db_del(pm_family
, pm_queue
->name
);
2744 static int remove_from_queue(char *queuename
, char *interface
)
2746 struct call_queue
*q
;
2747 struct member
*last_member
, *look
;
2748 int res
= RES_NOSUCHQUEUE
;
2750 AST_LIST_LOCK(&queues
);
2751 AST_LIST_TRAVERSE(&queues
, q
, list
) {
2752 ast_mutex_lock(&q
->lock
);
2753 if (strcmp(q
->name
, queuename
)) {
2754 ast_mutex_unlock(&q
->lock
);
2758 if ((last_member
= interface_exists(q
, interface
))) {
2759 if ((look
= q
->members
) == last_member
) {
2760 q
->members
= last_member
->next
;
2762 while (look
!= NULL
) {
2763 if (look
->next
== last_member
) {
2764 look
->next
= last_member
->next
;
2771 manager_event(EVENT_FLAG_AGENT
, "QueueMemberRemoved",
2774 "MemberName: %s\r\n",
2775 q
->name
, last_member
->interface
, last_member
->membername
);
2778 if (queue_persistent_members
)
2779 dump_queue_members(q
);
2785 ast_mutex_unlock(&q
->lock
);
2789 if (res
== RES_OKAY
)
2790 remove_from_interfaces(interface
);
2792 AST_LIST_UNLOCK(&queues
);
2798 static int add_to_queue(char *queuename
, char *interface
, char *membername
, int penalty
, int paused
, int dump
)
2800 struct call_queue
*q
;
2801 struct member
*new_member
;
2802 int res
= RES_NOSUCHQUEUE
;
2804 /* \note Ensure the appropriate realtime queue is loaded. Note that this
2805 * short-circuits if the queue is already in memory. */
2806 if (!(q
= load_realtime_queue(queuename
)))
2809 AST_LIST_LOCK(&queues
);
2811 ast_mutex_lock(&q
->lock
);
2812 if (interface_exists(q
, interface
) == NULL
) {
2813 add_to_interfaces(interface
);
2814 if ((new_member
= create_queue_member(interface
, membername
, penalty
, paused
))) {
2815 new_member
->dynamic
= 1;
2816 new_member
->next
= q
->members
;
2817 q
->members
= new_member
;
2818 manager_event(EVENT_FLAG_AGENT
, "QueueMemberAdded",
2821 "MemberName: %s\r\n"
2822 "Membership: %s\r\n"
2824 "CallsTaken: %d\r\n"
2828 q
->name
, new_member
->interface
, new_member
->membername
,
2829 new_member
->dynamic
? "dynamic" : "static",
2830 new_member
->penalty
, new_member
->calls
, (int) new_member
->lastcall
,
2831 new_member
->status
, new_member
->paused
);
2834 dump_queue_members(q
);
2838 res
= RES_OUTOFMEMORY
;
2843 ast_mutex_unlock(&q
->lock
);
2844 AST_LIST_UNLOCK(&queues
);
2849 static int set_member_paused(char *queuename
, char *interface
, int paused
)
2852 struct call_queue
*q
;
2855 /* Special event for when all queues are paused - individual events still generated */
2856 /* XXX In all other cases, we use the membername, but since this affects all queues, we cannot */
2857 if (ast_strlen_zero(queuename
))
2858 ast_queue_log("NONE", "NONE", interface
, (paused
? "PAUSEALL" : "UNPAUSEALL"), "%s", "");
2860 AST_LIST_LOCK(&queues
);
2861 AST_LIST_TRAVERSE(&queues
, q
, list
) {
2862 ast_mutex_lock(&q
->lock
);
2863 if (ast_strlen_zero(queuename
) || !strcasecmp(q
->name
, queuename
)) {
2864 if ((mem
= interface_exists(q
, interface
))) {
2866 if (mem
->paused
== paused
)
2867 ast_log(LOG_DEBUG
, "%spausing already-%spaused queue member %s:%s\n", (paused
? "" : "un"), (paused
? "" : "un"), q
->name
, interface
);
2868 mem
->paused
= paused
;
2870 if (queue_persistent_members
)
2871 dump_queue_members(q
);
2873 ast_queue_log(q
->name
, "NONE", mem
->membername
, (paused
? "PAUSE" : "UNPAUSE"), "%s", "");
2875 manager_event(EVENT_FLAG_AGENT
, "QueueMemberPaused",
2878 "MemberName: %s\r\n"
2880 q
->name
, mem
->interface
, mem
->membername
, paused
);
2883 ast_mutex_unlock(&q
->lock
);
2885 AST_LIST_UNLOCK(&queues
);
2887 return found
? RESULT_SUCCESS
: RESULT_FAILURE
;
2890 /* Reload dynamic queue members persisted into the astdb */
2891 static void reload_queue_members(void)
2902 struct ast_db_entry
*db_tree
;
2903 struct ast_db_entry
*entry
;
2904 struct call_queue
*cur_queue
;
2905 char queue_data
[PM_MAX_LEN
];
2907 AST_LIST_LOCK(&queues
);
2909 /* Each key in 'pm_family' is the name of a queue */
2910 db_tree
= ast_db_gettree(pm_family
, NULL
);
2911 for (entry
= db_tree
; entry
; entry
= entry
->next
) {
2913 queue_name
= entry
->key
+ strlen(pm_family
) + 2;
2915 AST_LIST_TRAVERSE(&queues
, cur_queue
, list
) {
2916 ast_mutex_lock(&cur_queue
->lock
);
2917 if (!strcmp(queue_name
, cur_queue
->name
))
2919 ast_mutex_unlock(&cur_queue
->lock
);
2923 /* If the queue no longer exists, remove it from the
2925 ast_db_del(pm_family
, queue_name
);
2928 ast_mutex_unlock(&cur_queue
->lock
);
2930 if (ast_db_get(pm_family
, queue_name
, queue_data
, PM_MAX_LEN
))
2933 cur_ptr
= queue_data
;
2934 while ((member
= strsep(&cur_ptr
, "|"))) {
2935 if (ast_strlen_zero(member
))
2938 interface
= strsep(&member
, ";");
2939 penalty_tok
= strsep(&member
, ";");
2940 paused_tok
= strsep(&member
, ";");
2941 membername
= strsep(&member
, ";");
2944 ast_log(LOG_WARNING
, "Error parsing persistent member string for '%s' (penalty)\n", queue_name
);
2947 penalty
= strtol(penalty_tok
, NULL
, 10);
2948 if (errno
== ERANGE
) {
2949 ast_log(LOG_WARNING
, "Error converting penalty: %s: Out of range.\n", penalty_tok
);
2954 ast_log(LOG_WARNING
, "Error parsing persistent member string for '%s' (paused)\n", queue_name
);
2957 paused
= strtol(paused_tok
, NULL
, 10);
2958 if ((errno
== ERANGE
) || paused
< 0 || paused
> 1) {
2959 ast_log(LOG_WARNING
, "Error converting paused: %s: Expected 0 or 1.\n", paused_tok
);
2962 if (ast_strlen_zero(membername
))
2963 membername
= interface
;
2966 ast_log(LOG_DEBUG
, "Reload Members: Queue: %s Member: %s Name: %s Penalty: %d Paused: %d\n", queue_name
, interface
, membername
, penalty
, paused
);
2968 if (add_to_queue(queue_name
, interface
, membername
, penalty
, paused
, 0) == RES_OUTOFMEMORY
) {
2969 ast_log(LOG_ERROR
, "Out of Memory when reloading persistent queue member\n");
2975 AST_LIST_UNLOCK(&queues
);
2977 ast_log(LOG_NOTICE
, "Queue members successfully reloaded from database.\n");
2978 ast_db_freetree(db_tree
);
2982 static int pqm_exec(struct ast_channel
*chan
, void *data
)
2984 struct ast_module_user
*lu
;
2986 int priority_jump
= 0;
2987 AST_DECLARE_APP_ARGS(args
,
2988 AST_APP_ARG(queuename
);
2989 AST_APP_ARG(interface
);
2990 AST_APP_ARG(options
);
2993 if (ast_strlen_zero(data
)) {
2994 ast_log(LOG_WARNING
, "PauseQueueMember requires an argument ([queuename]|interface[|options])\n");
2998 parse
= ast_strdupa(data
);
3000 AST_STANDARD_APP_ARGS(args
, parse
);
3002 lu
= ast_module_user_add(chan
);
3005 if (strchr(args
.options
, 'j'))
3009 if (ast_strlen_zero(args
.interface
)) {
3010 ast_log(LOG_WARNING
, "Missing interface argument to PauseQueueMember ([queuename]|interface[|options])\n");
3011 ast_module_user_remove(lu
);
3015 if (set_member_paused(args
.queuename
, args
.interface
, 1)) {
3016 ast_log(LOG_WARNING
, "Attempt to pause interface %s, not found\n", args
.interface
);
3017 if (priority_jump
|| ast_opt_priority_jumping
) {
3018 if (ast_goto_if_exists(chan
, chan
->context
, chan
->exten
, chan
->priority
+ 101)) {
3019 pbx_builtin_setvar_helper(chan
, "PQMSTATUS", "NOTFOUND");
3020 ast_module_user_remove(lu
);
3024 ast_module_user_remove(lu
);
3025 pbx_builtin_setvar_helper(chan
, "PQMSTATUS", "NOTFOUND");
3029 ast_module_user_remove(lu
);
3030 pbx_builtin_setvar_helper(chan
, "PQMSTATUS", "PAUSED");
3035 static int upqm_exec(struct ast_channel
*chan
, void *data
)
3037 struct ast_module_user
*lu
;
3039 int priority_jump
= 0;
3040 AST_DECLARE_APP_ARGS(args
,
3041 AST_APP_ARG(queuename
);
3042 AST_APP_ARG(interface
);
3043 AST_APP_ARG(options
);
3046 if (ast_strlen_zero(data
)) {
3047 ast_log(LOG_WARNING
, "UnpauseQueueMember requires an argument ([queuename]|interface[|options])\n");
3051 parse
= ast_strdupa(data
);
3053 AST_STANDARD_APP_ARGS(args
, parse
);
3055 lu
= ast_module_user_add(chan
);
3058 if (strchr(args
.options
, 'j'))
3062 if (ast_strlen_zero(args
.interface
)) {
3063 ast_log(LOG_WARNING
, "Missing interface argument to PauseQueueMember ([queuename]|interface[|options])\n");
3064 ast_module_user_remove(lu
);
3068 if (set_member_paused(args
.queuename
, args
.interface
, 0)) {
3069 ast_log(LOG_WARNING
, "Attempt to unpause interface %s, not found\n", args
.interface
);
3070 if (priority_jump
|| ast_opt_priority_jumping
) {
3071 if (ast_goto_if_exists(chan
, chan
->context
, chan
->exten
, chan
->priority
+ 101)) {
3072 pbx_builtin_setvar_helper(chan
, "UPQMSTATUS", "NOTFOUND");
3073 ast_module_user_remove(lu
);
3077 ast_module_user_remove(lu
);
3078 pbx_builtin_setvar_helper(chan
, "UPQMSTATUS", "NOTFOUND");
3082 ast_module_user_remove(lu
);
3083 pbx_builtin_setvar_helper(chan
, "UPQMSTATUS", "UNPAUSED");
3088 static int rqm_exec(struct ast_channel
*chan
, void *data
)
3091 struct ast_module_user
*lu
;
3092 char *parse
, *temppos
= NULL
;
3093 int priority_jump
= 0;
3094 AST_DECLARE_APP_ARGS(args
,
3095 AST_APP_ARG(queuename
);
3096 AST_APP_ARG(interface
);
3097 AST_APP_ARG(options
);
3101 if (ast_strlen_zero(data
)) {
3102 ast_log(LOG_WARNING
, "RemoveQueueMember requires an argument (queuename[|interface[|options]])\n");
3106 parse
= ast_strdupa(data
);
3108 AST_STANDARD_APP_ARGS(args
, parse
);
3110 lu
= ast_module_user_add(chan
);
3112 if (ast_strlen_zero(args
.interface
)) {
3113 args
.interface
= ast_strdupa(chan
->name
);
3114 temppos
= strrchr(args
.interface
, '-');
3120 if (strchr(args
.options
, 'j'))
3124 switch (remove_from_queue(args
.queuename
, args
.interface
)) {
3126 ast_queue_log(args
.queuename
, chan
->uniqueid
, args
.interface
, "REMOVEMEMBER", "%s", "");
3127 ast_log(LOG_NOTICE
, "Removed interface '%s' from queue '%s'\n", args
.interface
, args
.queuename
);
3128 pbx_builtin_setvar_helper(chan
, "RQMSTATUS", "REMOVED");
3132 ast_log(LOG_DEBUG
, "Unable to remove interface '%s' from queue '%s': Not there\n", args
.interface
, args
.queuename
);
3133 if (priority_jump
|| ast_opt_priority_jumping
)
3134 ast_goto_if_exists(chan
, chan
->context
, chan
->exten
, chan
->priority
+ 101);
3135 pbx_builtin_setvar_helper(chan
, "RQMSTATUS", "NOTINQUEUE");
3138 case RES_NOSUCHQUEUE
:
3139 ast_log(LOG_WARNING
, "Unable to remove interface from queue '%s': No such queue\n", args
.queuename
);
3140 pbx_builtin_setvar_helper(chan
, "RQMSTATUS", "NOSUCHQUEUE");
3145 ast_module_user_remove(lu
);
3150 static int aqm_exec(struct ast_channel
*chan
, void *data
)
3153 struct ast_module_user
*lu
;
3154 char *parse
, *temppos
= NULL
;
3155 int priority_jump
= 0;
3156 AST_DECLARE_APP_ARGS(args
,
3157 AST_APP_ARG(queuename
);
3158 AST_APP_ARG(interface
);
3159 AST_APP_ARG(penalty
);
3160 AST_APP_ARG(options
);
3161 AST_APP_ARG(membername
);
3165 if (ast_strlen_zero(data
)) {
3166 ast_log(LOG_WARNING
, "AddQueueMember requires an argument (queuename[|[interface]|[penalty][|options][|membername]])\n");
3170 parse
= ast_strdupa(data
);
3172 AST_STANDARD_APP_ARGS(args
, parse
);
3174 lu
= ast_module_user_add(chan
);
3176 if (ast_strlen_zero(args
.interface
)) {
3177 args
.interface
= ast_strdupa(chan
->name
);
3178 temppos
= strrchr(args
.interface
, '-');
3183 if (!ast_strlen_zero(args
.penalty
)) {
3184 if ((sscanf(args
.penalty
, "%d", &penalty
) != 1) || penalty
< 0) {
3185 ast_log(LOG_WARNING
, "Penalty '%s' is invalid, must be an integer >= 0\n", args
.penalty
);
3191 if (strchr(args
.options
, 'j'))
3195 if (ast_strlen_zero(args
.membername
))
3196 args
.membername
= args
.interface
;
3199 switch (add_to_queue(args
.queuename
, args
.interface
, args
.membername
, penalty
, 0, queue_persistent_members
)) {
3201 ast_queue_log(args
.queuename
, chan
->uniqueid
, args
.interface
, "ADDMEMBER", "%s", "");
3202 ast_log(LOG_NOTICE
, "Added interface '%s' to queue '%s'\n", args
.interface
, args
.queuename
);
3203 pbx_builtin_setvar_helper(chan
, "AQMSTATUS", "ADDED");
3207 ast_log(LOG_WARNING
, "Unable to add interface '%s' to queue '%s': Already there\n", args
.interface
, args
.queuename
);
3208 if (priority_jump
|| ast_opt_priority_jumping
)
3209 ast_goto_if_exists(chan
, chan
->context
, chan
->exten
, chan
->priority
+ 101);
3210 pbx_builtin_setvar_helper(chan
, "AQMSTATUS", "MEMBERALREADY");
3213 case RES_NOSUCHQUEUE
:
3214 ast_log(LOG_WARNING
, "Unable to add interface to queue '%s': No such queue\n", args
.queuename
);
3215 pbx_builtin_setvar_helper(chan
, "AQMSTATUS", "NOSUCHQUEUE");
3218 case RES_OUTOFMEMORY
:
3219 ast_log(LOG_ERROR
, "Out of memory adding member %s to queue %s\n", args
.interface
, args
.queuename
);
3223 ast_module_user_remove(lu
);
3228 static int ql_exec(struct ast_channel
*chan
, void *data
)
3230 struct ast_module_user
*u
;
3233 AST_DECLARE_APP_ARGS(args
,
3234 AST_APP_ARG(queuename
);
3235 AST_APP_ARG(uniqueid
);
3236 AST_APP_ARG(membername
);
3238 AST_APP_ARG(params
);
3241 if (ast_strlen_zero(data
)) {
3242 ast_log(LOG_WARNING
, "QueueLog requires arguments (queuename|uniqueid|membername|event[|additionalinfo]\n");
3246 u
= ast_module_user_add(chan
);
3248 parse
= ast_strdupa(data
);
3250 AST_STANDARD_APP_ARGS(args
, parse
);
3252 if (ast_strlen_zero(args
.queuename
) || ast_strlen_zero(args
.uniqueid
)
3253 || ast_strlen_zero(args
.membername
) || ast_strlen_zero(args
.event
)) {
3254 ast_log(LOG_WARNING
, "QueueLog requires arguments (queuename|uniqueid|membername|event[|additionalinfo])\n");
3255 ast_module_user_remove(u
);
3259 ast_queue_log(args
.queuename
, args
.uniqueid
, args
.membername
, args
.event
,
3260 "%s", args
.params
? args
.params
: "");
3262 ast_module_user_remove(u
);
3267 static int queue_exec(struct ast_channel
*chan
, void *data
)
3271 struct ast_module_user
*lu
;
3272 const char *user_priority
;
3273 const char *max_penalty_str
;
3276 enum queue_result reason
= QUEUE_UNKNOWN
;
3277 /* whether to exit Queue application after the timeout hits */
3280 AST_DECLARE_APP_ARGS(args
,
3281 AST_APP_ARG(queuename
);
3282 AST_APP_ARG(options
);
3284 AST_APP_ARG(announceoverride
);
3285 AST_APP_ARG(queuetimeoutstr
);
3288 /* Our queue entry */
3289 struct queue_ent qe
;
3291 if (ast_strlen_zero(data
)) {
3292 ast_log(LOG_WARNING
, "Queue requires an argument: queuename[|options[|URL[|announceoverride[|timeout[|agi]]]]]\n");
3296 parse
= ast_strdupa(data
);
3297 AST_STANDARD_APP_ARGS(args
, parse
);
3299 lu
= ast_module_user_add(chan
);
3301 /* Setup our queue entry */
3302 memset(&qe
, 0, sizeof(qe
));
3303 qe
.start
= time(NULL
);
3305 /* set the expire time based on the supplied timeout; */
3306 if (args
.queuetimeoutstr
)
3307 qe
.expire
= qe
.start
+ atoi(args
.queuetimeoutstr
);
3311 /* Get the priority from the variable ${QUEUE_PRIO} */
3312 user_priority
= pbx_builtin_getvar_helper(chan
, "QUEUE_PRIO");
3313 if (user_priority
) {
3314 if (sscanf(user_priority
, "%d", &prio
) == 1) {
3316 ast_log(LOG_DEBUG
, "%s: Got priority %d from ${QUEUE_PRIO}.\n",
3319 ast_log(LOG_WARNING
, "${QUEUE_PRIO}: Invalid value (%s), channel %s.\n",
3320 user_priority
, chan
->name
);
3324 if (option_debug
> 2)
3325 ast_log(LOG_DEBUG
, "NO QUEUE_PRIO variable found. Using default.\n");
3329 /* Get the maximum penalty from the variable ${QUEUE_MAX_PENALTY} */
3330 if ((max_penalty_str
= pbx_builtin_getvar_helper(chan
, "QUEUE_MAX_PENALTY"))) {
3331 if (sscanf(max_penalty_str
, "%d", &max_penalty
) == 1) {
3333 ast_log(LOG_DEBUG
, "%s: Got max penalty %d from ${QUEUE_MAX_PENALTY}.\n",
3334 chan
->name
, max_penalty
);
3336 ast_log(LOG_WARNING
, "${QUEUE_MAX_PENALTY}: Invalid value (%s), channel %s.\n",
3337 max_penalty_str
, chan
->name
);
3344 if (args
.options
&& (strchr(args
.options
, 'r')))
3348 ast_log(LOG_DEBUG
, "queue: %s, options: %s, url: %s, announce: %s, expires: %ld, priority: %d\n",
3349 args
.queuename
, args
.options
, args
.url
, args
.announceoverride
, (long)qe
.expire
, prio
);
3353 qe
.max_penalty
= max_penalty
;
3354 qe
.last_pos_said
= 0;
3356 qe
.last_periodic_announce_time
= time(NULL
);
3357 qe
.last_periodic_announce_sound
= 0;
3358 if (!join_queue(args
.queuename
, &qe
, &reason
)) {
3359 ast_queue_log(args
.queuename
, chan
->uniqueid
, "NONE", "ENTERQUEUE", "%s|%s", S_OR(args
.url
, ""),
3360 S_OR(chan
->cid
.cid_num
, ""));
3363 ast_indicate(chan
, AST_CONTROL_RINGING
);
3365 ast_moh_start(chan
, qe
.moh
, NULL
);
3368 /* This is the wait loop for callers 2 through maxlen */
3370 res
= wait_our_turn(&qe
, ringing
, &reason
);
3371 /* If they hungup, return immediately */
3373 /* Record this abandoned call */
3374 record_abandoned(&qe
);
3375 ast_queue_log(args
.queuename
, chan
->uniqueid
, "NONE", "ABANDON", "%d|%d|%ld",
3376 qe
.pos
, qe
.opos
, (long) time(NULL
) - qe
.start
);
3377 if (option_verbose
> 2) {
3378 ast_verbose(VERBOSE_PREFIX_3
"User disconnected from queue %s while waiting their turn\n", args
.queuename
);
3385 if (valid_exit(&qe
, res
)) {
3386 ast_queue_log(args
.queuename
, chan
->uniqueid
, "NONE", "EXITWITHKEY", "%s|%d", qe
.digits
, qe
.pos
);
3391 int makeannouncement
= 0;
3394 /* This is the wait loop for the head caller*/
3395 /* To exit, they may get their call answered; */
3396 /* they may dial a digit from the queue context; */
3397 /* or, they may timeout. */
3399 enum queue_member_status stat
;
3401 /* Leave if we have exceeded our queuetimeout */
3402 if (qe
.expire
&& (time(NULL
) > qe
.expire
)) {
3403 record_abandoned(&qe
);
3404 reason
= QUEUE_TIMEOUT
;
3406 ast_queue_log(args
.queuename
, chan
->uniqueid
, "NONE", "EXITWITHTIMEOUT", "%d", qe
.pos
);
3410 if (makeannouncement
) {
3411 /* Make a position announcement, if enabled */
3412 if (qe
.parent
->announcefrequency
&& !ringing
&&
3413 (res
= say_position(&qe
))) {
3414 ast_queue_log(args
.queuename
, chan
->uniqueid
, "NONE", "EXITWITHKEY", "%s|%d", qe
.digits
, qe
.pos
);
3419 makeannouncement
= 1;
3421 /* Make a periodic announcement, if enabled */
3422 if (qe
.parent
->periodicannouncefrequency
&& !ringing
&&
3423 (res
= say_periodic_announcement(&qe
))) {
3424 ast_queue_log(args
.queuename
, chan
->uniqueid
, "NONE", "EXITWITHKEY", "%c|%d", res
, qe
.pos
);
3428 /* Try calling all queue members for 'timeout' seconds */
3429 res
= try_calling(&qe
, args
.options
, args
.announceoverride
, args
.url
, &go_on
, args
.agi
);
3433 record_abandoned(&qe
);
3434 ast_queue_log(args
.queuename
, chan
->uniqueid
, "NONE", "ABANDON",
3435 "%d|%d|%ld", qe
.pos
, qe
.opos
,
3436 (long) time(NULL
) - qe
.start
);
3438 } else if (valid_exit(&qe
, res
)) {
3439 ast_queue_log(args
.queuename
, chan
->uniqueid
, "NONE", "EXITWITHKEY",
3440 "%s|%d", qe
.digits
, qe
.pos
);
3445 stat
= get_member_status(qe
.parent
, qe
.max_penalty
);
3447 /* leave the queue if no agents, if enabled */
3448 if (qe
.parent
->leavewhenempty
&& (stat
== QUEUE_NO_MEMBERS
)) {
3449 record_abandoned(&qe
);
3450 reason
= QUEUE_LEAVEEMPTY
;
3455 /* leave the queue if no reachable agents, if enabled */
3456 if ((qe
.parent
->leavewhenempty
== QUEUE_EMPTY_STRICT
) && (stat
== QUEUE_NO_REACHABLE_MEMBERS
)) {
3457 record_abandoned(&qe
);
3458 reason
= QUEUE_LEAVEUNAVAIL
;
3463 /* Leave if we have exceeded our queuetimeout */
3464 if (qe
.expire
&& (time(NULL
) > qe
.expire
)) {
3465 record_abandoned(&qe
);
3466 reason
= QUEUE_TIMEOUT
;
3468 ast_queue_log(args
.queuename
, chan
->uniqueid
, "NONE", "EXITWITHTIMEOUT", "%d", qe
.pos
);
3472 /* OK, we didn't get anybody; wait for 'retry' seconds; may get a digit to exit with */
3473 res
= wait_a_bit(&qe
);
3475 record_abandoned(&qe
);
3476 ast_queue_log(args
.queuename
, chan
->uniqueid
, "NONE", "ABANDON", "%d|%d|%ld", qe
.pos
, qe
.opos
, (long)time(NULL
) - qe
.start
);
3477 if (option_verbose
> 2) {
3478 ast_verbose(VERBOSE_PREFIX_3
"User disconnected from queue %s when they almost made it\n", args
.queuename
);
3483 if (res
&& valid_exit(&qe
, res
)) {
3484 ast_queue_log(args
.queuename
, chan
->uniqueid
, "NONE", "EXITWITHKEY", "%s|%d", qe
.digits
, qe
.pos
);
3487 /* exit after 'timeout' cycle if 'n' option enabled */
3489 if (option_verbose
> 2)
3490 ast_verbose(VERBOSE_PREFIX_3
"Exiting on time-out cycle\n");
3491 ast_queue_log(args
.queuename
, chan
->uniqueid
, "NONE", "EXITWITHTIMEOUT", "%d", qe
.pos
);
3492 record_abandoned(&qe
);
3493 reason
= QUEUE_TIMEOUT
;
3497 /* Since this is a priority queue and
3498 * it is not sure that we are still at the head
3499 * of the queue, go and check for our turn again.
3501 if (!is_our_turn(&qe
)) {
3503 ast_log(LOG_DEBUG
, "Darn priorities, going back in queue (%s)!\n",
3509 /* Don't allow return code > 0 */
3510 if (res
>= 0 && res
!= AST_PBX_KEEPALIVE
) {
3513 ast_indicate(chan
, -1);
3517 ast_stopstream(chan
);
3520 if (reason
!= QUEUE_UNKNOWN
)
3521 set_queue_result(chan
, reason
);
3523 ast_log(LOG_WARNING
, "Unable to join queue '%s'\n", args
.queuename
);
3524 set_queue_result(chan
, reason
);
3527 ast_module_user_remove(lu
);
3532 static int queue_function_qac(struct ast_channel
*chan
, char *cmd
, char *data
, char *buf
, size_t len
)
3535 struct call_queue
*q
;
3536 struct ast_module_user
*lu
;
3541 if (ast_strlen_zero(data
)) {
3542 ast_log(LOG_ERROR
, "%s requires an argument: queuename\n", cmd
);
3546 lu
= ast_module_user_add(chan
);
3548 AST_LIST_LOCK(&queues
);
3549 AST_LIST_TRAVERSE(&queues
, q
, list
) {
3550 if (!strcasecmp(q
->name
, data
)) {
3551 ast_mutex_lock(&q
->lock
);
3555 AST_LIST_UNLOCK(&queues
);
3558 for (m
= q
->members
; m
; m
= m
->next
) {
3559 /* Count the agents who are logged in and presently answering calls */
3560 if ((m
->status
!= AST_DEVICE_UNAVAILABLE
) && (m
->status
!= AST_DEVICE_INVALID
)) {
3564 ast_mutex_unlock(&q
->lock
);
3566 ast_log(LOG_WARNING
, "queue %s was not found\n", data
);
3568 snprintf(buf
, len
, "%d", count
);
3569 ast_module_user_remove(lu
);
3574 static int queue_function_queuewaitingcount(struct ast_channel
*chan
, char *cmd
, char *data
, char *buf
, size_t len
)
3577 struct call_queue
*q
;
3578 struct ast_module_user
*lu
;
3582 if (ast_strlen_zero(data
)) {
3583 ast_log(LOG_ERROR
, "%s requires an argument: queuename\n", cmd
);
3587 lu
= ast_module_user_add(chan
);
3589 AST_LIST_LOCK(&queues
);
3590 AST_LIST_TRAVERSE(&queues
, q
, list
) {
3591 if (!strcasecmp(q
->name
, data
)) {
3592 ast_mutex_lock(&q
->lock
);
3596 AST_LIST_UNLOCK(&queues
);
3600 ast_mutex_unlock(&q
->lock
);
3602 ast_log(LOG_WARNING
, "queue %s was not found\n", data
);
3604 snprintf(buf
, len
, "%d", count
);
3605 ast_module_user_remove(lu
);
3609 static int queue_function_queuememberlist(struct ast_channel
*chan
, char *cmd
, char *data
, char *buf
, size_t len
)
3611 struct ast_module_user
*u
;
3612 struct call_queue
*q
;
3615 /* Ensure an otherwise empty list doesn't return garbage */
3618 if (ast_strlen_zero(data
)) {
3619 ast_log(LOG_ERROR
, "QUEUE_MEMBER_LIST requires an argument: queuename\n");
3623 u
= ast_module_user_add(chan
);
3625 AST_LIST_LOCK(&queues
);
3626 AST_LIST_TRAVERSE(&queues
, q
, list
) {
3627 if (!strcasecmp(q
->name
, data
)) {
3628 ast_mutex_lock(&q
->lock
);
3632 AST_LIST_UNLOCK(&queues
);
3635 int buflen
= 0, count
= 0;
3637 for (m
= q
->members
; m
; m
= m
->next
) {
3638 /* strcat() is always faster than printf() */
3640 strncat(buf
+ buflen
, ",", len
- buflen
- 1);
3643 strncat(buf
+ buflen
, m
->interface
, len
- buflen
- 1);
3644 buflen
+= strlen(m
->interface
);
3645 /* Safeguard against overflow (negative length) */
3646 if (buflen
>= len
- 2) {
3647 ast_log(LOG_WARNING
, "Truncating list\n");
3651 ast_mutex_unlock(&q
->lock
);
3653 ast_log(LOG_WARNING
, "queue %s was not found\n", data
);
3655 /* We should already be terminated, but let's make sure. */
3656 buf
[len
- 1] = '\0';
3657 ast_module_user_remove(u
);
3662 static struct ast_custom_function queueagentcount_function
= {
3663 .name
= "QUEUEAGENTCOUNT",
3664 .synopsis
= "Count number of agents answering a queue",
3665 .syntax
= "QUEUEAGENTCOUNT(<queuename>)",
3667 "Returns the number of members currently associated with the specified queue.\n"
3668 "This function is deprecated. You should use QUEUE_MEMBER_COUNT() instead.\n",
3669 .read
= queue_function_qac
,
3672 static struct ast_custom_function queuemembercount_function
= {
3673 .name
= "QUEUE_MEMBER_COUNT",
3674 .synopsis
= "Count number of members answering a queue",
3675 .syntax
= "QUEUE_MEMBER_COUNT(<queuename>)",
3677 "Returns the number of members currently associated with the specified queue.\n",
3678 .read
= queue_function_qac
,
3681 static struct ast_custom_function queuewaitingcount_function
= {
3682 .name
= "QUEUE_WAITING_COUNT",
3683 .synopsis
= "Count number of calls currently waiting in a queue",
3684 .syntax
= "QUEUE_WAITING_COUNT(<queuename>)",
3686 "Returns the number of callers currently waiting in the specified queue.\n",
3687 .read
= queue_function_queuewaitingcount
,
3690 static struct ast_custom_function queuememberlist_function
= {
3691 .name
= "QUEUE_MEMBER_LIST",
3692 .synopsis
= "Returns a list of interfaces on a queue",
3693 .syntax
= "QUEUE_MEMBER_LIST(<queuename>)",
3695 "Returns a comma-separated list of members associated with the specified queue.\n",
3696 .read
= queue_function_queuememberlist
,
3699 static int reload_queues(void)
3701 struct call_queue
*q
;
3702 struct ast_config
*cfg
;
3704 struct ast_variable
*var
;
3705 struct member
*prev
, *cur
, *newm
, *next
;
3707 const char *general_val
= NULL
;
3712 AST_DECLARE_APP_ARGS(args
,
3713 AST_APP_ARG(interface
);
3714 AST_APP_ARG(penalty
);
3715 AST_APP_ARG(membername
);
3718 if (!(cfg
= ast_config_load("queues.conf"))) {
3719 ast_log(LOG_NOTICE
, "No call queueing config file (queues.conf), so no call queues\n");
3722 AST_LIST_LOCK(&queues
);
3724 /* Mark all queues as dead for the moment */
3725 AST_LIST_TRAVERSE(&queues
, q
, list
)
3728 /* Chug through config file */
3730 while ((cat
= ast_category_browse(cfg
, cat
)) ) {
3731 if (!strcasecmp(cat
, "general")) {
3732 /* Initialize global settings */
3733 queue_persistent_members
= 0;
3734 if ((general_val
= ast_variable_retrieve(cfg
, "general", "persistentmembers")))
3735 queue_persistent_members
= ast_true(general_val
);
3736 autofill_default
= 0;
3737 if ((general_val
= ast_variable_retrieve(cfg
, "general", "autofill")))
3738 autofill_default
= ast_true(general_val
);
3739 montype_default
= 0;
3740 if ((general_val
= ast_variable_retrieve(cfg
, "general", "monitor-type")))
3741 if (!strcasecmp(general_val
, "mixmonitor"))
3742 montype_default
= 1;
3743 } else { /* Define queue */
3744 /* Look for an existing one */
3745 AST_LIST_TRAVERSE(&queues
, q
, list
) {
3746 if (!strcmp(q
->name
, cat
))
3751 if (!(q
= alloc_queue(cat
))) {
3752 /* TODO: Handle memory allocation failure */
3759 ast_mutex_lock(&q
->lock
);
3760 /* Re-initialize the queue, and clear statistics */
3763 for (cur
= q
->members
; cur
; cur
= cur
->next
) {
3764 if (!cur
->dynamic
) {
3768 for (var
= ast_variable_browse(cfg
, cat
); var
; var
= var
->next
) {
3769 if (!strcasecmp(var
->name
, "member")) {
3770 /* Add a new member */
3771 ast_copy_string(parse
, var
->value
, sizeof(parse
));
3773 AST_NONSTANDARD_APP_ARGS(args
, parse
, ',');
3775 interface
= args
.interface
;
3776 if(!ast_strlen_zero(args
.penalty
)) {
3778 while (*tmp
&& *tmp
< 33) tmp
++;
3779 penalty
= atoi(tmp
);
3786 if (!ast_strlen_zero(args
.membername
)) {
3787 membername
= args
.membername
;
3788 while (*membername
&& *membername
< 33) membername
++;
3790 membername
= interface
;
3792 /* Find the old position in the list */
3793 for (prev
= NULL
, cur
= q
->members
; cur
; prev
= cur
, cur
= cur
->next
) {
3794 if (!strcmp(cur
->interface
, interface
)) {
3799 newm
= create_queue_member(interface
, membername
, penalty
, cur
? cur
->paused
: 0);
3803 newm
->next
= cur
->next
;
3811 /* Add them to the master int list if necessary */
3812 add_to_interfaces(interface
);
3813 newm
->next
= q
->members
;
3817 queue_set_param(q
, var
->name
, var
->value
, var
->lineno
, 1);
3821 /* Free remaining members marked as delme */
3822 for (prev
= NULL
, cur
= q
->members
;
3837 remove_from_interfaces(cur
->interface
);
3841 if (q
->strategy
== QUEUE_STRATEGY_ROUNDROBIN
)
3845 AST_LIST_INSERT_HEAD(&queues
, q
, list
);
3847 ast_mutex_unlock(&q
->lock
);
3851 ast_config_destroy(cfg
);
3852 AST_LIST_TRAVERSE_SAFE_BEGIN(&queues
, q
, list
) {
3854 AST_LIST_REMOVE_CURRENT(&queues
, list
);
3858 ast_log(LOG_DEBUG
, "XXX Leaking a little memory :( XXX\n");
3860 ast_mutex_lock(&q
->lock
);
3861 for (cur
= q
->members
; cur
; cur
= cur
->next
)
3862 cur
->status
= ast_device_state(cur
->interface
);
3863 ast_mutex_unlock(&q
->lock
);
3866 AST_LIST_TRAVERSE_SAFE_END
;
3867 AST_LIST_UNLOCK(&queues
);
3871 static int __queues_show(struct mansession
*s
, int manager
, int fd
, int argc
, char **argv
)
3873 struct call_queue
*q
;
3874 struct queue_ent
*qe
;
3876 int pos
, queue_show
;
3882 char *term
= manager
? "\r\n" : "\n";
3890 return RESULT_SHOWUSAGE
;
3892 /* We only want to load realtime queues when a specific queue is asked for. */
3894 load_realtime_queue(argv
[2]);
3896 AST_LIST_LOCK(&queues
);
3897 if (AST_LIST_EMPTY(&queues
)) {
3898 AST_LIST_UNLOCK(&queues
);
3901 astman_append(s
, "No such queue: %s.%s",argv
[2], term
);
3903 ast_cli(fd
, "No such queue: %s.%s",argv
[2], term
);
3906 astman_append(s
, "No queues.%s", term
);
3908 ast_cli(fd
, "No queues.%s", term
);
3910 return RESULT_SUCCESS
;
3912 AST_LIST_TRAVERSE(&queues
, q
, list
) {
3913 ast_mutex_lock(&q
->lock
);
3915 if (strcasecmp(q
->name
, argv
[2]) != 0) {
3916 ast_mutex_unlock(&q
->lock
);
3917 if (!AST_LIST_NEXT(q
, list
)) {
3918 ast_cli(fd
, "No such queue: %s.%s",argv
[2], term
);
3926 max_left
= sizeof(max_buf
);
3928 ast_build_string(&max
, &max_left
, "%d", q
->maxlen
);
3930 ast_build_string(&max
, &max_left
, "unlimited");
3932 if (q
->callscompleted
> 0)
3933 sl
= 100 * ((float) q
->callscompletedinsl
/ (float) q
->callscompleted
);
3935 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",
3936 q
->name
, q
->count
, max_buf
, int2strat(q
->strategy
), q
->holdtime
, q
->weight
,
3937 q
->callscompleted
, q
->callsabandoned
,sl
,q
->servicelevel
, term
);
3939 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",
3940 q
->name
, q
->count
, max_buf
, int2strat(q
->strategy
), q
->holdtime
, q
->weight
, q
->callscompleted
, q
->callsabandoned
,sl
,q
->servicelevel
, term
);
3943 astman_append(s
, " Members: %s", term
);
3945 ast_cli(fd
, " Members: %s", term
);
3946 for (mem
= q
->members
; mem
; mem
= mem
->next
) {
3949 max_left
= sizeof(max_buf
);
3951 ast_build_string(&max
, &max_left
, " with penalty %d", mem
->penalty
);
3953 ast_build_string(&max
, &max_left
, " (dynamic)");
3955 ast_build_string(&max
, &max_left
, " (paused)");
3956 ast_build_string(&max
, &max_left
, " (%s)", devstate2str(mem
->status
));
3958 ast_build_string(&max
, &max_left
, " has taken %d calls (last was %ld secs ago)",
3959 mem
->calls
, (long) (time(NULL
) - mem
->lastcall
));
3961 ast_build_string(&max
, &max_left
, " has taken no calls yet");
3963 astman_append(s
, " %s%s%s", mem
->interface
, max_buf
, term
);
3965 ast_cli(fd
, " %s%s%s", mem
->interface
, max_buf
, term
);
3968 astman_append(s
, " No Members%s", term
);
3970 ast_cli(fd
, " No Members%s", term
);
3974 astman_append(s
, " Callers: %s", term
);
3976 ast_cli(fd
, " Callers: %s", term
);
3977 for (qe
= q
->head
; qe
; qe
= qe
->next
) {
3979 astman_append(s
, " %d. %s (wait: %ld:%2.2ld, prio: %d)%s",
3980 pos
++, qe
->chan
->name
, (long) (now
- qe
->start
) / 60,
3981 (long) (now
- qe
->start
) % 60, qe
->prio
, term
);
3983 ast_cli(fd
, " %d. %s (wait: %ld:%2.2ld, prio: %d)%s", pos
++,
3984 qe
->chan
->name
, (long) (now
- qe
->start
) / 60,
3985 (long) (now
- qe
->start
) % 60, qe
->prio
, term
);
3988 astman_append(s
, " No Callers%s", term
);
3990 ast_cli(fd
, " No Callers%s", term
);
3992 astman_append(s
, "%s", term
);
3994 ast_cli(fd
, "%s", term
);
3995 ast_mutex_unlock(&q
->lock
);
3999 AST_LIST_UNLOCK(&queues
);
4000 return RESULT_SUCCESS
;
4003 static int queue_show(int fd
, int argc
, char **argv
)
4005 return __queues_show(NULL
, 0, fd
, argc
, argv
);
4008 static char *complete_queue(const char *line
, const char *word
, int pos
, int state
)
4010 struct call_queue
*q
;
4013 int wordlen
= strlen(word
);
4015 AST_LIST_LOCK(&queues
);
4016 AST_LIST_TRAVERSE(&queues
, q
, list
) {
4017 if (!strncasecmp(word
, q
->name
, wordlen
) && ++which
> state
) {
4018 ret
= ast_strdup(q
->name
);
4022 AST_LIST_UNLOCK(&queues
);
4027 /*!\brief callback to display queues status in manager
4028 \addtogroup Group_AMI
4030 static int manager_queues_show( struct mansession
*s
, struct message
*m
)
4032 char *a
[] = { "queue", "show" };
4034 __queues_show(s
, 1, -1, 2, a
);
4035 astman_append(s
, "\r\n\r\n"); /* Properly terminate Manager output */
4037 return RESULT_SUCCESS
;
4040 /* Dump queue status */
4041 static int manager_queues_status( struct mansession
*s
, struct message
*m
)
4045 char *id
= astman_get_header(m
,"ActionID");
4046 char *queuefilter
= astman_get_header(m
,"Queue");
4047 char *memberfilter
= astman_get_header(m
,"Member");
4048 char idText
[256] = "";
4049 struct call_queue
*q
;
4050 struct queue_ent
*qe
;
4054 astman_send_ack(s
, m
, "Queue status will follow");
4056 AST_LIST_LOCK(&queues
);
4057 if (!ast_strlen_zero(id
))
4058 snprintf(idText
, sizeof(idText
), "ActionID: %s\r\n", id
);
4060 AST_LIST_TRAVERSE(&queues
, q
, list
) {
4061 ast_mutex_lock(&q
->lock
);
4063 /* List queue properties */
4064 if (ast_strlen_zero(queuefilter
) || !strcmp(q
->name
, queuefilter
)) {
4065 if (q
->callscompleted
> 0)
4066 sl
= 100 * ((float) q
->callscompletedinsl
/ (float) q
->callscompleted
);
4067 astman_append(s
, "Event: QueueParams\r\n"
4074 "ServiceLevel: %d\r\n"
4075 "ServicelevelPerf: %2.1f\r\n"
4079 q
->name
, q
->maxlen
, q
->count
, q
->holdtime
, q
->callscompleted
,
4080 q
->callsabandoned
, q
->servicelevel
, sl
, q
->weight
, idText
);
4081 /* List Queue Members */
4082 for (mem
= q
->members
; mem
; mem
= mem
->next
) {
4083 if (ast_strlen_zero(memberfilter
) || !strcmp(mem
->interface
, memberfilter
)) {
4084 astman_append(s
, "Event: QueueMember\r\n"
4087 "Membership: %s\r\n"
4089 "CallsTaken: %d\r\n"
4095 q
->name
, mem
->interface
, mem
->dynamic
? "dynamic" : "static",
4096 mem
->penalty
, mem
->calls
, (int)mem
->lastcall
, mem
->status
, mem
->paused
, idText
);
4099 /* List Queue Entries */
4101 for (qe
= q
->head
; qe
; qe
= qe
->next
) {
4102 astman_append(s
, "Event: QueueEntry\r\n"
4107 "CallerIDName: %s\r\n"
4111 q
->name
, pos
++, qe
->chan
->name
,
4112 S_OR(qe
->chan
->cid
.cid_num
, "unknown"),
4113 S_OR(qe
->chan
->cid
.cid_name
, "unknown"),
4114 (long) (now
- qe
->start
), idText
);
4117 ast_mutex_unlock(&q
->lock
);
4121 "Event: QueueStatusComplete\r\n"
4125 AST_LIST_UNLOCK(&queues
);
4128 return RESULT_SUCCESS
;
4131 static int manager_add_queue_member(struct mansession
*s
, struct message
*m
)
4133 char *queuename
, *interface
, *penalty_s
, *paused_s
, *membername
;
4134 int paused
, penalty
= 0;
4136 queuename
= astman_get_header(m
, "Queue");
4137 interface
= astman_get_header(m
, "Interface");
4138 penalty_s
= astman_get_header(m
, "Penalty");
4139 paused_s
= astman_get_header(m
, "Paused");
4140 membername
= astman_get_header(m
, "MemberName");
4142 if (ast_strlen_zero(queuename
)) {
4143 astman_send_error(s
, m
, "'Queue' not specified.");
4147 if (ast_strlen_zero(interface
)) {
4148 astman_send_error(s
, m
, "'Interface' not specified.");
4152 if (ast_strlen_zero(penalty_s
))
4154 else if (sscanf(penalty_s
, "%d", &penalty
) != 1)
4157 if (ast_strlen_zero(paused_s
))
4160 paused
= abs(ast_true(paused_s
));
4162 if (ast_strlen_zero(membername
))
4163 membername
= interface
;
4165 switch (add_to_queue(queuename
, interface
, membername
, penalty
, paused
, queue_persistent_members
)) {
4167 ast_queue_log(queuename
, "MANAGER", interface
, "ADDMEMBER", "%s", "");
4168 astman_send_ack(s
, m
, "Added interface to queue");
4171 astman_send_error(s
, m
, "Unable to add interface: Already there");
4173 case RES_NOSUCHQUEUE
:
4174 astman_send_error(s
, m
, "Unable to add interface to queue: No such queue");
4176 case RES_OUTOFMEMORY
:
4177 astman_send_error(s
, m
, "Out of memory");
4184 static int manager_remove_queue_member(struct mansession
*s
, struct message
*m
)
4186 char *queuename
, *interface
;
4188 queuename
= astman_get_header(m
, "Queue");
4189 interface
= astman_get_header(m
, "Interface");
4191 if (ast_strlen_zero(queuename
) || ast_strlen_zero(interface
)) {
4192 astman_send_error(s
, m
, "Need 'Queue' and 'Interface' parameters.");
4196 switch (remove_from_queue(queuename
, interface
)) {
4198 ast_queue_log(queuename
, "MANAGER", interface
, "REMOVEMEMBER", "%s", "");
4199 astman_send_ack(s
, m
, "Removed interface from queue");
4202 astman_send_error(s
, m
, "Unable to remove interface: Not there");
4204 case RES_NOSUCHQUEUE
:
4205 astman_send_error(s
, m
, "Unable to remove interface from queue: No such queue");
4207 case RES_OUTOFMEMORY
:
4208 astman_send_error(s
, m
, "Out of memory");
4215 static int manager_pause_queue_member(struct mansession
*s
, struct message
*m
)
4217 char *queuename
, *interface
, *paused_s
;
4220 interface
= astman_get_header(m
, "Interface");
4221 paused_s
= astman_get_header(m
, "Paused");
4222 queuename
= astman_get_header(m
, "Queue"); /* Optional - if not supplied, pause the given Interface in all queues */
4224 if (ast_strlen_zero(interface
) || ast_strlen_zero(paused_s
)) {
4225 astman_send_error(s
, m
, "Need 'Interface' and 'Paused' parameters.");
4229 paused
= abs(ast_true(paused_s
));
4231 if (set_member_paused(queuename
, interface
, paused
))
4232 astman_send_error(s
, m
, "Interface not found");
4234 astman_send_ack(s
, m
, paused
? "Interface paused successfully" : "Interface unpaused successfully");
4238 static int handle_queue_add_member(int fd
, int argc
, char *argv
[])
4240 char *queuename
, *interface
, *membername
;
4243 if ((argc
!= 6) && (argc
!= 8) && (argc
!= 10)) {
4244 return RESULT_SHOWUSAGE
;
4245 } else if (strcmp(argv
[4], "to")) {
4246 return RESULT_SHOWUSAGE
;
4247 } else if ((argc
== 8) && strcmp(argv
[6], "penalty")) {
4248 return RESULT_SHOWUSAGE
;
4249 } else if ((argc
== 10) && strcmp(argv
[8], "as")) {
4250 return RESULT_SHOWUSAGE
;
4253 queuename
= argv
[5];
4254 interface
= argv
[3];
4256 if (sscanf(argv
[7], "%d", &penalty
) == 1) {
4258 ast_cli(fd
, "Penalty must be >= 0\n");
4262 ast_cli(fd
, "Penalty must be an integer >= 0\n");
4270 membername
= argv
[9];
4272 membername
= interface
;
4275 switch (add_to_queue(queuename
, interface
, membername
, penalty
, 0, queue_persistent_members
)) {
4277 ast_queue_log(queuename
, "CLI", interface
, "ADDMEMBER", "%s", "");
4278 ast_cli(fd
, "Added interface '%s' to queue '%s'\n", interface
, queuename
);
4279 return RESULT_SUCCESS
;
4281 ast_cli(fd
, "Unable to add interface '%s' to queue '%s': Already there\n", interface
, queuename
);
4282 return RESULT_FAILURE
;
4283 case RES_NOSUCHQUEUE
:
4284 ast_cli(fd
, "Unable to add interface to queue '%s': No such queue\n", queuename
);
4285 return RESULT_FAILURE
;
4286 case RES_OUTOFMEMORY
:
4287 ast_cli(fd
, "Out of memory\n");
4288 return RESULT_FAILURE
;
4290 return RESULT_FAILURE
;
4294 static char *complete_queue_add_member(const char *line
, const char *word
, int pos
, int state
)
4296 /* 0 - queue; 1 - add; 2 - member; 3 - <interface>; 4 - to; 5 - <queue>; 6 - penalty; 7 - <penalty>; 8 - as; 9 - <membername> */
4298 case 3: /* Don't attempt to complete name of interface (infinite possibilities) */
4300 case 4: /* only one possible match, "to" */
4301 return state
== 0 ? ast_strdup("to") : NULL
;
4302 case 5: /* <queue> */
4303 return complete_queue(line
, word
, pos
, state
);
4304 case 6: /* only one possible match, "penalty" */
4305 return state
== 0 ? ast_strdup("penalty") : NULL
;
4307 if (state
< 100) { /* 0-99 */
4309 if ((num
= ast_malloc(3))) {
4310 sprintf(num
, "%d", state
);
4316 case 8: /* only one possible match, "as" */
4317 return state
== 0 ? ast_strdup("as") : NULL
;
4318 case 9: /* Don't attempt to complete name of member (infinite possibilities) */
4325 static int handle_queue_remove_member(int fd
, int argc
, char *argv
[])
4327 char *queuename
, *interface
;
4330 return RESULT_SHOWUSAGE
;
4331 } else if (strcmp(argv
[4], "from")) {
4332 return RESULT_SHOWUSAGE
;
4335 queuename
= argv
[5];
4336 interface
= argv
[3];
4338 switch (remove_from_queue(queuename
, interface
)) {
4340 ast_queue_log(queuename
, "CLI", interface
, "REMOVEMEMBER", "%s", "");
4341 ast_cli(fd
, "Removed interface '%s' from queue '%s'\n", interface
, queuename
);
4342 return RESULT_SUCCESS
;
4344 ast_cli(fd
, "Unable to remove interface '%s' from queue '%s': Not there\n", interface
, queuename
);
4345 return RESULT_FAILURE
;
4346 case RES_NOSUCHQUEUE
:
4347 ast_cli(fd
, "Unable to remove interface from queue '%s': No such queue\n", queuename
);
4348 return RESULT_FAILURE
;
4349 case RES_OUTOFMEMORY
:
4350 ast_cli(fd
, "Out of memory\n");
4351 return RESULT_FAILURE
;
4353 return RESULT_FAILURE
;
4357 static char *complete_queue_remove_member(const char *line
, const char *word
, int pos
, int state
)
4360 struct call_queue
*q
;
4363 /* 0 - queue; 1 - remove; 2 - member; 3 - <member>; 4 - from; 5 - <queue> */
4364 if (pos
> 5 || pos
< 3)
4366 if (pos
== 4) /* only one possible match, 'from' */
4367 return state
== 0 ? ast_strdup("from") : NULL
;
4369 if (pos
== 5) /* No need to duplicate code */
4370 return complete_queue(line
, word
, pos
, state
);
4372 /* here is the case for 3, <member> */
4373 if (!AST_LIST_EMPTY(&queues
)) { /* XXX unnecessary ? the traverse does that for us */
4374 AST_LIST_TRAVERSE(&queues
, q
, list
) {
4375 ast_mutex_lock(&q
->lock
);
4376 for (m
= q
->members
; m
; m
= m
->next
) {
4377 if (++which
> state
) {
4378 ast_mutex_unlock(&q
->lock
);
4379 return ast_strdup(m
->interface
);
4382 ast_mutex_unlock(&q
->lock
);
4389 static char queue_show_usage
[] =
4390 "Usage: queue show\n"
4391 " Provides summary information on a specified queue.\n";
4393 static char qam_cmd_usage
[] =
4394 "Usage: queue add member <channel> to <queue> [penalty <penalty>]\n";
4396 static char qrm_cmd_usage
[] =
4397 "Usage: queue remove member <channel> from <queue>\n";
4399 static struct ast_cli_entry cli_show_queue_deprecated
= {
4400 { "show", "queue", NULL
},
4402 NULL
, complete_queue
};
4404 static struct ast_cli_entry cli_add_queue_member_deprecated
= {
4405 { "add", "queue", "member", NULL
},
4406 handle_queue_add_member
, NULL
,
4407 NULL
, complete_queue_add_member
};
4409 static struct ast_cli_entry cli_remove_queue_member_deprecated
= {
4410 { "remove", "queue", "member", NULL
},
4411 handle_queue_remove_member
, NULL
,
4412 NULL
, complete_queue_remove_member
};
4414 static struct ast_cli_entry cli_queue
[] = {
4416 { { "show", "queues", NULL
},
4420 { { "queue", "show", NULL
},
4421 queue_show
, "Show status of a specified queue",
4422 queue_show_usage
, complete_queue
, &cli_show_queue_deprecated
},
4424 { { "queue", "add", "member", NULL
},
4425 handle_queue_add_member
, "Add a channel to a specified queue",
4426 qam_cmd_usage
, complete_queue_add_member
, &cli_add_queue_member_deprecated
},
4428 { { "queue", "remove", "member", NULL
},
4429 handle_queue_remove_member
, "Removes a channel from a specified queue",
4430 qrm_cmd_usage
, complete_queue_remove_member
, &cli_remove_queue_member_deprecated
},
4433 static int unload_module(void)
4437 ast_cli_unregister_multiple(cli_queue
, sizeof(cli_queue
) / sizeof(struct ast_cli_entry
));
4438 res
= ast_manager_unregister("QueueStatus");
4439 res
|= ast_manager_unregister("Queues");
4440 res
|= ast_manager_unregister("QueueStatus");
4441 res
|= ast_manager_unregister("QueueAdd");
4442 res
|= ast_manager_unregister("QueueRemove");
4443 res
|= ast_manager_unregister("QueuePause");
4444 res
|= ast_unregister_application(app_aqm
);
4445 res
|= ast_unregister_application(app_rqm
);
4446 res
|= ast_unregister_application(app_pqm
);
4447 res
|= ast_unregister_application(app_upqm
);
4448 res
|= ast_unregister_application(app_ql
);
4449 res
|= ast_unregister_application(app
);
4450 res
|= ast_custom_function_unregister(&queueagentcount_function
);
4451 res
|= ast_custom_function_unregister(&queuemembercount_function
);
4452 res
|= ast_custom_function_unregister(&queuememberlist_function
);
4453 res
|= ast_custom_function_unregister(&queuewaitingcount_function
);
4455 ast_module_user_hangup_all();
4457 clear_and_free_interfaces();
4462 static int load_module(void)
4465 if(!reload_queues())
4466 return AST_MODULE_LOAD_DECLINE
;
4467 if (queue_persistent_members
)
4468 reload_queue_members();
4469 ast_cli_register_multiple(cli_queue
, sizeof(cli_queue
) / sizeof(struct ast_cli_entry
));
4470 res
= ast_register_application(app
, queue_exec
, synopsis
, descrip
);
4471 res
|= ast_register_application(app_aqm
, aqm_exec
, app_aqm_synopsis
, app_aqm_descrip
);
4472 res
|= ast_register_application(app_rqm
, rqm_exec
, app_rqm_synopsis
, app_rqm_descrip
);
4473 res
|= ast_register_application(app_pqm
, pqm_exec
, app_pqm_synopsis
, app_pqm_descrip
);
4474 res
|= ast_register_application(app_upqm
, upqm_exec
, app_upqm_synopsis
, app_upqm_descrip
);
4475 res
|= ast_register_application(app_ql
, ql_exec
, app_ql_synopsis
, app_ql_descrip
);
4476 res
|= ast_manager_register("Queues", 0, manager_queues_show
, "Queues");
4477 res
|= ast_manager_register("QueueStatus", 0, manager_queues_status
, "Queue Status");
4478 res
|= ast_manager_register("QueueAdd", EVENT_FLAG_AGENT
, manager_add_queue_member
, "Add interface to queue.");
4479 res
|= ast_manager_register("QueueRemove", EVENT_FLAG_AGENT
, manager_remove_queue_member
, "Remove interface from queue.");
4480 res
|= ast_manager_register("QueuePause", EVENT_FLAG_AGENT
, manager_pause_queue_member
, "Makes a queue member temporarily unavailable");
4481 res
|= ast_custom_function_register(&queueagentcount_function
);
4482 res
|= ast_custom_function_register(&queuemembercount_function
);
4483 res
|= ast_custom_function_register(&queuememberlist_function
);
4484 res
|= ast_custom_function_register(&queuewaitingcount_function
);
4485 res
|= ast_devstate_add(statechange_queue
, NULL
);
4490 static int reload(void)
4496 AST_MODULE_INFO(ASTERISK_GPL_KEY
, AST_MODFLAG_DEFAULT
, "True Call Queueing",
4497 .load
= load_module
,
4498 .unload
= unload_module
,