update comment to match the state of the code
[asterisk-bristuff.git] / apps / app_queue.c
blob95a98df68a60dd165b22984eaa50125cc3ad47a1
1 /*
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.
19 /*! \file
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
59 #include "asterisk.h"
61 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
63 #include <stdlib.h>
64 #include <errno.h>
65 #include <unistd.h>
66 #include <string.h>
67 #include <stdlib.h>
68 #include <stdio.h>
69 #include <sys/time.h>
70 #include <sys/signal.h>
71 #include <netinet/in.h>
73 #include "asterisk/lock.h"
74 #include "asterisk/file.h"
75 #include "asterisk/logger.h"
76 #include "asterisk/channel.h"
77 #include "asterisk/pbx.h"
78 #include "asterisk/options.h"
79 #include "asterisk/app.h"
80 #include "asterisk/linkedlists.h"
81 #include "asterisk/module.h"
82 #include "asterisk/translate.h"
83 #include "asterisk/say.h"
84 #include "asterisk/features.h"
85 #include "asterisk/musiconhold.h"
86 #include "asterisk/cli.h"
87 #include "asterisk/manager.h"
88 #include "asterisk/config.h"
89 #include "asterisk/monitor.h"
90 #include "asterisk/utils.h"
91 #include "asterisk/causes.h"
92 #include "asterisk/astdb.h"
93 #include "asterisk/devicestate.h"
94 #include "asterisk/stringfields.h"
95 #include "asterisk/astobj2.h"
97 enum {
98 QUEUE_STRATEGY_RINGALL = 0,
99 QUEUE_STRATEGY_ROUNDROBIN,
100 QUEUE_STRATEGY_LEASTRECENT,
101 QUEUE_STRATEGY_FEWESTCALLS,
102 QUEUE_STRATEGY_RANDOM,
103 QUEUE_STRATEGY_RRMEMORY
106 static struct strategy {
107 int strategy;
108 char *name;
109 } strategies[] = {
110 { QUEUE_STRATEGY_RINGALL, "ringall" },
111 { QUEUE_STRATEGY_ROUNDROBIN, "roundrobin" },
112 { QUEUE_STRATEGY_LEASTRECENT, "leastrecent" },
113 { QUEUE_STRATEGY_FEWESTCALLS, "fewestcalls" },
114 { QUEUE_STRATEGY_RANDOM, "random" },
115 { QUEUE_STRATEGY_RRMEMORY, "rrmemory" },
118 #define DEFAULT_RETRY 5
119 #define DEFAULT_TIMEOUT 15
120 #define RECHECK 1 /* Recheck every second to see we we're at the top yet */
121 #define MAX_PERIODIC_ANNOUNCEMENTS 10 /* The maximum periodic announcements we can have */
123 #define RES_OKAY 0 /* Action completed */
124 #define RES_EXISTS (-1) /* Entry already exists */
125 #define RES_OUTOFMEMORY (-2) /* Out of memory */
126 #define RES_NOSUCHQUEUE (-3) /* No such queue */
127 #define RES_NOT_DYNAMIC (-4) /* Member is not dynamic */
129 static char *app = "Queue";
131 static char *synopsis = "Queue a call for a call queue";
133 static char *descrip =
134 " Queue(queuename[|options[|URL][|announceoverride][|timeout][|AGI]):\n"
135 "Queues an incoming call in a particular call queue as defined in queues.conf.\n"
136 "This application will return to the dialplan if the queue does not exist, or\n"
137 "any of the join options cause the caller to not enter the queue.\n"
138 "The option string may contain zero or more of the following characters:\n"
139 " 'd' -- data-quality (modem) call (minimum delay).\n"
140 " 'h' -- allow callee to hang up by hitting *.\n"
141 " 'H' -- allow caller to hang up by hitting *.\n"
142 " 'n' -- no retries on the timeout; will exit this application and \n"
143 " go to the next step.\n"
144 " 'i' -- ignore call forward requests from queue members and do nothing\n"
145 " when they are requested.\n"
146 " 'r' -- ring instead of playing MOH\n"
147 " 't' -- allow the called user transfer the calling user\n"
148 " 'T' -- to allow the calling user to transfer the call.\n"
149 " 'w' -- allow the called user to write the conversation to disk via Monitor\n"
150 " 'W' -- allow the calling user to write the conversation to disk via Monitor\n"
151 " In addition to transferring the call, a call may be parked and then picked\n"
152 "up by another user.\n"
153 " The optional URL will be sent to the called party if the channel supports\n"
154 "it.\n"
155 " The optional AGI parameter will setup an AGI script to be executed on the \n"
156 "calling party's channel once they are connected to a queue member.\n"
157 " The timeout will cause the queue to fail out after a specified number of\n"
158 "seconds, checked between each queues.conf 'timeout' and 'retry' cycle.\n"
159 " This application sets the following channel variable upon completion:\n"
160 " QUEUESTATUS The status of the call as a text string, one of\n"
161 " TIMEOUT | FULL | JOINEMPTY | LEAVEEMPTY | JOINUNAVAIL | LEAVEUNAVAIL\n";
163 static char *app_aqm = "AddQueueMember" ;
164 static char *app_aqm_synopsis = "Dynamically adds queue members" ;
165 static char *app_aqm_descrip =
166 " AddQueueMember(queuename[|interface[|penalty[|options[|membername]]]]):\n"
167 "Dynamically adds interface to an existing queue.\n"
168 "If the interface is already in the queue and there exists an n+101 priority\n"
169 "then it will then jump to this priority. Otherwise it will return an error\n"
170 "The option string may contain zero or more of the following characters:\n"
171 " 'j' -- jump to +101 priority when appropriate.\n"
172 " This application sets the following channel variable upon completion:\n"
173 " AQMSTATUS The status of the attempt to add a queue member as a \n"
174 " text string, one of\n"
175 " ADDED | MEMBERALREADY | NOSUCHQUEUE \n"
176 "Example: AddQueueMember(techsupport|SIP/3000)\n"
179 static char *app_rqm = "RemoveQueueMember" ;
180 static char *app_rqm_synopsis = "Dynamically removes queue members" ;
181 static char *app_rqm_descrip =
182 " RemoveQueueMember(queuename[|interface[|options]]):\n"
183 "Dynamically removes interface to an existing queue\n"
184 "If the interface is NOT in the queue and there exists an n+101 priority\n"
185 "then it will then jump to this priority. Otherwise it will return an error\n"
186 "The option string may contain zero or more of the following characters:\n"
187 " 'j' -- jump to +101 priority when appropriate.\n"
188 " This application sets the following channel variable upon completion:\n"
189 " RQMSTATUS The status of the attempt to remove a queue member as a\n"
190 " text string, one of\n"
191 " REMOVED | NOTINQUEUE | NOSUCHQUEUE \n"
192 "Example: RemoveQueueMember(techsupport|SIP/3000)\n"
195 static char *app_pqm = "PauseQueueMember" ;
196 static char *app_pqm_synopsis = "Pauses a queue member" ;
197 static char *app_pqm_descrip =
198 " PauseQueueMember([queuename]|interface[|options]):\n"
199 "Pauses (blocks calls for) a queue member.\n"
200 "The given interface will be paused in the given queue. This prevents\n"
201 "any calls from being sent from the queue to the interface until it is\n"
202 "unpaused with UnpauseQueueMember or the manager interface. If no\n"
203 "queuename is given, the interface is paused in every queue it is a\n"
204 "member of. If the interface is not in the named queue, or if no queue\n"
205 "is given and the interface is not in any queue, it will jump to\n"
206 "priority n+101, if it exists and the appropriate options are set.\n"
207 "The application will fail if the interface is not found and no extension\n"
208 "to jump to exists.\n"
209 "The option string may contain zero or more of the following characters:\n"
210 " 'j' -- jump to +101 priority when appropriate.\n"
211 " This application sets the following channel variable upon completion:\n"
212 " PQMSTATUS The status of the attempt to pause a queue member as a\n"
213 " text string, one of\n"
214 " PAUSED | NOTFOUND\n"
215 "Example: PauseQueueMember(|SIP/3000)\n";
217 static char *app_upqm = "UnpauseQueueMember" ;
218 static char *app_upqm_synopsis = "Unpauses a queue member" ;
219 static char *app_upqm_descrip =
220 " UnpauseQueueMember([queuename]|interface[|options]):\n"
221 "Unpauses (resumes calls to) a queue member.\n"
222 "This is the counterpart to PauseQueueMember and operates exactly the\n"
223 "same way, except it unpauses instead of pausing the given interface.\n"
224 "The option string may contain zero or more of the following characters:\n"
225 " 'j' -- jump to +101 priority when appropriate.\n"
226 " This application sets the following channel variable upon completion:\n"
227 " UPQMSTATUS The status of the attempt to unpause a queue \n"
228 " member as a text string, one of\n"
229 " UNPAUSED | NOTFOUND\n"
230 "Example: UnpauseQueueMember(|SIP/3000)\n";
232 static char *app_ql = "QueueLog" ;
233 static char *app_ql_synopsis = "Writes to the queue_log" ;
234 static char *app_ql_descrip =
235 " QueueLog(queuename|uniqueid|agent|event[|additionalinfo]):\n"
236 "Allows you to write your own events into the queue log\n"
237 "Example: QueueLog(101|${UNIQUEID}|${AGENT}|WENTONBREAK|600)\n";
239 /*! \brief Persistent Members astdb family */
240 static const char *pm_family = "Queue/PersistentMembers";
241 /* The maximum length of each persistent member queue database entry */
242 #define PM_MAX_LEN 8192
244 /*! \brief queues.conf [general] option */
245 static int queue_persistent_members = 0;
247 /*! \brief queues.conf per-queue weight option */
248 static int use_weight = 0;
250 /*! \brief queues.conf [general] option */
251 static int autofill_default = 0;
253 /*! \brief queues.conf [general] option */
254 static int montype_default = 0;
256 enum queue_result {
257 QUEUE_UNKNOWN = 0,
258 QUEUE_TIMEOUT = 1,
259 QUEUE_JOINEMPTY = 2,
260 QUEUE_LEAVEEMPTY = 3,
261 QUEUE_JOINUNAVAIL = 4,
262 QUEUE_LEAVEUNAVAIL = 5,
263 QUEUE_FULL = 6,
266 const struct {
267 enum queue_result id;
268 char *text;
269 } queue_results[] = {
270 { QUEUE_UNKNOWN, "UNKNOWN" },
271 { QUEUE_TIMEOUT, "TIMEOUT" },
272 { QUEUE_JOINEMPTY,"JOINEMPTY" },
273 { QUEUE_LEAVEEMPTY, "LEAVEEMPTY" },
274 { QUEUE_JOINUNAVAIL, "JOINUNAVAIL" },
275 { QUEUE_LEAVEUNAVAIL, "LEAVEUNAVAIL" },
276 { QUEUE_FULL, "FULL" },
279 /*! \brief We define a custom "local user" structure because we
280 use it not only for keeping track of what is in use but
281 also for keeping track of who we're dialing. */
283 struct callattempt {
284 struct callattempt *q_next;
285 struct ast_channel *chan;
286 char interface[256];
287 int stillgoing;
288 int metric;
289 int oldstatus;
290 time_t lastcall;
291 struct member *member;
295 struct queue_ent {
296 struct call_queue *parent; /*!< What queue is our parent */
297 char moh[80]; /*!< Name of musiconhold to be used */
298 char announce[80]; /*!< Announcement to play for member when call is answered */
299 char context[AST_MAX_CONTEXT]; /*!< Context when user exits queue */
300 char digits[AST_MAX_EXTENSION]; /*!< Digits entered while in queue */
301 int valid_digits; /*!< Digits entered correspond to valid extension. Exited */
302 int pos; /*!< Where we are in the queue */
303 int prio; /*!< Our priority */
304 int last_pos_said; /*!< Last position we told the user */
305 time_t last_periodic_announce_time; /*!< The last time we played a periodic announcement */
306 int last_periodic_announce_sound; /*!< The last periodic announcement we made */
307 time_t last_pos; /*!< Last time we told the user their position */
308 int opos; /*!< Where we started in the queue */
309 int handled; /*!< Whether our call was handled */
310 int max_penalty; /*!< Limit the members that can take this call to this penalty or lower */
311 time_t start; /*!< When we started holding */
312 time_t expire; /*!< When this entry should expire (time out of queue) */
313 struct ast_channel *chan; /*!< Our channel */
314 struct queue_ent *next; /*!< The next queue entry */
317 struct member {
318 char interface[80]; /*!< Technology/Location */
319 char membername[80]; /*!< Member name to use in queue logs */
320 int penalty; /*!< Are we a last resort? */
321 int calls; /*!< Number of calls serviced by this member */
322 int dynamic; /*!< Are we dynamically added? */
323 int realtime; /*!< Is this member realtime? */
324 int status; /*!< Status of queue member */
325 int paused; /*!< Are we paused (not accepting calls)? */
326 time_t lastcall; /*!< When last successful call was hungup */
327 unsigned int dead:1; /*!< Used to detect members deleted in realtime */
328 unsigned int delme:1; /*!< Flag to delete entry on reload */
331 struct member_interface {
332 char interface[80];
333 AST_LIST_ENTRY(member_interface) list; /*!< Next call queue */
336 static AST_LIST_HEAD_STATIC(interfaces, member_interface);
338 /* values used in multi-bit flags in call_queue */
339 #define QUEUE_EMPTY_NORMAL 1
340 #define QUEUE_EMPTY_STRICT 2
341 #define ANNOUNCEHOLDTIME_ALWAYS 1
342 #define ANNOUNCEHOLDTIME_ONCE 2
343 #define QUEUE_EVENT_VARIABLES 3
345 struct call_queue {
346 ast_mutex_t lock;
347 char name[80]; /*!< Name */
348 char moh[80]; /*!< Music On Hold class to be used */
349 char announce[80]; /*!< Announcement to play when call is answered */
350 char context[AST_MAX_CONTEXT]; /*!< Exit context */
351 unsigned int monjoin:1;
352 unsigned int dead:1;
353 unsigned int joinempty:2;
354 unsigned int eventwhencalled:2;
355 unsigned int leavewhenempty:2;
356 unsigned int ringinuse:1;
357 unsigned int setinterfacevar:1;
358 unsigned int reportholdtime:1;
359 unsigned int wrapped:1;
360 unsigned int timeoutrestart:1;
361 unsigned int announceholdtime:2;
362 int strategy:4;
363 unsigned int maskmemberstatus:1;
364 unsigned int realtime:1;
365 unsigned int found:1;
366 int announcefrequency; /*!< How often to announce their position */
367 int periodicannouncefrequency; /*!< How often to play periodic announcement */
368 int roundingseconds; /*!< How many seconds do we round to? */
369 int holdtime; /*!< Current avg holdtime, based on recursive boxcar filter */
370 int callscompleted; /*!< Number of queue calls completed */
371 int callsabandoned; /*!< Number of queue calls abandoned */
372 int servicelevel; /*!< seconds setting for servicelevel*/
373 int callscompletedinsl; /*!< Number of calls answered with servicelevel*/
374 char monfmt[8]; /*!< Format to use when recording calls */
375 int montype; /*!< Monitor type Monitor vs. MixMonitor */
376 char sound_next[80]; /*!< Sound file: "Your call is now first in line" (def. queue-youarenext) */
377 char sound_thereare[80]; /*!< Sound file: "There are currently" (def. queue-thereare) */
378 char sound_calls[80]; /*!< Sound file: "calls waiting to speak to a representative." (def. queue-callswaiting)*/
379 char sound_holdtime[80]; /*!< Sound file: "The current estimated total holdtime is" (def. queue-holdtime) */
380 char sound_minutes[80]; /*!< Sound file: "minutes." (def. queue-minutes) */
381 char sound_lessthan[80]; /*!< Sound file: "less-than" (def. queue-lessthan) */
382 char sound_seconds[80]; /*!< Sound file: "seconds." (def. queue-seconds) */
383 char sound_thanks[80]; /*!< Sound file: "Thank you for your patience." (def. queue-thankyou) */
384 char sound_reporthold[80]; /*!< Sound file: "Hold time" (def. queue-reporthold) */
385 char sound_periodicannounce[MAX_PERIODIC_ANNOUNCEMENTS][80];/*!< Sound files: Custom announce, no default */
387 int count; /*!< How many entries */
388 int maxlen; /*!< Max number of entries */
389 int wrapuptime; /*!< Wrapup Time */
391 int retry; /*!< Retry calling everyone after this amount of time */
392 int timeout; /*!< How long to wait for an answer */
393 int weight; /*!< Respective weight */
394 int autopause; /*!< Auto pause queue members if they fail to answer */
396 /* Queue strategy things */
397 int rrpos; /*!< Round Robin - position */
398 int memberdelay; /*!< Seconds to delay connecting member to caller */
399 int autofill; /*!< Ignore the head call status and ring an available agent */
401 struct ao2_container *members; /*!< Head of the list of members */
402 /*!
403 * \brief Number of members _logged in_
404 * \note There will be members in the members container that are not logged
405 * in, so this can not simply be replaced with ao2_container_count().
407 int membercount;
408 struct queue_ent *head; /*!< Head of the list of callers */
409 AST_LIST_ENTRY(call_queue) list; /*!< Next call queue */
412 static AST_LIST_HEAD_STATIC(queues, call_queue);
414 static int set_member_paused(const char *queuename, const char *interface, int paused);
416 static void rr_dep_warning(void)
418 static unsigned int warned = 0;
420 if (!warned) {
421 ast_log(LOG_NOTICE, "The 'roundrobin' queue strategy is deprecated. Please use the 'rrmemory' strategy instead.\n");
422 warned = 1;
426 static void monjoin_dep_warning(void)
428 static unsigned int warned = 0;
429 if (!warned) {
430 ast_log(LOG_NOTICE, "The 'monitor-join' queue option is deprecated. Please use monitor-type=mixmonitor instead.\n");
431 warned = 1;
435 static void set_queue_result(struct ast_channel *chan, enum queue_result res)
437 int i;
439 for (i = 0; i < sizeof(queue_results) / sizeof(queue_results[0]); i++) {
440 if (queue_results[i].id == res) {
441 pbx_builtin_setvar_helper(chan, "QUEUESTATUS", queue_results[i].text);
442 return;
447 static char *int2strat(int strategy)
449 int x;
451 for (x = 0; x < sizeof(strategies) / sizeof(strategies[0]); x++) {
452 if (strategy == strategies[x].strategy)
453 return strategies[x].name;
456 return "<unknown>";
459 static int strat2int(const char *strategy)
461 int x;
463 for (x = 0; x < sizeof(strategies) / sizeof(strategies[0]); x++) {
464 if (!strcasecmp(strategy, strategies[x].name))
465 return strategies[x].strategy;
468 return -1;
471 /*! \brief Insert the 'new' entry after the 'prev' entry of queue 'q' */
472 static inline void insert_entry(struct call_queue *q, struct queue_ent *prev, struct queue_ent *new, int *pos)
474 struct queue_ent *cur;
476 if (!q || !new)
477 return;
478 if (prev) {
479 cur = prev->next;
480 prev->next = new;
481 } else {
482 cur = q->head;
483 q->head = new;
485 new->next = cur;
486 new->parent = q;
487 new->pos = ++(*pos);
488 new->opos = *pos;
491 enum queue_member_status {
492 QUEUE_NO_MEMBERS,
493 QUEUE_NO_REACHABLE_MEMBERS,
494 QUEUE_NORMAL
497 static enum queue_member_status get_member_status(struct call_queue *q, int max_penalty)
499 struct member *member;
500 struct ao2_iterator mem_iter;
501 enum queue_member_status result = QUEUE_NO_MEMBERS;
503 ast_mutex_lock(&q->lock);
504 mem_iter = ao2_iterator_init(q->members, 0);
505 while ((member = ao2_iterator_next(&mem_iter))) {
506 if (max_penalty && (member->penalty > max_penalty)) {
507 ao2_ref(member, -1);
508 continue;
511 if (member->paused) {
512 ao2_ref(member, -1);
513 continue;
516 switch (member->status) {
517 case AST_DEVICE_INVALID:
518 /* nothing to do */
519 ao2_ref(member, -1);
520 break;
521 case AST_DEVICE_UNAVAILABLE:
522 result = QUEUE_NO_REACHABLE_MEMBERS;
523 ao2_ref(member, -1);
524 break;
525 default:
526 ast_mutex_unlock(&q->lock);
527 ao2_ref(member, -1);
528 return QUEUE_NORMAL;
532 ast_mutex_unlock(&q->lock);
533 return result;
536 struct statechange {
537 AST_LIST_ENTRY(statechange) entry;
538 int state;
539 char dev[0];
542 static void *handle_statechange(struct statechange *sc)
544 struct call_queue *q;
545 struct member *cur;
546 struct ao2_iterator mem_iter;
547 struct member_interface *curint;
548 char *loc;
549 char *technology;
551 technology = ast_strdupa(sc->dev);
552 loc = strchr(technology, '/');
553 if (loc) {
554 *loc++ = '\0';
555 } else {
556 return NULL;
559 AST_LIST_LOCK(&interfaces);
560 AST_LIST_TRAVERSE(&interfaces, curint, list) {
561 char *interface;
562 char *slash_pos;
563 interface = ast_strdupa(curint->interface);
564 if ((slash_pos = strchr(interface, '/')))
565 if ((slash_pos = strchr(slash_pos + 1, '/')))
566 *slash_pos = '\0';
568 if (!strcasecmp(interface, sc->dev))
569 break;
571 AST_LIST_UNLOCK(&interfaces);
573 if (!curint) {
574 if (option_debug > 2)
575 ast_log(LOG_DEBUG, "Device '%s/%s' changed to state '%d' (%s) but we don't care because they're not a member of any queue.\n", technology, loc, sc->state, devstate2str(sc->state));
576 return NULL;
579 if (option_debug)
580 ast_log(LOG_DEBUG, "Device '%s/%s' changed to state '%d' (%s)\n", technology, loc, sc->state, devstate2str(sc->state));
581 AST_LIST_LOCK(&queues);
582 AST_LIST_TRAVERSE(&queues, q, list) {
583 ast_mutex_lock(&q->lock);
584 mem_iter = ao2_iterator_init(q->members, 0);
585 while ((cur = ao2_iterator_next(&mem_iter))) {
586 char *interface;
587 char *slash_pos;
588 interface = ast_strdupa(cur->interface);
589 if ((slash_pos = strchr(interface, '/')))
590 if ((slash_pos = strchr(slash_pos + 1, '/')))
591 *slash_pos = '\0';
593 if (strcasecmp(sc->dev, interface)) {
594 ao2_ref(cur, -1);
595 continue;
598 if (cur->status != sc->state) {
599 cur->status = sc->state;
600 if (q->maskmemberstatus) {
601 ao2_ref(cur, -1);
602 continue;
605 manager_event(EVENT_FLAG_AGENT, "QueueMemberStatus",
606 "Queue: %s\r\n"
607 "Location: %s\r\n"
608 "MemberName: %s\r\n"
609 "Membership: %s\r\n"
610 "Penalty: %d\r\n"
611 "CallsTaken: %d\r\n"
612 "LastCall: %d\r\n"
613 "Status: %d\r\n"
614 "Paused: %d\r\n",
615 q->name, cur->interface, cur->membername, cur->dynamic ? "dynamic" : cur->realtime ? "realtime" : "static",
616 cur->penalty, cur->calls, (int)cur->lastcall, cur->status, cur->paused);
618 ao2_ref(cur, -1);
620 ast_mutex_unlock(&q->lock);
622 AST_LIST_UNLOCK(&queues);
624 return NULL;
628 * \brief Data used by the device state thread
630 static struct {
631 /*! Set to 1 to stop the thread */
632 unsigned int stop:1;
633 /*! The device state monitoring thread */
634 pthread_t thread;
635 /*! Lock for the state change queue */
636 ast_mutex_t lock;
637 /*! Condition for the state change queue */
638 ast_cond_t cond;
639 /*! Queue of state changes */
640 AST_LIST_HEAD_NOLOCK(, statechange) state_change_q;
641 } device_state = {
642 .thread = AST_PTHREADT_NULL,
645 static void *device_state_thread(void *data)
647 struct statechange *sc = NULL;
649 while (!device_state.stop) {
650 ast_mutex_lock(&device_state.lock);
651 if (!(sc = AST_LIST_REMOVE_HEAD(&device_state.state_change_q, entry))) {
652 ast_cond_wait(&device_state.cond, &device_state.lock);
653 sc = AST_LIST_REMOVE_HEAD(&device_state.state_change_q, entry);
655 ast_mutex_unlock(&device_state.lock);
657 /* Check to see if we were woken up to see the request to stop */
658 if (device_state.stop)
659 break;
661 if (!sc)
662 continue;
664 handle_statechange(sc);
666 free(sc);
667 sc = NULL;
670 if (sc)
671 free(sc);
673 while ((sc = AST_LIST_REMOVE_HEAD(&device_state.state_change_q, entry)))
674 free(sc);
676 return NULL;
679 static int statechange_queue(const char *dev, int state, void *ign)
681 struct statechange *sc;
683 if (!(sc = ast_calloc(1, sizeof(*sc) + strlen(dev) + 1)))
684 return 0;
686 sc->state = state;
687 strcpy(sc->dev, dev);
689 ast_mutex_lock(&device_state.lock);
690 AST_LIST_INSERT_TAIL(&device_state.state_change_q, sc, entry);
691 ast_cond_signal(&device_state.cond);
692 ast_mutex_unlock(&device_state.lock);
694 return 0;
697 static struct member *create_queue_member(const char *interface, const char *membername, int penalty, int paused)
699 struct member *cur;
701 if ((cur = ao2_alloc(sizeof(*cur), NULL))) {
702 cur->penalty = penalty;
703 cur->paused = paused;
704 ast_copy_string(cur->interface, interface, sizeof(cur->interface));
705 if(!ast_strlen_zero(membername))
706 ast_copy_string(cur->membername, membername, sizeof(cur->membername));
707 else
708 ast_copy_string(cur->membername, interface, sizeof(cur->membername));
709 if (!strchr(cur->interface, '/'))
710 ast_log(LOG_WARNING, "No location at interface '%s'\n", interface);
711 cur->status = ast_device_state(interface);
714 return cur;
717 static struct call_queue *alloc_queue(const char *queuename)
719 struct call_queue *q;
721 if ((q = ast_calloc(1, sizeof(*q)))) {
722 ast_mutex_init(&q->lock);
723 ast_copy_string(q->name, queuename, sizeof(q->name));
725 return q;
728 static int compress_char(const char c)
730 if (c < 32)
731 return 0;
732 else if (c > 96)
733 return c - 64;
734 else
735 return c - 32;
738 static int member_hash_fn(const void *obj, const int flags)
740 const struct member *mem = obj;
741 const char *chname = strchr(mem->interface, '/');
742 int ret = 0, i;
743 if (!chname)
744 chname = mem->interface;
745 for (i = 0; i < 5 && chname[i]; i++)
746 ret += compress_char(chname[i]) << (i * 6);
747 return ret;
750 static int member_cmp_fn(void *obj1, void *obj2, int flags)
752 struct member *mem1 = obj1, *mem2 = obj2;
753 return strcmp(mem1->interface, mem2->interface) ? 0 : CMP_MATCH;
756 static void init_queue(struct call_queue *q)
758 int i;
760 q->dead = 0;
761 q->retry = DEFAULT_RETRY;
762 q->timeout = -1;
763 q->maxlen = 0;
764 q->announcefrequency = 0;
765 q->announceholdtime = 0;
766 q->roundingseconds = 0; /* Default - don't announce seconds */
767 q->servicelevel = 0;
768 q->ringinuse = 1;
769 q->setinterfacevar = 0;
770 q->autofill = autofill_default;
771 q->montype = montype_default;
772 q->moh[0] = '\0';
773 q->announce[0] = '\0';
774 q->context[0] = '\0';
775 q->monfmt[0] = '\0';
776 q->periodicannouncefrequency = 0;
777 if(!q->members)
778 q->members = ao2_container_alloc(37, member_hash_fn, member_cmp_fn);
779 q->membercount = 0;
780 q->found = 1;
781 ast_copy_string(q->sound_next, "queue-youarenext", sizeof(q->sound_next));
782 ast_copy_string(q->sound_thereare, "queue-thereare", sizeof(q->sound_thereare));
783 ast_copy_string(q->sound_calls, "queue-callswaiting", sizeof(q->sound_calls));
784 ast_copy_string(q->sound_holdtime, "queue-holdtime", sizeof(q->sound_holdtime));
785 ast_copy_string(q->sound_minutes, "queue-minutes", sizeof(q->sound_minutes));
786 ast_copy_string(q->sound_seconds, "queue-seconds", sizeof(q->sound_seconds));
787 ast_copy_string(q->sound_thanks, "queue-thankyou", sizeof(q->sound_thanks));
788 ast_copy_string(q->sound_lessthan, "queue-less-than", sizeof(q->sound_lessthan));
789 ast_copy_string(q->sound_reporthold, "queue-reporthold", sizeof(q->sound_reporthold));
790 ast_copy_string(q->sound_periodicannounce[0], "queue-periodic-announce", sizeof(q->sound_periodicannounce[0]));
791 for (i = 1; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) {
792 q->sound_periodicannounce[i][0]='\0';
796 static void clear_queue(struct call_queue *q)
798 q->holdtime = 0;
799 q->callscompleted = 0;
800 q->callsabandoned = 0;
801 q->callscompletedinsl = 0;
802 q->wrapuptime = 0;
805 static int add_to_interfaces(const char *interface)
807 struct member_interface *curint;
809 AST_LIST_LOCK(&interfaces);
810 AST_LIST_TRAVERSE(&interfaces, curint, list) {
811 if (!strcasecmp(curint->interface, interface))
812 break;
815 if (curint) {
816 AST_LIST_UNLOCK(&interfaces);
817 return 0;
820 if (option_debug)
821 ast_log(LOG_DEBUG, "Adding %s to the list of interfaces that make up all of our queue members.\n", interface);
823 if ((curint = ast_calloc(1, sizeof(*curint)))) {
824 ast_copy_string(curint->interface, interface, sizeof(curint->interface));
825 AST_LIST_INSERT_HEAD(&interfaces, curint, list);
827 AST_LIST_UNLOCK(&interfaces);
829 return 0;
832 static int interface_exists_global(const char *interface)
834 struct call_queue *q;
835 struct member *mem, tmpmem;
836 int ret = 0;
838 ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
840 AST_LIST_LOCK(&queues);
841 AST_LIST_TRAVERSE(&queues, q, list) {
842 ast_mutex_lock(&q->lock);
843 if ((mem = ao2_find(q->members, &tmpmem, OBJ_POINTER))) {
844 ao2_ref(mem, -1);
845 ret = 1;
847 ast_mutex_unlock(&q->lock);
848 if (ret)
849 break;
851 AST_LIST_UNLOCK(&queues);
853 return ret;
856 static int remove_from_interfaces(const char *interface)
858 struct member_interface *curint;
860 AST_LIST_LOCK(&interfaces);
861 AST_LIST_TRAVERSE_SAFE_BEGIN(&interfaces, curint, list) {
862 if (!strcasecmp(curint->interface, interface)) {
863 if (!interface_exists_global(interface)) {
864 if (option_debug)
865 ast_log(LOG_DEBUG, "Removing %s from the list of interfaces that make up all of our queue members.\n", interface);
866 AST_LIST_REMOVE_CURRENT(&interfaces, list);
867 free(curint);
869 break;
872 AST_LIST_TRAVERSE_SAFE_END;
873 AST_LIST_UNLOCK(&interfaces);
875 return 0;
878 static void clear_and_free_interfaces(void)
880 struct member_interface *curint;
882 AST_LIST_LOCK(&interfaces);
883 while ((curint = AST_LIST_REMOVE_HEAD(&interfaces, list)))
884 free(curint);
885 AST_LIST_UNLOCK(&interfaces);
888 /*! \brief Configure a queue parameter.
889 \par
890 For error reporting, line number is passed for .conf static configuration.
891 For Realtime queues, linenum is -1.
892 The failunknown flag is set for config files (and static realtime) to show
893 errors for unknown parameters. It is cleared for dynamic realtime to allow
894 extra fields in the tables. */
895 static void queue_set_param(struct call_queue *q, const char *param, const char *val, int linenum, int failunknown)
897 if (!strcasecmp(param, "musicclass") ||
898 !strcasecmp(param, "music") || !strcasecmp(param, "musiconhold")) {
899 ast_copy_string(q->moh, val, sizeof(q->moh));
900 } else if (!strcasecmp(param, "announce")) {
901 ast_copy_string(q->announce, val, sizeof(q->announce));
902 } else if (!strcasecmp(param, "context")) {
903 ast_copy_string(q->context, val, sizeof(q->context));
904 } else if (!strcasecmp(param, "timeout")) {
905 q->timeout = atoi(val);
906 if (q->timeout < 0)
907 q->timeout = DEFAULT_TIMEOUT;
908 } else if (!strcasecmp(param, "ringinuse")) {
909 q->ringinuse = ast_true(val);
910 } else if (!strcasecmp(param, "setinterfacevar")) {
911 q->setinterfacevar = ast_true(val);
912 } else if (!strcasecmp(param, "monitor-join")) {
913 monjoin_dep_warning();
914 q->monjoin = ast_true(val);
915 } else if (!strcasecmp(param, "monitor-format")) {
916 ast_copy_string(q->monfmt, val, sizeof(q->monfmt));
917 } else if (!strcasecmp(param, "queue-youarenext")) {
918 ast_copy_string(q->sound_next, val, sizeof(q->sound_next));
919 } else if (!strcasecmp(param, "queue-thereare")) {
920 ast_copy_string(q->sound_thereare, val, sizeof(q->sound_thereare));
921 } else if (!strcasecmp(param, "queue-callswaiting")) {
922 ast_copy_string(q->sound_calls, val, sizeof(q->sound_calls));
923 } else if (!strcasecmp(param, "queue-holdtime")) {
924 ast_copy_string(q->sound_holdtime, val, sizeof(q->sound_holdtime));
925 } else if (!strcasecmp(param, "queue-minutes")) {
926 ast_copy_string(q->sound_minutes, val, sizeof(q->sound_minutes));
927 } else if (!strcasecmp(param, "queue-seconds")) {
928 ast_copy_string(q->sound_seconds, val, sizeof(q->sound_seconds));
929 } else if (!strcasecmp(param, "queue-lessthan")) {
930 ast_copy_string(q->sound_lessthan, val, sizeof(q->sound_lessthan));
931 } else if (!strcasecmp(param, "queue-thankyou")) {
932 ast_copy_string(q->sound_thanks, val, sizeof(q->sound_thanks));
933 } else if (!strcasecmp(param, "queue-reporthold")) {
934 ast_copy_string(q->sound_reporthold, val, sizeof(q->sound_reporthold));
935 } else if (!strcasecmp(param, "announce-frequency")) {
936 q->announcefrequency = atoi(val);
937 } else if (!strcasecmp(param, "announce-round-seconds")) {
938 q->roundingseconds = atoi(val);
939 if (q->roundingseconds>60 || q->roundingseconds<0) {
940 if (linenum >= 0) {
941 ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
942 "using 0 instead for queue '%s' at line %d of queues.conf\n",
943 val, param, q->name, linenum);
944 } else {
945 ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
946 "using 0 instead for queue '%s'\n", val, param, q->name);
948 q->roundingseconds=0;
950 } else if (!strcasecmp(param, "announce-holdtime")) {
951 if (!strcasecmp(val, "once"))
952 q->announceholdtime = ANNOUNCEHOLDTIME_ONCE;
953 else if (ast_true(val))
954 q->announceholdtime = ANNOUNCEHOLDTIME_ALWAYS;
955 else
956 q->announceholdtime = 0;
957 } else if (!strcasecmp(param, "periodic-announce")) {
958 if (strchr(val, '|')) {
959 char *s, *buf = ast_strdupa(val);
960 unsigned int i = 0;
962 while ((s = strsep(&buf, "|"))) {
963 ast_copy_string(q->sound_periodicannounce[i], s, sizeof(q->sound_periodicannounce[i]));
964 i++;
965 if (i == MAX_PERIODIC_ANNOUNCEMENTS)
966 break;
968 } else {
969 ast_copy_string(q->sound_periodicannounce[0], val, sizeof(q->sound_periodicannounce[0]));
971 } else if (!strcasecmp(param, "periodic-announce-frequency")) {
972 q->periodicannouncefrequency = atoi(val);
973 } else if (!strcasecmp(param, "retry")) {
974 q->retry = atoi(val);
975 if (q->retry <= 0)
976 q->retry = DEFAULT_RETRY;
977 } else if (!strcasecmp(param, "wrapuptime")) {
978 q->wrapuptime = atoi(val);
979 } else if (!strcasecmp(param, "autofill")) {
980 q->autofill = ast_true(val);
981 } else if (!strcasecmp(param, "monitor-type")) {
982 if (!strcasecmp(val, "mixmonitor"))
983 q->montype = 1;
984 } else if (!strcasecmp(param, "autopause")) {
985 q->autopause = ast_true(val);
986 } else if (!strcasecmp(param, "maxlen")) {
987 q->maxlen = atoi(val);
988 if (q->maxlen < 0)
989 q->maxlen = 0;
990 } else if (!strcasecmp(param, "servicelevel")) {
991 q->servicelevel= atoi(val);
992 } else if (!strcasecmp(param, "strategy")) {
993 q->strategy = strat2int(val);
994 if (q->strategy < 0) {
995 ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
996 val, q->name);
997 q->strategy = QUEUE_STRATEGY_RINGALL;
999 } else if (!strcasecmp(param, "joinempty")) {
1000 if (!strcasecmp(val, "strict"))
1001 q->joinempty = QUEUE_EMPTY_STRICT;
1002 else if (ast_true(val))
1003 q->joinempty = QUEUE_EMPTY_NORMAL;
1004 else
1005 q->joinempty = 0;
1006 } else if (!strcasecmp(param, "leavewhenempty")) {
1007 if (!strcasecmp(val, "strict"))
1008 q->leavewhenempty = QUEUE_EMPTY_STRICT;
1009 else if (ast_true(val))
1010 q->leavewhenempty = QUEUE_EMPTY_NORMAL;
1011 else
1012 q->leavewhenempty = 0;
1013 } else if (!strcasecmp(param, "eventmemberstatus")) {
1014 q->maskmemberstatus = !ast_true(val);
1015 } else if (!strcasecmp(param, "eventwhencalled")) {
1016 if (!strcasecmp(val, "vars")) {
1017 q->eventwhencalled = QUEUE_EVENT_VARIABLES;
1018 } else {
1019 q->eventwhencalled = ast_true(val);
1021 } else if (!strcasecmp(param, "reportholdtime")) {
1022 q->reportholdtime = ast_true(val);
1023 } else if (!strcasecmp(param, "memberdelay")) {
1024 q->memberdelay = atoi(val);
1025 } else if (!strcasecmp(param, "weight")) {
1026 q->weight = atoi(val);
1027 if (q->weight)
1028 use_weight++;
1029 /* With Realtime queues, if the last queue using weights is deleted in realtime,
1030 we will not see any effect on use_weight until next reload. */
1031 } else if (!strcasecmp(param, "timeoutrestart")) {
1032 q->timeoutrestart = ast_true(val);
1033 } else if (failunknown) {
1034 if (linenum >= 0) {
1035 ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s at line %d of queues.conf\n",
1036 q->name, param, linenum);
1037 } else {
1038 ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s\n", q->name, param);
1043 static void rt_handle_member_record(struct call_queue *q, char *interface, const char *membername, const char *penalty_str, const char *paused_str)
1045 struct member *m, tmpmem;
1046 int penalty = 0;
1047 int paused = 0;
1049 if (penalty_str) {
1050 penalty = atoi(penalty_str);
1051 if (penalty < 0)
1052 penalty = 0;
1055 if (paused_str) {
1056 paused = atoi(paused_str);
1057 if (paused < 0)
1058 paused = 0;
1061 /* Find the member, or the place to put a new one. */
1062 ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
1063 m = ao2_find(q->members, &tmpmem, OBJ_POINTER);
1065 /* Create a new one if not found, else update penalty */
1066 if (!m) {
1067 if ((m = create_queue_member(interface, membername, penalty, paused))) {
1068 m->dead = 0;
1069 m->realtime = 1;
1070 add_to_interfaces(interface);
1071 ao2_link(q->members, m);
1072 q->membercount++;
1074 } else {
1075 m->dead = 0; /* Do not delete this one. */
1076 if (paused_str)
1077 m->paused = paused;
1078 m->penalty = penalty;
1079 ao2_ref(m, -1);
1083 static void free_members(struct call_queue *q, int all)
1085 /* Free non-dynamic members */
1086 struct member *cur;
1087 struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
1089 while ((cur = ao2_iterator_next(&mem_iter))) {
1090 if (all || !cur->dynamic) {
1091 ao2_unlink(q->members, cur);
1092 remove_from_interfaces(cur->interface);
1093 q->membercount--;
1095 ao2_ref(cur, -1);
1099 static void destroy_queue(struct call_queue *q)
1101 free_members(q, 1);
1102 ast_mutex_destroy(&q->lock);
1103 ao2_ref(q->members, -1);
1104 free(q);
1107 /*!\brief Reload a single queue via realtime.
1108 \return Return the queue, or NULL if it doesn't exist.
1109 \note Should be called with the global qlock locked. */
1110 static struct call_queue *find_queue_by_name_rt(const char *queuename, struct ast_variable *queue_vars, struct ast_config *member_config)
1112 struct ast_variable *v;
1113 struct call_queue *q;
1114 struct member *m;
1115 struct ao2_iterator mem_iter;
1116 char *interface = NULL;
1117 char *tmp, *tmp_name;
1118 char tmpbuf[64]; /* Must be longer than the longest queue param name. */
1120 /* Find the queue in the in-core list (we will create a new one if not found). */
1121 AST_LIST_TRAVERSE(&queues, q, list) {
1122 if (!strcasecmp(q->name, queuename))
1123 break;
1126 /* Static queues override realtime. */
1127 if (q) {
1128 ast_mutex_lock(&q->lock);
1129 if (!q->realtime) {
1130 if (q->dead) {
1131 ast_mutex_unlock(&q->lock);
1132 return NULL;
1133 } else {
1134 ast_log(LOG_WARNING, "Static queue '%s' already exists. Not loading from realtime\n", q->name);
1135 ast_mutex_unlock(&q->lock);
1136 return q;
1139 } else if (!member_config)
1140 /* Not found in the list, and it's not realtime ... */
1141 return NULL;
1143 /* Check if queue is defined in realtime. */
1144 if (!queue_vars) {
1145 /* Delete queue from in-core list if it has been deleted in realtime. */
1146 if (q) {
1147 /*! \note Hmm, can't seem to distinguish a DB failure from a not
1148 found condition... So we might delete an in-core queue
1149 in case of DB failure. */
1150 ast_log(LOG_DEBUG, "Queue %s not found in realtime.\n", queuename);
1152 q->dead = 1;
1153 /* Delete if unused (else will be deleted when last caller leaves). */
1154 if (!q->count) {
1155 /* Delete. */
1156 AST_LIST_REMOVE(&queues, q, list);
1157 ast_mutex_unlock(&q->lock);
1158 destroy_queue(q);
1159 } else
1160 ast_mutex_unlock(&q->lock);
1162 return NULL;
1165 /* Create a new queue if an in-core entry does not exist yet. */
1166 if (!q) {
1167 if (!(q = alloc_queue(queuename)))
1168 return NULL;
1169 ast_mutex_lock(&q->lock);
1170 clear_queue(q);
1171 q->realtime = 1;
1172 init_queue(q); /* Ensure defaults for all parameters not set explicitly. */
1173 AST_LIST_INSERT_HEAD(&queues, q, list);
1176 memset(tmpbuf, 0, sizeof(tmpbuf));
1177 for (v = queue_vars; v; v = v->next) {
1178 /* Convert to dashes `-' from underscores `_' as the latter are more SQL friendly. */
1179 if ((tmp = strchr(v->name, '_'))) {
1180 ast_copy_string(tmpbuf, v->name, sizeof(tmpbuf));
1181 tmp_name = tmpbuf;
1182 tmp = tmp_name;
1183 while ((tmp = strchr(tmp, '_')))
1184 *tmp++ = '-';
1185 } else
1186 tmp_name = v->name;
1187 queue_set_param(q, tmp_name, v->value, -1, 0);
1190 if (q->strategy == QUEUE_STRATEGY_ROUNDROBIN)
1191 rr_dep_warning();
1193 /* Temporarily set realtime members dead so we can detect deleted ones.
1194 * Also set the membercount correctly for realtime*/
1195 mem_iter = ao2_iterator_init(q->members, 0);
1196 while ((m = ao2_iterator_next(&mem_iter))) {
1197 q->membercount++;
1198 if (m->realtime)
1199 m->dead = 1;
1200 ao2_ref(m, -1);
1203 while ((interface = ast_category_browse(member_config, interface))) {
1204 rt_handle_member_record(q, interface,
1205 ast_variable_retrieve(member_config, interface, "membername"),
1206 ast_variable_retrieve(member_config, interface, "penalty"),
1207 ast_variable_retrieve(member_config, interface, "paused"));
1210 /* Delete all realtime members that have been deleted in DB. */
1211 mem_iter = ao2_iterator_init(q->members, 0);
1212 while ((m = ao2_iterator_next(&mem_iter))) {
1213 if (m->dead) {
1214 ao2_unlink(q->members, m);
1215 ast_mutex_unlock(&q->lock);
1216 remove_from_interfaces(m->interface);
1217 ast_mutex_lock(&q->lock);
1218 q->membercount--;
1220 ao2_ref(m, -1);
1223 ast_mutex_unlock(&q->lock);
1225 return q;
1228 static int update_realtime_member_field(struct member *mem, const char *queue_name, const char *field, const char *value)
1230 struct ast_variable *var;
1231 int ret = -1;
1233 if(!(var = ast_load_realtime("queue_members", "interface", mem->interface, "queue_name", queue_name, NULL)))
1234 return ret;
1235 while (var) {
1236 if(!strcmp(var->name, "uniqueid"))
1237 break;
1238 var = var->next;
1240 if(var && !ast_strlen_zero(var->value)) {
1241 if ((ast_update_realtime("queue_members", "uniqueid", var->value, field, value, NULL)) > -1)
1242 ret = 0;
1244 return ret;
1247 static void update_realtime_members(struct call_queue *q)
1249 struct ast_config *member_config = NULL;
1250 struct member *m;
1251 char *interface = NULL;
1252 struct ao2_iterator mem_iter;
1254 member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", q->name , NULL);
1255 if (!member_config) {
1256 /*This queue doesn't have realtime members*/
1257 if (option_debug > 2)
1258 ast_log(LOG_DEBUG, "Queue %s has no realtime members defined. No need for update\n", q->name);
1259 return;
1262 ast_mutex_lock(&q->lock);
1264 /* Temporarily set realtime members dead so we can detect deleted ones.*/
1265 mem_iter = ao2_iterator_init(q->members, 0);
1266 while ((m = ao2_iterator_next(&mem_iter))) {
1267 if (m->realtime)
1268 m->dead = 1;
1269 ao2_ref(m, -1);
1272 while ((interface = ast_category_browse(member_config, interface))) {
1273 rt_handle_member_record(q, interface,
1274 S_OR(ast_variable_retrieve(member_config, interface, "membername"), interface),
1275 ast_variable_retrieve(member_config, interface, "penalty"),
1276 ast_variable_retrieve(member_config, interface, "paused"));
1279 /* Delete all realtime members that have been deleted in DB. */
1280 mem_iter = ao2_iterator_init(q->members, 0);
1281 while ((m = ao2_iterator_next(&mem_iter))) {
1282 if (m->dead) {
1283 ao2_unlink(q->members, m);
1284 ast_mutex_unlock(&q->lock);
1285 remove_from_interfaces(m->interface);
1286 ast_mutex_lock(&q->lock);
1287 q->membercount--;
1289 ao2_ref(m, -1);
1291 ast_mutex_unlock(&q->lock);
1294 static struct call_queue *load_realtime_queue(const char *queuename)
1296 struct ast_variable *queue_vars;
1297 struct ast_config *member_config = NULL;
1298 struct call_queue *q;
1300 /* Find the queue in the in-core list first. */
1301 AST_LIST_LOCK(&queues);
1302 AST_LIST_TRAVERSE(&queues, q, list) {
1303 if (!strcasecmp(q->name, queuename)) {
1304 break;
1307 AST_LIST_UNLOCK(&queues);
1309 if (!q || q->realtime) {
1310 /*! \note Load from realtime before taking the global qlock, to avoid blocking all
1311 queue operations while waiting for the DB.
1313 This will be two separate database transactions, so we might
1314 see queue parameters as they were before another process
1315 changed the queue and member list as it was after the change.
1316 Thus we might see an empty member list when a queue is
1317 deleted. In practise, this is unlikely to cause a problem. */
1319 queue_vars = ast_load_realtime("queues", "name", queuename, NULL);
1320 if (queue_vars) {
1321 member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", queuename, NULL);
1322 if (!member_config) {
1323 ast_log(LOG_ERROR, "no queue_members defined in your config (extconfig.conf).\n");
1324 return NULL;
1328 AST_LIST_LOCK(&queues);
1330 q = find_queue_by_name_rt(queuename, queue_vars, member_config);
1331 if (member_config)
1332 ast_config_destroy(member_config);
1333 if (queue_vars)
1334 ast_variables_destroy(queue_vars);
1336 AST_LIST_UNLOCK(&queues);
1337 } else {
1338 update_realtime_members(q);
1340 return q;
1343 static int join_queue(char *queuename, struct queue_ent *qe, enum queue_result *reason)
1345 struct call_queue *q;
1346 struct queue_ent *cur, *prev = NULL;
1347 int res = -1;
1348 int pos = 0;
1349 int inserted = 0;
1350 enum queue_member_status stat;
1352 if (!(q = load_realtime_queue(queuename)))
1353 return res;
1355 AST_LIST_LOCK(&queues);
1356 ast_mutex_lock(&q->lock);
1358 /* This is our one */
1359 stat = get_member_status(q, qe->max_penalty);
1360 if (!q->joinempty && (stat == QUEUE_NO_MEMBERS))
1361 *reason = QUEUE_JOINEMPTY;
1362 else if ((q->joinempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS))
1363 *reason = QUEUE_JOINUNAVAIL;
1364 else if (q->maxlen && (q->count >= q->maxlen))
1365 *reason = QUEUE_FULL;
1366 else {
1367 /* There's space for us, put us at the right position inside
1368 * the queue.
1369 * Take into account the priority of the calling user */
1370 inserted = 0;
1371 prev = NULL;
1372 cur = q->head;
1373 while (cur) {
1374 /* We have higher priority than the current user, enter
1375 * before him, after all the other users with priority
1376 * higher or equal to our priority. */
1377 if ((!inserted) && (qe->prio > cur->prio)) {
1378 insert_entry(q, prev, qe, &pos);
1379 inserted = 1;
1381 cur->pos = ++pos;
1382 prev = cur;
1383 cur = cur->next;
1385 /* No luck, join at the end of the queue */
1386 if (!inserted)
1387 insert_entry(q, prev, qe, &pos);
1388 ast_copy_string(qe->moh, q->moh, sizeof(qe->moh));
1389 ast_copy_string(qe->announce, q->announce, sizeof(qe->announce));
1390 ast_copy_string(qe->context, q->context, sizeof(qe->context));
1391 q->count++;
1392 res = 0;
1393 manager_event(EVENT_FLAG_CALL, "Join",
1394 "Channel: %s\r\nCallerID: %s\r\nCallerIDName: %s\r\nQueue: %s\r\nPosition: %d\r\nCount: %d\r\nUniqueid: %s\r\n",
1395 qe->chan->name,
1396 S_OR(qe->chan->cid.cid_num, "unknown"), /* XXX somewhere else it is <unknown> */
1397 S_OR(qe->chan->cid.cid_name, "unknown"),
1398 q->name, qe->pos, q->count, qe->chan->uniqueid );
1399 if (option_debug)
1400 ast_log(LOG_DEBUG, "Queue '%s' Join, Channel '%s', Position '%d'\n", q->name, qe->chan->name, qe->pos );
1402 ast_mutex_unlock(&q->lock);
1403 AST_LIST_UNLOCK(&queues);
1405 return res;
1408 static int play_file(struct ast_channel *chan, char *filename)
1410 int res;
1412 ast_stopstream(chan);
1414 res = ast_streamfile(chan, filename, chan->language);
1415 if (!res)
1416 res = ast_waitstream(chan, AST_DIGIT_ANY);
1418 ast_stopstream(chan);
1420 return res;
1423 static int valid_exit(struct queue_ent *qe, char digit)
1425 int digitlen = strlen(qe->digits);
1427 /* Prevent possible buffer overflow */
1428 if (digitlen < sizeof(qe->digits) - 2) {
1429 qe->digits[digitlen] = digit;
1430 qe->digits[digitlen + 1] = '\0';
1431 } else {
1432 qe->digits[0] = '\0';
1433 return 0;
1436 /* If there's no context to goto, short-circuit */
1437 if (ast_strlen_zero(qe->context))
1438 return 0;
1440 /* If the extension is bad, then reset the digits to blank */
1441 if (!ast_canmatch_extension(qe->chan, qe->context, qe->digits, 1, qe->chan->cid.cid_num)) {
1442 qe->digits[0] = '\0';
1443 return 0;
1446 /* We have an exact match */
1447 if (!ast_goto_if_exists(qe->chan, qe->context, qe->digits, 1)) {
1448 qe->valid_digits = 1;
1449 /* Return 1 on a successful goto */
1450 return 1;
1453 return 0;
1456 static int say_position(struct queue_ent *qe)
1458 int res = 0, avgholdmins, avgholdsecs;
1459 time_t now;
1461 /* Check to see if this is ludicrous -- if we just announced position, don't do it again*/
1462 time(&now);
1463 if ((now - qe->last_pos) < 15)
1464 return 0;
1466 /* If either our position has changed, or we are over the freq timer, say position */
1467 if ((qe->last_pos_said == qe->pos) && ((now - qe->last_pos) < qe->parent->announcefrequency))
1468 return 0;
1470 ast_moh_stop(qe->chan);
1471 /* Say we're next, if we are */
1472 if (qe->pos == 1) {
1473 res = play_file(qe->chan, qe->parent->sound_next);
1474 if (res)
1475 goto playout;
1476 else
1477 goto posout;
1478 } else {
1479 res = play_file(qe->chan, qe->parent->sound_thereare);
1480 if (res)
1481 goto playout;
1482 res = ast_say_number(qe->chan, qe->pos, AST_DIGIT_ANY, qe->chan->language, (char *) NULL); /* Needs gender */
1483 if (res)
1484 goto playout;
1485 res = play_file(qe->chan, qe->parent->sound_calls);
1486 if (res)
1487 goto playout;
1489 /* Round hold time to nearest minute */
1490 avgholdmins = abs(((qe->parent->holdtime + 30) - (now - qe->start)) / 60);
1492 /* If they have specified a rounding then round the seconds as well */
1493 if (qe->parent->roundingseconds) {
1494 avgholdsecs = (abs(((qe->parent->holdtime + 30) - (now - qe->start))) - 60 * avgholdmins) / qe->parent->roundingseconds;
1495 avgholdsecs *= qe->parent->roundingseconds;
1496 } else {
1497 avgholdsecs = 0;
1500 if (option_verbose > 2)
1501 ast_verbose(VERBOSE_PREFIX_3 "Hold time for %s is %d minutes %d seconds\n", qe->parent->name, avgholdmins, avgholdsecs);
1503 /* If the hold time is >1 min, if it's enabled, and if it's not
1504 supposed to be only once and we have already said it, say it */
1505 if ((avgholdmins+avgholdsecs) > 0 && (qe->parent->announceholdtime) &&
1506 (!(qe->parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE) && qe->last_pos)) {
1507 res = play_file(qe->chan, qe->parent->sound_holdtime);
1508 if (res)
1509 goto playout;
1511 if (avgholdmins > 0) {
1512 if (avgholdmins < 2) {
1513 res = play_file(qe->chan, qe->parent->sound_lessthan);
1514 if (res)
1515 goto playout;
1517 res = ast_say_number(qe->chan, 2, AST_DIGIT_ANY, qe->chan->language, NULL);
1518 if (res)
1519 goto playout;
1520 } else {
1521 res = ast_say_number(qe->chan, avgholdmins, AST_DIGIT_ANY, qe->chan->language, NULL);
1522 if (res)
1523 goto playout;
1526 res = play_file(qe->chan, qe->parent->sound_minutes);
1527 if (res)
1528 goto playout;
1530 if (avgholdsecs>0) {
1531 res = ast_say_number(qe->chan, avgholdsecs, AST_DIGIT_ANY, qe->chan->language, NULL);
1532 if (res)
1533 goto playout;
1535 res = play_file(qe->chan, qe->parent->sound_seconds);
1536 if (res)
1537 goto playout;
1542 posout:
1543 if (option_verbose > 2)
1544 ast_verbose(VERBOSE_PREFIX_3 "Told %s in %s their queue position (which was %d)\n",
1545 qe->chan->name, qe->parent->name, qe->pos);
1546 res = play_file(qe->chan, qe->parent->sound_thanks);
1548 playout:
1549 if ((res > 0 && !valid_exit(qe, res)) || res < 0)
1550 res = 0;
1552 /* Set our last_pos indicators */
1553 qe->last_pos = now;
1554 qe->last_pos_said = qe->pos;
1556 /* Don't restart music on hold if we're about to exit the caller from the queue */
1557 if (!res)
1558 ast_moh_start(qe->chan, qe->moh, NULL);
1560 return res;
1563 static void recalc_holdtime(struct queue_ent *qe, int newholdtime)
1565 int oldvalue;
1567 /* Calculate holdtime using a recursive boxcar filter */
1568 /* Thanks to SRT for this contribution */
1569 /* 2^2 (4) is the filter coefficient; a higher exponent would give old entries more weight */
1571 ast_mutex_lock(&qe->parent->lock);
1572 oldvalue = qe->parent->holdtime;
1573 qe->parent->holdtime = (((oldvalue << 2) - oldvalue) + newholdtime) >> 2;
1574 ast_mutex_unlock(&qe->parent->lock);
1578 static void leave_queue(struct queue_ent *qe)
1580 struct call_queue *q;
1581 struct queue_ent *cur, *prev = NULL;
1582 int pos = 0;
1584 if (!(q = qe->parent))
1585 return;
1586 ast_mutex_lock(&q->lock);
1588 prev = NULL;
1589 for (cur = q->head; cur; cur = cur->next) {
1590 if (cur == qe) {
1591 q->count--;
1593 /* Take us out of the queue */
1594 manager_event(EVENT_FLAG_CALL, "Leave",
1595 "Channel: %s\r\nQueue: %s\r\nCount: %d\r\nUniqueid: %s\r\n",
1596 qe->chan->name, q->name, q->count, qe->chan->uniqueid);
1597 if (option_debug)
1598 ast_log(LOG_DEBUG, "Queue '%s' Leave, Channel '%s'\n", q->name, qe->chan->name );
1599 /* Take us out of the queue */
1600 if (prev)
1601 prev->next = cur->next;
1602 else
1603 q->head = cur->next;
1604 } else {
1605 /* Renumber the people after us in the queue based on a new count */
1606 cur->pos = ++pos;
1607 prev = cur;
1610 ast_mutex_unlock(&q->lock);
1612 if (q->dead && !q->count) {
1613 /* It's dead and nobody is in it, so kill it */
1614 AST_LIST_LOCK(&queues);
1615 AST_LIST_REMOVE(&queues, q, list);
1616 AST_LIST_UNLOCK(&queues);
1617 destroy_queue(q);
1621 /* Hang up a list of outgoing calls */
1622 static void hangupcalls(struct callattempt *outgoing, struct ast_channel *exception)
1624 struct callattempt *oo;
1626 while (outgoing) {
1627 /* Hangup any existing lines we have open */
1628 if (outgoing->chan && (outgoing->chan != exception))
1629 ast_hangup(outgoing->chan);
1630 oo = outgoing;
1631 outgoing = outgoing->q_next;
1632 if (oo->member)
1633 ao2_ref(oo->member, -1);
1634 free(oo);
1638 static int update_status(struct call_queue *q, struct member *member, int status)
1640 struct member *cur;
1641 struct ao2_iterator mem_iter;
1643 /* Since a reload could have taken place, we have to traverse the list to
1644 be sure it's still valid */
1645 ast_mutex_lock(&q->lock);
1646 mem_iter = ao2_iterator_init(q->members, 0);
1647 while ((cur = ao2_iterator_next(&mem_iter))) {
1648 if (member != cur) {
1649 ao2_ref(cur, -1);
1650 continue;
1653 cur->status = status;
1654 if (!q->maskmemberstatus) {
1655 manager_event(EVENT_FLAG_AGENT, "QueueMemberStatus",
1656 "Queue: %s\r\n"
1657 "Location: %s\r\n"
1658 "MemberName: %s\r\n"
1659 "Membership: %s\r\n"
1660 "Penalty: %d\r\n"
1661 "CallsTaken: %d\r\n"
1662 "LastCall: %d\r\n"
1663 "Status: %d\r\n"
1664 "Paused: %d\r\n",
1665 q->name, cur->interface, cur->membername, cur->dynamic ? "dynamic" : cur->realtime ? "realtime": "static",
1666 cur->penalty, cur->calls, (int)cur->lastcall, cur->status, cur->paused);
1668 ao2_ref(cur, -1);
1670 ast_mutex_unlock(&q->lock);
1671 return 0;
1674 static int update_dial_status(struct call_queue *q, struct member *member, int status)
1676 if (status == AST_CAUSE_BUSY)
1677 status = AST_DEVICE_BUSY;
1678 else if (status == AST_CAUSE_UNREGISTERED)
1679 status = AST_DEVICE_UNAVAILABLE;
1680 else if (status == AST_CAUSE_NOSUCHDRIVER)
1681 status = AST_DEVICE_INVALID;
1682 else
1683 status = AST_DEVICE_UNKNOWN;
1684 return update_status(q, member, status);
1687 /* traverse all defined queues which have calls waiting and contain this member
1688 return 0 if no other queue has precedence (higher weight) or 1 if found */
1689 static int compare_weight(struct call_queue *rq, struct member *member)
1691 struct call_queue *q;
1692 struct member *mem;
1693 int found = 0;
1695 /* &qlock and &rq->lock already set by try_calling()
1696 * to solve deadlock */
1697 AST_LIST_TRAVERSE(&queues, q, list) {
1698 if (q == rq) /* don't check myself, could deadlock */
1699 continue;
1700 ast_mutex_lock(&q->lock);
1701 if (q->count && q->members) {
1702 if ((mem = ao2_find(q->members, member, OBJ_POINTER))) {
1703 ast_log(LOG_DEBUG, "Found matching member %s in queue '%s'\n", mem->interface, q->name);
1704 if (q->weight > rq->weight) {
1705 ast_log(LOG_DEBUG, "Queue '%s' (weight %d, calls %d) is preferred over '%s' (weight %d, calls %d)\n", q->name, q->weight, q->count, rq->name, rq->weight, rq->count);
1706 found = 1;
1708 ao2_ref(mem, -1);
1711 ast_mutex_unlock(&q->lock);
1712 if (found)
1713 break;
1715 return found;
1718 /*! \brief common hangup actions */
1719 static void do_hang(struct callattempt *o)
1721 o->stillgoing = 0;
1722 ast_hangup(o->chan);
1723 o->chan = NULL;
1726 static char *vars2manager(struct ast_channel *chan, char *vars, size_t len)
1728 char *tmp = alloca(len);
1730 if (pbx_builtin_serialize_variables(chan, tmp, len)) {
1731 int i, j;
1733 /* convert "\n" to "\nVariable: " */
1734 strcpy(vars, "Variable: ");
1736 for (i = 0, j = 10; (i < len - 1) && (j < len - 1); i++, j++) {
1737 vars[j] = tmp[i];
1739 if (tmp[i + 1] == '\0')
1740 break;
1741 if (tmp[i] == '\n') {
1742 vars[j] = '\r';
1743 vars[++j] = '\n';
1745 ast_copy_string(&(vars[j]), "Variable: ", len - j);
1746 j += 9;
1749 if (j > len - 1)
1750 j = len - 1;
1751 vars[j - 2] = '\r';
1752 vars[j - 1] = '\n';
1753 vars[j] = '\0';
1754 } else {
1755 /* there are no channel variables; leave it blank */
1756 *vars = '\0';
1758 return vars;
1761 static int ring_entry(struct queue_ent *qe, struct callattempt *tmp, int *busies)
1763 int res;
1764 int status;
1765 char tech[256];
1766 char *location;
1768 /* on entry here, we know that tmp->chan == NULL */
1769 if (qe->parent->wrapuptime && (time(NULL) - tmp->lastcall < qe->parent->wrapuptime)) {
1770 if (option_debug)
1771 ast_log(LOG_DEBUG, "Wrapuptime not yet expired for %s\n", tmp->interface);
1772 if (qe->chan->cdr)
1773 ast_cdr_busy(qe->chan->cdr);
1774 tmp->stillgoing = 0;
1775 (*busies)++;
1776 return 0;
1779 if (!qe->parent->ringinuse && (tmp->member->status != AST_DEVICE_NOT_INUSE) && (tmp->member->status != AST_DEVICE_UNKNOWN)) {
1780 if (option_debug)
1781 ast_log(LOG_DEBUG, "%s in use, can't receive call\n", tmp->interface);
1782 if (qe->chan->cdr)
1783 ast_cdr_busy(qe->chan->cdr);
1784 tmp->stillgoing = 0;
1785 return 0;
1788 if (tmp->member->paused) {
1789 if (option_debug)
1790 ast_log(LOG_DEBUG, "%s paused, can't receive call\n", tmp->interface);
1791 if (qe->chan->cdr)
1792 ast_cdr_busy(qe->chan->cdr);
1793 tmp->stillgoing = 0;
1794 return 0;
1796 if (use_weight && compare_weight(qe->parent,tmp->member)) {
1797 ast_log(LOG_DEBUG, "Priority queue delaying call to %s:%s\n", qe->parent->name, tmp->interface);
1798 if (qe->chan->cdr)
1799 ast_cdr_busy(qe->chan->cdr);
1800 tmp->stillgoing = 0;
1801 (*busies)++;
1802 return 0;
1805 ast_copy_string(tech, tmp->interface, sizeof(tech));
1806 if ((location = strchr(tech, '/')))
1807 *location++ = '\0';
1808 else
1809 location = "";
1811 /* Request the peer */
1812 tmp->chan = ast_request(tech, qe->chan->nativeformats, location, &status);
1813 if (!tmp->chan) { /* If we can't, just go on to the next call */
1814 if (qe->chan->cdr)
1815 ast_cdr_busy(qe->chan->cdr);
1816 tmp->stillgoing = 0;
1817 update_dial_status(qe->parent, tmp->member, status);
1819 ast_mutex_lock(&qe->parent->lock);
1820 qe->parent->rrpos++;
1821 ast_mutex_unlock(&qe->parent->lock);
1823 (*busies)++;
1824 return 0;
1825 } else if (status != tmp->oldstatus)
1826 update_dial_status(qe->parent, tmp->member, status);
1828 tmp->chan->appl = "AppQueue";
1829 tmp->chan->data = "(Outgoing Line)";
1830 tmp->chan->whentohangup = 0;
1831 if (tmp->chan->cid.cid_num)
1832 free(tmp->chan->cid.cid_num);
1833 tmp->chan->cid.cid_num = ast_strdup(qe->chan->cid.cid_num);
1834 if (tmp->chan->cid.cid_name)
1835 free(tmp->chan->cid.cid_name);
1836 tmp->chan->cid.cid_name = ast_strdup(qe->chan->cid.cid_name);
1837 if (tmp->chan->cid.cid_ani)
1838 free(tmp->chan->cid.cid_ani);
1839 tmp->chan->cid.cid_ani = ast_strdup(qe->chan->cid.cid_ani);
1841 /* Inherit specially named variables from parent channel */
1842 ast_channel_inherit_variables(qe->chan, tmp->chan);
1844 /* Presense of ADSI CPE on outgoing channel follows ours */
1845 tmp->chan->adsicpe = qe->chan->adsicpe;
1847 /* Place the call, but don't wait on the answer */
1848 if ((res = ast_call(tmp->chan, location, 0))) {
1849 /* Again, keep going even if there's an error */
1850 if (option_debug)
1851 ast_log(LOG_DEBUG, "ast call on peer returned %d\n", res);
1852 if (option_verbose > 2)
1853 ast_verbose(VERBOSE_PREFIX_3 "Couldn't call %s\n", tmp->interface);
1854 do_hang(tmp);
1855 (*busies)++;
1856 return 0;
1857 } else if (qe->parent->eventwhencalled) {
1858 char vars[2048];
1860 manager_event(EVENT_FLAG_AGENT, "AgentCalled",
1861 "AgentCalled: %s\r\n"
1862 "AgentName: %s\r\n"
1863 "ChannelCalling: %s\r\n"
1864 "CallerID: %s\r\n"
1865 "CallerIDName: %s\r\n"
1866 "Context: %s\r\n"
1867 "Extension: %s\r\n"
1868 "Priority: %d\r\n"
1869 "%s",
1870 tmp->interface, tmp->member->membername, qe->chan->name,
1871 tmp->chan->cid.cid_num ? tmp->chan->cid.cid_num : "unknown",
1872 tmp->chan->cid.cid_name ? tmp->chan->cid.cid_name : "unknown",
1873 qe->chan->context, qe->chan->exten, qe->chan->priority,
1874 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
1875 if (option_verbose > 2)
1876 ast_verbose(VERBOSE_PREFIX_3 "Called %s\n", tmp->interface);
1879 return 1;
1882 /*! \brief find the entry with the best metric, or NULL */
1883 static struct callattempt *find_best(struct callattempt *outgoing)
1885 struct callattempt *best = NULL, *cur;
1887 for (cur = outgoing; cur; cur = cur->q_next) {
1888 if (cur->stillgoing && /* Not already done */
1889 !cur->chan && /* Isn't already going */
1890 (!best || cur->metric < best->metric)) { /* We haven't found one yet, or it's better */
1891 best = cur;
1895 return best;
1898 static int ring_one(struct queue_ent *qe, struct callattempt *outgoing, int *busies)
1900 int ret = 0;
1902 while (ret == 0) {
1903 struct callattempt *best = find_best(outgoing);
1904 if (!best) {
1905 if (option_debug)
1906 ast_log(LOG_DEBUG, "Nobody left to try ringing in queue\n");
1907 break;
1909 if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
1910 struct callattempt *cur;
1911 /* Ring everyone who shares this best metric (for ringall) */
1912 for (cur = outgoing; cur; cur = cur->q_next) {
1913 if (cur->stillgoing && !cur->chan && cur->metric <= best->metric) {
1914 if (option_debug)
1915 ast_log(LOG_DEBUG, "(Parallel) Trying '%s' with metric %d\n", cur->interface, cur->metric);
1916 ring_entry(qe, cur, busies);
1919 } else {
1920 /* Ring just the best channel */
1921 if (option_debug)
1922 ast_log(LOG_DEBUG, "Trying '%s' with metric %d\n", best->interface, best->metric);
1923 ring_entry(qe, best, busies);
1925 if (best->chan) /* break out with result = 1 */
1926 ret = 1;
1929 return ret;
1932 static int store_next(struct queue_ent *qe, struct callattempt *outgoing)
1934 struct callattempt *best = find_best(outgoing);
1936 if (best) {
1937 /* Ring just the best channel */
1938 if (option_debug)
1939 ast_log(LOG_DEBUG, "Next is '%s' with metric %d\n", best->interface, best->metric);
1940 qe->parent->rrpos = best->metric % 1000;
1941 } else {
1942 /* Just increment rrpos */
1943 if (qe->parent->wrapped) {
1944 /* No more channels, start over */
1945 qe->parent->rrpos = 0;
1946 } else {
1947 /* Prioritize next entry */
1948 qe->parent->rrpos++;
1951 qe->parent->wrapped = 0;
1953 return 0;
1956 static int say_periodic_announcement(struct queue_ent *qe)
1958 int res = 0;
1959 time_t now;
1961 /* Get the current time */
1962 time(&now);
1964 /* Check to see if it is time to announce */
1965 if ((now - qe->last_periodic_announce_time) < qe->parent->periodicannouncefrequency)
1966 return 0;
1968 /* Stop the music on hold so we can play our own file */
1969 ast_moh_stop(qe->chan);
1971 if (option_verbose > 2)
1972 ast_verbose(VERBOSE_PREFIX_3 "Playing periodic announcement\n");
1974 /* Check to make sure we have a sound file. If not, reset to the first sound file */
1975 if (qe->last_periodic_announce_sound >= MAX_PERIODIC_ANNOUNCEMENTS || !strlen(qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound])) {
1976 qe->last_periodic_announce_sound = 0;
1979 /* play the announcement */
1980 res = play_file(qe->chan, qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound]);
1982 if ((res > 0 && !valid_exit(qe, res)) || res < 0)
1983 res = 0;
1985 /* Resume Music on Hold if the caller is going to stay in the queue */
1986 if (!res)
1987 ast_moh_start(qe->chan, qe->moh, NULL);
1989 /* update last_periodic_announce_time */
1990 qe->last_periodic_announce_time = now;
1992 /* Update the current periodic announcement to the next announcement */
1993 qe->last_periodic_announce_sound++;
1995 return res;
1998 static void record_abandoned(struct queue_ent *qe)
2000 ast_mutex_lock(&qe->parent->lock);
2001 manager_event(EVENT_FLAG_AGENT, "QueueCallerAbandon",
2002 "Queue: %s\r\n"
2003 "Uniqueid: %s\r\n"
2004 "Position: %d\r\n"
2005 "OriginalPosition: %d\r\n"
2006 "HoldTime: %d\r\n",
2007 qe->parent->name, qe->chan->uniqueid, qe->pos, qe->opos, (int)(time(NULL) - qe->start));
2009 qe->parent->callsabandoned++;
2010 ast_mutex_unlock(&qe->parent->lock);
2013 /*! \brief RNA == Ring No Answer. Common code that is executed when we try a queue member and they don't answer. */
2014 static void rna(int rnatime, struct queue_ent *qe, char *interface, char *membername)
2016 if (option_verbose > 2)
2017 ast_verbose( VERBOSE_PREFIX_3 "Nobody picked up in %d ms\n", rnatime);
2018 ast_queue_log(qe->parent->name, qe->chan->uniqueid, membername, "RINGNOANSWER", "%d", rnatime);
2019 if (qe->parent->autopause) {
2020 if (!set_member_paused(qe->parent->name, interface, 1)) {
2021 if (option_verbose > 2)
2022 ast_verbose( VERBOSE_PREFIX_3 "Auto-Pausing Queue Member %s in queue %s since they failed to answer.\n", interface, qe->parent->name);
2023 } else {
2024 if (option_verbose > 2)
2025 ast_verbose( VERBOSE_PREFIX_3 "Failed to pause Queue Member %s in queue %s!\n", interface, qe->parent->name);
2028 return;
2031 #define AST_MAX_WATCHERS 256
2033 static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callattempt *outgoing, int *to, char *digit, int prebusies, int caller_disconnect, int forwardsallowed)
2035 char *queue = qe->parent->name;
2036 struct callattempt *o;
2037 int status;
2038 int sentringing = 0;
2039 int numbusies = prebusies;
2040 int numnochan = 0;
2041 int stillgoing = 0;
2042 int orig = *to;
2043 struct ast_frame *f;
2044 struct callattempt *peer = NULL;
2045 struct ast_channel *winner;
2046 struct ast_channel *in = qe->chan;
2047 char on[80] = "";
2048 char membername[80] = "";
2049 long starttime = 0;
2050 long endtime = 0;
2052 starttime = (long) time(NULL);
2054 while (*to && !peer) {
2055 int numlines, retry, pos = 1;
2056 struct ast_channel *watchers[AST_MAX_WATCHERS];
2057 watchers[0] = in;
2059 for (retry = 0; retry < 2; retry++) {
2060 numlines = 0;
2061 for (o = outgoing; o; o = o->q_next) { /* Keep track of important channels */
2062 if (o->stillgoing) { /* Keep track of important channels */
2063 stillgoing = 1;
2064 if (o->chan)
2065 watchers[pos++] = o->chan;
2067 numlines++;
2069 if (pos > 1 /* found */ || !stillgoing /* nobody listening */ ||
2070 (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) /* ring would not be delivered */)
2071 break;
2072 /* On "ringall" strategy we only move to the next penalty level
2073 when *all* ringing phones are done in the current penalty level */
2074 ring_one(qe, outgoing, &numbusies);
2075 /* and retry... */
2077 if (pos == 1 /* not found */) {
2078 if (numlines == (numbusies + numnochan)) {
2079 ast_log(LOG_DEBUG, "Everyone is busy at this time\n");
2080 } else {
2081 ast_log(LOG_NOTICE, "No one is answering queue '%s' (%d/%d/%d)\n", queue, numlines, numbusies, numnochan);
2083 *to = 0;
2084 return NULL;
2086 winner = ast_waitfor_n(watchers, pos, to);
2087 for (o = outgoing; o; o = o->q_next) {
2088 if (o->stillgoing && (o->chan) && (o->chan->_state == AST_STATE_UP)) {
2089 if (!peer) {
2090 if (option_verbose > 2)
2091 ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
2092 peer = o;
2094 } else if (o->chan && (o->chan == winner)) {
2096 ast_copy_string(on, o->member->interface, sizeof(on));
2097 ast_copy_string(membername, o->member->membername, sizeof(membername));
2099 if (!ast_strlen_zero(o->chan->call_forward) && !forwardsallowed) {
2100 if (option_verbose > 2)
2101 ast_verbose(VERBOSE_PREFIX_3 "Forwarding %s to '%s' prevented.\n", in->name, o->chan->call_forward);
2102 numnochan++;
2103 do_hang(o);
2104 winner = NULL;
2105 continue;
2106 } else if (!ast_strlen_zero(o->chan->call_forward)) {
2107 char tmpchan[256];
2108 char *stuff;
2109 char *tech;
2111 ast_copy_string(tmpchan, o->chan->call_forward, sizeof(tmpchan));
2112 if ((stuff = strchr(tmpchan, '/'))) {
2113 *stuff++ = '\0';
2114 tech = tmpchan;
2115 } else {
2116 snprintf(tmpchan, sizeof(tmpchan), "%s@%s", o->chan->call_forward, o->chan->context);
2117 stuff = tmpchan;
2118 tech = "Local";
2120 /* Before processing channel, go ahead and check for forwarding */
2121 if (option_verbose > 2)
2122 ast_verbose(VERBOSE_PREFIX_3 "Now forwarding %s to '%s/%s' (thanks to %s)\n", in->name, tech, stuff, o->chan->name);
2123 /* Setup parameters */
2124 o->chan = ast_request(tech, in->nativeformats, stuff, &status);
2125 if (status != o->oldstatus)
2126 update_dial_status(qe->parent, o->member, status);
2127 if (!o->chan) {
2128 ast_log(LOG_NOTICE, "Unable to create local channel for call forward to '%s/%s'\n", tech, stuff);
2129 o->stillgoing = 0;
2130 numnochan++;
2131 } else {
2132 ast_channel_inherit_variables(in, o->chan);
2133 if (o->chan->cid.cid_num)
2134 free(o->chan->cid.cid_num);
2135 o->chan->cid.cid_num = ast_strdup(in->cid.cid_num);
2137 if (o->chan->cid.cid_name)
2138 free(o->chan->cid.cid_name);
2139 o->chan->cid.cid_name = ast_strdup(in->cid.cid_name);
2141 ast_string_field_set(o->chan, accountcode, in->accountcode);
2142 o->chan->cdrflags = in->cdrflags;
2144 if (in->cid.cid_ani) {
2145 if (o->chan->cid.cid_ani)
2146 free(o->chan->cid.cid_ani);
2147 o->chan->cid.cid_ani = ast_strdup(in->cid.cid_ani);
2149 if (o->chan->cid.cid_rdnis)
2150 free(o->chan->cid.cid_rdnis);
2151 o->chan->cid.cid_rdnis = ast_strdup(S_OR(in->macroexten, in->exten));
2152 if (ast_call(o->chan, tmpchan, 0)) {
2153 ast_log(LOG_NOTICE, "Failed to dial on local channel for call forward to '%s'\n", tmpchan);
2154 do_hang(o);
2155 numnochan++;
2158 /* Hangup the original channel now, in case we needed it */
2159 ast_hangup(winner);
2160 continue;
2162 f = ast_read(winner);
2163 if (f) {
2164 if (f->frametype == AST_FRAME_CONTROL) {
2165 switch (f->subclass) {
2166 case AST_CONTROL_ANSWER:
2167 /* This is our guy if someone answered. */
2168 if (!peer) {
2169 if (option_verbose > 2)
2170 ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
2171 peer = o;
2173 break;
2174 case AST_CONTROL_BUSY:
2175 if (option_verbose > 2)
2176 ast_verbose( VERBOSE_PREFIX_3 "%s is busy\n", o->chan->name);
2177 if (in->cdr)
2178 ast_cdr_busy(in->cdr);
2179 do_hang(o);
2180 endtime = (long)time(NULL);
2181 endtime -= starttime;
2182 rna(endtime*1000, qe, on, membername);
2183 if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
2184 if (qe->parent->timeoutrestart)
2185 *to = orig;
2186 ring_one(qe, outgoing, &numbusies);
2188 numbusies++;
2189 break;
2190 case AST_CONTROL_CONGESTION:
2191 if (option_verbose > 2)
2192 ast_verbose( VERBOSE_PREFIX_3 "%s is circuit-busy\n", o->chan->name);
2193 if (in->cdr)
2194 ast_cdr_busy(in->cdr);
2195 endtime = (long)time(NULL);
2196 endtime -= starttime;
2197 rna(endtime*1000, qe, on, membername);
2198 do_hang(o);
2199 if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
2200 if (qe->parent->timeoutrestart)
2201 *to = orig;
2202 ring_one(qe, outgoing, &numbusies);
2204 numbusies++;
2205 break;
2206 case AST_CONTROL_RINGING:
2207 if (option_verbose > 2)
2208 ast_verbose( VERBOSE_PREFIX_3 "%s is ringing\n", o->chan->name);
2209 if (!sentringing) {
2210 #if 0
2211 ast_indicate(in, AST_CONTROL_RINGING);
2212 #endif
2213 sentringing++;
2215 break;
2216 case AST_CONTROL_OFFHOOK:
2217 /* Ignore going off hook */
2218 break;
2219 default:
2220 ast_log(LOG_DEBUG, "Dunno what to do with control type %d\n", f->subclass);
2223 ast_frfree(f);
2224 } else {
2225 endtime = (long) time(NULL) - starttime;
2226 rna(endtime * 1000, qe, on, membername);
2227 do_hang(o);
2228 if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
2229 if (qe->parent->timeoutrestart)
2230 *to = orig;
2231 ring_one(qe, outgoing, &numbusies);
2236 if (winner == in) {
2237 f = ast_read(in);
2238 if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) {
2239 /* Got hung up */
2240 *to = -1;
2241 if (f)
2242 ast_frfree(f);
2243 return NULL;
2245 if ((f->frametype == AST_FRAME_DTMF) && caller_disconnect && (f->subclass == '*')) {
2246 if (option_verbose > 3)
2247 ast_verbose(VERBOSE_PREFIX_3 "User hit %c to disconnect call.\n", f->subclass);
2248 *to = 0;
2249 ast_frfree(f);
2250 return NULL;
2252 if ((f->frametype == AST_FRAME_DTMF) && valid_exit(qe, f->subclass)) {
2253 if (option_verbose > 3)
2254 ast_verbose(VERBOSE_PREFIX_3 "User pressed digit: %c\n", f->subclass);
2255 *to = 0;
2256 *digit = f->subclass;
2257 ast_frfree(f);
2258 return NULL;
2260 ast_frfree(f);
2262 if (!*to)
2263 rna(orig, qe, on, membername);
2266 return peer;
2269 static int is_our_turn(struct queue_ent *qe)
2271 struct queue_ent *ch;
2272 struct member *cur;
2273 int avl = 0;
2274 int idx = 0;
2275 int res;
2277 if (!qe->parent->autofill) {
2278 /* Atomically read the parent head -- does not need a lock */
2279 ch = qe->parent->head;
2280 /* If we are now at the top of the head, break out */
2281 if (ch == qe) {
2282 if (option_debug)
2283 ast_log(LOG_DEBUG, "It's our turn (%s).\n", qe->chan->name);
2284 res = 1;
2285 } else {
2286 if (option_debug)
2287 ast_log(LOG_DEBUG, "It's not our turn (%s).\n", qe->chan->name);
2288 res = 0;
2291 } else {
2292 /* This needs a lock. How many members are available to be served? */
2293 ast_mutex_lock(&qe->parent->lock);
2295 ch = qe->parent->head;
2297 if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
2298 if (option_debug)
2299 ast_log(LOG_DEBUG, "Even though there are %d available members, the strategy is ringall so only the head call is allowed in\n", avl);
2300 avl = 1;
2301 } else {
2302 struct ao2_iterator mem_iter = ao2_iterator_init(qe->parent->members, 0);
2303 while ((cur = ao2_iterator_next(&mem_iter))) {
2304 switch (cur->status) {
2305 case AST_DEVICE_NOT_INUSE:
2306 case AST_DEVICE_UNKNOWN:
2307 if (!cur->paused)
2308 avl++;
2309 break;
2311 ao2_ref(cur, -1);
2315 if (option_debug)
2316 ast_log(LOG_DEBUG, "There are %d available members.\n", avl);
2318 while ((idx < avl) && (ch) && (ch != qe)) {
2319 idx++;
2320 ch = ch->next;
2323 /* If the queue entry is within avl [the number of available members] calls from the top ... */
2324 if (ch && idx < avl) {
2325 if (option_debug)
2326 ast_log(LOG_DEBUG, "It's our turn (%s).\n", qe->chan->name);
2327 res = 1;
2328 } else {
2329 if (option_debug)
2330 ast_log(LOG_DEBUG, "It's not our turn (%s).\n", qe->chan->name);
2331 res = 0;
2334 ast_mutex_unlock(&qe->parent->lock);
2337 return res;
2340 static int wait_our_turn(struct queue_ent *qe, int ringing, enum queue_result *reason)
2342 int res = 0;
2344 /* This is the holding pen for callers 2 through maxlen */
2345 for (;;) {
2346 enum queue_member_status stat;
2348 if (is_our_turn(qe))
2349 break;
2351 /* If we have timed out, break out */
2352 if (qe->expire && (time(NULL) > qe->expire)) {
2353 *reason = QUEUE_TIMEOUT;
2354 break;
2357 stat = get_member_status(qe->parent, qe->max_penalty);
2359 /* leave the queue if no agents, if enabled */
2360 if (qe->parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) {
2361 *reason = QUEUE_LEAVEEMPTY;
2362 ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long)time(NULL) - qe->start);
2363 leave_queue(qe);
2364 break;
2367 /* leave the queue if no reachable agents, if enabled */
2368 if ((qe->parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) {
2369 *reason = QUEUE_LEAVEUNAVAIL;
2370 ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long)time(NULL) - qe->start);
2371 leave_queue(qe);
2372 break;
2375 /* Make a position announcement, if enabled */
2376 if (qe->parent->announcefrequency && !ringing &&
2377 (res = say_position(qe)))
2378 break;
2380 /* Make a periodic announcement, if enabled */
2381 if (qe->parent->periodicannouncefrequency && !ringing &&
2382 (res = say_periodic_announcement(qe)))
2383 break;
2385 /* Wait a second before checking again */
2386 if ((res = ast_waitfordigit(qe->chan, RECHECK * 1000))) {
2387 if (res > 0 && !valid_exit(qe, res))
2388 res = 0;
2389 else
2390 break;
2394 return res;
2397 static int update_queue(struct call_queue *q, struct member *member, int callcompletedinsl)
2399 ast_mutex_lock(&q->lock);
2400 time(&member->lastcall);
2401 member->calls++;
2402 q->callscompleted++;
2403 if (callcompletedinsl)
2404 q->callscompletedinsl++;
2405 ast_mutex_unlock(&q->lock);
2406 return 0;
2409 static int calc_metric(struct call_queue *q, struct member *mem, int pos, struct queue_ent *qe, struct callattempt *tmp)
2411 if (qe->max_penalty && (mem->penalty > qe->max_penalty))
2412 return -1;
2414 switch (q->strategy) {
2415 case QUEUE_STRATEGY_RINGALL:
2416 /* Everyone equal, except for penalty */
2417 tmp->metric = mem->penalty * 1000000;
2418 break;
2419 case QUEUE_STRATEGY_ROUNDROBIN:
2420 if (!pos) {
2421 if (!q->wrapped) {
2422 /* No more channels, start over */
2423 q->rrpos = 0;
2424 } else {
2425 /* Prioritize next entry */
2426 q->rrpos++;
2428 q->wrapped = 0;
2430 /* Fall through */
2431 case QUEUE_STRATEGY_RRMEMORY:
2432 if (pos < q->rrpos) {
2433 tmp->metric = 1000 + pos;
2434 } else {
2435 if (pos > q->rrpos)
2436 /* Indicate there is another priority */
2437 q->wrapped = 1;
2438 tmp->metric = pos;
2440 tmp->metric += mem->penalty * 1000000;
2441 break;
2442 case QUEUE_STRATEGY_RANDOM:
2443 tmp->metric = ast_random() % 1000;
2444 tmp->metric += mem->penalty * 1000000;
2445 break;
2446 case QUEUE_STRATEGY_FEWESTCALLS:
2447 tmp->metric = mem->calls;
2448 tmp->metric += mem->penalty * 1000000;
2449 break;
2450 case QUEUE_STRATEGY_LEASTRECENT:
2451 if (!mem->lastcall)
2452 tmp->metric = 0;
2453 else
2454 tmp->metric = 1000000 - (time(NULL) - mem->lastcall);
2455 tmp->metric += mem->penalty * 1000000;
2456 break;
2457 default:
2458 ast_log(LOG_WARNING, "Can't calculate metric for unknown strategy %d\n", q->strategy);
2459 break;
2461 return 0;
2464 static int try_calling(struct queue_ent *qe, const char *options, char *announceoverride, const char *url, int *tries, int *noption, const char *agi)
2466 struct member *cur;
2467 struct callattempt *outgoing = NULL; /* the list of calls we are building */
2468 int to;
2469 char oldexten[AST_MAX_EXTENSION]="";
2470 char oldcontext[AST_MAX_CONTEXT]="";
2471 char queuename[256]="";
2472 struct ast_channel *peer;
2473 struct ast_channel *which;
2474 struct callattempt *lpeer;
2475 struct member *member;
2476 struct ast_app *app;
2477 int res = 0, bridge = 0;
2478 int numbusies = 0;
2479 int x=0;
2480 char *announce = NULL;
2481 char digit = 0;
2482 time_t callstart;
2483 time_t now = time(NULL);
2484 struct ast_bridge_config bridge_config;
2485 char nondataquality = 1;
2486 char *agiexec = NULL;
2487 int ret = 0;
2488 const char *monitorfilename;
2489 const char *monitor_exec;
2490 const char *monitor_options;
2491 char tmpid[256], tmpid2[256];
2492 char meid[1024], meid2[1024];
2493 char mixmonargs[1512];
2494 struct ast_app *mixmonapp = NULL;
2495 char *p;
2496 char vars[2048];
2497 int forwardsallowed = 1;
2498 int callcompletedinsl;
2499 struct ao2_iterator memi;
2501 memset(&bridge_config, 0, sizeof(bridge_config));
2502 time(&now);
2504 for (; options && *options; options++)
2505 switch (*options) {
2506 case 't':
2507 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_REDIRECT);
2508 break;
2509 case 'T':
2510 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_REDIRECT);
2511 break;
2512 case 'w':
2513 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMON);
2514 break;
2515 case 'W':
2516 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMON);
2517 break;
2518 case 'd':
2519 nondataquality = 0;
2520 break;
2521 case 'h':
2522 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_DISCONNECT);
2523 break;
2524 case 'H':
2525 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT);
2526 break;
2527 case 'n':
2528 if (qe->parent->strategy == QUEUE_STRATEGY_ROUNDROBIN || qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY)
2529 (*tries)++;
2530 else
2531 *tries = qe->parent->membercount;
2532 *noption = 1;
2533 break;
2534 case 'i':
2535 forwardsallowed = 0;
2536 break;
2539 /* Hold the lock while we setup the outgoing calls */
2540 if (use_weight)
2541 AST_LIST_LOCK(&queues);
2542 ast_mutex_lock(&qe->parent->lock);
2543 if (option_debug)
2544 ast_log(LOG_DEBUG, "%s is trying to call a queue member.\n",
2545 qe->chan->name);
2546 ast_copy_string(queuename, qe->parent->name, sizeof(queuename));
2547 if (!ast_strlen_zero(qe->announce))
2548 announce = qe->announce;
2549 if (!ast_strlen_zero(announceoverride))
2550 announce = announceoverride;
2552 memi = ao2_iterator_init(qe->parent->members, 0);
2553 while ((cur = ao2_iterator_next(&memi))) {
2554 struct callattempt *tmp = ast_calloc(1, sizeof(*tmp));
2556 if (!tmp) {
2557 ao2_ref(cur, -1);
2558 ast_mutex_unlock(&qe->parent->lock);
2559 if (use_weight)
2560 AST_LIST_UNLOCK(&queues);
2561 goto out;
2563 tmp->stillgoing = -1;
2564 tmp->member = cur;
2565 tmp->oldstatus = cur->status;
2566 tmp->lastcall = cur->lastcall;
2567 ast_copy_string(tmp->interface, cur->interface, sizeof(tmp->interface));
2568 /* Special case: If we ring everyone, go ahead and ring them, otherwise
2569 just calculate their metric for the appropriate strategy */
2570 if (!calc_metric(qe->parent, cur, x++, qe, tmp)) {
2571 /* Put them in the list of outgoing thingies... We're ready now.
2572 XXX If we're forcibly removed, these outgoing calls won't get
2573 hung up XXX */
2574 tmp->q_next = outgoing;
2575 outgoing = tmp;
2576 /* If this line is up, don't try anybody else */
2577 if (outgoing->chan && (outgoing->chan->_state == AST_STATE_UP))
2578 break;
2579 } else {
2580 ao2_ref(cur, -1);
2581 free(tmp);
2584 if (qe->expire && (!qe->parent->timeout || (qe->expire - now) <= qe->parent->timeout))
2585 to = (qe->expire - now) * 1000;
2586 else
2587 to = (qe->parent->timeout) ? qe->parent->timeout * 1000 : -1;
2588 ring_one(qe, outgoing, &numbusies);
2589 ast_mutex_unlock(&qe->parent->lock);
2590 if (use_weight)
2591 AST_LIST_UNLOCK(&queues);
2592 lpeer = wait_for_answer(qe, outgoing, &to, &digit, numbusies, ast_test_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT), forwardsallowed);
2593 ast_mutex_lock(&qe->parent->lock);
2594 if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY) {
2595 store_next(qe, outgoing);
2597 ast_mutex_unlock(&qe->parent->lock);
2598 peer = lpeer ? lpeer->chan : NULL;
2599 if (!peer) {
2600 if (to) {
2601 /* Must gotten hung up */
2602 res = -1;
2603 } else {
2604 /* User exited by pressing a digit */
2605 res = digit;
2607 if (option_debug && res == -1)
2608 ast_log(LOG_DEBUG, "%s: Nobody answered.\n", qe->chan->name);
2609 } else { /* peer is valid */
2610 /* Ah ha! Someone answered within the desired timeframe. Of course after this
2611 we will always return with -1 so that it is hung up properly after the
2612 conversation. */
2613 qe->handled++;
2614 if (!strcmp(qe->chan->tech->type, "Zap"))
2615 ast_channel_setoption(qe->chan, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
2616 if (!strcmp(peer->tech->type, "Zap"))
2617 ast_channel_setoption(peer, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
2618 /* Update parameters for the queue */
2619 time(&now);
2620 recalc_holdtime(qe, (now - qe->start));
2621 ast_mutex_lock(&qe->parent->lock);
2622 callcompletedinsl = ((now - qe->start) <= qe->parent->servicelevel);
2623 ast_mutex_unlock(&qe->parent->lock);
2624 member = lpeer->member;
2625 hangupcalls(outgoing, peer);
2626 outgoing = NULL;
2627 if (announce || qe->parent->reportholdtime || qe->parent->memberdelay) {
2628 int res2;
2630 res2 = ast_autoservice_start(qe->chan);
2631 if (!res2) {
2632 if (qe->parent->memberdelay) {
2633 ast_log(LOG_NOTICE, "Delaying member connect for %d seconds\n", qe->parent->memberdelay);
2634 res2 |= ast_safe_sleep(peer, qe->parent->memberdelay * 1000);
2636 if (!res2 && announce) {
2637 play_file(peer, announce);
2639 if (!res2 && qe->parent->reportholdtime) {
2640 if (!play_file(peer, qe->parent->sound_reporthold)) {
2641 int holdtime;
2643 time(&now);
2644 holdtime = abs((now - qe->start) / 60);
2645 if (holdtime < 2) {
2646 play_file(peer, qe->parent->sound_lessthan);
2647 ast_say_number(peer, 2, AST_DIGIT_ANY, peer->language, NULL);
2648 } else
2649 ast_say_number(peer, holdtime, AST_DIGIT_ANY, peer->language, NULL);
2650 play_file(peer, qe->parent->sound_minutes);
2654 res2 |= ast_autoservice_stop(qe->chan);
2655 if (peer->_softhangup) {
2656 /* Agent must have hung up */
2657 ast_log(LOG_WARNING, "Agent on %s hungup on the customer.\n", peer->name);
2658 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "AGENTDUMP", "%s", "");
2659 record_abandoned(qe);
2660 if (qe->parent->eventwhencalled)
2661 manager_event(EVENT_FLAG_AGENT, "AgentDump",
2662 "Queue: %s\r\n"
2663 "Uniqueid: %s\r\n"
2664 "Channel: %s\r\n"
2665 "Member: %s\r\n"
2666 "MemberName: %s\r\n"
2667 "%s",
2668 queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
2669 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
2670 ast_hangup(peer);
2671 goto out;
2672 } else if (res2) {
2673 /* Caller must have hung up just before being connected*/
2674 ast_log(LOG_NOTICE, "Caller was about to talk to agent on %s but the caller hungup.\n", peer->name);
2675 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "ABANDON", "%d|%d|%ld", qe->pos, qe->opos, (long)time(NULL) - qe->start);
2676 record_abandoned(qe);
2677 ast_hangup(peer);
2678 return -1;
2681 /* Stop music on hold */
2682 ast_moh_stop(qe->chan);
2683 /* If appropriate, log that we have a destination channel */
2684 if (qe->chan->cdr)
2685 ast_cdr_setdestchan(qe->chan->cdr, peer->name);
2686 /* Make sure channels are compatible */
2687 res = ast_channel_make_compatible(qe->chan, peer);
2688 if (res < 0) {
2689 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "SYSCOMPAT", "%s", "");
2690 ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", qe->chan->name, peer->name);
2691 record_abandoned(qe);
2692 ast_hangup(peer);
2693 return -1;
2696 if (qe->parent->setinterfacevar)
2697 pbx_builtin_setvar_helper(qe->chan, "MEMBERINTERFACE", member->interface);
2699 /* Begin Monitoring */
2700 if (qe->parent->monfmt && *qe->parent->monfmt) {
2701 if (!qe->parent->montype) {
2702 if (option_debug)
2703 ast_log(LOG_DEBUG, "Starting Monitor as requested.\n");
2704 monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME");
2705 if (pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC") || pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC_ARGS"))
2706 which = qe->chan;
2707 else
2708 which = peer;
2709 if (monitorfilename)
2710 ast_monitor_start(which, qe->parent->monfmt, monitorfilename, 1 );
2711 else if (qe->chan->cdr)
2712 ast_monitor_start(which, qe->parent->monfmt, qe->chan->cdr->uniqueid, 1 );
2713 else {
2714 /* Last ditch effort -- no CDR, make up something */
2715 snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random());
2716 ast_monitor_start(which, qe->parent->monfmt, tmpid, 1 );
2718 if (qe->parent->monjoin)
2719 ast_monitor_setjoinfiles(which, 1);
2720 } else {
2721 if (option_debug)
2722 ast_log(LOG_DEBUG, "Starting MixMonitor as requested.\n");
2723 monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME");
2724 if (!monitorfilename) {
2725 if (qe->chan->cdr)
2726 ast_copy_string(tmpid, qe->chan->cdr->uniqueid, sizeof(tmpid)-1);
2727 else
2728 snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random());
2729 } else {
2730 ast_copy_string(tmpid2, monitorfilename, sizeof(tmpid2)-1);
2731 for (p = tmpid2; *p ; p++) {
2732 if (*p == '^' && *(p+1) == '{') {
2733 *p = '$';
2737 memset(tmpid, 0, sizeof(tmpid));
2738 pbx_substitute_variables_helper(qe->chan, tmpid2, tmpid, sizeof(tmpid) - 1);
2741 monitor_exec = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC");
2742 monitor_options = pbx_builtin_getvar_helper(qe->chan, "MONITOR_OPTIONS");
2744 if (monitor_exec) {
2745 ast_copy_string(meid2, monitor_exec, sizeof(meid2)-1);
2746 for (p = meid2; *p ; p++) {
2747 if (*p == '^' && *(p+1) == '{') {
2748 *p = '$';
2752 memset(meid, 0, sizeof(meid));
2753 pbx_substitute_variables_helper(qe->chan, meid2, meid, sizeof(meid) - 1);
2756 snprintf(tmpid2, sizeof(tmpid2)-1, "%s.%s", tmpid, qe->parent->monfmt);
2758 mixmonapp = pbx_findapp("MixMonitor");
2760 if (strchr(tmpid2, '|')) {
2761 ast_log(LOG_WARNING, "monitor-format (in queues.conf) and MONITOR_FILENAME cannot contain a '|'! Not recording.\n");
2762 mixmonapp = NULL;
2765 if (!monitor_options)
2766 monitor_options = "";
2768 if (strchr(monitor_options, '|')) {
2769 ast_log(LOG_WARNING, "MONITOR_OPTIONS cannot contain a '|'! Not recording.\n");
2770 mixmonapp = NULL;
2773 if (mixmonapp) {
2774 if (!ast_strlen_zero(monitor_exec))
2775 snprintf(mixmonargs, sizeof(mixmonargs)-1, "%s|b%s|%s", tmpid2, monitor_options, monitor_exec);
2776 else
2777 snprintf(mixmonargs, sizeof(mixmonargs)-1, "%s|b%s", tmpid2, monitor_options);
2779 if (option_debug)
2780 ast_log(LOG_DEBUG, "Arguments being passed to MixMonitor: %s\n", mixmonargs);
2781 /* We purposely lock the CDR so that pbx_exec does not update the application data */
2782 if (qe->chan->cdr)
2783 ast_set_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED);
2784 ret = pbx_exec(qe->chan, mixmonapp, mixmonargs);
2785 if (qe->chan->cdr)
2786 ast_clear_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED);
2788 } else
2789 ast_log(LOG_WARNING, "Asked to run MixMonitor on this call, but cannot find the MixMonitor app!\n");
2793 /* Drop out of the queue at this point, to prepare for next caller */
2794 leave_queue(qe);
2795 if (!ast_strlen_zero(url) && ast_channel_supports_html(peer)) {
2796 if (option_debug)
2797 ast_log(LOG_DEBUG, "app_queue: sendurl=%s.\n", url);
2798 ast_channel_sendurl(peer, url);
2800 if (!ast_strlen_zero(agi)) {
2801 if (option_debug)
2802 ast_log(LOG_DEBUG, "app_queue: agi=%s.\n", agi);
2803 app = pbx_findapp("agi");
2804 if (app) {
2805 agiexec = ast_strdupa(agi);
2806 ret = pbx_exec(qe->chan, app, agiexec);
2807 } else
2808 ast_log(LOG_WARNING, "Asked to execute an AGI on this channel, but could not find application (agi)!\n");
2810 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "CONNECT", "%ld|%s", (long)time(NULL) - qe->start, peer->uniqueid);
2811 if (qe->parent->eventwhencalled)
2812 manager_event(EVENT_FLAG_AGENT, "AgentConnect",
2813 "Queue: %s\r\n"
2814 "Uniqueid: %s\r\n"
2815 "Channel: %s\r\n"
2816 "Member: %s\r\n"
2817 "MemberName: %s\r\n"
2818 "Holdtime: %ld\r\n"
2819 "BridgedChannel: %s\r\n"
2820 "%s",
2821 queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
2822 (long)time(NULL) - qe->start, peer->uniqueid,
2823 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
2824 ast_copy_string(oldcontext, qe->chan->context, sizeof(oldcontext));
2825 ast_copy_string(oldexten, qe->chan->exten, sizeof(oldexten));
2826 time(&callstart);
2828 if (member->status == AST_DEVICE_NOT_INUSE)
2829 ast_log(LOG_WARNING, "The device state of this queue member, %s, is still 'Not in Use' when it probably should not be! Please check UPGRADE.txt for correct configuration settings.\n", member->membername);
2832 bridge = ast_bridge_call(qe->chan,peer, &bridge_config);
2834 if (strcasecmp(oldcontext, qe->chan->context) || strcasecmp(oldexten, qe->chan->exten)) {
2835 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld",
2836 qe->chan->exten, qe->chan->context, (long) (callstart - qe->start),
2837 (long) (time(NULL) - callstart));
2838 } else if (qe->chan->_softhangup) {
2839 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETECALLER", "%ld|%ld|%d",
2840 (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
2841 if (qe->parent->eventwhencalled)
2842 manager_event(EVENT_FLAG_AGENT, "AgentComplete",
2843 "Queue: %s\r\n"
2844 "Uniqueid: %s\r\n"
2845 "Channel: %s\r\n"
2846 "Member: %s\r\n"
2847 "MemberName: %s\r\n"
2848 "HoldTime: %ld\r\n"
2849 "TalkTime: %ld\r\n"
2850 "Reason: caller\r\n"
2851 "%s",
2852 queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
2853 (long)(callstart - qe->start), (long)(time(NULL) - callstart),
2854 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
2855 } else {
2856 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETEAGENT", "%ld|%ld|%d",
2857 (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
2858 if (qe->parent->eventwhencalled)
2859 manager_event(EVENT_FLAG_AGENT, "AgentComplete",
2860 "Queue: %s\r\n"
2861 "Uniqueid: %s\r\n"
2862 "Channel: %s\r\n"
2863 "MemberName: %s\r\n"
2864 "HoldTime: %ld\r\n"
2865 "TalkTime: %ld\r\n"
2866 "Reason: agent\r\n"
2867 "%s",
2868 queuename, qe->chan->uniqueid, peer->name, member->membername, (long)(callstart - qe->start),
2869 (long)(time(NULL) - callstart),
2870 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
2873 if (bridge != AST_PBX_NO_HANGUP_PEER)
2874 ast_hangup(peer);
2875 update_queue(qe->parent, member, callcompletedinsl);
2876 res = bridge ? bridge : 1;
2878 out:
2879 hangupcalls(outgoing, NULL);
2881 return res;
2884 static int wait_a_bit(struct queue_ent *qe)
2886 /* Don't need to hold the lock while we setup the outgoing calls */
2887 int retrywait = qe->parent->retry * 1000;
2889 int res = ast_waitfordigit(qe->chan, retrywait);
2890 if (res > 0 && !valid_exit(qe, res))
2891 res = 0;
2893 return res;
2896 static struct member *interface_exists(struct call_queue *q, const char *interface)
2898 struct member *mem;
2899 struct ao2_iterator mem_iter;
2901 if (!q)
2902 return NULL;
2904 mem_iter = ao2_iterator_init(q->members, 0);
2905 while ((mem = ao2_iterator_next(&mem_iter))) {
2906 if (!strcasecmp(interface, mem->interface))
2907 return mem;
2908 ao2_ref(mem, -1);
2911 return NULL;
2915 /* Dump all members in a specific queue to the database
2917 * <pm_family>/<queuename> = <interface>;<penalty>;<paused>[|...]
2920 static void dump_queue_members(struct call_queue *pm_queue)
2922 struct member *cur_member;
2923 char value[PM_MAX_LEN];
2924 int value_len = 0;
2925 int res;
2926 struct ao2_iterator mem_iter;
2928 memset(value, 0, sizeof(value));
2930 if (!pm_queue)
2931 return;
2933 mem_iter = ao2_iterator_init(pm_queue->members, 0);
2934 while ((cur_member = ao2_iterator_next(&mem_iter))) {
2935 if (!cur_member->dynamic) {
2936 ao2_ref(cur_member, -1);
2937 continue;
2940 res = snprintf(value + value_len, sizeof(value) - value_len, "%s%s;%d;%d;%s",
2941 value_len ? "|" : "", cur_member->interface, cur_member->penalty, cur_member->paused, cur_member->membername);
2943 ao2_ref(cur_member, -1);
2945 if (res != strlen(value + value_len)) {
2946 ast_log(LOG_WARNING, "Could not create persistent member string, out of space\n");
2947 break;
2949 value_len += res;
2952 if (value_len && !cur_member) {
2953 if (ast_db_put(pm_family, pm_queue->name, value))
2954 ast_log(LOG_WARNING, "failed to create persistent dynamic entry!\n");
2955 } else
2956 /* Delete the entry if the queue is empty or there is an error */
2957 ast_db_del(pm_family, pm_queue->name);
2960 static int remove_from_queue(const char *queuename, const char *interface)
2962 struct call_queue *q;
2963 struct member *mem, tmpmem;
2964 int res = RES_NOSUCHQUEUE;
2966 ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
2968 AST_LIST_LOCK(&queues);
2969 AST_LIST_TRAVERSE(&queues, q, list) {
2970 ast_mutex_lock(&q->lock);
2971 if (strcmp(q->name, queuename)) {
2972 ast_mutex_unlock(&q->lock);
2973 continue;
2976 if ((mem = ao2_find(q->members, &tmpmem, OBJ_POINTER))) {
2977 /* XXX future changes should beware of this assumption!! */
2978 if(!mem->dynamic) {
2979 res = RES_NOT_DYNAMIC;
2980 ao2_ref(mem, -1);
2981 ast_mutex_unlock(&q->lock);
2982 break;
2984 q->membercount--;
2985 manager_event(EVENT_FLAG_AGENT, "QueueMemberRemoved",
2986 "Queue: %s\r\n"
2987 "Location: %s\r\n"
2988 "MemberName: %s\r\n",
2989 q->name, mem->interface, mem->membername);
2990 ao2_unlink(q->members, mem);
2991 ao2_ref(mem, -1);
2993 if (queue_persistent_members)
2994 dump_queue_members(q);
2996 res = RES_OKAY;
2997 } else {
2998 res = RES_EXISTS;
3000 ast_mutex_unlock(&q->lock);
3001 break;
3004 if (res == RES_OKAY)
3005 remove_from_interfaces(interface);
3007 AST_LIST_UNLOCK(&queues);
3009 return res;
3013 static int add_to_queue(const char *queuename, const char *interface, const char *membername, int penalty, int paused, int dump)
3015 struct call_queue *q;
3016 struct member *new_member, *old_member;
3017 int res = RES_NOSUCHQUEUE;
3019 /* \note Ensure the appropriate realtime queue is loaded. Note that this
3020 * short-circuits if the queue is already in memory. */
3021 if (!(q = load_realtime_queue(queuename)))
3022 return res;
3024 AST_LIST_LOCK(&queues);
3026 ast_mutex_lock(&q->lock);
3027 if ((old_member = interface_exists(q, interface)) == NULL) {
3028 add_to_interfaces(interface);
3029 if ((new_member = create_queue_member(interface, membername, penalty, paused))) {
3030 new_member->dynamic = 1;
3031 ao2_link(q->members, new_member);
3032 q->membercount++;
3033 manager_event(EVENT_FLAG_AGENT, "QueueMemberAdded",
3034 "Queue: %s\r\n"
3035 "Location: %s\r\n"
3036 "MemberName: %s\r\n"
3037 "Membership: %s\r\n"
3038 "Penalty: %d\r\n"
3039 "CallsTaken: %d\r\n"
3040 "LastCall: %d\r\n"
3041 "Status: %d\r\n"
3042 "Paused: %d\r\n",
3043 q->name, new_member->interface, new_member->membername,
3044 "dynamic",
3045 new_member->penalty, new_member->calls, (int) new_member->lastcall,
3046 new_member->status, new_member->paused);
3048 if (dump)
3049 dump_queue_members(q);
3051 res = RES_OKAY;
3052 } else {
3053 res = RES_OUTOFMEMORY;
3055 } else {
3056 ao2_ref(old_member, -1);
3057 res = RES_EXISTS;
3059 ast_mutex_unlock(&q->lock);
3060 AST_LIST_UNLOCK(&queues);
3062 return res;
3065 static int set_member_paused(const char *queuename, const char *interface, int paused)
3067 int found = 0;
3068 struct call_queue *q;
3069 struct member *mem;
3071 /* Special event for when all queues are paused - individual events still generated */
3072 /* XXX In all other cases, we use the membername, but since this affects all queues, we cannot */
3073 if (ast_strlen_zero(queuename))
3074 ast_queue_log("NONE", "NONE", interface, (paused ? "PAUSEALL" : "UNPAUSEALL"), "%s", "");
3076 AST_LIST_LOCK(&queues);
3077 AST_LIST_TRAVERSE(&queues, q, list) {
3078 ast_mutex_lock(&q->lock);
3079 if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
3080 if ((mem = interface_exists(q, interface))) {
3081 found++;
3082 if (mem->paused == paused)
3083 ast_log(LOG_DEBUG, "%spausing already-%spaused queue member %s:%s\n", (paused ? "" : "un"), (paused ? "" : "un"), q->name, interface);
3084 mem->paused = paused;
3086 if (queue_persistent_members)
3087 dump_queue_members(q);
3089 if(mem->realtime)
3090 update_realtime_member_field(mem, q->name, "paused", paused ? "1" : "0");
3092 ast_queue_log(q->name, "NONE", mem->membername, (paused ? "PAUSE" : "UNPAUSE"), "%s", "");
3094 manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
3095 "Queue: %s\r\n"
3096 "Location: %s\r\n"
3097 "MemberName: %s\r\n"
3098 "Paused: %d\r\n",
3099 q->name, mem->interface, mem->membername, paused);
3100 ao2_ref(mem, -1);
3103 ast_mutex_unlock(&q->lock);
3105 AST_LIST_UNLOCK(&queues);
3107 return found ? RESULT_SUCCESS : RESULT_FAILURE;
3110 /* Reload dynamic queue members persisted into the astdb */
3111 static void reload_queue_members(void)
3113 char *cur_ptr;
3114 char *queue_name;
3115 char *member;
3116 char *interface;
3117 char *membername = NULL;
3118 char *penalty_tok;
3119 int penalty = 0;
3120 char *paused_tok;
3121 int paused = 0;
3122 struct ast_db_entry *db_tree;
3123 struct ast_db_entry *entry;
3124 struct call_queue *cur_queue;
3125 char queue_data[PM_MAX_LEN];
3127 AST_LIST_LOCK(&queues);
3129 /* Each key in 'pm_family' is the name of a queue */
3130 db_tree = ast_db_gettree(pm_family, NULL);
3131 for (entry = db_tree; entry; entry = entry->next) {
3133 queue_name = entry->key + strlen(pm_family) + 2;
3135 AST_LIST_TRAVERSE(&queues, cur_queue, list) {
3136 ast_mutex_lock(&cur_queue->lock);
3137 if (!strcmp(queue_name, cur_queue->name))
3138 break;
3139 ast_mutex_unlock(&cur_queue->lock);
3142 if (!cur_queue)
3143 cur_queue = load_realtime_queue(queue_name);
3145 if (!cur_queue) {
3146 /* If the queue no longer exists, remove it from the
3147 * database */
3148 ast_log(LOG_WARNING, "Error loading persistent queue: '%s': it does not exist\n", queue_name);
3149 ast_db_del(pm_family, queue_name);
3150 continue;
3151 } else
3152 ast_mutex_unlock(&cur_queue->lock);
3154 if (ast_db_get(pm_family, queue_name, queue_data, PM_MAX_LEN))
3155 continue;
3157 cur_ptr = queue_data;
3158 while ((member = strsep(&cur_ptr, "|"))) {
3159 if (ast_strlen_zero(member))
3160 continue;
3162 interface = strsep(&member, ";");
3163 penalty_tok = strsep(&member, ";");
3164 paused_tok = strsep(&member, ";");
3165 membername = strsep(&member, ";");
3167 if (!penalty_tok) {
3168 ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (penalty)\n", queue_name);
3169 break;
3171 penalty = strtol(penalty_tok, NULL, 10);
3172 if (errno == ERANGE) {
3173 ast_log(LOG_WARNING, "Error converting penalty: %s: Out of range.\n", penalty_tok);
3174 break;
3177 if (!paused_tok) {
3178 ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (paused)\n", queue_name);
3179 break;
3181 paused = strtol(paused_tok, NULL, 10);
3182 if ((errno == ERANGE) || paused < 0 || paused > 1) {
3183 ast_log(LOG_WARNING, "Error converting paused: %s: Expected 0 or 1.\n", paused_tok);
3184 break;
3186 if (ast_strlen_zero(membername))
3187 membername = interface;
3189 if (option_debug)
3190 ast_log(LOG_DEBUG, "Reload Members: Queue: %s Member: %s Name: %s Penalty: %d Paused: %d\n", queue_name, interface, membername, penalty, paused);
3192 if (add_to_queue(queue_name, interface, membername, penalty, paused, 0) == RES_OUTOFMEMORY) {
3193 ast_log(LOG_ERROR, "Out of Memory when reloading persistent queue member\n");
3194 break;
3199 AST_LIST_UNLOCK(&queues);
3200 if (db_tree) {
3201 ast_log(LOG_NOTICE, "Queue members successfully reloaded from database.\n");
3202 ast_db_freetree(db_tree);
3206 static int pqm_exec(struct ast_channel *chan, void *data)
3208 struct ast_module_user *lu;
3209 char *parse;
3210 int priority_jump = 0;
3211 AST_DECLARE_APP_ARGS(args,
3212 AST_APP_ARG(queuename);
3213 AST_APP_ARG(interface);
3214 AST_APP_ARG(options);
3217 if (ast_strlen_zero(data)) {
3218 ast_log(LOG_WARNING, "PauseQueueMember requires an argument ([queuename]|interface[|options])\n");
3219 return -1;
3222 parse = ast_strdupa(data);
3224 AST_STANDARD_APP_ARGS(args, parse);
3226 lu = ast_module_user_add(chan);
3228 if (args.options) {
3229 if (strchr(args.options, 'j'))
3230 priority_jump = 1;
3233 if (ast_strlen_zero(args.interface)) {
3234 ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename]|interface[|options])\n");
3235 ast_module_user_remove(lu);
3236 return -1;
3239 if (set_member_paused(args.queuename, args.interface, 1)) {
3240 ast_log(LOG_WARNING, "Attempt to pause interface %s, not found\n", args.interface);
3241 if (priority_jump || ast_opt_priority_jumping) {
3242 if (ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101)) {
3243 pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND");
3244 ast_module_user_remove(lu);
3245 return 0;
3248 ast_module_user_remove(lu);
3249 pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND");
3250 return -1;
3253 ast_module_user_remove(lu);
3254 pbx_builtin_setvar_helper(chan, "PQMSTATUS", "PAUSED");
3256 return 0;
3259 static int upqm_exec(struct ast_channel *chan, void *data)
3261 struct ast_module_user *lu;
3262 char *parse;
3263 int priority_jump = 0;
3264 AST_DECLARE_APP_ARGS(args,
3265 AST_APP_ARG(queuename);
3266 AST_APP_ARG(interface);
3267 AST_APP_ARG(options);
3270 if (ast_strlen_zero(data)) {
3271 ast_log(LOG_WARNING, "UnpauseQueueMember requires an argument ([queuename]|interface[|options])\n");
3272 return -1;
3275 parse = ast_strdupa(data);
3277 AST_STANDARD_APP_ARGS(args, parse);
3279 lu = ast_module_user_add(chan);
3281 if (args.options) {
3282 if (strchr(args.options, 'j'))
3283 priority_jump = 1;
3286 if (ast_strlen_zero(args.interface)) {
3287 ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename]|interface[|options])\n");
3288 ast_module_user_remove(lu);
3289 return -1;
3292 if (set_member_paused(args.queuename, args.interface, 0)) {
3293 ast_log(LOG_WARNING, "Attempt to unpause interface %s, not found\n", args.interface);
3294 if (priority_jump || ast_opt_priority_jumping) {
3295 if (ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101)) {
3296 pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND");
3297 ast_module_user_remove(lu);
3298 return 0;
3301 ast_module_user_remove(lu);
3302 pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND");
3303 return -1;
3306 ast_module_user_remove(lu);
3307 pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "UNPAUSED");
3309 return 0;
3312 static int rqm_exec(struct ast_channel *chan, void *data)
3314 int res=-1;
3315 struct ast_module_user *lu;
3316 char *parse, *temppos = NULL;
3317 int priority_jump = 0;
3318 AST_DECLARE_APP_ARGS(args,
3319 AST_APP_ARG(queuename);
3320 AST_APP_ARG(interface);
3321 AST_APP_ARG(options);
3325 if (ast_strlen_zero(data)) {
3326 ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename[|interface[|options]])\n");
3327 return -1;
3330 parse = ast_strdupa(data);
3332 AST_STANDARD_APP_ARGS(args, parse);
3334 lu = ast_module_user_add(chan);
3336 if (ast_strlen_zero(args.interface)) {
3337 args.interface = ast_strdupa(chan->name);
3338 temppos = strrchr(args.interface, '-');
3339 if (temppos)
3340 *temppos = '\0';
3343 if (args.options) {
3344 if (strchr(args.options, 'j'))
3345 priority_jump = 1;
3348 switch (remove_from_queue(args.queuename, args.interface)) {
3349 case RES_OKAY:
3350 ast_queue_log(args.queuename, chan->uniqueid, args.interface, "REMOVEMEMBER", "%s", "");
3351 ast_log(LOG_NOTICE, "Removed interface '%s' from queue '%s'\n", args.interface, args.queuename);
3352 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "REMOVED");
3353 res = 0;
3354 break;
3355 case RES_EXISTS:
3356 ast_log(LOG_DEBUG, "Unable to remove interface '%s' from queue '%s': Not there\n", args.interface, args.queuename);
3357 if (priority_jump || ast_opt_priority_jumping)
3358 ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
3359 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTINQUEUE");
3360 res = 0;
3361 break;
3362 case RES_NOSUCHQUEUE:
3363 ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", args.queuename);
3364 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOSUCHQUEUE");
3365 res = 0;
3366 break;
3367 case RES_NOT_DYNAMIC:
3368 ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': '%s' is not a dynamic member\n", args.queuename, args.interface);
3369 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTDYNAMIC");
3370 res = 0;
3371 break;
3374 ast_module_user_remove(lu);
3376 return res;
3379 static int aqm_exec(struct ast_channel *chan, void *data)
3381 int res=-1;
3382 struct ast_module_user *lu;
3383 char *parse, *temppos = NULL;
3384 int priority_jump = 0;
3385 AST_DECLARE_APP_ARGS(args,
3386 AST_APP_ARG(queuename);
3387 AST_APP_ARG(interface);
3388 AST_APP_ARG(penalty);
3389 AST_APP_ARG(options);
3390 AST_APP_ARG(membername);
3392 int penalty = 0;
3394 if (ast_strlen_zero(data)) {
3395 ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename[|[interface]|[penalty][|options][|membername]])\n");
3396 return -1;
3399 parse = ast_strdupa(data);
3401 AST_STANDARD_APP_ARGS(args, parse);
3403 lu = ast_module_user_add(chan);
3405 if (ast_strlen_zero(args.interface)) {
3406 args.interface = ast_strdupa(chan->name);
3407 temppos = strrchr(args.interface, '-');
3408 if (temppos)
3409 *temppos = '\0';
3412 if (!ast_strlen_zero(args.penalty)) {
3413 if ((sscanf(args.penalty, "%d", &penalty) != 1) || penalty < 0) {
3414 ast_log(LOG_WARNING, "Penalty '%s' is invalid, must be an integer >= 0\n", args.penalty);
3415 penalty = 0;
3419 if (args.options) {
3420 if (strchr(args.options, 'j'))
3421 priority_jump = 1;
3424 switch (add_to_queue(args.queuename, args.interface, args.membername, penalty, 0, queue_persistent_members)) {
3425 case RES_OKAY:
3426 ast_queue_log(args.queuename, chan->uniqueid, args.interface, "ADDMEMBER", "%s", "");
3427 ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", args.interface, args.queuename);
3428 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "ADDED");
3429 res = 0;
3430 break;
3431 case RES_EXISTS:
3432 ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': Already there\n", args.interface, args.queuename);
3433 if (priority_jump || ast_opt_priority_jumping)
3434 ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
3435 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "MEMBERALREADY");
3436 res = 0;
3437 break;
3438 case RES_NOSUCHQUEUE:
3439 ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", args.queuename);
3440 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "NOSUCHQUEUE");
3441 res = 0;
3442 break;
3443 case RES_OUTOFMEMORY:
3444 ast_log(LOG_ERROR, "Out of memory adding member %s to queue %s\n", args.interface, args.queuename);
3445 break;
3448 ast_module_user_remove(lu);
3450 return res;
3453 static int ql_exec(struct ast_channel *chan, void *data)
3455 struct ast_module_user *u;
3456 char *parse;
3458 AST_DECLARE_APP_ARGS(args,
3459 AST_APP_ARG(queuename);
3460 AST_APP_ARG(uniqueid);
3461 AST_APP_ARG(membername);
3462 AST_APP_ARG(event);
3463 AST_APP_ARG(params);
3466 if (ast_strlen_zero(data)) {
3467 ast_log(LOG_WARNING, "QueueLog requires arguments (queuename|uniqueid|membername|event[|additionalinfo]\n");
3468 return -1;
3471 u = ast_module_user_add(chan);
3473 parse = ast_strdupa(data);
3475 AST_STANDARD_APP_ARGS(args, parse);
3477 if (ast_strlen_zero(args.queuename) || ast_strlen_zero(args.uniqueid)
3478 || ast_strlen_zero(args.membername) || ast_strlen_zero(args.event)) {
3479 ast_log(LOG_WARNING, "QueueLog requires arguments (queuename|uniqueid|membername|event[|additionalinfo])\n");
3480 ast_module_user_remove(u);
3481 return -1;
3484 ast_queue_log(args.queuename, args.uniqueid, args.membername, args.event,
3485 "%s", args.params ? args.params : "");
3487 ast_module_user_remove(u);
3489 return 0;
3492 static int queue_exec(struct ast_channel *chan, void *data)
3494 int res=-1;
3495 int ringing=0;
3496 struct ast_module_user *lu;
3497 const char *user_priority;
3498 const char *max_penalty_str;
3499 int prio;
3500 int max_penalty;
3501 enum queue_result reason = QUEUE_UNKNOWN;
3502 /* whether to exit Queue application after the timeout hits */
3503 int tries = 0;
3504 int noption = 0;
3505 char *parse;
3506 AST_DECLARE_APP_ARGS(args,
3507 AST_APP_ARG(queuename);
3508 AST_APP_ARG(options);
3509 AST_APP_ARG(url);
3510 AST_APP_ARG(announceoverride);
3511 AST_APP_ARG(queuetimeoutstr);
3512 AST_APP_ARG(agi);
3514 /* Our queue entry */
3515 struct queue_ent qe;
3517 if (ast_strlen_zero(data)) {
3518 ast_log(LOG_WARNING, "Queue requires an argument: queuename[|options[|URL[|announceoverride[|timeout[|agi]]]]]\n");
3519 return -1;
3522 parse = ast_strdupa(data);
3523 AST_STANDARD_APP_ARGS(args, parse);
3525 lu = ast_module_user_add(chan);
3527 /* Setup our queue entry */
3528 memset(&qe, 0, sizeof(qe));
3529 qe.start = time(NULL);
3531 /* set the expire time based on the supplied timeout; */
3532 if (args.queuetimeoutstr)
3533 qe.expire = qe.start + atoi(args.queuetimeoutstr);
3534 else
3535 qe.expire = 0;
3537 /* Get the priority from the variable ${QUEUE_PRIO} */
3538 user_priority = pbx_builtin_getvar_helper(chan, "QUEUE_PRIO");
3539 if (user_priority) {
3540 if (sscanf(user_priority, "%d", &prio) == 1) {
3541 if (option_debug)
3542 ast_log(LOG_DEBUG, "%s: Got priority %d from ${QUEUE_PRIO}.\n",
3543 chan->name, prio);
3544 } else {
3545 ast_log(LOG_WARNING, "${QUEUE_PRIO}: Invalid value (%s), channel %s.\n",
3546 user_priority, chan->name);
3547 prio = 0;
3549 } else {
3550 if (option_debug > 2)
3551 ast_log(LOG_DEBUG, "NO QUEUE_PRIO variable found. Using default.\n");
3552 prio = 0;
3555 /* Get the maximum penalty from the variable ${QUEUE_MAX_PENALTY} */
3556 if ((max_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MAX_PENALTY"))) {
3557 if (sscanf(max_penalty_str, "%d", &max_penalty) == 1) {
3558 if (option_debug)
3559 ast_log(LOG_DEBUG, "%s: Got max penalty %d from ${QUEUE_MAX_PENALTY}.\n",
3560 chan->name, max_penalty);
3561 } else {
3562 ast_log(LOG_WARNING, "${QUEUE_MAX_PENALTY}: Invalid value (%s), channel %s.\n",
3563 max_penalty_str, chan->name);
3564 max_penalty = 0;
3566 } else {
3567 max_penalty = 0;
3570 if (args.options && (strchr(args.options, 'r')))
3571 ringing = 1;
3573 if (option_debug)
3574 ast_log(LOG_DEBUG, "queue: %s, options: %s, url: %s, announce: %s, expires: %ld, priority: %d\n",
3575 args.queuename, args.options, args.url, args.announceoverride, (long)qe.expire, prio);
3577 qe.chan = chan;
3578 qe.prio = prio;
3579 qe.max_penalty = max_penalty;
3580 qe.last_pos_said = 0;
3581 qe.last_pos = 0;
3582 qe.last_periodic_announce_time = time(NULL);
3583 qe.last_periodic_announce_sound = 0;
3584 qe.valid_digits = 0;
3585 if (!join_queue(args.queuename, &qe, &reason)) {
3586 int makeannouncement = 0;
3588 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ENTERQUEUE", "%s|%s", S_OR(args.url, ""),
3589 S_OR(chan->cid.cid_num, ""));
3590 check_turns:
3591 if (ringing) {
3592 ast_indicate(chan, AST_CONTROL_RINGING);
3593 } else {
3594 ast_moh_start(chan, qe.moh, NULL);
3597 /* This is the wait loop for callers 2 through maxlen */
3598 res = wait_our_turn(&qe, ringing, &reason);
3599 if (res)
3600 goto stop;
3602 for (;;) {
3603 /* This is the wait loop for the head caller*/
3604 /* To exit, they may get their call answered; */
3605 /* they may dial a digit from the queue context; */
3606 /* or, they may timeout. */
3608 enum queue_member_status stat;
3610 /* Leave if we have exceeded our queuetimeout */
3611 if (qe.expire && (time(NULL) > qe.expire)) {
3612 record_abandoned(&qe);
3613 reason = QUEUE_TIMEOUT;
3614 res = 0;
3615 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
3616 break;
3619 if (makeannouncement) {
3620 /* Make a position announcement, if enabled */
3621 if (qe.parent->announcefrequency && !ringing)
3622 if ((res = say_position(&qe)))
3623 goto stop;
3626 makeannouncement = 1;
3628 /* Make a periodic announcement, if enabled */
3629 if (qe.parent->periodicannouncefrequency && !ringing)
3630 if ((res = say_periodic_announcement(&qe)))
3631 goto stop;
3633 /* Try calling all queue members for 'timeout' seconds */
3634 res = try_calling(&qe, args.options, args.announceoverride, args.url, &tries, &noption, args.agi);
3635 if (res)
3636 goto stop;
3638 stat = get_member_status(qe.parent, qe.max_penalty);
3640 /* exit after 'timeout' cycle if 'n' option enabled */
3641 if (noption && tries >= qe.parent->membercount) {
3642 if (option_verbose > 2)
3643 ast_verbose(VERBOSE_PREFIX_3 "Exiting on time-out cycle\n");
3644 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
3645 record_abandoned(&qe);
3646 reason = QUEUE_TIMEOUT;
3647 res = 0;
3648 break;
3651 /* leave the queue if no agents, if enabled */
3652 if (qe.parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) {
3653 record_abandoned(&qe);
3654 reason = QUEUE_LEAVEEMPTY;
3655 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
3656 res = 0;
3657 break;
3660 /* leave the queue if no reachable agents, if enabled */
3661 if ((qe.parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) {
3662 record_abandoned(&qe);
3663 reason = QUEUE_LEAVEUNAVAIL;
3664 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
3665 res = 0;
3666 break;
3669 /* Leave if we have exceeded our queuetimeout */
3670 if (qe.expire && (time(NULL) > qe.expire)) {
3671 record_abandoned(&qe);
3672 reason = QUEUE_TIMEOUT;
3673 res = 0;
3674 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
3675 break;
3678 /* If using dynamic realtime members, we should regenerate the member list for this queue */
3679 update_realtime_members(qe.parent);
3681 /* OK, we didn't get anybody; wait for 'retry' seconds; may get a digit to exit with */
3682 res = wait_a_bit(&qe);
3683 if (res)
3684 goto stop;
3687 /* Since this is a priority queue and
3688 * it is not sure that we are still at the head
3689 * of the queue, go and check for our turn again.
3691 if (!is_our_turn(&qe)) {
3692 if (option_debug)
3693 ast_log(LOG_DEBUG, "Darn priorities, going back in queue (%s)!\n",
3694 qe.chan->name);
3695 goto check_turns;
3699 stop:
3700 if (res) {
3701 if (res < 0) {
3702 if (!qe.handled) {
3703 record_abandoned(&qe);
3704 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ABANDON",
3705 "%d|%d|%ld", qe.pos, qe.opos,
3706 (long) time(NULL) - qe.start);
3708 res = -1;
3709 } else if (qe.valid_digits) {
3710 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHKEY",
3711 "%s|%d", qe.digits, qe.pos);
3715 /* Don't allow return code > 0 */
3716 if (res >= 0 && res != AST_PBX_KEEPALIVE) {
3717 res = 0;
3718 if (ringing) {
3719 ast_indicate(chan, -1);
3720 } else {
3721 ast_moh_stop(chan);
3723 ast_stopstream(chan);
3725 leave_queue(&qe);
3726 if (reason != QUEUE_UNKNOWN)
3727 set_queue_result(chan, reason);
3728 } else {
3729 ast_log(LOG_WARNING, "Unable to join queue '%s'\n", args.queuename);
3730 set_queue_result(chan, reason);
3731 res = 0;
3733 ast_module_user_remove(lu);
3735 return res;
3738 static int queue_function_qac(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
3740 int count = 0;
3741 struct call_queue *q;
3742 struct ast_module_user *lu;
3743 struct member *m;
3744 struct ao2_iterator mem_iter;
3746 buf[0] = '\0';
3748 if (ast_strlen_zero(data)) {
3749 ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
3750 return -1;
3753 lu = ast_module_user_add(chan);
3755 AST_LIST_LOCK(&queues);
3756 AST_LIST_TRAVERSE(&queues, q, list) {
3757 if (!strcasecmp(q->name, data)) {
3758 ast_mutex_lock(&q->lock);
3759 break;
3762 AST_LIST_UNLOCK(&queues);
3764 if (q) {
3765 mem_iter = ao2_iterator_init(q->members, 0);
3766 while ((m = ao2_iterator_next(&mem_iter))) {
3767 /* Count the agents who are logged in and presently answering calls */
3768 if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
3769 count++;
3771 ao2_ref(m, -1);
3773 ast_mutex_unlock(&q->lock);
3774 } else
3775 ast_log(LOG_WARNING, "queue %s was not found\n", data);
3777 snprintf(buf, len, "%d", count);
3778 ast_module_user_remove(lu);
3780 return 0;
3783 static int queue_function_queuewaitingcount(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
3785 int count = 0;
3786 struct call_queue *q;
3787 struct ast_module_user *lu;
3789 buf[0] = '\0';
3791 if (ast_strlen_zero(data)) {
3792 ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
3793 return -1;
3796 lu = ast_module_user_add(chan);
3798 AST_LIST_LOCK(&queues);
3799 AST_LIST_TRAVERSE(&queues, q, list) {
3800 if (!strcasecmp(q->name, data)) {
3801 ast_mutex_lock(&q->lock);
3802 break;
3805 AST_LIST_UNLOCK(&queues);
3807 if (q) {
3808 count = q->count;
3809 ast_mutex_unlock(&q->lock);
3810 } else
3811 ast_log(LOG_WARNING, "queue %s was not found\n", data);
3813 snprintf(buf, len, "%d", count);
3814 ast_module_user_remove(lu);
3815 return 0;
3818 static int queue_function_queuememberlist(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
3820 struct ast_module_user *u;
3821 struct call_queue *q;
3822 struct member *m;
3824 /* Ensure an otherwise empty list doesn't return garbage */
3825 buf[0] = '\0';
3827 if (ast_strlen_zero(data)) {
3828 ast_log(LOG_ERROR, "QUEUE_MEMBER_LIST requires an argument: queuename\n");
3829 return -1;
3832 u = ast_module_user_add(chan);
3834 AST_LIST_LOCK(&queues);
3835 AST_LIST_TRAVERSE(&queues, q, list) {
3836 if (!strcasecmp(q->name, data)) {
3837 ast_mutex_lock(&q->lock);
3838 break;
3841 AST_LIST_UNLOCK(&queues);
3843 if (q) {
3844 int buflen = 0, count = 0;
3845 struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
3847 while ((m = ao2_iterator_next(&mem_iter))) {
3848 /* strcat() is always faster than printf() */
3849 if (count++) {
3850 strncat(buf + buflen, ",", len - buflen - 1);
3851 buflen++;
3853 strncat(buf + buflen, m->membername, len - buflen - 1);
3854 buflen += strlen(m->membername);
3855 /* Safeguard against overflow (negative length) */
3856 if (buflen >= len - 2) {
3857 ao2_ref(m, -1);
3858 ast_log(LOG_WARNING, "Truncating list\n");
3859 break;
3861 ao2_ref(m, -1);
3863 ast_mutex_unlock(&q->lock);
3864 } else
3865 ast_log(LOG_WARNING, "queue %s was not found\n", data);
3867 /* We should already be terminated, but let's make sure. */
3868 buf[len - 1] = '\0';
3869 ast_module_user_remove(u);
3871 return 0;
3874 static struct ast_custom_function queueagentcount_function = {
3875 .name = "QUEUEAGENTCOUNT",
3876 .synopsis = "Count number of agents answering a queue",
3877 .syntax = "QUEUEAGENTCOUNT(<queuename>)",
3878 .desc =
3879 "Returns the number of members currently associated with the specified queue.\n"
3880 "This function is deprecated. You should use QUEUE_MEMBER_COUNT() instead.\n",
3881 .read = queue_function_qac,
3884 static struct ast_custom_function queuemembercount_function = {
3885 .name = "QUEUE_MEMBER_COUNT",
3886 .synopsis = "Count number of members answering a queue",
3887 .syntax = "QUEUE_MEMBER_COUNT(<queuename>)",
3888 .desc =
3889 "Returns the number of members currently associated with the specified queue.\n",
3890 .read = queue_function_qac,
3893 static struct ast_custom_function queuewaitingcount_function = {
3894 .name = "QUEUE_WAITING_COUNT",
3895 .synopsis = "Count number of calls currently waiting in a queue",
3896 .syntax = "QUEUE_WAITING_COUNT(<queuename>)",
3897 .desc =
3898 "Returns the number of callers currently waiting in the specified queue.\n",
3899 .read = queue_function_queuewaitingcount,
3902 static struct ast_custom_function queuememberlist_function = {
3903 .name = "QUEUE_MEMBER_LIST",
3904 .synopsis = "Returns a list of interfaces on a queue",
3905 .syntax = "QUEUE_MEMBER_LIST(<queuename>)",
3906 .desc =
3907 "Returns a comma-separated list of members associated with the specified queue.\n",
3908 .read = queue_function_queuememberlist,
3911 static int reload_queues(void)
3913 struct call_queue *q;
3914 struct ast_config *cfg;
3915 char *cat, *tmp;
3916 struct ast_variable *var;
3917 struct member *cur, *newm;
3918 struct ao2_iterator mem_iter;
3919 int new;
3920 const char *general_val = NULL;
3921 char parse[80];
3922 char *interface;
3923 char *membername = NULL;
3924 int penalty;
3925 AST_DECLARE_APP_ARGS(args,
3926 AST_APP_ARG(interface);
3927 AST_APP_ARG(penalty);
3928 AST_APP_ARG(membername);
3931 if (!(cfg = ast_config_load("queues.conf"))) {
3932 ast_log(LOG_NOTICE, "No call queueing config file (queues.conf), so no call queues\n");
3933 return 0;
3935 AST_LIST_LOCK(&queues);
3936 use_weight=0;
3937 /* Mark all non-realtime queues as dead for the moment */
3938 AST_LIST_TRAVERSE(&queues, q, list) {
3939 if(!q->realtime) {
3940 q->dead = 1;
3941 q->found = 0;
3945 /* Chug through config file */
3946 cat = NULL;
3947 while ((cat = ast_category_browse(cfg, cat)) ) {
3948 if (!strcasecmp(cat, "general")) {
3949 /* Initialize global settings */
3950 queue_persistent_members = 0;
3951 if ((general_val = ast_variable_retrieve(cfg, "general", "persistentmembers")))
3952 queue_persistent_members = ast_true(general_val);
3953 autofill_default = 0;
3954 if ((general_val = ast_variable_retrieve(cfg, "general", "autofill")))
3955 autofill_default = ast_true(general_val);
3956 montype_default = 0;
3957 if ((general_val = ast_variable_retrieve(cfg, "general", "monitor-type")))
3958 if (!strcasecmp(general_val, "mixmonitor"))
3959 montype_default = 1;
3960 } else { /* Define queue */
3961 /* Look for an existing one */
3962 AST_LIST_TRAVERSE(&queues, q, list) {
3963 if (!strcmp(q->name, cat))
3964 break;
3966 if (!q) {
3967 /* Make one then */
3968 if (!(q = alloc_queue(cat))) {
3969 /* TODO: Handle memory allocation failure */
3971 new = 1;
3972 } else
3973 new = 0;
3974 if (q) {
3975 if (!new)
3976 ast_mutex_lock(&q->lock);
3977 /* Check if a queue with this name already exists */
3978 if (q->found) {
3979 ast_log(LOG_WARNING, "Queue '%s' already defined! Skipping!\n", cat);
3980 if(!new)
3981 ast_mutex_unlock(&q->lock);
3982 continue;
3984 /* Re-initialize the queue, and clear statistics */
3985 init_queue(q);
3986 clear_queue(q);
3987 mem_iter = ao2_iterator_init(q->members, 0);
3988 while ((cur = ao2_iterator_next(&mem_iter))) {
3989 if (!cur->dynamic) {
3990 cur->delme = 1;
3992 ao2_ref(cur, -1);
3994 for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
3995 if (!strcasecmp(var->name, "member")) {
3996 struct member tmpmem;
3998 /* Add a new member */
3999 ast_copy_string(parse, var->value, sizeof(parse));
4001 AST_NONSTANDARD_APP_ARGS(args, parse, ',');
4003 interface = args.interface;
4004 if(!ast_strlen_zero(args.penalty)) {
4005 tmp = args.penalty;
4006 while (*tmp && *tmp < 33) tmp++;
4007 penalty = atoi(tmp);
4008 if (penalty < 0) {
4009 penalty = 0;
4011 } else
4012 penalty = 0;
4014 if (!ast_strlen_zero(args.membername)) {
4015 membername = args.membername;
4016 while (*membername && *membername < 33) membername++;
4019 /* Find the old position in the list */
4020 ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
4021 cur = ao2_find(q->members, &tmpmem, OBJ_POINTER | OBJ_UNLINK);
4023 newm = create_queue_member(interface, membername, penalty, cur ? cur->paused : 0);
4024 ao2_link(q->members, newm);
4026 if (cur)
4027 ao2_ref(cur, -1);
4028 else {
4029 /* Add them to the master int list if necessary */
4030 add_to_interfaces(interface);
4031 q->membercount++;
4033 } else {
4034 queue_set_param(q, var->name, var->value, var->lineno, 1);
4038 /* Free remaining members marked as delme */
4039 mem_iter = ao2_iterator_init(q->members, 0);
4040 while ((cur = ao2_iterator_next(&mem_iter))) {
4041 if (! cur->delme) {
4042 ao2_ref(cur, -1);
4043 continue;
4046 remove_from_interfaces(cur->interface);
4047 q->membercount--;
4048 ao2_unlink(q->members, cur);
4049 ao2_ref(cur, -1);
4052 if (q->strategy == QUEUE_STRATEGY_ROUNDROBIN)
4053 rr_dep_warning();
4055 if (new) {
4056 AST_LIST_INSERT_HEAD(&queues, q, list);
4057 } else
4058 ast_mutex_unlock(&q->lock);
4062 ast_config_destroy(cfg);
4063 AST_LIST_TRAVERSE_SAFE_BEGIN(&queues, q, list) {
4064 if (q->dead) {
4065 AST_LIST_REMOVE_CURRENT(&queues, list);
4066 if (!q->count)
4067 destroy_queue(q);
4068 else
4069 ast_log(LOG_DEBUG, "XXX Leaking a little memory :( XXX\n");
4070 } else {
4071 ast_mutex_lock(&q->lock);
4072 mem_iter = ao2_iterator_init(q->members, 0);
4073 while ((cur = ao2_iterator_next(&mem_iter))) {
4074 if (cur->dynamic)
4075 q->membercount++;
4076 cur->status = ast_device_state(cur->interface);
4077 ao2_ref(cur, -1);
4079 ast_mutex_unlock(&q->lock);
4082 AST_LIST_TRAVERSE_SAFE_END;
4083 AST_LIST_UNLOCK(&queues);
4084 return 1;
4087 static int __queues_show(struct mansession *s, int manager, int fd, int argc, char **argv)
4089 struct call_queue *q;
4090 struct queue_ent *qe;
4091 struct member *mem;
4092 int pos, queue_show;
4093 time_t now;
4094 char max_buf[80];
4095 char *max;
4096 size_t max_left;
4097 float sl = 0;
4098 char *term = manager ? "\r\n" : "\n";
4099 struct ao2_iterator mem_iter;
4101 time(&now);
4102 if (argc == 2)
4103 queue_show = 0;
4104 else if (argc == 3)
4105 queue_show = 1;
4106 else
4107 return RESULT_SHOWUSAGE;
4109 /* We only want to load realtime queues when a specific queue is asked for. */
4110 if (queue_show)
4111 load_realtime_queue(argv[2]);
4113 AST_LIST_LOCK(&queues);
4114 if (AST_LIST_EMPTY(&queues)) {
4115 AST_LIST_UNLOCK(&queues);
4116 if (queue_show) {
4117 if (s)
4118 astman_append(s, "No such queue: %s.%s",argv[2], term);
4119 else
4120 ast_cli(fd, "No such queue: %s.%s",argv[2], term);
4121 } else {
4122 if (s)
4123 astman_append(s, "No queues.%s", term);
4124 else
4125 ast_cli(fd, "No queues.%s", term);
4127 return RESULT_SUCCESS;
4129 AST_LIST_TRAVERSE(&queues, q, list) {
4130 ast_mutex_lock(&q->lock);
4131 if (queue_show) {
4132 if (strcasecmp(q->name, argv[2]) != 0) {
4133 ast_mutex_unlock(&q->lock);
4134 if (!AST_LIST_NEXT(q, list)) {
4135 ast_cli(fd, "No such queue: %s.%s",argv[2], term);
4136 break;
4138 continue;
4141 max_buf[0] = '\0';
4142 max = max_buf;
4143 max_left = sizeof(max_buf);
4144 if (q->maxlen)
4145 ast_build_string(&max, &max_left, "%d", q->maxlen);
4146 else
4147 ast_build_string(&max, &max_left, "unlimited");
4148 sl = 0;
4149 if (q->callscompleted > 0)
4150 sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
4151 if (s)
4152 astman_append(s, "%-12.12s has %d calls (max %s) in '%s' strategy (%ds holdtime), W:%d, C:%d, A:%d, SL:%2.1f%% within %ds%s",
4153 q->name, q->count, max_buf, int2strat(q->strategy), q->holdtime, q->weight,
4154 q->callscompleted, q->callsabandoned,sl,q->servicelevel, term);
4155 else
4156 ast_cli(fd, "%-12.12s has %d calls (max %s) in '%s' strategy (%ds holdtime), W:%d, C:%d, A:%d, SL:%2.1f%% within %ds%s",
4157 q->name, q->count, max_buf, int2strat(q->strategy), q->holdtime, q->weight, q->callscompleted, q->callsabandoned,sl,q->servicelevel, term);
4158 if (ao2_container_count(q->members)) {
4159 if (s)
4160 astman_append(s, " Members: %s", term);
4161 else
4162 ast_cli(fd, " Members: %s", term);
4163 mem_iter = ao2_iterator_init(q->members, 0);
4164 while ((mem = ao2_iterator_next(&mem_iter))) {
4165 max_buf[0] = '\0';
4166 max = max_buf;
4167 max_left = sizeof(max_buf);
4168 if (mem->penalty)
4169 ast_build_string(&max, &max_left, " with penalty %d", mem->penalty);
4170 if (mem->dynamic)
4171 ast_build_string(&max, &max_left, " (dynamic)");
4172 if (mem->realtime)
4173 ast_build_string(&max, &max_left, " (realtime)");
4174 if (mem->paused)
4175 ast_build_string(&max, &max_left, " (paused)");
4176 ast_build_string(&max, &max_left, " (%s)", devstate2str(mem->status));
4177 if (mem->calls) {
4178 ast_build_string(&max, &max_left, " has taken %d calls (last was %ld secs ago)",
4179 mem->calls, (long) (time(NULL) - mem->lastcall));
4180 } else
4181 ast_build_string(&max, &max_left, " has taken no calls yet");
4182 if (s)
4183 astman_append(s, " %s%s%s", mem->membername, max_buf, term);
4184 else
4185 ast_cli(fd, " %s%s%s", mem->membername, max_buf, term);
4186 ao2_ref(mem, -1);
4188 } else if (s)
4189 astman_append(s, " No Members%s", term);
4190 else
4191 ast_cli(fd, " No Members%s", term);
4192 if (q->head) {
4193 pos = 1;
4194 if (s)
4195 astman_append(s, " Callers: %s", term);
4196 else
4197 ast_cli(fd, " Callers: %s", term);
4198 for (qe = q->head; qe; qe = qe->next) {
4199 if (s)
4200 astman_append(s, " %d. %s (wait: %ld:%2.2ld, prio: %d)%s",
4201 pos++, qe->chan->name, (long) (now - qe->start) / 60,
4202 (long) (now - qe->start) % 60, qe->prio, term);
4203 else
4204 ast_cli(fd, " %d. %s (wait: %ld:%2.2ld, prio: %d)%s", pos++,
4205 qe->chan->name, (long) (now - qe->start) / 60,
4206 (long) (now - qe->start) % 60, qe->prio, term);
4208 } else if (s)
4209 astman_append(s, " No Callers%s", term);
4210 else
4211 ast_cli(fd, " No Callers%s", term);
4212 if (s)
4213 astman_append(s, "%s", term);
4214 else
4215 ast_cli(fd, "%s", term);
4216 ast_mutex_unlock(&q->lock);
4217 if (queue_show)
4218 break;
4220 AST_LIST_UNLOCK(&queues);
4221 return RESULT_SUCCESS;
4224 static int queue_show(int fd, int argc, char **argv)
4226 return __queues_show(NULL, 0, fd, argc, argv);
4229 static char *complete_queue(const char *line, const char *word, int pos, int state)
4231 struct call_queue *q;
4232 char *ret = NULL;
4233 int which = 0;
4234 int wordlen = strlen(word);
4236 AST_LIST_LOCK(&queues);
4237 AST_LIST_TRAVERSE(&queues, q, list) {
4238 if (!strncasecmp(word, q->name, wordlen) && ++which > state) {
4239 ret = ast_strdup(q->name);
4240 break;
4243 AST_LIST_UNLOCK(&queues);
4245 return ret;
4248 static char *complete_queue_show(const char *line, const char *word, int pos, int state)
4250 if (pos == 2)
4251 return complete_queue(line, word, pos, state);
4252 return NULL;
4255 /*!\brief callback to display queues status in manager
4256 \addtogroup Group_AMI
4258 static int manager_queues_show(struct mansession *s, const struct message *m)
4260 char *a[] = { "queue", "show" };
4262 __queues_show(s, 1, -1, 2, a);
4263 astman_append(s, "\r\n\r\n"); /* Properly terminate Manager output */
4265 return RESULT_SUCCESS;
4268 /* Dump queue status */
4269 static int manager_queues_status(struct mansession *s, const struct message *m)
4271 time_t now;
4272 int pos;
4273 const char *id = astman_get_header(m,"ActionID");
4274 const char *queuefilter = astman_get_header(m,"Queue");
4275 const char *memberfilter = astman_get_header(m,"Member");
4276 char idText[256] = "";
4277 struct call_queue *q;
4278 struct queue_ent *qe;
4279 float sl = 0;
4280 struct member *mem;
4281 struct ao2_iterator mem_iter;
4283 astman_send_ack(s, m, "Queue status will follow");
4284 time(&now);
4285 AST_LIST_LOCK(&queues);
4286 if (!ast_strlen_zero(id))
4287 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
4289 AST_LIST_TRAVERSE(&queues, q, list) {
4290 ast_mutex_lock(&q->lock);
4292 /* List queue properties */
4293 if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) {
4294 sl = ((q->callscompleted > 0) ? 100 * ((float)q->callscompletedinsl / (float)q->callscompleted) : 0);
4295 astman_append(s, "Event: QueueParams\r\n"
4296 "Queue: %s\r\n"
4297 "Max: %d\r\n"
4298 "Calls: %d\r\n"
4299 "Holdtime: %d\r\n"
4300 "Completed: %d\r\n"
4301 "Abandoned: %d\r\n"
4302 "ServiceLevel: %d\r\n"
4303 "ServicelevelPerf: %2.1f\r\n"
4304 "Weight: %d\r\n"
4305 "%s"
4306 "\r\n",
4307 q->name, q->maxlen, q->count, q->holdtime, q->callscompleted,
4308 q->callsabandoned, q->servicelevel, sl, q->weight, idText);
4309 /* List Queue Members */
4310 mem_iter = ao2_iterator_init(q->members, 0);
4311 while ((mem = ao2_iterator_next(&mem_iter))) {
4312 if (ast_strlen_zero(memberfilter) || !strcmp(mem->interface, memberfilter)) {
4313 astman_append(s, "Event: QueueMember\r\n"
4314 "Queue: %s\r\n"
4315 "Name: %s\r\n"
4316 "Location: %s\r\n"
4317 "Membership: %s\r\n"
4318 "Penalty: %d\r\n"
4319 "CallsTaken: %d\r\n"
4320 "LastCall: %d\r\n"
4321 "Status: %d\r\n"
4322 "Paused: %d\r\n"
4323 "%s"
4324 "\r\n",
4325 q->name, mem->membername, mem->interface, mem->dynamic ? "dynamic" : "static",
4326 mem->penalty, mem->calls, (int)mem->lastcall, mem->status, mem->paused, idText);
4328 ao2_ref(mem, -1);
4330 /* List Queue Entries */
4331 pos = 1;
4332 for (qe = q->head; qe; qe = qe->next) {
4333 astman_append(s, "Event: QueueEntry\r\n"
4334 "Queue: %s\r\n"
4335 "Position: %d\r\n"
4336 "Channel: %s\r\n"
4337 "CallerID: %s\r\n"
4338 "CallerIDName: %s\r\n"
4339 "Wait: %ld\r\n"
4340 "%s"
4341 "\r\n",
4342 q->name, pos++, qe->chan->name,
4343 S_OR(qe->chan->cid.cid_num, "unknown"),
4344 S_OR(qe->chan->cid.cid_name, "unknown"),
4345 (long) (now - qe->start), idText);
4348 ast_mutex_unlock(&q->lock);
4351 astman_append(s,
4352 "Event: QueueStatusComplete\r\n"
4353 "%s"
4354 "\r\n",idText);
4356 AST_LIST_UNLOCK(&queues);
4359 return RESULT_SUCCESS;
4362 static int manager_add_queue_member(struct mansession *s, const struct message *m)
4364 const char *queuename, *interface, *penalty_s, *paused_s, *membername;
4365 int paused, penalty = 0;
4367 queuename = astman_get_header(m, "Queue");
4368 interface = astman_get_header(m, "Interface");
4369 penalty_s = astman_get_header(m, "Penalty");
4370 paused_s = astman_get_header(m, "Paused");
4371 membername = astman_get_header(m, "MemberName");
4373 if (ast_strlen_zero(queuename)) {
4374 astman_send_error(s, m, "'Queue' not specified.");
4375 return 0;
4378 if (ast_strlen_zero(interface)) {
4379 astman_send_error(s, m, "'Interface' not specified.");
4380 return 0;
4383 if (ast_strlen_zero(penalty_s))
4384 penalty = 0;
4385 else if (sscanf(penalty_s, "%d", &penalty) != 1)
4386 penalty = 0;
4388 if (ast_strlen_zero(paused_s))
4389 paused = 0;
4390 else
4391 paused = abs(ast_true(paused_s));
4393 switch (add_to_queue(queuename, interface, membername, penalty, paused, queue_persistent_members)) {
4394 case RES_OKAY:
4395 ast_queue_log(queuename, "MANAGER", interface, "ADDMEMBER", "%s", "");
4396 astman_send_ack(s, m, "Added interface to queue");
4397 break;
4398 case RES_EXISTS:
4399 astman_send_error(s, m, "Unable to add interface: Already there");
4400 break;
4401 case RES_NOSUCHQUEUE:
4402 astman_send_error(s, m, "Unable to add interface to queue: No such queue");
4403 break;
4404 case RES_OUTOFMEMORY:
4405 astman_send_error(s, m, "Out of memory");
4406 break;
4409 return 0;
4412 static int manager_remove_queue_member(struct mansession *s, const struct message *m)
4414 const char *queuename, *interface;
4416 queuename = astman_get_header(m, "Queue");
4417 interface = astman_get_header(m, "Interface");
4419 if (ast_strlen_zero(queuename) || ast_strlen_zero(interface)) {
4420 astman_send_error(s, m, "Need 'Queue' and 'Interface' parameters.");
4421 return 0;
4424 switch (remove_from_queue(queuename, interface)) {
4425 case RES_OKAY:
4426 ast_queue_log(queuename, "MANAGER", interface, "REMOVEMEMBER", "%s", "");
4427 astman_send_ack(s, m, "Removed interface from queue");
4428 break;
4429 case RES_EXISTS:
4430 astman_send_error(s, m, "Unable to remove interface: Not there");
4431 break;
4432 case RES_NOSUCHQUEUE:
4433 astman_send_error(s, m, "Unable to remove interface from queue: No such queue");
4434 break;
4435 case RES_OUTOFMEMORY:
4436 astman_send_error(s, m, "Out of memory");
4437 break;
4438 case RES_NOT_DYNAMIC:
4439 astman_send_error(s, m, "Member not dynamic");
4440 break;
4443 return 0;
4446 static int manager_pause_queue_member(struct mansession *s, const struct message *m)
4448 const char *queuename, *interface, *paused_s;
4449 int paused;
4451 interface = astman_get_header(m, "Interface");
4452 paused_s = astman_get_header(m, "Paused");
4453 queuename = astman_get_header(m, "Queue"); /* Optional - if not supplied, pause the given Interface in all queues */
4455 if (ast_strlen_zero(interface) || ast_strlen_zero(paused_s)) {
4456 astman_send_error(s, m, "Need 'Interface' and 'Paused' parameters.");
4457 return 0;
4460 paused = abs(ast_true(paused_s));
4462 if (set_member_paused(queuename, interface, paused))
4463 astman_send_error(s, m, "Interface not found");
4464 else
4465 astman_send_ack(s, m, paused ? "Interface paused successfully" : "Interface unpaused successfully");
4466 return 0;
4469 static int handle_queue_add_member(int fd, int argc, char *argv[])
4471 char *queuename, *interface, *membername = NULL;
4472 int penalty;
4474 if ((argc != 6) && (argc != 8) && (argc != 10)) {
4475 return RESULT_SHOWUSAGE;
4476 } else if (strcmp(argv[4], "to")) {
4477 return RESULT_SHOWUSAGE;
4478 } else if ((argc == 8) && strcmp(argv[6], "penalty")) {
4479 return RESULT_SHOWUSAGE;
4480 } else if ((argc == 10) && strcmp(argv[8], "as")) {
4481 return RESULT_SHOWUSAGE;
4484 queuename = argv[5];
4485 interface = argv[3];
4486 if (argc >= 8) {
4487 if (sscanf(argv[7], "%d", &penalty) == 1) {
4488 if (penalty < 0) {
4489 ast_cli(fd, "Penalty must be >= 0\n");
4490 penalty = 0;
4492 } else {
4493 ast_cli(fd, "Penalty must be an integer >= 0\n");
4494 penalty = 0;
4496 } else {
4497 penalty = 0;
4500 if (argc >= 10) {
4501 membername = argv[9];
4504 switch (add_to_queue(queuename, interface, membername, penalty, 0, queue_persistent_members)) {
4505 case RES_OKAY:
4506 ast_queue_log(queuename, "CLI", interface, "ADDMEMBER", "%s", "");
4507 ast_cli(fd, "Added interface '%s' to queue '%s'\n", interface, queuename);
4508 return RESULT_SUCCESS;
4509 case RES_EXISTS:
4510 ast_cli(fd, "Unable to add interface '%s' to queue '%s': Already there\n", interface, queuename);
4511 return RESULT_FAILURE;
4512 case RES_NOSUCHQUEUE:
4513 ast_cli(fd, "Unable to add interface to queue '%s': No such queue\n", queuename);
4514 return RESULT_FAILURE;
4515 case RES_OUTOFMEMORY:
4516 ast_cli(fd, "Out of memory\n");
4517 return RESULT_FAILURE;
4518 default:
4519 return RESULT_FAILURE;
4523 static char *complete_queue_add_member(const char *line, const char *word, int pos, int state)
4525 /* 0 - queue; 1 - add; 2 - member; 3 - <interface>; 4 - to; 5 - <queue>; 6 - penalty; 7 - <penalty>; 8 - as; 9 - <membername> */
4526 switch (pos) {
4527 case 3: /* Don't attempt to complete name of interface (infinite possibilities) */
4528 return NULL;
4529 case 4: /* only one possible match, "to" */
4530 return state == 0 ? ast_strdup("to") : NULL;
4531 case 5: /* <queue> */
4532 return complete_queue(line, word, pos, state);
4533 case 6: /* only one possible match, "penalty" */
4534 return state == 0 ? ast_strdup("penalty") : NULL;
4535 case 7:
4536 if (state < 100) { /* 0-99 */
4537 char *num;
4538 if ((num = ast_malloc(3))) {
4539 sprintf(num, "%d", state);
4541 return num;
4542 } else {
4543 return NULL;
4545 case 8: /* only one possible match, "as" */
4546 return state == 0 ? ast_strdup("as") : NULL;
4547 case 9: /* Don't attempt to complete name of member (infinite possibilities) */
4548 return NULL;
4549 default:
4550 return NULL;
4554 static int handle_queue_remove_member(int fd, int argc, char *argv[])
4556 char *queuename, *interface;
4558 if (argc != 6) {
4559 return RESULT_SHOWUSAGE;
4560 } else if (strcmp(argv[4], "from")) {
4561 return RESULT_SHOWUSAGE;
4564 queuename = argv[5];
4565 interface = argv[3];
4567 switch (remove_from_queue(queuename, interface)) {
4568 case RES_OKAY:
4569 ast_queue_log(queuename, "CLI", interface, "REMOVEMEMBER", "%s", "");
4570 ast_cli(fd, "Removed interface '%s' from queue '%s'\n", interface, queuename);
4571 return RESULT_SUCCESS;
4572 case RES_EXISTS:
4573 ast_cli(fd, "Unable to remove interface '%s' from queue '%s': Not there\n", interface, queuename);
4574 return RESULT_FAILURE;
4575 case RES_NOSUCHQUEUE:
4576 ast_cli(fd, "Unable to remove interface from queue '%s': No such queue\n", queuename);
4577 return RESULT_FAILURE;
4578 case RES_OUTOFMEMORY:
4579 ast_cli(fd, "Out of memory\n");
4580 return RESULT_FAILURE;
4581 case RES_NOT_DYNAMIC:
4582 ast_cli(fd, "Member not dynamic\n");
4583 return RESULT_FAILURE;
4584 default:
4585 return RESULT_FAILURE;
4589 static char *complete_queue_remove_member(const char *line, const char *word, int pos, int state)
4591 int which = 0;
4592 struct call_queue *q;
4593 struct member *m;
4594 struct ao2_iterator mem_iter;
4596 /* 0 - queue; 1 - remove; 2 - member; 3 - <member>; 4 - from; 5 - <queue> */
4597 if (pos > 5 || pos < 3)
4598 return NULL;
4599 if (pos == 4) /* only one possible match, 'from' */
4600 return state == 0 ? ast_strdup("from") : NULL;
4602 if (pos == 5) /* No need to duplicate code */
4603 return complete_queue(line, word, pos, state);
4605 /* here is the case for 3, <member> */
4606 if (!AST_LIST_EMPTY(&queues)) { /* XXX unnecessary ? the traverse does that for us */
4607 AST_LIST_TRAVERSE(&queues, q, list) {
4608 ast_mutex_lock(&q->lock);
4609 mem_iter = ao2_iterator_init(q->members, 0);
4610 while ((m = ao2_iterator_next(&mem_iter))) {
4611 if (++which > state) {
4612 char *tmp;
4613 ast_mutex_unlock(&q->lock);
4614 tmp = m->membername;
4615 ao2_ref(m, -1);
4616 return ast_strdup(tmp);
4618 ao2_ref(m, -1);
4620 ast_mutex_unlock(&q->lock);
4624 return NULL;
4627 static char queue_show_usage[] =
4628 "Usage: queue show\n"
4629 " Provides summary information on a specified queue.\n";
4631 static char qam_cmd_usage[] =
4632 "Usage: queue add member <channel> to <queue> [penalty <penalty>]\n";
4634 static char qrm_cmd_usage[] =
4635 "Usage: queue remove member <channel> from <queue>\n";
4637 static struct ast_cli_entry cli_show_queue_deprecated = {
4638 { "show", "queue", NULL },
4639 queue_show, NULL,
4640 NULL, complete_queue_show };
4642 static struct ast_cli_entry cli_add_queue_member_deprecated = {
4643 { "add", "queue", "member", NULL },
4644 handle_queue_add_member, NULL,
4645 NULL, complete_queue_add_member };
4647 static struct ast_cli_entry cli_remove_queue_member_deprecated = {
4648 { "remove", "queue", "member", NULL },
4649 handle_queue_remove_member, NULL,
4650 NULL, complete_queue_remove_member };
4652 static struct ast_cli_entry cli_queue[] = {
4653 /* Deprecated */
4654 { { "show", "queues", NULL },
4655 queue_show, NULL,
4656 NULL, NULL },
4658 { { "queue", "show", NULL },
4659 queue_show, "Show status of a specified queue",
4660 queue_show_usage, complete_queue_show, &cli_show_queue_deprecated },
4662 { { "queue", "add", "member", NULL },
4663 handle_queue_add_member, "Add a channel to a specified queue",
4664 qam_cmd_usage, complete_queue_add_member, &cli_add_queue_member_deprecated },
4666 { { "queue", "remove", "member", NULL },
4667 handle_queue_remove_member, "Removes a channel from a specified queue",
4668 qrm_cmd_usage, complete_queue_remove_member, &cli_remove_queue_member_deprecated },
4671 static int unload_module(void)
4673 int res;
4675 if (device_state.thread != AST_PTHREADT_NULL) {
4676 device_state.stop = 1;
4677 ast_mutex_lock(&device_state.lock);
4678 ast_cond_signal(&device_state.cond);
4679 ast_mutex_unlock(&device_state.lock);
4680 pthread_join(device_state.thread, NULL);
4683 ast_cli_unregister_multiple(cli_queue, sizeof(cli_queue) / sizeof(struct ast_cli_entry));
4684 res = ast_manager_unregister("QueueStatus");
4685 res |= ast_manager_unregister("Queues");
4686 res |= ast_manager_unregister("QueueStatus");
4687 res |= ast_manager_unregister("QueueAdd");
4688 res |= ast_manager_unregister("QueueRemove");
4689 res |= ast_manager_unregister("QueuePause");
4690 res |= ast_unregister_application(app_aqm);
4691 res |= ast_unregister_application(app_rqm);
4692 res |= ast_unregister_application(app_pqm);
4693 res |= ast_unregister_application(app_upqm);
4694 res |= ast_unregister_application(app_ql);
4695 res |= ast_unregister_application(app);
4696 res |= ast_custom_function_unregister(&queueagentcount_function);
4697 res |= ast_custom_function_unregister(&queuemembercount_function);
4698 res |= ast_custom_function_unregister(&queuememberlist_function);
4699 res |= ast_custom_function_unregister(&queuewaitingcount_function);
4700 ast_devstate_del(statechange_queue, NULL);
4702 ast_module_user_hangup_all();
4704 clear_and_free_interfaces();
4706 return res;
4709 static int load_module(void)
4711 int res;
4713 if (!reload_queues())
4714 return AST_MODULE_LOAD_DECLINE;
4716 if (queue_persistent_members)
4717 reload_queue_members();
4719 ast_mutex_init(&device_state.lock);
4720 ast_cond_init(&device_state.cond, NULL);
4721 ast_pthread_create(&device_state.thread, NULL, device_state_thread, NULL);
4723 ast_cli_register_multiple(cli_queue, sizeof(cli_queue) / sizeof(struct ast_cli_entry));
4724 res = ast_register_application(app, queue_exec, synopsis, descrip);
4725 res |= ast_register_application(app_aqm, aqm_exec, app_aqm_synopsis, app_aqm_descrip);
4726 res |= ast_register_application(app_rqm, rqm_exec, app_rqm_synopsis, app_rqm_descrip);
4727 res |= ast_register_application(app_pqm, pqm_exec, app_pqm_synopsis, app_pqm_descrip);
4728 res |= ast_register_application(app_upqm, upqm_exec, app_upqm_synopsis, app_upqm_descrip);
4729 res |= ast_register_application(app_ql, ql_exec, app_ql_synopsis, app_ql_descrip);
4730 res |= ast_manager_register("Queues", 0, manager_queues_show, "Queues");
4731 res |= ast_manager_register("QueueStatus", 0, manager_queues_status, "Queue Status");
4732 res |= ast_manager_register("QueueAdd", EVENT_FLAG_AGENT, manager_add_queue_member, "Add interface to queue.");
4733 res |= ast_manager_register("QueueRemove", EVENT_FLAG_AGENT, manager_remove_queue_member, "Remove interface from queue.");
4734 res |= ast_manager_register("QueuePause", EVENT_FLAG_AGENT, manager_pause_queue_member, "Makes a queue member temporarily unavailable");
4735 res |= ast_custom_function_register(&queueagentcount_function);
4736 res |= ast_custom_function_register(&queuemembercount_function);
4737 res |= ast_custom_function_register(&queuememberlist_function);
4738 res |= ast_custom_function_register(&queuewaitingcount_function);
4739 res |= ast_devstate_add(statechange_queue, NULL);
4741 return res;
4744 static int reload(void)
4746 reload_queues();
4747 return 0;
4750 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "True Call Queueing",
4751 .load = load_module,
4752 .unload = unload_module,
4753 .reload = reload,