00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063 #include "asterisk.h"
00064
00065 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 95890 $")
00066
00067 #include <stdlib.h>
00068 #include <errno.h>
00069 #include <unistd.h>
00070 #include <string.h>
00071 #include <stdlib.h>
00072 #include <stdio.h>
00073 #include <sys/time.h>
00074 #include <sys/signal.h>
00075 #include <netinet/in.h>
00076
00077 #include "asterisk/lock.h"
00078 #include "asterisk/file.h"
00079 #include "asterisk/logger.h"
00080 #include "asterisk/channel.h"
00081 #include "asterisk/pbx.h"
00082 #include "asterisk/options.h"
00083 #include "asterisk/app.h"
00084 #include "asterisk/linkedlists.h"
00085 #include "asterisk/module.h"
00086 #include "asterisk/translate.h"
00087 #include "asterisk/say.h"
00088 #include "asterisk/features.h"
00089 #include "asterisk/musiconhold.h"
00090 #include "asterisk/cli.h"
00091 #include "asterisk/manager.h"
00092 #include "asterisk/config.h"
00093 #include "asterisk/monitor.h"
00094 #include "asterisk/utils.h"
00095 #include "asterisk/causes.h"
00096 #include "asterisk/astdb.h"
00097 #include "asterisk/devicestate.h"
00098 #include "asterisk/stringfields.h"
00099 #include "asterisk/astobj2.h"
00100 #include "asterisk/global_datastores.h"
00101
00102 enum {
00103 QUEUE_STRATEGY_RINGALL = 0,
00104 QUEUE_STRATEGY_ROUNDROBIN,
00105 QUEUE_STRATEGY_LEASTRECENT,
00106 QUEUE_STRATEGY_FEWESTCALLS,
00107 QUEUE_STRATEGY_RANDOM,
00108 QUEUE_STRATEGY_RRMEMORY
00109 };
00110
00111 static struct strategy {
00112 int strategy;
00113 char *name;
00114 } strategies[] = {
00115 { QUEUE_STRATEGY_RINGALL, "ringall" },
00116 { QUEUE_STRATEGY_ROUNDROBIN, "roundrobin" },
00117 { QUEUE_STRATEGY_LEASTRECENT, "leastrecent" },
00118 { QUEUE_STRATEGY_FEWESTCALLS, "fewestcalls" },
00119 { QUEUE_STRATEGY_RANDOM, "random" },
00120 { QUEUE_STRATEGY_RRMEMORY, "rrmemory" },
00121 };
00122
00123 #define DEFAULT_RETRY 5
00124 #define DEFAULT_TIMEOUT 15
00125 #define RECHECK 1
00126 #define MAX_PERIODIC_ANNOUNCEMENTS 10
00127
00128 #define RES_OKAY 0
00129 #define RES_EXISTS (-1)
00130 #define RES_OUTOFMEMORY (-2)
00131 #define RES_NOSUCHQUEUE (-3)
00132 #define RES_NOT_DYNAMIC (-4)
00133
00134 static char *app = "Queue";
00135
00136 static char *synopsis = "Queue a call for a call queue";
00137
00138 static char *descrip =
00139 " Queue(queuename[|options[|URL][|announceoverride][|timeout][|AGI]):\n"
00140 "Queues an incoming call in a particular call queue as defined in queues.conf.\n"
00141 "This application will return to the dialplan if the queue does not exist, or\n"
00142 "any of the join options cause the caller to not enter the queue.\n"
00143 "The option string may contain zero or more of the following characters:\n"
00144 " 'd' -- data-quality (modem) call (minimum delay).\n"
00145 " 'h' -- allow callee to hang up by hitting *.\n"
00146 " 'H' -- allow caller to hang up by hitting *.\n"
00147 " 'n' -- no retries on the timeout; will exit this application and \n"
00148 " go to the next step.\n"
00149 " 'i' -- ignore call forward requests from queue members and do nothing\n"
00150 " when they are requested.\n"
00151 " 'r' -- ring instead of playing MOH\n"
00152 " 't' -- allow the called user transfer the calling user\n"
00153 " 'T' -- to allow the calling user to transfer the call.\n"
00154 " 'w' -- allow the called user to write the conversation to disk via Monitor\n"
00155 " 'W' -- allow the calling user to write the conversation to disk via Monitor\n"
00156 " In addition to transferring the call, a call may be parked and then picked\n"
00157 "up by another user.\n"
00158 " The optional URL will be sent to the called party if the channel supports\n"
00159 "it.\n"
00160 " The optional AGI parameter will setup an AGI script to be executed on the \n"
00161 "calling party's channel once they are connected to a queue member.\n"
00162 " The timeout will cause the queue to fail out after a specified number of\n"
00163 "seconds, checked between each queues.conf 'timeout' and 'retry' cycle.\n"
00164 " This application sets the following channel variable upon completion:\n"
00165 " QUEUESTATUS The status of the call as a text string, one of\n"
00166 " TIMEOUT | FULL | JOINEMPTY | LEAVEEMPTY | JOINUNAVAIL | LEAVEUNAVAIL\n";
00167
00168 static char *app_aqm = "AddQueueMember" ;
00169 static char *app_aqm_synopsis = "Dynamically adds queue members" ;
00170 static char *app_aqm_descrip =
00171 " AddQueueMember(queuename[|interface[|penalty[|options[|membername]]]]):\n"
00172 "Dynamically adds interface to an existing queue.\n"
00173 "If the interface is already in the queue and there exists an n+101 priority\n"
00174 "then it will then jump to this priority. Otherwise it will return an error\n"
00175 "The option string may contain zero or more of the following characters:\n"
00176 " 'j' -- jump to +101 priority when appropriate.\n"
00177 " This application sets the following channel variable upon completion:\n"
00178 " AQMSTATUS The status of the attempt to add a queue member as a \n"
00179 " text string, one of\n"
00180 " ADDED | MEMBERALREADY | NOSUCHQUEUE \n"
00181 "Example: AddQueueMember(techsupport|SIP/3000)\n"
00182 "";
00183
00184 static char *app_rqm = "RemoveQueueMember" ;
00185 static char *app_rqm_synopsis = "Dynamically removes queue members" ;
00186 static char *app_rqm_descrip =
00187 " RemoveQueueMember(queuename[|interface[|options]]):\n"
00188 "Dynamically removes interface to an existing queue\n"
00189 "If the interface is NOT in the queue and there exists an n+101 priority\n"
00190 "then it will then jump to this priority. Otherwise it will return an error\n"
00191 "The option string may contain zero or more of the following characters:\n"
00192 " 'j' -- jump to +101 priority when appropriate.\n"
00193 " This application sets the following channel variable upon completion:\n"
00194 " RQMSTATUS The status of the attempt to remove a queue member as a\n"
00195 " text string, one of\n"
00196 " REMOVED | NOTINQUEUE | NOSUCHQUEUE \n"
00197 "Example: RemoveQueueMember(techsupport|SIP/3000)\n"
00198 "";
00199
00200 static char *app_pqm = "PauseQueueMember" ;
00201 static char *app_pqm_synopsis = "Pauses a queue member" ;
00202 static char *app_pqm_descrip =
00203 " PauseQueueMember([queuename]|interface[|options]):\n"
00204 "Pauses (blocks calls for) a queue member.\n"
00205 "The given interface will be paused in the given queue. This prevents\n"
00206 "any calls from being sent from the queue to the interface until it is\n"
00207 "unpaused with UnpauseQueueMember or the manager interface. If no\n"
00208 "queuename is given, the interface is paused in every queue it is a\n"
00209 "member of. If the interface is not in the named queue, or if no queue\n"
00210 "is given and the interface is not in any queue, it will jump to\n"
00211 "priority n+101, if it exists and the appropriate options are set.\n"
00212 "The application will fail if the interface is not found and no extension\n"
00213 "to jump to exists.\n"
00214 "The option string may contain zero or more of the following characters:\n"
00215 " 'j' -- jump to +101 priority when appropriate.\n"
00216 " This application sets the following channel variable upon completion:\n"
00217 " PQMSTATUS The status of the attempt to pause a queue member as a\n"
00218 " text string, one of\n"
00219 " PAUSED | NOTFOUND\n"
00220 "Example: PauseQueueMember(|SIP/3000)\n";
00221
00222 static char *app_upqm = "UnpauseQueueMember" ;
00223 static char *app_upqm_synopsis = "Unpauses a queue member" ;
00224 static char *app_upqm_descrip =
00225 " UnpauseQueueMember([queuename]|interface[|options]):\n"
00226 "Unpauses (resumes calls to) a queue member.\n"
00227 "This is the counterpart to PauseQueueMember and operates exactly the\n"
00228 "same way, except it unpauses instead of pausing the given interface.\n"
00229 "The option string may contain zero or more of the following characters:\n"
00230 " 'j' -- jump to +101 priority when appropriate.\n"
00231 " This application sets the following channel variable upon completion:\n"
00232 " UPQMSTATUS The status of the attempt to unpause a queue \n"
00233 " member as a text string, one of\n"
00234 " UNPAUSED | NOTFOUND\n"
00235 "Example: UnpauseQueueMember(|SIP/3000)\n";
00236
00237 static char *app_ql = "QueueLog" ;
00238 static char *app_ql_synopsis = "Writes to the queue_log" ;
00239 static char *app_ql_descrip =
00240 " QueueLog(queuename|uniqueid|agent|event[|additionalinfo]):\n"
00241 "Allows you to write your own events into the queue log\n"
00242 "Example: QueueLog(101|${UNIQUEID}|${AGENT}|WENTONBREAK|600)\n";
00243
00244
00245 static const char *pm_family = "Queue/PersistentMembers";
00246
00247 #define PM_MAX_LEN 8192
00248
00249
00250 static int queue_persistent_members = 0;
00251
00252
00253 static int use_weight = 0;
00254
00255
00256 static int autofill_default = 0;
00257
00258
00259 static int montype_default = 0;
00260
00261 enum queue_result {
00262 QUEUE_UNKNOWN = 0,
00263 QUEUE_TIMEOUT = 1,
00264 QUEUE_JOINEMPTY = 2,
00265 QUEUE_LEAVEEMPTY = 3,
00266 QUEUE_JOINUNAVAIL = 4,
00267 QUEUE_LEAVEUNAVAIL = 5,
00268 QUEUE_FULL = 6,
00269 };
00270
00271 const struct {
00272 enum queue_result id;
00273 char *text;
00274 } queue_results[] = {
00275 { QUEUE_UNKNOWN, "UNKNOWN" },
00276 { QUEUE_TIMEOUT, "TIMEOUT" },
00277 { QUEUE_JOINEMPTY,"JOINEMPTY" },
00278 { QUEUE_LEAVEEMPTY, "LEAVEEMPTY" },
00279 { QUEUE_JOINUNAVAIL, "JOINUNAVAIL" },
00280 { QUEUE_LEAVEUNAVAIL, "LEAVEUNAVAIL" },
00281 { QUEUE_FULL, "FULL" },
00282 };
00283
00284
00285
00286
00287
00288
00289
00290
00291
00292
00293
00294
00295 struct callattempt {
00296 struct callattempt *q_next;
00297 struct callattempt *call_next;
00298 struct ast_channel *chan;
00299 char interface[256];
00300 int stillgoing;
00301 int metric;
00302 int oldstatus;
00303 time_t lastcall;
00304 struct member *member;
00305 };
00306
00307
00308 struct queue_ent {
00309 struct call_queue *parent;
00310 char moh[80];
00311 char announce[80];
00312 char context[AST_MAX_CONTEXT];
00313 char digits[AST_MAX_EXTENSION];
00314 int valid_digits;
00315 int pos;
00316 int prio;
00317 int last_pos_said;
00318 time_t last_periodic_announce_time;
00319 int last_periodic_announce_sound;
00320 time_t last_pos;
00321 int opos;
00322 int handled;
00323 int pending;
00324 int max_penalty;
00325 time_t start;
00326 time_t expire;
00327 struct ast_channel *chan;
00328 struct queue_ent *next;
00329 };
00330
00331 struct member {
00332 char interface[80];
00333 char membername[80];
00334 int penalty;
00335 int calls;
00336 int dynamic;
00337 int realtime;
00338 int status;
00339 int paused;
00340 time_t lastcall;
00341 unsigned int dead:1;
00342 unsigned int delme:1;
00343 };
00344
00345 struct member_interface {
00346 char interface[80];
00347 AST_LIST_ENTRY(member_interface) list;
00348 };
00349
00350 static AST_LIST_HEAD_STATIC(interfaces, member_interface);
00351
00352
00353 #define QUEUE_EMPTY_NORMAL 1
00354 #define QUEUE_EMPTY_STRICT 2
00355 #define ANNOUNCEHOLDTIME_ALWAYS 1
00356 #define ANNOUNCEHOLDTIME_ONCE 2
00357 #define QUEUE_EVENT_VARIABLES 3
00358
00359 struct call_queue {
00360 ast_mutex_t lock;
00361 char name[80];
00362 char moh[80];
00363 char announce[80];
00364 char context[AST_MAX_CONTEXT];
00365 unsigned int monjoin:1;
00366 unsigned int dead:1;
00367 unsigned int joinempty:2;
00368 unsigned int eventwhencalled:2;
00369 unsigned int leavewhenempty:2;
00370 unsigned int ringinuse:1;
00371 unsigned int setinterfacevar:1;
00372 unsigned int reportholdtime:1;
00373 unsigned int wrapped:1;
00374 unsigned int timeoutrestart:1;
00375 unsigned int announceholdtime:2;
00376 int strategy:4;
00377 unsigned int maskmemberstatus:1;
00378 unsigned int realtime:1;
00379 unsigned int found:1;
00380 int announcefrequency;
00381 int periodicannouncefrequency;
00382 int roundingseconds;
00383 int holdtime;
00384 int callscompleted;
00385 int callsabandoned;
00386 int servicelevel;
00387 int callscompletedinsl;
00388 char monfmt[8];
00389 int montype;
00390 char sound_next[80];
00391 char sound_thereare[80];
00392 char sound_calls[80];
00393 char sound_holdtime[80];
00394 char sound_minutes[80];
00395 char sound_lessthan[80];
00396 char sound_seconds[80];
00397 char sound_thanks[80];
00398 char sound_reporthold[80];
00399 char sound_periodicannounce[MAX_PERIODIC_ANNOUNCEMENTS][80];
00400
00401 int count;
00402 int maxlen;
00403 int wrapuptime;
00404
00405 int retry;
00406 int timeout;
00407 int weight;
00408 int autopause;
00409
00410
00411 int rrpos;
00412 int memberdelay;
00413 int autofill;
00414
00415 struct ao2_container *members;
00416
00417
00418
00419
00420
00421 int membercount;
00422 struct queue_ent *head;
00423 AST_LIST_ENTRY(call_queue) list;
00424 };
00425
00426 static AST_LIST_HEAD_STATIC(queues, call_queue);
00427
00428 static int set_member_paused(const char *queuename, const char *interface, int paused);
00429
00430 static void rr_dep_warning(void)
00431 {
00432 static unsigned int warned = 0;
00433
00434 if (!warned) {
00435 ast_log(LOG_NOTICE, "The 'roundrobin' queue strategy is deprecated. Please use the 'rrmemory' strategy instead.\n");
00436 warned = 1;
00437 }
00438 }
00439
00440 static void monjoin_dep_warning(void)
00441 {
00442 static unsigned int warned = 0;
00443 if (!warned) {
00444 ast_log(LOG_NOTICE, "The 'monitor-join' queue option is deprecated. Please use monitor-type=mixmonitor instead.\n");
00445 warned = 1;
00446 }
00447 }
00448
00449 static void set_queue_result(struct ast_channel *chan, enum queue_result res)
00450 {
00451 int i;
00452
00453 for (i = 0; i < sizeof(queue_results) / sizeof(queue_results[0]); i++) {
00454 if (queue_results[i].id == res) {
00455 pbx_builtin_setvar_helper(chan, "QUEUESTATUS", queue_results[i].text);
00456 return;
00457 }
00458 }
00459 }
00460
00461 static char *int2strat(int strategy)
00462 {
00463 int x;
00464
00465 for (x = 0; x < sizeof(strategies) / sizeof(strategies[0]); x++) {
00466 if (strategy == strategies[x].strategy)
00467 return strategies[x].name;
00468 }
00469
00470 return "<unknown>";
00471 }
00472
00473 static int strat2int(const char *strategy)
00474 {
00475 int x;
00476
00477 for (x = 0; x < sizeof(strategies) / sizeof(strategies[0]); x++) {
00478 if (!strcasecmp(strategy, strategies[x].name))
00479 return strategies[x].strategy;
00480 }
00481
00482 return -1;
00483 }
00484
00485
00486 static inline void insert_entry(struct call_queue *q, struct queue_ent *prev, struct queue_ent *new, int *pos)
00487 {
00488 struct queue_ent *cur;
00489
00490 if (!q || !new)
00491 return;
00492 if (prev) {
00493 cur = prev->next;
00494 prev->next = new;
00495 } else {
00496 cur = q->head;
00497 q->head = new;
00498 }
00499 new->next = cur;
00500 new->parent = q;
00501 new->pos = ++(*pos);
00502 new->opos = *pos;
00503 }
00504
00505 enum queue_member_status {
00506 QUEUE_NO_MEMBERS,
00507 QUEUE_NO_REACHABLE_MEMBERS,
00508 QUEUE_NORMAL
00509 };
00510
00511 static enum queue_member_status get_member_status(struct call_queue *q, int max_penalty)
00512 {
00513 struct member *member;
00514 struct ao2_iterator mem_iter;
00515 enum queue_member_status result = QUEUE_NO_MEMBERS;
00516
00517 ast_mutex_lock(&q->lock);
00518 mem_iter = ao2_iterator_init(q->members, 0);
00519 while ((member = ao2_iterator_next(&mem_iter))) {
00520 if (max_penalty && (member->penalty > max_penalty)) {
00521 ao2_ref(member, -1);
00522 continue;
00523 }
00524
00525 if (member->paused) {
00526 ao2_ref(member, -1);
00527 continue;
00528 }
00529
00530 switch (member->status) {
00531 case AST_DEVICE_INVALID:
00532
00533 ao2_ref(member, -1);
00534 break;
00535 case AST_DEVICE_UNAVAILABLE:
00536 result = QUEUE_NO_REACHABLE_MEMBERS;
00537 ao2_ref(member, -1);
00538 break;
00539 default:
00540 ast_mutex_unlock(&q->lock);
00541 ao2_ref(member, -1);
00542 return QUEUE_NORMAL;
00543 }
00544 }
00545
00546 ast_mutex_unlock(&q->lock);
00547 return result;
00548 }
00549
00550 struct statechange {
00551 AST_LIST_ENTRY(statechange) entry;
00552 int state;
00553 char dev[0];
00554 };
00555
00556 static void *handle_statechange(struct statechange *sc)
00557 {
00558 struct call_queue *q;
00559 struct member *cur;
00560 struct ao2_iterator mem_iter;
00561 struct member_interface *curint;
00562 char *loc;
00563 char *technology;
00564
00565 technology = ast_strdupa(sc->dev);
00566 loc = strchr(technology, '/');
00567 if (loc) {
00568 *loc++ = '\0';
00569 } else {
00570 return NULL;
00571 }
00572
00573 AST_LIST_LOCK(&interfaces);
00574 AST_LIST_TRAVERSE(&interfaces, curint, list) {
00575 char *interface;
00576 char *slash_pos;
00577 interface = ast_strdupa(curint->interface);
00578 if ((slash_pos = strchr(interface, '/')))
00579 if ((slash_pos = strchr(slash_pos + 1, '/')))
00580 *slash_pos = '\0';
00581
00582 if (!strcasecmp(interface, sc->dev))
00583 break;
00584 }
00585 AST_LIST_UNLOCK(&interfaces);
00586
00587 if (!curint) {
00588 if (option_debug > 2)
00589 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));
00590 return NULL;
00591 }
00592
00593 if (option_debug)
00594 ast_log(LOG_DEBUG, "Device '%s/%s' changed to state '%d' (%s)\n", technology, loc, sc->state, devstate2str(sc->state));
00595 AST_LIST_LOCK(&queues);
00596 AST_LIST_TRAVERSE(&queues, q, list) {
00597 ast_mutex_lock(&q->lock);
00598 mem_iter = ao2_iterator_init(q->members, 0);
00599 while ((cur = ao2_iterator_next(&mem_iter))) {
00600 char *interface;
00601 char *slash_pos;
00602 interface = ast_strdupa(cur->interface);
00603 if ((slash_pos = strchr(interface, '/')))
00604 if ((slash_pos = strchr(slash_pos + 1, '/')))
00605 *slash_pos = '\0';
00606
00607 if (strcasecmp(sc->dev, interface)) {
00608 ao2_ref(cur, -1);
00609 continue;
00610 }
00611
00612 if (cur->status != sc->state) {
00613 cur->status = sc->state;
00614 if (q->maskmemberstatus) {
00615 ao2_ref(cur, -1);
00616 continue;
00617 }
00618
00619 manager_event(EVENT_FLAG_AGENT, "QueueMemberStatus",
00620 "Queue: %s\r\n"
00621 "Location: %s\r\n"
00622 "MemberName: %s\r\n"
00623 "Membership: %s\r\n"
00624 "Penalty: %d\r\n"
00625 "CallsTaken: %d\r\n"
00626 "LastCall: %d\r\n"
00627 "Status: %d\r\n"
00628 "Paused: %d\r\n",
00629 q->name, cur->interface, cur->membername, cur->dynamic ? "dynamic" : cur->realtime ? "realtime" : "static",
00630 cur->penalty, cur->calls, (int)cur->lastcall, cur->status, cur->paused);
00631 }
00632 ao2_ref(cur, -1);
00633 }
00634 ast_mutex_unlock(&q->lock);
00635 }
00636 AST_LIST_UNLOCK(&queues);
00637
00638 return NULL;
00639 }
00640
00641
00642
00643
00644 static struct {
00645
00646 unsigned int stop:1;
00647
00648 pthread_t thread;
00649
00650 ast_mutex_t lock;
00651
00652 ast_cond_t cond;
00653
00654 AST_LIST_HEAD_NOLOCK(, statechange) state_change_q;
00655 } device_state = {
00656 .thread = AST_PTHREADT_NULL,
00657 };
00658
00659 static void *device_state_thread(void *data)
00660 {
00661 struct statechange *sc = NULL;
00662
00663 while (!device_state.stop) {
00664 ast_mutex_lock(&device_state.lock);
00665 if (!(sc = AST_LIST_REMOVE_HEAD(&device_state.state_change_q, entry))) {
00666 ast_cond_wait(&device_state.cond, &device_state.lock);
00667 sc = AST_LIST_REMOVE_HEAD(&device_state.state_change_q, entry);
00668 }
00669 ast_mutex_unlock(&device_state.lock);
00670
00671
00672 if (device_state.stop)
00673 break;
00674
00675 if (!sc)
00676 continue;
00677
00678 handle_statechange(sc);
00679
00680 free(sc);
00681 sc = NULL;
00682 }
00683
00684 if (sc)
00685 free(sc);
00686
00687 while ((sc = AST_LIST_REMOVE_HEAD(&device_state.state_change_q, entry)))
00688 free(sc);
00689
00690 return NULL;
00691 }
00692
00693 static int statechange_queue(const char *dev, int state, void *ign, char *cid_num, char *cid_name)
00694 {
00695 struct statechange *sc;
00696
00697 if (!(sc = ast_calloc(1, sizeof(*sc) + strlen(dev) + 1)))
00698 return 0;
00699
00700 sc->state = state;
00701 strcpy(sc->dev, dev);
00702
00703 ast_mutex_lock(&device_state.lock);
00704 AST_LIST_INSERT_TAIL(&device_state.state_change_q, sc, entry);
00705 ast_cond_signal(&device_state.cond);
00706 ast_mutex_unlock(&device_state.lock);
00707
00708 return 0;
00709 }
00710
00711 static struct member *create_queue_member(const char *interface, const char *membername, int penalty, int paused)
00712 {
00713 struct member *cur;
00714
00715 if ((cur = ao2_alloc(sizeof(*cur), NULL))) {
00716 cur->penalty = penalty;
00717 cur->paused = paused;
00718 ast_copy_string(cur->interface, interface, sizeof(cur->interface));
00719 if(!ast_strlen_zero(membername))
00720 ast_copy_string(cur->membername, membername, sizeof(cur->membername));
00721 else
00722 ast_copy_string(cur->membername, interface, sizeof(cur->membername));
00723 if (!strchr(cur->interface, '/'))
00724 ast_log(LOG_WARNING, "No location at interface '%s'\n", interface);
00725 cur->status = ast_device_state(interface);
00726 }
00727
00728 return cur;
00729 }
00730
00731 static struct call_queue *alloc_queue(const char *queuename)
00732 {
00733 struct call_queue *q;
00734
00735 if ((q = ast_calloc(1, sizeof(*q)))) {
00736 ast_mutex_init(&q->lock);
00737 ast_copy_string(q->name, queuename, sizeof(q->name));
00738 }
00739 return q;
00740 }
00741
00742 static int compress_char(const char c)
00743 {
00744 if (c < 32)
00745 return 0;
00746 else if (c > 96)
00747 return c - 64;
00748 else
00749 return c - 32;
00750 }
00751
00752 static int member_hash_fn(const void *obj, const int flags)
00753 {
00754 const struct member *mem = obj;
00755 const char *chname = strchr(mem->interface, '/');
00756 int ret = 0, i;
00757 if (!chname)
00758 chname = mem->interface;
00759 for (i = 0; i < 5 && chname[i]; i++)
00760 ret += compress_char(chname[i]) << (i * 6);
00761 return ret;
00762 }
00763
00764 static int member_cmp_fn(void *obj1, void *obj2, int flags)
00765 {
00766 struct member *mem1 = obj1, *mem2 = obj2;
00767 return strcmp(mem1->interface, mem2->interface) ? 0 : CMP_MATCH;
00768 }
00769
00770 static void init_queue(struct call_queue *q)
00771 {
00772 int i;
00773
00774 q->dead = 0;
00775 q->retry = DEFAULT_RETRY;
00776 q->timeout = -1;
00777 q->maxlen = 0;
00778 q->announcefrequency = 0;
00779 q->announceholdtime = 0;
00780 q->roundingseconds = 0;
00781 q->servicelevel = 0;
00782 q->ringinuse = 1;
00783 q->setinterfacevar = 0;
00784 q->autofill = autofill_default;
00785 q->montype = montype_default;
00786 q->moh[0] = '\0';
00787 q->announce[0] = '\0';
00788 q->context[0] = '\0';
00789 q->monfmt[0] = '\0';
00790 q->periodicannouncefrequency = 0;
00791 if(!q->members)
00792 q->members = ao2_container_alloc(37, member_hash_fn, member_cmp_fn);
00793 q->membercount = 0;
00794 q->found = 1;
00795 ast_copy_string(q->sound_next, "queue-youarenext", sizeof(q->sound_next));
00796 ast_copy_string(q->sound_thereare, "queue-thereare", sizeof(q->sound_thereare));
00797 ast_copy_string(q->sound_calls, "queue-callswaiting", sizeof(q->sound_calls));
00798 ast_copy_string(q->sound_holdtime, "queue-holdtime", sizeof(q->sound_holdtime));
00799 ast_copy_string(q->sound_minutes, "queue-minutes", sizeof(q->sound_minutes));
00800 ast_copy_string(q->sound_seconds, "queue-seconds", sizeof(q->sound_seconds));
00801 ast_copy_string(q->sound_thanks, "queue-thankyou", sizeof(q->sound_thanks));
00802 ast_copy_string(q->sound_lessthan, "queue-less-than", sizeof(q->sound_lessthan));
00803 ast_copy_string(q->sound_reporthold, "queue-reporthold", sizeof(q->sound_reporthold));
00804 ast_copy_string(q->sound_periodicannounce[0], "queue-periodic-announce", sizeof(q->sound_periodicannounce[0]));
00805 for (i = 1; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) {
00806 q->sound_periodicannounce[i][0]='\0';
00807 }
00808 }
00809
00810 static void clear_queue(struct call_queue *q)
00811 {
00812 q->holdtime = 0;
00813 q->callscompleted = 0;
00814 q->callsabandoned = 0;
00815 q->callscompletedinsl = 0;
00816 q->wrapuptime = 0;
00817 }
00818
00819 static int add_to_interfaces(const char *interface)
00820 {
00821 struct member_interface *curint;
00822
00823 AST_LIST_LOCK(&interfaces);
00824 AST_LIST_TRAVERSE(&interfaces, curint, list) {
00825 if (!strcasecmp(curint->interface, interface))
00826 break;
00827 }
00828
00829 if (curint) {
00830 AST_LIST_UNLOCK(&interfaces);
00831 return 0;
00832 }
00833
00834 if (option_debug)
00835 ast_log(LOG_DEBUG, "Adding %s to the list of interfaces that make up all of our queue members.\n", interface);
00836
00837 if ((curint = ast_calloc(1, sizeof(*curint)))) {
00838 ast_copy_string(curint->interface, interface, sizeof(curint->interface));
00839 AST_LIST_INSERT_HEAD(&interfaces, curint, list);
00840 }
00841 AST_LIST_UNLOCK(&interfaces);
00842
00843 return 0;
00844 }
00845
00846 static int interface_exists_global(const char *interface)
00847 {
00848 struct call_queue *q;
00849 struct member *mem, tmpmem;
00850 int ret = 0;
00851
00852 ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
00853
00854 AST_LIST_LOCK(&queues);
00855 AST_LIST_TRAVERSE(&queues, q, list) {
00856 ast_mutex_lock(&q->lock);
00857 if ((mem = ao2_find(q->members, &tmpmem, OBJ_POINTER))) {
00858 ao2_ref(mem, -1);
00859 ret = 1;
00860 }
00861 ast_mutex_unlock(&q->lock);
00862 if (ret)
00863 break;
00864 }
00865 AST_LIST_UNLOCK(&queues);
00866
00867 return ret;
00868 }
00869
00870 static int remove_from_interfaces(const char *interface)
00871 {
00872 struct member_interface *curint;
00873
00874 AST_LIST_LOCK(&interfaces);
00875 AST_LIST_TRAVERSE_SAFE_BEGIN(&interfaces, curint, list) {
00876 if (!strcasecmp(curint->interface, interface)) {
00877 if (!interface_exists_global(interface)) {
00878 if (option_debug)
00879 ast_log(LOG_DEBUG, "Removing %s from the list of interfaces that make up all of our queue members.\n", interface);
00880 AST_LIST_REMOVE_CURRENT(&interfaces, list);
00881 free(curint);
00882 }
00883 break;
00884 }
00885 }
00886 AST_LIST_TRAVERSE_SAFE_END;
00887 AST_LIST_UNLOCK(&interfaces);
00888
00889 return 0;
00890 }
00891
00892 static void clear_and_free_interfaces(void)
00893 {
00894 struct member_interface *curint;
00895
00896 AST_LIST_LOCK(&interfaces);
00897 while ((curint = AST_LIST_REMOVE_HEAD(&interfaces, list)))
00898 free(curint);
00899 AST_LIST_UNLOCK(&interfaces);
00900 }
00901
00902
00903
00904
00905
00906
00907
00908
00909 static void queue_set_param(struct call_queue *q, const char *param, const char *val, int linenum, int failunknown)
00910 {
00911 if (!strcasecmp(param, "musicclass") ||
00912 !strcasecmp(param, "music") || !strcasecmp(param, "musiconhold")) {
00913 ast_copy_string(q->moh, val, sizeof(q->moh));
00914 } else if (!strcasecmp(param, "announce")) {
00915 ast_copy_string(q->announce, val, sizeof(q->announce));
00916 } else if (!strcasecmp(param, "context")) {
00917 ast_copy_string(q->context, val, sizeof(q->context));
00918 } else if (!strcasecmp(param, "timeout")) {
00919 q->timeout = atoi(val);
00920 if (q->timeout < 0)
00921 q->timeout = DEFAULT_TIMEOUT;
00922 } else if (!strcasecmp(param, "ringinuse")) {
00923 q->ringinuse = ast_true(val);
00924 } else if (!strcasecmp(param, "setinterfacevar")) {
00925 q->setinterfacevar = ast_true(val);
00926 } else if (!strcasecmp(param, "monitor-join")) {
00927 monjoin_dep_warning();
00928 q->monjoin = ast_true(val);
00929 } else if (!strcasecmp(param, "monitor-format")) {
00930 ast_copy_string(q->monfmt, val, sizeof(q->monfmt));
00931 } else if (!strcasecmp(param, "queue-youarenext")) {
00932 ast_copy_string(q->sound_next, val, sizeof(q->sound_next));
00933 } else if (!strcasecmp(param, "queue-thereare")) {
00934 ast_copy_string(q->sound_thereare, val, sizeof(q->sound_thereare));
00935 } else if (!strcasecmp(param, "queue-callswaiting")) {
00936 ast_copy_string(q->sound_calls, val, sizeof(q->sound_calls));
00937 } else if (!strcasecmp(param, "queue-holdtime")) {
00938 ast_copy_string(q->sound_holdtime, val, sizeof(q->sound_holdtime));
00939 } else if (!strcasecmp(param, "queue-minutes")) {
00940 ast_copy_string(q->sound_minutes, val, sizeof(q->sound_minutes));
00941 } else if (!strcasecmp(param, "queue-seconds")) {
00942 ast_copy_string(q->sound_seconds, val, sizeof(q->sound_seconds));
00943 } else if (!strcasecmp(param, "queue-lessthan")) {
00944 ast_copy_string(q->sound_lessthan, val, sizeof(q->sound_lessthan));
00945 } else if (!strcasecmp(param, "queue-thankyou")) {
00946 ast_copy_string(q->sound_thanks, val, sizeof(q->sound_thanks));
00947 } else if (!strcasecmp(param, "queue-reporthold")) {
00948 ast_copy_string(q->sound_reporthold, val, sizeof(q->sound_reporthold));
00949 } else if (!strcasecmp(param, "announce-frequency")) {
00950 q->announcefrequency = atoi(val);
00951 } else if (!strcasecmp(param, "announce-round-seconds")) {
00952 q->roundingseconds = atoi(val);
00953 if (q->roundingseconds>60 || q->roundingseconds<0) {
00954 if (linenum >= 0) {
00955 ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
00956 "using 0 instead for queue '%s' at line %d of queues.conf\n",
00957 val, param, q->name, linenum);
00958 } else {
00959 ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
00960 "using 0 instead for queue '%s'\n", val, param, q->name);
00961 }
00962 q->roundingseconds=0;
00963 }
00964 } else if (!strcasecmp(param, "announce-holdtime")) {
00965 if (!strcasecmp(val, "once"))
00966 q->announceholdtime = ANNOUNCEHOLDTIME_ONCE;
00967 else if (ast_true(val))
00968 q->announceholdtime = ANNOUNCEHOLDTIME_ALWAYS;
00969 else
00970 q->announceholdtime = 0;
00971 } else if (!strcasecmp(param, "periodic-announce")) {
00972 if (strchr(val, '|')) {
00973 char *s, *buf = ast_strdupa(val);
00974 unsigned int i = 0;
00975
00976 while ((s = strsep(&buf, "|"))) {
00977 ast_copy_string(q->sound_periodicannounce[i], s, sizeof(q->sound_periodicannounce[i]));
00978 i++;
00979 if (i == MAX_PERIODIC_ANNOUNCEMENTS)
00980 break;
00981 }
00982 } else {
00983 ast_copy_string(q->sound_periodicannounce[0], val, sizeof(q->sound_periodicannounce[0]));
00984 }
00985 } else if (!strcasecmp(param, "periodic-announce-frequency")) {
00986 q->periodicannouncefrequency = atoi(val);
00987 } else if (!strcasecmp(param, "retry")) {
00988 q->retry = atoi(val);
00989 if (q->retry <= 0)
00990 q->retry = DEFAULT_RETRY;
00991 } else if (!strcasecmp(param, "wrapuptime")) {
00992 q->wrapuptime = atoi(val);
00993 } else if (!strcasecmp(param, "autofill")) {
00994 q->autofill = ast_true(val);
00995 } else if (!strcasecmp(param, "monitor-type")) {
00996 if (!strcasecmp(val, "mixmonitor"))
00997 q->montype = 1;
00998 } else if (!strcasecmp(param, "autopause")) {
00999 q->autopause = ast_true(val);
01000 } else if (!strcasecmp(param, "maxlen")) {
01001 q->maxlen = atoi(val);
01002 if (q->maxlen < 0)
01003 q->maxlen = 0;
01004 } else if (!strcasecmp(param, "servicelevel")) {
01005 q->servicelevel= atoi(val);
01006 } else if (!strcasecmp(param, "strategy")) {
01007 q->strategy = strat2int(val);
01008 if (q->strategy < 0) {
01009 ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
01010 val, q->name);
01011 q->strategy = QUEUE_STRATEGY_RINGALL;
01012 }
01013 } else if (!strcasecmp(param, "joinempty")) {
01014 if (!strcasecmp(val, "strict"))
01015 q->joinempty = QUEUE_EMPTY_STRICT;
01016 else if (ast_true(val))
01017 q->joinempty = QUEUE_EMPTY_NORMAL;
01018 else
01019 q->joinempty = 0;
01020 } else if (!strcasecmp(param, "leavewhenempty")) {
01021 if (!strcasecmp(val, "strict"))
01022 q->leavewhenempty = QUEUE_EMPTY_STRICT;
01023 else if (ast_true(val))
01024 q->leavewhenempty = QUEUE_EMPTY_NORMAL;
01025 else
01026 q->leavewhenempty = 0;
01027 } else if (!strcasecmp(param, "eventmemberstatus")) {
01028 q->maskmemberstatus = !ast_true(val);
01029 } else if (!strcasecmp(param, "eventwhencalled")) {
01030 if (!strcasecmp(val, "vars")) {
01031 q->eventwhencalled = QUEUE_EVENT_VARIABLES;
01032 } else {
01033 q->eventwhencalled = ast_true(val) ? 1 : 0;
01034 }
01035 } else if (!strcasecmp(param, "reportholdtime")) {
01036 q->reportholdtime = ast_true(val);
01037 } else if (!strcasecmp(param, "memberdelay")) {
01038 q->memberdelay = atoi(val);
01039 } else if (!strcasecmp(param, "weight")) {
01040 q->weight = atoi(val);
01041 if (q->weight)
01042 use_weight++;
01043
01044
01045 } else if (!strcasecmp(param, "timeoutrestart")) {
01046 q->timeoutrestart = ast_true(val);
01047 } else if (failunknown) {
01048 if (linenum >= 0) {
01049 ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s at line %d of queues.conf\n",
01050 q->name, param, linenum);
01051 } else {
01052 ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s\n", q->name, param);
01053 }
01054 }
01055 }
01056
01057 static void rt_handle_member_record(struct call_queue *q, char *interface, const char *membername, const char *penalty_str, const char *paused_str)
01058 {
01059 struct member *m, tmpmem;
01060 int penalty = 0;
01061 int paused = 0;
01062
01063 if (penalty_str) {
01064 penalty = atoi(penalty_str);
01065 if (penalty < 0)
01066 penalty = 0;
01067 }
01068
01069 if (paused_str) {
01070 paused = atoi(paused_str);
01071 if (paused < 0)
01072 paused = 0;
01073 }
01074
01075
01076 ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
01077 m = ao2_find(q->members, &tmpmem, OBJ_POINTER);
01078
01079
01080 if (!m) {
01081 if ((m = create_queue_member(interface, membername, penalty, paused))) {
01082 m->dead = 0;
01083 m->realtime = 1;
01084 add_to_interfaces(interface);
01085 ao2_link(q->members, m);
01086 ao2_ref(m, -1);
01087 m = NULL;
01088 q->membercount++;
01089 }
01090 } else {
01091 m->dead = 0;
01092 if (paused_str)
01093 m->paused = paused;
01094 m->penalty = penalty;
01095 ao2_ref(m, -1);
01096 }
01097 }
01098
01099 static void free_members(struct call_queue *q, int all)
01100 {
01101
01102 struct member *cur;
01103 struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
01104
01105 while ((cur = ao2_iterator_next(&mem_iter))) {
01106 if (all || !cur->dynamic) {
01107 ao2_unlink(q->members, cur);
01108 remove_from_interfaces(cur->interface);
01109 q->membercount--;
01110 }
01111 ao2_ref(cur, -1);
01112 }
01113 }
01114
01115 static void destroy_queue(struct call_queue *q)
01116 {
01117 free_members(q, 1);
01118 ast_mutex_destroy(&q->lock);
01119 ao2_ref(q->members, -1);
01120 free(q);
01121 }
01122
01123
01124
01125
01126 static struct call_queue *find_queue_by_name_rt(const char *queuename, struct ast_variable *queue_vars, struct ast_config *member_config)
01127 {
01128 struct ast_variable *v;
01129 struct call_queue *q;
01130 struct member *m;
01131 struct ao2_iterator mem_iter;
01132 char *interface = NULL;
01133 char *tmp, *tmp_name;
01134 char tmpbuf[64];
01135
01136
01137 AST_LIST_TRAVERSE(&queues, q, list) {
01138 if (!strcasecmp(q->name, queuename))
01139 break;
01140 }
01141
01142
01143 if (q) {
01144 ast_mutex_lock(&q->lock);
01145 if (!q->realtime) {
01146 if (q->dead) {
01147 ast_mutex_unlock(&q->lock);
01148 return NULL;
01149 } else {
01150 ast_log(LOG_WARNING, "Static queue '%s' already exists. Not loading from realtime\n", q->name);
01151 ast_mutex_unlock(&q->lock);
01152 return q;
01153 }
01154 }
01155 } else if (!member_config)
01156
01157 return NULL;
01158
01159
01160 if (!queue_vars) {
01161
01162 if (q) {
01163
01164
01165
01166 ast_log(LOG_DEBUG, "Queue %s not found in realtime.\n", queuename);
01167
01168 q->dead = 1;
01169
01170 if (!q->count) {
01171
01172 AST_LIST_REMOVE(&queues, q, list);
01173 ast_mutex_unlock(&q->lock);
01174 destroy_queue(q);
01175 } else
01176 ast_mutex_unlock(&q->lock);
01177 }
01178 return NULL;
01179 }
01180
01181
01182 if (!q) {
01183 if (!(q = alloc_queue(queuename)))
01184 return NULL;
01185 ast_mutex_lock(&q->lock);
01186 clear_queue(q);
01187 q->realtime = 1;
01188 init_queue(q);
01189 AST_LIST_INSERT_HEAD(&queues, q, list);
01190 }
01191
01192 memset(tmpbuf, 0, sizeof(tmpbuf));
01193 for (v = queue_vars; v; v = v->next) {
01194
01195 if ((tmp = strchr(v->name, '_'))) {
01196 ast_copy_string(tmpbuf, v->name, sizeof(tmpbuf));
01197 tmp_name = tmpbuf;
01198 tmp = tmp_name;
01199 while ((tmp = strchr(tmp, '_')))
01200 *tmp++ = '-';
01201 } else
01202 tmp_name = v->name;
01203 queue_set_param(q, tmp_name, v->value, -1, 0);
01204 }
01205
01206 if (q->strategy == QUEUE_STRATEGY_ROUNDROBIN)
01207 rr_dep_warning();
01208
01209
01210
01211 mem_iter = ao2_iterator_init(q->members, 0);
01212 while ((m = ao2_iterator_next(&mem_iter))) {
01213 q->membercount++;
01214 if (m->realtime)
01215 m->dead = 1;
01216 ao2_ref(m, -1);
01217 }
01218
01219 while ((interface = ast_category_browse(member_config, interface))) {
01220 rt_handle_member_record(q, interface,
01221 ast_variable_retrieve(member_config, interface, "membername"),
01222 ast_variable_retrieve(member_config, interface, "penalty"),
01223 ast_variable_retrieve(member_config, interface, "paused"));
01224 }
01225
01226
01227 mem_iter = ao2_iterator_init(q->members, 0);
01228 while ((m = ao2_iterator_next(&mem_iter))) {
01229 if (m->dead) {
01230 ao2_unlink(q->members, m);
01231 ast_mutex_unlock(&q->lock);
01232 remove_from_interfaces(m->interface);
01233 ast_mutex_lock(&q->lock);
01234 q->membercount--;
01235 }
01236 ao2_ref(m, -1);
01237 }
01238
01239 ast_mutex_unlock(&q->lock);
01240
01241 return q;
01242 }
01243
01244 static int update_realtime_member_field(struct member *mem, const char *queue_name, const char *field, const char *value)
01245 {
01246 struct ast_variable *var;
01247 int ret = -1;
01248
01249 if(!(var = ast_load_realtime("queue_members", "interface", mem->interface, "queue_name", queue_name, NULL)))
01250 return ret;
01251 while (var) {
01252 if(!strcmp(var->name, "uniqueid"))
01253 break;
01254 var = var->next;
01255 }
01256 if(var && !ast_strlen_zero(var->value)) {
01257 if ((ast_update_realtime("queue_members", "uniqueid", var->value, field, value, NULL)) > -1)
01258 ret = 0;
01259 }
01260 return ret;
01261 }
01262
01263 static void update_realtime_members(struct call_queue *q)
01264 {
01265 struct ast_config *member_config = NULL;
01266 struct member *m;
01267 char *interface = NULL;
01268 struct ao2_iterator mem_iter;
01269
01270 member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", q->name , NULL);
01271 if (!member_config) {
01272
01273 if (option_debug > 2)
01274 ast_log(LOG_DEBUG, "Queue %s has no realtime members defined. No need for update\n", q->name);
01275 return;
01276 }
01277
01278 ast_mutex_lock(&q->lock);
01279
01280
01281 mem_iter = ao2_iterator_init(q->members, 0);
01282 while ((m = ao2_iterator_next(&mem_iter))) {
01283 if (m->realtime)
01284 m->dead = 1;
01285 ao2_ref(m, -1);
01286 }
01287
01288 while ((interface = ast_category_browse(member_config, interface))) {
01289 rt_handle_member_record(q, interface,
01290 S_OR(ast_variable_retrieve(member_config, interface, "membername"), interface),
01291 ast_variable_retrieve(member_config, interface, "penalty"),
01292 ast_variable_retrieve(member_config, interface, "paused"));
01293 }
01294
01295
01296 mem_iter = ao2_iterator_init(q->members, 0);
01297 while ((m = ao2_iterator_next(&mem_iter))) {
01298 if (m->dead) {
01299 ao2_unlink(q->members, m);
01300 ast_mutex_unlock(&q->lock);
01301 remove_from_interfaces(m->interface);
01302 ast_mutex_lock(&q->lock);
01303 q->membercount--;
01304 }
01305 ao2_ref(m, -1);
01306 }
01307 ast_mutex_unlock(&q->lock);
01308 }
01309
01310 static struct call_queue *load_realtime_queue(const char *queuename)
01311 {
01312 struct ast_variable *queue_vars;
01313 struct ast_config *member_config = NULL;
01314 struct call_queue *q;
01315
01316
01317 AST_LIST_LOCK(&queues);
01318 AST_LIST_TRAVERSE(&queues, q, list) {
01319 if (!strcasecmp(q->name, queuename)) {
01320 break;
01321 }
01322 }
01323 AST_LIST_UNLOCK(&queues);
01324
01325 if (!q || q->realtime) {
01326
01327
01328
01329
01330
01331
01332
01333
01334
01335 queue_vars = ast_load_realtime("queues", "name", queuename, NULL);
01336 if (queue_vars) {
01337 member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", queuename, NULL);
01338 if (!member_config) {
01339 ast_log(LOG_ERROR, "no queue_members defined in your config (extconfig.conf).\n");
01340 ast_variables_destroy(queue_vars);
01341 return NULL;
01342 }
01343 }
01344
01345 AST_LIST_LOCK(&queues);
01346
01347 q = find_queue_by_name_rt(queuename, queue_vars, member_config);
01348 if (member_config)
01349 ast_config_destroy(member_config);
01350 if (queue_vars)
01351 ast_variables_destroy(queue_vars);
01352
01353 AST_LIST_UNLOCK(&queues);
01354 } else {
01355 update_realtime_members(q);
01356 }
01357 return q;
01358 }
01359
01360 static int join_queue(char *queuename, struct queue_ent *qe, enum queue_result *reason)
01361 {
01362 struct call_queue *q;
01363 struct queue_ent *cur, *prev = NULL;
01364 int res = -1;
01365 int pos = 0;
01366 int inserted = 0;
01367 enum queue_member_status stat;
01368
01369 if (!(q = load_realtime_queue(queuename)))
01370 return res;
01371
01372 AST_LIST_LOCK(&queues);
01373 ast_mutex_lock(&q->lock);
01374
01375
01376 stat = get_member_status(q, qe->max_penalty);
01377 if (!q->joinempty && (stat == QUEUE_NO_MEMBERS))
01378 *reason = QUEUE_JOINEMPTY;
01379 else if ((q->joinempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS))
01380 *reason = QUEUE_JOINUNAVAIL;
01381 else if (q->maxlen && (q->count >= q->maxlen))
01382 *reason = QUEUE_FULL;
01383 else {
01384
01385
01386
01387 inserted = 0;
01388 prev = NULL;
01389 cur = q->head;
01390 while (cur) {
01391
01392
01393
01394 if ((!inserted) && (qe->prio > cur->prio)) {
01395 insert_entry(q, prev, qe, &pos);
01396 inserted = 1;
01397 }
01398 cur->pos = ++pos;
01399 prev = cur;
01400 cur = cur->next;
01401 }
01402
01403 if (!inserted)
01404 insert_entry(q, prev, qe, &pos);
01405 ast_copy_string(qe->moh, q->moh, sizeof(qe->moh));
01406 ast_copy_string(qe->announce, q->announce, sizeof(qe->announce));
01407 ast_copy_string(qe->context, q->context, sizeof(qe->context));
01408 q->count++;
01409 res = 0;
01410 manager_event(EVENT_FLAG_CALL, "Join",
01411 "Channel: %s\r\nCallerID: %s\r\nCallerIDName: %s\r\nQueue: %s\r\nPosition: %d\r\nCount: %d\r\nUniqueid: %s\r\n",
01412 qe->chan->name,
01413 S_OR(qe->chan->cid.cid_num, "unknown"),
01414 S_OR(qe->chan->cid.cid_name, "unknown"),
01415 q->name, qe->pos, q->count, qe->chan->uniqueid );
01416 if (option_debug)
01417 ast_log(LOG_DEBUG, "Queue '%s' Join, Channel '%s', Position '%d'\n", q->name, qe->chan->name, qe->pos );
01418 }
01419 ast_mutex_unlock(&q->lock);
01420 AST_LIST_UNLOCK(&queues);
01421
01422 return res;
01423 }
01424
01425 static int play_file(struct ast_channel *chan, char *filename)
01426 {
01427 int res;
01428
01429 ast_stopstream(chan);
01430
01431 res = ast_streamfile(chan, filename, chan->language);
01432 if (!res)
01433 res = ast_waitstream(chan, AST_DIGIT_ANY);
01434
01435 ast_stopstream(chan);
01436
01437 return res;
01438 }
01439
01440 static int valid_exit(struct queue_ent *qe, char digit)
01441 {
01442 int digitlen = strlen(qe->digits);
01443
01444
01445 if (digitlen < sizeof(qe->digits) - 2) {
01446 qe->digits[digitlen] = digit;
01447 qe->digits[digitlen + 1] = '\0';
01448 } else {
01449 qe->digits[0] = '\0';
01450 return 0;
01451 }
01452
01453
01454 if (ast_strlen_zero(qe->context))
01455 return 0;
01456
01457
01458 if (!ast_canmatch_extension(qe->chan, qe->context, qe->digits, 1, qe->chan->cid.cid_num)) {
01459 qe->digits[0] = '\0';
01460 return 0;
01461 }
01462
01463
01464 if (!ast_goto_if_exists(qe->chan, qe->context, qe->digits, 1)) {
01465 qe->valid_digits = 1;
01466
01467 return 1;
01468 }
01469
01470 return 0;
01471 }
01472
01473 static int say_position(struct queue_ent *qe)
01474 {
01475 int res = 0, avgholdmins, avgholdsecs;
01476 time_t now;
01477
01478
01479 time(&now);
01480 if ((now - qe->last_pos) < 15)
01481 return 0;
01482
01483
01484 if ((qe->last_pos_said == qe->pos) && ((now - qe->last_pos) < qe->parent->announcefrequency))
01485 return 0;
01486
01487 ast_moh_stop(qe->chan);
01488
01489 if (qe->pos == 1) {
01490 res = play_file(qe->chan, qe->parent->sound_next);
01491 if (res)
01492 goto playout;
01493 else
01494 goto posout;
01495 } else {
01496 res = play_file(qe->chan, qe->parent->sound_thereare);
01497 if (res)
01498 goto playout;
01499 res = ast_say_number(qe->chan, qe->pos, AST_DIGIT_ANY, qe->chan->language, (char *) NULL);
01500 if (res)
01501 goto playout;
01502 res = play_file(qe->chan, qe->parent->sound_calls);
01503 if (res)
01504 goto playout;
01505 }
01506
01507 avgholdmins = abs(((qe->parent->holdtime + 30) - (now - qe->start)) / 60);
01508
01509
01510 if (qe->parent->roundingseconds) {
01511 avgholdsecs = (abs(((qe->parent->holdtime + 30) - (now - qe->start))) - 60 * avgholdmins) / qe->parent->roundingseconds;
01512 avgholdsecs *= qe->parent->roundingseconds;
01513 } else {
01514 avgholdsecs = 0;
01515 }
01516
01517 if (option_verbose > 2)
01518 ast_verbose(VERBOSE_PREFIX_3 "Hold time for %s is %d minutes %d seconds\n", qe->parent->name, avgholdmins, avgholdsecs);
01519
01520
01521
01522 if ((avgholdmins+avgholdsecs) > 0 && (qe->parent->announceholdtime) &&
01523 (!(qe->parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE) && qe->last_pos)) {
01524 res = play_file(qe->chan, qe->parent->sound_holdtime);
01525 if (res)
01526 goto playout;
01527
01528 if (avgholdmins > 0) {
01529 if (avgholdmins < 2) {
01530 res = play_file(qe->chan, qe->parent->sound_lessthan);
01531 if (res)
01532 goto playout;
01533
01534 res = ast_say_number(qe->chan, 2, AST_DIGIT_ANY, qe->chan->language, NULL);
01535 if (res)
01536 goto playout;
01537 } else {
01538 res = ast_say_number(qe->chan, avgholdmins, AST_DIGIT_ANY, qe->chan->language, NULL);
01539 if (res)
01540 goto playout;
01541 }
01542
01543 res = play_file(qe->chan, qe->parent->sound_minutes);
01544 if (res)
01545 goto playout;
01546 }
01547 if (avgholdsecs>0) {
01548 res = ast_say_number(qe->chan, avgholdsecs, AST_DIGIT_ANY, qe->chan->language, NULL);
01549 if (res)
01550 goto playout;
01551
01552 res = play_file(qe->chan, qe->parent->sound_seconds);
01553 if (res)
01554 goto playout;
01555 }
01556
01557 }
01558
01559 posout:
01560 if (option_verbose > 2)
01561 ast_verbose(VERBOSE_PREFIX_3 "Told %s in %s their queue position (which was %d)\n",
01562 qe->chan->name, qe->parent->name, qe->pos);
01563 res = play_file(qe->chan, qe->parent->sound_thanks);
01564
01565 playout:
01566 if ((res > 0 && !valid_exit(qe, res)) || res < 0)
01567 res = 0;
01568
01569
01570 qe->last_pos = now;
01571 qe->last_pos_said = qe->pos;
01572
01573
01574 if (!res)
01575 ast_moh_start(qe->chan, qe->moh, NULL);
01576
01577 return res;
01578 }
01579
01580 static void recalc_holdtime(struct queue_ent *qe, int newholdtime)
01581 {
01582 int oldvalue;
01583
01584
01585
01586
01587
01588 ast_mutex_lock(&qe->parent->lock);
01589 oldvalue = qe->parent->holdtime;
01590 qe->parent->holdtime = (((oldvalue << 2) - oldvalue) + newholdtime) >> 2;
01591 ast_mutex_unlock(&qe->parent->lock);
01592 }
01593
01594
01595 static void leave_queue(struct queue_ent *qe)
01596 {
01597 struct call_queue *q;
01598 struct queue_ent *cur, *prev = NULL;
01599 int pos = 0;
01600
01601 if (!(q = qe->parent))
01602 return;
01603 ast_mutex_lock(&q->lock);
01604
01605 prev = NULL;
01606 for (cur = q->head; cur; cur = cur->next) {
01607 if (cur == qe) {
01608 q->count--;
01609
01610
01611 manager_event(EVENT_FLAG_CALL, "Leave",
01612 "Channel: %s\r\nQueue: %s\r\nCount: %d\r\nUniqueid: %s\r\n",
01613 qe->chan->name, q->name, q->count, qe->chan->uniqueid);
01614 if (option_debug)
01615 ast_log(LOG_DEBUG, "Queue '%s' Leave, Channel '%s'\n", q->name, qe->chan->name );
01616
01617 if (prev)
01618 prev->next = cur->next;
01619 else
01620 q->head = cur->next;
01621 } else {
01622
01623 cur->pos = ++pos;
01624 prev = cur;
01625 }
01626 }
01627 ast_mutex_unlock(&q->lock);
01628
01629 if (q->dead && !q->count) {
01630
01631 AST_LIST_LOCK(&queues);
01632 AST_LIST_REMOVE(&queues, q, list);
01633 AST_LIST_UNLOCK(&queues);
01634 destroy_queue(q);
01635 }
01636 }
01637
01638
01639 static void hangupcalls(struct callattempt *outgoing, struct ast_channel *exception)
01640 {
01641 struct callattempt *oo;
01642
01643 while (outgoing) {
01644
01645 if (outgoing->chan && (outgoing->chan != exception))
01646 ast_hangup(outgoing->chan);
01647 oo = outgoing;
01648 outgoing = outgoing->q_next;
01649 if (oo->member)
01650 ao2_ref(oo->member, -1);
01651 free(oo);
01652 }
01653 }
01654
01655 static int update_status(struct call_queue *q, struct member *member, int status)
01656 {
01657 struct member *cur;
01658 struct ao2_iterator mem_iter;
01659
01660
01661
01662 ast_mutex_lock(&q->lock);
01663 mem_iter = ao2_iterator_init(q->members, 0);
01664 while ((cur = ao2_iterator_next(&mem_iter))) {
01665 if (member != cur) {
01666 ao2_ref(cur, -1);
01667 continue;
01668 }
01669
01670 cur->status = status;
01671 if (!q->maskmemberstatus) {
01672 manager_event(EVENT_FLAG_AGENT, "QueueMemberStatus",
01673 "Queue: %s\r\n"
01674 "Location: %s\r\n"
01675 "MemberName: %s\r\n"
01676 "Membership: %s\r\n"
01677 "Penalty: %d\r\n"
01678 "CallsTaken: %d\r\n"
01679 "LastCall: %d\r\n"
01680 "Status: %d\r\n"
01681 "Paused: %d\r\n",
01682 q->name, cur->interface, cur->membername, cur->dynamic ? "dynamic" : cur->realtime ? "realtime": "static",
01683 cur->penalty, cur->calls, (int)cur->lastcall, cur->status, cur->paused);
01684 }
01685 ao2_ref(cur, -1);
01686 }
01687 ast_mutex_unlock(&q->lock);
01688 return 0;
01689 }
01690
01691 static int update_dial_status(struct call_queue *q, struct member *member, int status)
01692 {
01693 if (status == AST_CAUSE_BUSY)
01694 status = AST_DEVICE_BUSY;
01695 else if (status == AST_CAUSE_UNREGISTERED)
01696 status = AST_DEVICE_UNAVAILABLE;
01697 else if (status == AST_CAUSE_NOSUCHDRIVER)
01698 status = AST_DEVICE_INVALID;
01699 else
01700 status = AST_DEVICE_UNKNOWN;
01701 return update_status(q, member, status);
01702 }
01703
01704
01705
01706 static int compare_weight(struct call_queue *rq, struct member *member)
01707 {
01708 struct call_queue *q;
01709 struct member *mem;
01710 int found = 0;
01711
01712
01713
01714 AST_LIST_TRAVERSE(&queues, q, list) {
01715 if (q == rq)
01716 continue;
01717 ast_mutex_lock(&q->lock);
01718 if (q->count && q->members) {
01719 if ((mem = ao2_find(q->members, member, OBJ_POINTER))) {
01720 ast_log(LOG_DEBUG, "Found matching member %s in queue '%s'\n", mem->interface, q->name);
01721 if (q->weight > rq->weight) {
01722 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);
01723 found = 1;
01724 }
01725 ao2_ref(mem, -1);
01726 }
01727 }
01728 ast_mutex_unlock(&q->lock);
01729 if (found)
01730 break;
01731 }
01732 return found;
01733 }
01734
01735
01736 static void do_hang(struct callattempt *o)
01737 {
01738 o->stillgoing = 0;
01739 ast_hangup(o->chan);
01740 o->chan = NULL;
01741 }
01742
01743 static char *vars2manager(struct ast_channel *chan, char *vars, size_t len)
01744 {
01745 char *tmp = alloca(len);
01746
01747 if (pbx_builtin_serialize_variables(chan, tmp, len)) {
01748 int i, j;
01749
01750
01751 strcpy(vars, "Variable: ");
01752
01753 for (i = 0, j = 10; (i < len - 1) && (j < len - 1); i++, j++) {
01754 vars[j] = tmp[i];
01755
01756 if (tmp[i + 1] == '\0')
01757 break;
01758 if (tmp[i] == '\n') {
01759 vars[j++] = '\r';
01760 vars[j++] = '\n';
01761
01762 ast_copy_string(&(vars[j]), "Variable: ", len - j);
01763 j += 9;
01764 }
01765 }
01766 if (j > len - 1)
01767 j = len - 1;
01768 vars[j - 2] = '\r';
01769 vars[j - 1] = '\n';
01770 vars[j] = '\0';
01771 } else {
01772
01773 *vars = '\0';
01774 }
01775 return vars;
01776 }
01777
01778 static int ring_entry(struct queue_ent *qe, struct callattempt *tmp, int *busies)
01779 {
01780 int res;
01781 int status;
01782 char tech[256];
01783 char *location;
01784
01785
01786 if (qe->parent->wrapuptime && (time(NULL) - tmp->lastcall < qe->parent->wrapuptime)) {
01787 if (option_debug)
01788 ast_log(LOG_DEBUG, "Wrapuptime not yet expired for %s\n", tmp->interface);
01789 if (qe->chan->cdr)
01790 ast_cdr_busy(qe->chan->cdr);
01791 tmp->stillgoing = 0;
01792 (*busies)++;
01793 return 0;
01794 }
01795
01796 if (!qe->parent->ringinuse && (tmp->member->status != AST_DEVICE_NOT_INUSE) && (tmp->member->status != AST_DEVICE_UNKNOWN)) {
01797 if (option_debug)
01798 ast_log(LOG_DEBUG, "%s in use, can't receive call\n", tmp->interface);
01799 if (qe->chan->cdr)
01800 ast_cdr_busy(qe->chan->cdr);
01801 tmp->stillgoing = 0;
01802 return 0;
01803 }
01804
01805 if (tmp->member->paused) {
01806 if (option_debug)
01807 ast_log(LOG_DEBUG, "%s paused, can't receive call\n", tmp->interface);
01808 if (qe->chan->cdr)
01809 ast_cdr_busy(qe->chan->cdr);
01810 tmp->stillgoing = 0;
01811 return 0;
01812 }
01813 if (use_weight && compare_weight(qe->parent,tmp->member)) {
01814 ast_log(LOG_DEBUG, "Priority queue delaying call to %s:%s\n", qe->parent->name, tmp->interface);
01815 if (qe->chan->cdr)
01816 ast_cdr_busy(qe->chan->cdr);
01817 tmp->stillgoing = 0;
01818 (*busies)++;
01819 return 0;
01820 }
01821
01822 ast_copy_string(tech, tmp->interface, sizeof(tech));
01823 if ((location = strchr(tech, '/')))
01824 *location++ = '\0';
01825 else
01826 location = "";
01827
01828
01829 tmp->chan = ast_request(tech, qe->chan->nativeformats, location, &status);
01830 if (!tmp->chan) {
01831 if (qe->chan->cdr)
01832 ast_cdr_busy(qe->chan->cdr);
01833 tmp->stillgoing = 0;
01834 update_dial_status(qe->parent, tmp->member, status);
01835
01836 ast_mutex_lock(&qe->parent->lock);
01837 qe->parent->rrpos++;
01838 ast_mutex_unlock(&qe->parent->lock);
01839
01840 (*busies)++;
01841 return 0;
01842 } else if (status != tmp->oldstatus)
01843 update_dial_status(qe->parent, tmp->member, status);
01844
01845 tmp->chan->appl = "AppQueue";
01846 tmp->chan->data = "(Outgoing Line)";
01847 tmp->chan->whentohangup = 0;
01848 if (tmp->chan->cid.cid_num)
01849 free(tmp->chan->cid.cid_num);
01850 tmp->chan->cid.cid_num = ast_strdup(qe->chan->cid.cid_num);
01851 if (tmp->chan->cid.cid_name)
01852 free(tmp->chan->cid.cid_name);
01853 tmp->chan->cid.cid_name = ast_strdup(qe->chan->cid.cid_name);
01854 if (tmp->chan->cid.cid_ani)
01855 free(tmp->chan->cid.cid_ani);
01856 tmp->chan->cid.cid_ani = ast_strdup(qe->chan->cid.cid_ani);
01857
01858
01859 ast_channel_inherit_variables(qe->chan, tmp->chan);
01860
01861
01862 tmp->chan->adsicpe = qe->chan->adsicpe;
01863
01864
01865 if ((res = ast_call(tmp->chan, location, 0))) {
01866
01867 if (option_debug)
01868 ast_log(LOG_DEBUG, "ast call on peer returned %d\n", res);
01869 if (option_verbose > 2)
01870 ast_verbose(VERBOSE_PREFIX_3 "Couldn't call %s\n", tmp->interface);
01871 do_hang(tmp);
01872 (*busies)++;
01873 return 0;
01874 } else if (qe->parent->eventwhencalled) {
01875 char vars[2048];
01876
01877 manager_event(EVENT_FLAG_AGENT, "AgentCalled",
01878 "AgentCalled: %s\r\n"
01879 "AgentName: %s\r\n"
01880 "ChannelCalling: %s\r\n"
01881 "CallerID: %s\r\n"
01882 "CallerIDName: %s\r\n"
01883 "Context: %s\r\n"
01884 "Extension: %s\r\n"
01885 "Priority: %d\r\n"
01886 "%s",
01887 tmp->interface, tmp->member->membername, qe->chan->name,
01888 tmp->chan->cid.cid_num ? tmp->chan->cid.cid_num : "unknown",
01889 tmp->chan->cid.cid_name ? tmp->chan->cid.cid_name : "unknown",
01890 qe->chan->context, qe->chan->exten, qe->chan->priority,
01891 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
01892 if (option_verbose > 2)
01893 ast_verbose(VERBOSE_PREFIX_3 "Called %s\n", tmp->interface);
01894 }
01895
01896 return 1;
01897 }
01898
01899
01900 static struct callattempt *find_best(struct callattempt *outgoing)
01901 {
01902 struct callattempt *best = NULL, *cur;
01903
01904 for (cur = outgoing; cur; cur = cur->q_next) {
01905 if (cur->stillgoing &&
01906 !cur->chan &&
01907 (!best || cur->metric < best->metric)) {
01908 best = cur;
01909 }
01910 }
01911
01912 return best;
01913 }
01914
01915 static int ring_one(struct queue_ent *qe, struct callattempt *outgoing, int *busies)
01916 {
01917 int ret = 0;
01918
01919 while (ret == 0) {
01920 struct callattempt *best = find_best(outgoing);
01921 if (!best) {
01922 if (option_debug)
01923 ast_log(LOG_DEBUG, "Nobody left to try ringing in queue\n");
01924 break;
01925 }
01926 if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
01927 struct callattempt *cur;
01928
01929 for (cur = outgoing; cur; cur = cur->q_next) {
01930 if (cur->stillgoing && !cur->chan && cur->metric <= best->metric) {
01931 if (option_debug)
01932 ast_log(LOG_DEBUG, "(Parallel) Trying '%s' with metric %d\n", cur->interface, cur->metric);
01933 ret |= ring_entry(qe, cur, busies);
01934 }
01935 }
01936 } else {
01937
01938 if (option_debug)
01939 ast_log(LOG_DEBUG, "Trying '%s' with metric %d\n", best->interface, best->metric);
01940 ret = ring_entry(qe, best, busies);
01941 }
01942 }
01943
01944 return ret;
01945 }
01946
01947 static int store_next(struct queue_ent *qe, struct callattempt *outgoing)
01948 {
01949 struct callattempt *best = find_best(outgoing);
01950
01951 if (best) {
01952
01953 if (option_debug)
01954 ast_log(LOG_DEBUG, "Next is '%s' with metric %d\n", best->interface, best->metric);
01955 qe->parent->rrpos = best->metric % 1000;
01956 } else {
01957
01958 if (qe->parent->wrapped) {
01959
01960 qe->parent->rrpos = 0;
01961 } else {
01962
01963 qe->parent->rrpos++;
01964 }
01965 }
01966 qe->parent->wrapped = 0;
01967
01968 return 0;
01969 }
01970
01971 static int say_periodic_announcement(struct queue_ent *qe)
01972 {
01973 int res = 0;
01974 time_t now;
01975
01976
01977 time(&now);
01978
01979
01980 if ((now - qe->last_periodic_announce_time) < qe->parent->periodicannouncefrequency)
01981 return 0;
01982
01983
01984 ast_moh_stop(qe->chan);
01985
01986 if (option_verbose > 2)
01987 ast_verbose(VERBOSE_PREFIX_3 "Playing periodic announcement\n");
01988
01989
01990 if (qe->last_periodic_announce_sound >= MAX_PERIODIC_ANNOUNCEMENTS || !strlen(qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound])) {
01991 qe->last_periodic_announce_sound = 0;
01992 }
01993
01994
01995 res = play_file(qe->chan, qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound]);
01996
01997 if ((res > 0 && !valid_exit(qe, res)) || res < 0)
01998 res = 0;
01999
02000
02001 if (!res)
02002 ast_moh_start(qe->chan, qe->moh, NULL);
02003
02004
02005 qe->last_periodic_announce_time = now;
02006
02007
02008 qe->last_periodic_announce_sound++;
02009
02010 return res;
02011 }
02012
02013 static void record_abandoned(struct queue_ent *qe)
02014 {
02015 ast_mutex_lock(&qe->parent->lock);
02016 manager_event(EVENT_FLAG_AGENT, "QueueCallerAbandon",
02017 "Queue: %s\r\n"
02018 "Uniqueid: %s\r\n"
02019 "Position: %d\r\n"
02020 "OriginalPosition: %d\r\n"
02021 "HoldTime: %d\r\n",
02022 qe->parent->name, qe->chan->uniqueid, qe->pos, qe->opos, (int)(time(NULL) - qe->start));
02023
02024 qe->parent->callsabandoned++;
02025 ast_mutex_unlock(&qe->parent->lock);
02026 }
02027
02028
02029 static void rna(int rnatime, struct queue_ent *qe, char *interface, char *membername)
02030 {
02031 if (option_verbose > 2)
02032 ast_verbose( VERBOSE_PREFIX_3 "Nobody picked up in %d ms\n", rnatime);
02033 ast_queue_log(qe->parent->name, qe->chan->uniqueid, membername, "RINGNOANSWER", "%d", rnatime);
02034 if (qe->parent->autopause) {
02035 if (!set_member_paused(qe->parent->name, interface, 1)) {
02036 if (option_verbose > 2)
02037 ast_verbose( VERBOSE_PREFIX_3 "Auto-Pausing Queue Member %s in queue %s since they failed to answer.\n", interface, qe->parent->name);
02038 } else {
02039 if (option_verbose > 2)
02040 ast_verbose( VERBOSE_PREFIX_3 "Failed to pause Queue Member %s in queue %s!\n", interface, qe->parent->name);
02041 }
02042 }
02043 return;
02044 }
02045
02046 #define AST_MAX_WATCHERS 256
02047
02048 static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callattempt *outgoing, int *to, char *digit, int prebusies, int caller_disconnect, int forwardsallowed)
02049 {
02050 char *queue = qe->parent->name;
02051 struct callattempt *o, *start = NULL, *prev = NULL;
02052 int status;
02053 int numbusies = prebusies;
02054 int numnochan = 0;
02055 int stillgoing = 0;
02056 int orig = *to;
02057 struct ast_frame *f;
02058 struct callattempt *peer = NULL;
02059 struct ast_channel *winner;
02060 struct ast_channel *in = qe->chan;
02061 char on[80] = "";
02062 char membername[80] = "";
02063 long starttime = 0;
02064 long endtime = 0;
02065
02066 starttime = (long) time(NULL);
02067
02068 while (*to && !peer) {
02069 int numlines, retry, pos = 1;
02070 struct ast_channel *watchers[AST_MAX_WATCHERS];
02071 watchers[0] = in;
02072 start = NULL;
02073
02074 for (retry = 0; retry < 2; retry++) {
02075 numlines = 0;
02076 for (o = outgoing; o; o = o->q_next) {
02077 if (o->stillgoing) {
02078 stillgoing = 1;
02079 if (o->chan) {
02080 watchers[pos++] = o->chan;
02081 if (!start)
02082 start = o;
02083 else
02084 prev->call_next = o;
02085 prev = o;
02086 }
02087 }
02088 numlines++;
02089 }
02090 if (pos > 1 || !stillgoing ||
02091 (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) )
02092 break;
02093
02094
02095 ring_one(qe, outgoing, &numbusies);
02096
02097 }
02098 if (pos == 1 ) {
02099 if (numlines == (numbusies + numnochan)) {
02100 ast_log(LOG_DEBUG, "Everyone is busy at this time\n");
02101 } else {
02102 ast_log(LOG_NOTICE, "No one is answering queue '%s' (%d/%d/%d)\n", queue, numlines, numbusies, numnochan);
02103 }
02104 *to = 0;
02105 return NULL;
02106 }
02107 winner = ast_waitfor_n(watchers, pos, to);
02108 for (o = start; o; o = o->call_next) {
02109 if (o->stillgoing && (o->chan) && (o->chan->_state == AST_STATE_UP)) {
02110 if (!peer) {
02111 if (option_verbose > 2)
02112 ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
02113 peer = o;
02114 }
02115 } else if (o->chan && (o->chan == winner)) {
02116
02117 ast_copy_string(on, o->member->interface, sizeof(on));
02118 ast_copy_string(membername, o->member->membername, sizeof(membername));
02119
02120 if (!ast_strlen_zero(o->chan->call_forward) && !forwardsallowed) {
02121 if (option_verbose > 2)
02122 ast_verbose(VERBOSE_PREFIX_3 "Forwarding %s to '%s' prevented.\n", in->name, o->chan->call_forward);
02123 numnochan++;
02124 do_hang(o);
02125 winner = NULL;
02126 continue;
02127 } else if (!ast_strlen_zero(o->chan->call_forward)) {
02128 char tmpchan[256];
02129 char *stuff;
02130 char *tech;
02131
02132 ast_copy_string(tmpchan, o->chan->call_forward, sizeof(tmpchan));
02133 if ((stuff = strchr(tmpchan, '/'))) {
02134 *stuff++ = '\0';
02135 tech = tmpchan;
02136 } else {
02137 snprintf(tmpchan, sizeof(tmpchan), "%s@%s", o->chan->call_forward, o->chan->context);
02138 stuff = tmpchan;
02139 tech = "Local";
02140 }
02141
02142 if (option_verbose > 2)
02143 ast_verbose(VERBOSE_PREFIX_3 "Now forwarding %s to '%s/%s' (thanks to %s)\n", in->name, tech, stuff, o->chan->name);
02144
02145 o->chan = ast_request(tech, in->nativeformats, stuff, &status);
02146 if (status != o->oldstatus)
02147 update_dial_status(qe->parent, o->member, status);
02148 if (!o->chan) {
02149 ast_log(LOG_NOTICE, "Unable to create local channel for call forward to '%s/%s'\n", tech, stuff);
02150 o->stillgoing = 0;
02151 numnochan++;
02152 } else {
02153 ast_channel_inherit_variables(in, o->chan);
02154 ast_channel_datastore_inherit(in, o->chan);
02155 if (o->chan->cid.cid_num)
02156 free(o->chan->cid.cid_num);
02157 o->chan->cid.cid_num = ast_strdup(in->cid.cid_num);
02158
02159 if (o->chan->cid.cid_name)
02160 free(o->chan->cid.cid_name);
02161 o->chan->cid.cid_name = ast_strdup(in->cid.cid_name);
02162
02163 ast_string_field_set(o->chan, accountcode, in->accountcode);
02164 o->chan->cdrflags = in->cdrflags;
02165
02166 if (in->cid.cid_ani) {
02167 if (o->chan->cid.cid_ani)
02168 free(o->chan->cid.cid_ani);
02169 o->chan->cid.cid_ani = ast_strdup(in->cid.cid_ani);
02170 }
02171 if (o->chan->cid.cid_rdnis)
02172 free(o->chan->cid.cid_rdnis);
02173 o->chan->cid.cid_rdnis = ast_strdup(S_OR(in->macroexten, in->exten));
02174 if (ast_call(o->chan, tmpchan, 0)) {
02175 ast_log(LOG_NOTICE, "Failed to dial on local channel for call forward to '%s'\n", tmpchan);
02176 do_hang(o);
02177 numnochan++;
02178 }
02179 }
02180
02181 ast_hangup(winner);
02182 continue;
02183 }
02184 f = ast_read(winner);
02185 if (f) {
02186 if (f->frametype == AST_FRAME_CONTROL) {
02187 switch (f->subclass) {
02188 case AST_CONTROL_ANSWER:
02189
02190 if (!peer) {
02191 if (option_verbose > 2)
02192 ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
02193 peer = o;
02194 }
02195 break;
02196 case AST_CONTROL_BUSY:
02197 if (option_verbose > 2)
02198 ast_verbose( VERBOSE_PREFIX_3 "%s is busy\n", o->chan->name);
02199 if (in->cdr)
02200 ast_cdr_busy(in->cdr);
02201 do_hang(o);
02202 endtime = (long)time(NULL);
02203 endtime -= starttime;
02204 rna(endtime*1000, qe, on, membername);
02205 if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
02206 if (qe->parent->timeoutrestart)
02207 *to = orig;
02208 ring_one(qe, outgoing, &numbusies);
02209 }
02210 numbusies++;
02211 break;
02212 case AST_CONTROL_CONGESTION:
02213 if (option_verbose > 2)
02214 ast_verbose( VERBOSE_PREFIX_3 "%s is circuit-busy\n", o->chan->name);
02215 if (in->cdr)
02216 ast_cdr_busy(in->cdr);
02217 endtime = (long)time(NULL);
02218 endtime -= starttime;
02219 rna(endtime*1000, qe, on, membername);
02220 do_hang(o);
02221 if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
02222 if (qe->parent->timeoutrestart)
02223 *to = orig;
02224 ring_one(qe, outgoing, &numbusies);
02225 }
02226 numbusies++;
02227 break;
02228 case AST_CONTROL_RINGING:
02229 if (option_verbose > 2)
02230 ast_verbose( VERBOSE_PREFIX_3 "%s is ringing\n", o->chan->name);
02231 break;
02232 case AST_CONTROL_OFFHOOK:
02233
02234 break;
02235 default:
02236 ast_log(LOG_DEBUG, "Dunno what to do with control type %d\n", f->subclass);
02237 }
02238 }
02239 ast_frfree(f);
02240 } else {
02241 endtime = (long) time(NULL) - starttime;
02242 rna(endtime * 1000, qe, on, membername);
02243 do_hang(o);
02244 if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
02245 if (qe->parent->timeoutrestart)
02246 *to = orig;
02247 ring_one(qe, outgoing, &numbusies);
02248 }
02249 }
02250 }
02251 }
02252 if (winner == in) {
02253 f = ast_read(in);
02254 if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) {
02255
02256 *to = -1;
02257 if (f)
02258 ast_frfree(f);
02259 return NULL;
02260 }
02261 if ((f->frametype == AST_FRAME_DTMF) && caller_disconnect && (f->subclass == '*')) {
02262 if (option_verbose > 3)
02263 ast_verbose(VERBOSE_PREFIX_3 "User hit %c to disconnect call.\n", f->subclass);
02264 *to = 0;
02265 ast_frfree(f);
02266 return NULL;
02267 }
02268 if ((f->frametype == AST_FRAME_DTMF) && valid_exit(qe, f->subclass)) {
02269 if (option_verbose > 3)
02270 ast_verbose(VERBOSE_PREFIX_3 "User pressed digit: %c\n", f->subclass);
02271 *to = 0;
02272 *digit = f->subclass;
02273 ast_frfree(f);
02274 return NULL;
02275 }
02276 ast_frfree(f);
02277 }
02278 if (!*to) {
02279 for (o = start; o; o = o->call_next)
02280 rna(orig, qe, o->interface, o->member->membername);
02281 }
02282 }
02283
02284 return peer;
02285 }
02286
02287 static int is_our_turn(struct queue_ent *qe)
02288 {
02289 struct queue_ent *ch;
02290 struct member *cur;
02291 int avl = 0;
02292 int idx = 0;
02293 int res;
02294
02295 if (!qe->parent->autofill) {
02296
02297 ch = qe->parent->head;
02298
02299 if (ch == qe) {
02300 if (option_debug)
02301 ast_log(LOG_DEBUG, "It's our turn (%s).\n", qe->chan->name);
02302 res = 1;
02303 } else {
02304 if (option_debug)
02305 ast_log(LOG_DEBUG, "It's not our turn (%s).\n", qe->chan->name);
02306 res = 0;
02307 }
02308
02309 } else {
02310
02311 ast_mutex_lock(&qe->parent->lock);
02312
02313 ch = qe->parent->head;
02314
02315 if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
02316 if (option_debug)
02317 ast_log(LOG_DEBUG, "Even though there may be multiple members available, the strategy is ringall so only the head call is allowed in\n");
02318 avl = 1;
02319 } else {
02320 struct ao2_iterator mem_iter = ao2_iterator_init(qe->parent->members, 0);
02321 while ((cur = ao2_iterator_next(&mem_iter))) {
02322 switch (cur->status) {
02323 case AST_DEVICE_NOT_INUSE:
02324 case AST_DEVICE_UNKNOWN:
02325 if (!cur->paused)
02326 avl++;
02327 break;
02328 }
02329 ao2_ref(cur, -1);
02330 }
02331 }
02332
02333 if (option_debug)
02334 ast_log(LOG_DEBUG, "There are %d available members.\n", avl);
02335
02336 while ((idx < avl) && (ch) && !ch->pending && (ch != qe)) {
02337 idx++;
02338 ch = ch->next;
02339 }
02340
02341
02342 if (ch && idx < avl) {
02343 if (option_debug)
02344 ast_log(LOG_DEBUG, "It's our turn (%s).\n", qe->chan->name);
02345 res = 1;
02346 } else {
02347 if (option_debug)
02348 ast_log(LOG_DEBUG, "It's not our turn (%s).\n", qe->chan->name);
02349 res = 0;
02350 }
02351
02352 ast_mutex_unlock(&qe->parent->lock);
02353 }
02354
02355 return res;
02356 }
02357
02358 static int wait_our_turn(struct queue_ent *qe, int ringing, enum queue_result *reason)
02359 {
02360 int res = 0;
02361
02362
02363 for (;;) {
02364 enum queue_member_status stat;
02365
02366 if (is_our_turn(qe))
02367 break;
02368
02369
02370 if (qe->expire && (time(NULL) > qe->expire)) {
02371 *reason = QUEUE_TIMEOUT;
02372 break;
02373 }
02374
02375 stat = get_member_status(qe->parent, qe->max_penalty);
02376
02377
02378 if (qe->parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) {
02379 *reason = QUEUE_LEAVEEMPTY;
02380 ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long)time(NULL) - qe->start);
02381 leave_queue(qe);
02382 break;
02383 }
02384
02385
02386 if ((qe->parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) {
02387 *reason = QUEUE_LEAVEUNAVAIL;
02388 ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long)time(NULL) - qe->start);
02389 leave_queue(qe);
02390 break;
02391 }
02392
02393
02394 if (qe->parent->announcefrequency && !ringing &&
02395 (res = say_position(qe)))
02396 break;
02397
02398
02399 if (qe->parent->periodicannouncefrequency && !ringing &&
02400 (res = say_periodic_announcement(qe)))
02401 break;
02402
02403
02404 if ((res = ast_waitfordigit(qe->chan, RECHECK * 1000))) {
02405 if (res > 0 && !valid_exit(qe, res))
02406 res = 0;
02407 else
02408 break;
02409 }
02410 }
02411
02412 return res;
02413 }
02414
02415 static int update_queue(struct call_queue *q, struct member *member, int callcompletedinsl)
02416 {
02417 ast_mutex_lock(&q->lock);
02418 time(&member->lastcall);
02419 member->calls++;
02420 q->callscompleted++;
02421 if (callcompletedinsl)
02422 q->callscompletedinsl++;
02423 ast_mutex_unlock(&q->lock);
02424 return 0;
02425 }
02426
02427 static int calc_metric(struct call_queue *q, struct member *mem, int pos, struct queue_ent *qe, struct callattempt *tmp)
02428 {
02429 if (qe->max_penalty && (mem->penalty > qe->max_penalty))
02430 return -1;
02431
02432 switch (q->strategy) {
02433 case QUEUE_STRATEGY_RINGALL:
02434
02435 tmp->metric = mem->penalty * 1000000;
02436 break;
02437 case QUEUE_STRATEGY_ROUNDROBIN:
02438 if (!pos) {
02439 if (!q->wrapped) {
02440
02441 q->rrpos = 0;
02442 } else {
02443
02444 q->rrpos++;
02445 }
02446 q->wrapped = 0;
02447 }
02448
02449 case QUEUE_STRATEGY_RRMEMORY:
02450 if (pos < q->rrpos) {
02451 tmp->metric = 1000 + pos;
02452 } else {
02453 if (pos > q->rrpos)
02454
02455 q->wrapped = 1;
02456 tmp->metric = pos;
02457 }
02458 tmp->metric += mem->penalty * 1000000;
02459 break;
02460 case QUEUE_STRATEGY_RANDOM:
02461 tmp->metric = ast_random() % 1000;
02462 tmp->metric += mem->penalty * 1000000;
02463 break;
02464 case QUEUE_STRATEGY_FEWESTCALLS:
02465 tmp->metric = mem->calls;
02466 tmp->metric += mem->penalty * 1000000;
02467 break;
02468 case QUEUE_STRATEGY_LEASTRECENT:
02469 if (!mem->lastcall)
02470 tmp->metric = 0;
02471 else
02472 tmp->metric = 1000000 - (time(NULL) - mem->lastcall);
02473 tmp->metric += mem->penalty * 1000000;
02474 break;
02475 default:
02476 ast_log(LOG_WARNING, "Can't calculate metric for unknown strategy %d\n", q->strategy);
02477 break;
02478 }
02479 return 0;
02480 }
02481
02482 static int try_calling(struct queue_ent *qe, const char *options, char *announceoverride, const char *url, int *tries, int *noption, const char *agi)
02483 {
02484 struct member *cur;
02485 struct callattempt *outgoing = NULL;
02486 int to;
02487 char oldexten[AST_MAX_EXTENSION]="";
02488 char oldcontext[AST_MAX_CONTEXT]="";
02489 char queuename[256]="";
02490 struct ast_channel *peer;
02491 struct ast_channel *which;
02492 struct callattempt *lpeer;
02493 struct member *member;
02494 struct ast_app *app;
02495 int res = 0, bridge = 0;
02496 int numbusies = 0;
02497 int x=0;
02498 char *announce = NULL;
02499 char digit = 0;
02500 time_t callstart;
02501 time_t now = time(NULL);
02502 struct ast_bridge_config bridge_config;
02503 char nondataquality = 1;
02504 char *agiexec = NULL;
02505 int ret = 0;
02506 const char *monitorfilename;
02507 const char *monitor_exec;
02508 const char *monitor_options;
02509 char tmpid[256], tmpid2[256];
02510 char meid[1024], meid2[1024];
02511 char mixmonargs[1512];
02512 struct ast_app *mixmonapp = NULL;
02513 char *p;
02514 char vars[2048];
02515 int forwardsallowed = 1;
02516 int callcompletedinsl;
02517 struct ao2_iterator memi;
02518 struct ast_datastore *datastore;
02519
02520 ast_channel_lock(qe->chan);
02521 datastore = ast_channel_datastore_find(qe->chan, &dialed_interface_info, NULL);
02522 ast_channel_unlock(qe->chan);
02523
02524 memset(&bridge_config, 0, sizeof(bridge_config));
02525 time(&now);
02526
02527 for (; options && *options; options++)
02528 switch (*options) {
02529 case 't':
02530 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_REDIRECT);
02531 break;
02532 case 'T':
02533 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_REDIRECT);
02534 break;
02535 case 'w':
02536 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMON);
02537 break;
02538 case 'W':
02539 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMON);
02540 break;
02541 case 'd':
02542 nondataquality = 0;
02543 break;
02544 case 'h':
02545 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_DISCONNECT);
02546 break;
02547 case 'H':
02548 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT);
02549 break;
02550 case 'n':
02551 if (qe->parent->strategy == QUEUE_STRATEGY_ROUNDROBIN || qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY)
02552 (*tries)++;
02553 else
02554 *tries = qe->parent->membercount;
02555 *noption = 1;
02556 break;
02557 case 'i':
02558 forwardsallowed = 0;
02559 break;
02560 }
02561
02562
02563 if (use_weight)
02564 AST_LIST_LOCK(&queues);
02565 ast_mutex_lock(&qe->parent->lock);
02566 if (option_debug)
02567 ast_log(LOG_DEBUG, "%s is trying to call a queue member.\n",
02568 qe->chan->name);
02569 ast_copy_string(queuename, qe->parent->name, sizeof(queuename));
02570 if (!ast_strlen_zero(qe->announce))
02571 announce = qe->announce;
02572 if (!ast_strlen_zero(announceoverride))
02573 announce = announceoverride;
02574
02575 memi = ao2_iterator_init(qe->parent->members, 0);
02576 while ((cur = ao2_iterator_next(&memi))) {
02577 struct callattempt *tmp = ast_calloc(1, sizeof(*tmp));
02578 struct ast_dialed_interface *di;
02579 AST_LIST_HEAD(, ast_dialed_interface) *dialed_interfaces;
02580 if (!tmp) {
02581 ao2_ref(cur, -1);
02582 ast_mutex_unlock(&qe->parent->lock);
02583 if (use_weight)
02584 AST_LIST_UNLOCK(&queues);
02585 goto out;
02586 }
02587 if (!datastore) {
02588 if (!(datastore = ast_channel_datastore_alloc(&dialed_interface_info, NULL))) {
02589 ao2_ref(cur, -1);
02590 ast_mutex_unlock(&qe->parent->lock);
02591 if (use_weight)
02592 AST_LIST_UNLOCK(&queues);
02593 free(tmp);
02594 goto out;
02595 }
02596 datastore->inheritance = DATASTORE_INHERIT_FOREVER;
02597 if (!(dialed_interfaces = ast_calloc(1, sizeof(*dialed_interfaces)))) {
02598 ao2_ref(cur, -1);
02599 ast_mutex_unlock(&qe->parent->lock);
02600 if (use_weight)
02601 AST_LIST_UNLOCK(&queues);
02602 free(tmp);
02603 goto out;
02604 }
02605 datastore->data = dialed_interfaces;
02606 AST_LIST_HEAD_INIT(dialed_interfaces);
02607
02608 ast_channel_lock(qe->chan);
02609 ast_channel_datastore_add(qe->chan, datastore);
02610 ast_channel_unlock(qe->chan);
02611 } else
02612 dialed_interfaces = datastore->data;
02613
02614 AST_LIST_LOCK(dialed_interfaces);
02615 AST_LIST_TRAVERSE(dialed_interfaces, di, list) {
02616 if (!strcasecmp(cur->interface, di->interface)) {
02617 ast_log(LOG_DEBUG, "Skipping dialing interface '%s' since it has already been dialed\n",
02618 di->interface);
02619 break;
02620 }
02621 }
02622 AST_LIST_UNLOCK(dialed_interfaces);
02623
02624 if (di) {
02625 free(tmp);
02626 continue;
02627 }
02628
02629
02630
02631
02632
02633 if (strncasecmp(cur->interface, "Local/", 6)) {
02634 if (!(di = ast_calloc(1, sizeof(*di) + strlen(cur->interface)))) {
02635 ao2_ref(cur, -1);
02636 ast_mutex_unlock(&qe->parent->lock);
02637 if (use_weight)
02638 AST_LIST_UNLOCK(&queues);
02639 free(tmp);
02640 goto out;
02641 }
02642 strcpy(di->interface, cur->interface);
02643
02644 AST_LIST_LOCK(dialed_interfaces);
02645 AST_LIST_INSERT_TAIL(dialed_interfaces, di, list);
02646 AST_LIST_UNLOCK(dialed_interfaces);
02647 }
02648
02649 tmp->stillgoing = -1;
02650 tmp->member = cur;
02651 tmp->oldstatus = cur->status;
02652 tmp->lastcall = cur->lastcall;
02653 ast_copy_string(tmp->interface, cur->interface, sizeof(tmp->interface));
02654
02655
02656 if (!calc_metric(qe->parent, cur, x++, qe, tmp)) {
02657
02658
02659
02660 tmp->q_next = outgoing;
02661 outgoing = tmp;
02662
02663 if (outgoing->chan && (outgoing->chan->_state == AST_STATE_UP))
02664 break;
02665 } else {
02666 ao2_ref(cur, -1);
02667 free(tmp);
02668 }
02669 }
02670 if (qe->expire && (!qe->parent->timeout || (qe->expire - now) <= qe->parent->timeout))
02671 to = (qe->expire - now) * 1000;
02672 else
02673 to = (qe->parent->timeout) ? qe->parent->timeout * 1000 : -1;
02674 ++qe->pending;
02675 ring_one(qe, outgoing, &numbusies);
02676 ast_mutex_unlock(&qe->parent->lock);
02677 if (use_weight)
02678 AST_LIST_UNLOCK(&queues);
02679 lpeer = wait_for_answer(qe, outgoing, &to, &digit, numbusies, ast_test_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT), forwardsallowed);
02680 if (datastore) {
02681 ast_channel_datastore_remove(qe->chan, datastore);
02682 ast_channel_datastore_free(datastore);
02683 }
02684 ast_mutex_lock(&qe->parent->lock);
02685 if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY) {
02686 store_next(qe, outgoing);
02687 }
02688 ast_mutex_unlock(&qe->parent->lock);
02689 peer = lpeer ? lpeer->chan : NULL;
02690 if (!peer) {
02691 qe->pending = 0;
02692 if (to) {
02693
02694 res = -1;
02695 } else {
02696
02697 res = digit;
02698 }
02699 if (option_debug && res == -1)
02700 ast_log(LOG_DEBUG, "%s: Nobody answered.\n", qe->chan->name);
02701 } else {
02702
02703
02704
02705 qe->handled++;
02706 if (!strcmp(qe->chan->tech->type, "Zap"))
02707 ast_channel_setoption(qe->chan, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
02708 if (!strcmp(peer->tech->type, "Zap"))
02709 ast_channel_setoption(peer, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
02710
02711 time(&now);
02712 recalc_holdtime(qe, (now - qe->start));
02713 ast_mutex_lock(&qe->parent->lock);
02714 callcompletedinsl = ((now - qe->start) <= qe->parent->servicelevel);
02715 ast_mutex_unlock(&qe->parent->lock);
02716 member = lpeer->member;
02717
02718 ao2_ref(member, 1);
02719 hangupcalls(outgoing, peer);
02720 outgoing = NULL;
02721 if (announce || qe->parent->reportholdtime || qe->parent->memberdelay) {
02722 int res2;
02723
02724 res2 = ast_autoservice_start(qe->chan);
02725 if (!res2) {
02726 if (qe->parent->memberdelay) {
02727 ast_log(LOG_NOTICE, "Delaying member connect for %d seconds\n", qe->parent->memberdelay);
02728 res2 |= ast_safe_sleep(peer, qe->parent->memberdelay * 1000);
02729 }
02730 if (!res2 && announce) {
02731 play_file(peer, announce);
02732 }
02733 if (!res2 && qe->parent->reportholdtime) {
02734 if (!play_file(peer, qe->parent->sound_reporthold)) {
02735 int holdtime;
02736
02737 time(&now);
02738 holdtime = abs((now - qe->start) / 60);
02739 if (holdtime < 2) {
02740 play_file(peer, qe->parent->sound_lessthan);
02741 ast_say_number(peer, 2, AST_DIGIT_ANY, peer->language, NULL);
02742 } else
02743 ast_say_number(peer, holdtime, AST_DIGIT_ANY, peer->language, NULL);
02744 play_file(peer, qe->parent->sound_minutes);
02745 }
02746 }
02747 }
02748 res2 |= ast_autoservice_stop(qe->chan);
02749 if (peer->_softhangup) {
02750
02751 ast_log(LOG_WARNING, "Agent on %s hungup on the customer.\n", peer->name);
02752 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "AGENTDUMP", "%s", "");
02753 record_abandoned(qe);
02754 if (qe->parent->eventwhencalled)
02755 manager_event(EVENT_FLAG_AGENT, "AgentDump",
02756 "Queue: %s\r\n"
02757 "Uniqueid: %s\r\n"
02758 "Channel: %s\r\n"
02759 "Member: %s\r\n"
02760 "MemberName: %s\r\n"
02761 "%s",
02762 queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
02763 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
02764 ast_hangup(peer);
02765 ao2_ref(member, -1);
02766 goto out;
02767 } else if (res2) {
02768
02769 ast_log(LOG_NOTICE, "Caller was about to talk to agent on %s but the caller hungup.\n", peer->name);
02770 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "ABANDON", "%d|%d|%ld", qe->pos, qe->opos, (long)time(NULL) - qe->start);
02771 record_abandoned(qe);
02772 ast_hangup(peer);
02773 ao2_ref(member, -1);
02774 return -1;
02775 }
02776 }
02777
02778 ast_moh_stop(qe->chan);
02779
02780 if (qe->chan->cdr)
02781 ast_cdr_setdestchan(qe->chan->cdr, peer->name);
02782
02783 res = ast_channel_make_compatible(qe->chan, peer);
02784 if (res < 0) {
02785 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "SYSCOMPAT", "%s", "");
02786 ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", qe->chan->name, peer->name);
02787 record_abandoned(qe);
02788 ast_hangup(peer);
02789 ao2_ref(member, -1);
02790 return -1;
02791 }
02792
02793 if (qe->parent->setinterfacevar)
02794 pbx_builtin_setvar_helper(qe->chan, "MEMBERINTERFACE", member->interface);
02795
02796
02797 if (qe->parent->monfmt && *qe->parent->monfmt) {
02798 if (!qe->parent->montype) {
02799 if (option_debug)
02800 ast_log(LOG_DEBUG, "Starting Monitor as requested.\n");
02801 monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME");
02802 if (pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC") || pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC_ARGS"))
02803 which = qe->chan;
02804 else
02805 which = peer;
02806 if (monitorfilename)
02807 ast_monitor_start(which, qe->parent->monfmt, monitorfilename, NULL, NULL, 1 );
02808 else if (qe->chan->cdr)
02809 ast_monitor_start(which, qe->parent->monfmt, qe->chan->cdr->uniqueid, NULL, NULL, 1 );
02810 else {
02811
02812 snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random());
02813 ast_monitor_start(which, qe->parent->monfmt, tmpid, NULL, NULL, 1 );
02814 }
02815 if (qe->parent->monjoin)
02816 ast_monitor_setjoinfiles(which, 1);
02817 } else {
02818 if (option_debug)
02819 ast_log(LOG_DEBUG, "Starting MixMonitor as requested.\n");
02820 monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME");
02821 if (!monitorfilename) {
02822 if (qe->chan->cdr)
02823 ast_copy_string(tmpid, qe->chan->cdr->uniqueid, sizeof(tmpid)-1);
02824 else
02825 snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random());
02826 } else {
02827 ast_copy_string(tmpid2, monitorfilename, sizeof(tmpid2)-1);
02828 for (p = tmpid2; *p ; p++) {
02829 if (*p == '^' && *(p+1) == '{') {
02830 *p = '$';
02831 }
02832 }
02833
02834 memset(tmpid, 0, sizeof(tmpid));
02835 pbx_substitute_variables_helper(qe->chan, tmpid2, tmpid, sizeof(tmpid) - 1);
02836 }
02837
02838 monitor_exec = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC");
02839 monitor_options = pbx_builtin_getvar_helper(qe->chan, "MONITOR_OPTIONS");
02840
02841 if (monitor_exec) {
02842 ast_copy_string(meid2, monitor_exec, sizeof(meid2)-1);
02843 for (p = meid2; *p ; p++) {
02844 if (*p == '^' && *(p+1) == '{') {
02845 *p = '$';
02846 }
02847 }
02848
02849 memset(meid, 0, sizeof(meid));
02850 pbx_substitute_variables_helper(qe->chan, meid2, meid, sizeof(meid) - 1);
02851 }
02852
02853 snprintf(tmpid2, sizeof(tmpid2)-1, "%s.%s", tmpid, qe->parent->monfmt);
02854
02855 mixmonapp = pbx_findapp("MixMonitor");
02856
02857 if (strchr(tmpid2, '|')) {
02858 ast_log(LOG_WARNING, "monitor-format (in queues.conf) and MONITOR_FILENAME cannot contain a '|'! Not recording.\n");
02859 mixmonapp = NULL;
02860 }
02861
02862 if (!monitor_options)
02863 monitor_options = "";
02864
02865 if (strchr(monitor_options, '|')) {
02866 ast_log(LOG_WARNING, "MONITOR_OPTIONS cannot contain a '|'! Not recording.\n");
02867 mixmonapp = NULL;
02868 }
02869
02870 if (mixmonapp) {
02871 if (!ast_strlen_zero(monitor_exec))
02872 snprintf(mixmonargs, sizeof(mixmonargs)-1, "%s|b%s|%s", tmpid2, monitor_options, monitor_exec);
02873 else
02874 snprintf(mixmonargs, sizeof(mixmonargs)-1, "%s|b%s", tmpid2, monitor_options);
02875
02876 if (option_debug)
02877 ast_log(LOG_DEBUG, "Arguments being passed to MixMonitor: %s\n", mixmonargs);
02878
02879 if (qe->chan->cdr)
02880 ast_set_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED);
02881 ret = pbx_exec(qe->chan, mixmonapp, mixmonargs);
02882 if (qe->chan->cdr)
02883 ast_clear_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED);
02884
02885 } else
02886 ast_log(LOG_WARNING, "Asked to run MixMonitor on this call, but cannot find the MixMonitor app!\n");
02887
02888 }
02889 }
02890
02891 leave_queue(qe);
02892 if (!ast_strlen_zero(url) && ast_channel_supports_html(peer)) {
02893 if (option_debug)
02894 ast_log(LOG_DEBUG, "app_queue: sendurl=%s.\n", url);
02895 ast_channel_sendurl(peer, url);
02896 }
02897 if (!ast_strlen_zero(agi)) {
02898 if (option_debug)
02899 ast_log(LOG_DEBUG, "app_queue: agi=%s.\n", agi);
02900 app = pbx_findapp("agi");
02901 if (app) {
02902 agiexec = ast_strdupa(agi);
02903 ret = pbx_exec(qe->chan, app, agiexec);
02904 } else
02905 ast_log(LOG_WARNING, "Asked to execute an AGI on this channel, but could not find application (agi)!\n");
02906 }
02907 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "CONNECT", "%ld|%s", (long)time(NULL) - qe->start, peer->uniqueid);
02908 if (qe->parent->eventwhencalled)
02909 manager_event(EVENT_FLAG_AGENT, "AgentConnect",
02910 "Queue: %s\r\n"
02911 "Uniqueid: %s\r\n"
02912 "Channel: %s\r\n"
02913 "Member: %s\r\n"
02914 "MemberName: %s\r\n"
02915 "Holdtime: %ld\r\n"
02916 "BridgedChannel: %s\r\n"
02917 "%s",
02918 queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
02919 (long)time(NULL) - qe->start, peer->uniqueid,
02920 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
02921 ast_copy_string(oldcontext, qe->chan->context, sizeof(oldcontext));
02922 ast_copy_string(oldexten, qe->chan->exten, sizeof(oldexten));
02923 time(&callstart);
02924
02925 if (member->status == AST_DEVICE_NOT_INUSE)
02926 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);
02927
02928
02929 bridge = ast_bridge_call(qe->chan,peer, &bridge_config);
02930
02931 if (strcasecmp(oldcontext, qe->chan->context) || strcasecmp(oldexten, qe->chan->exten)) {
02932 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld",
02933 qe->chan->exten, qe->chan->context, (long) (callstart - qe->start),
02934 (long) (time(NULL) - callstart));
02935 } else if (qe->chan->_softhangup) {
02936 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETECALLER", "%ld|%ld|%d",
02937 (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
02938 if (qe->parent->eventwhencalled)
02939 manager_event(EVENT_FLAG_AGENT, "AgentComplete",
02940 "Queue: %s\r\n"
02941 "Uniqueid: %s\r\n"
02942 "Channel: %s\r\n"
02943 "Member: %s\r\n"
02944 "MemberName: %s\r\n"
02945 "HoldTime: %ld\r\n"
02946 "TalkTime: %ld\r\n"
02947 "Reason: caller\r\n"
02948 "%s",
02949 queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
02950 (long)(callstart - qe->start), (long)(time(NULL) - callstart),
02951 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
02952 } else {
02953 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETEAGENT", "%ld|%ld|%d",
02954 (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
02955 if (qe->parent->eventwhencalled)
02956 manager_event(EVENT_FLAG_AGENT, "AgentComplete",
02957 "Queue: %s\r\n"
02958 "Uniqueid: %s\r\n"
02959 "Channel: %s\r\n"
02960 "MemberName: %s\r\n"
02961 "HoldTime: %ld\r\n"
02962 "TalkTime: %ld\r\n"
02963 "Reason: agent\r\n"
02964 "%s",
02965 queuename, qe->chan->uniqueid, peer->name, member->membername, (long)(callstart - qe->start),
02966 (long)(time(NULL) - callstart),
02967 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
02968 }
02969
02970 if (bridge != AST_PBX_NO_HANGUP_PEER)
02971 ast_hangup(peer);
02972 update_queue(qe->parent, member, callcompletedinsl);
02973 res = bridge ? bridge : 1;
02974 ao2_ref(member, -1);
02975 }
02976 out:
02977 hangupcalls(outgoing, NULL);
02978
02979 return res;
02980 }
02981
02982 static int wait_a_bit(struct queue_ent *qe)
02983 {
02984
02985 int retrywait = qe->parent->retry * 1000;
02986
02987 int res = ast_waitfordigit(qe->chan, retrywait);
02988 if (res > 0 && !valid_exit(qe, res))
02989 res = 0;
02990
02991 return res;
02992 }
02993
02994 static struct member *interface_exists(struct call_queue *q, const char *interface)
02995 {
02996 struct member *mem;
02997 struct ao2_iterator mem_iter;
02998
02999 if (!q)
03000 return NULL;
03001
03002 mem_iter = ao2_iterator_init(q->members, 0);
03003 while ((mem = ao2_iterator_next(&mem_iter))) {
03004 if (!strcasecmp(interface, mem->interface))
03005 return mem;
03006 ao2_ref(mem, -1);
03007 }
03008
03009 return NULL;
03010 }
03011
03012
03013
03014
03015
03016
03017
03018 static void dump_queue_members(struct call_queue *pm_queue)
03019 {
03020 struct member *cur_member;
03021 char value[PM_MAX_LEN];
03022 int value_len = 0;
03023 int res;
03024 struct ao2_iterator mem_iter;
03025
03026 memset(value, 0, sizeof(value));
03027
03028 if (!pm_queue)
03029 return;
03030
03031 mem_iter = ao2_iterator_init(pm_queue->members, 0);
03032 while ((cur_member = ao2_iterator_next(&mem_iter))) {
03033 if (!cur_member->dynamic) {
03034 ao2_ref(cur_member, -1);
03035 continue;
03036 }
03037
03038 res = snprintf(value + value_len, sizeof(value) - value_len, "%s%s;%d;%d;%s",
03039 value_len ? "|" : "", cur_member->interface, cur_member->penalty, cur_member->paused, cur_member->membername);
03040
03041 ao2_ref(cur_member, -1);
03042
03043 if (res != strlen(value + value_len)) {
03044 ast_log(LOG_WARNING, "Could not create persistent member string, out of space\n");
03045 break;
03046 }
03047 value_len += res;
03048 }
03049
03050 if (value_len && !cur_member) {
03051 if (ast_db_put(pm_family, pm_queue->name, value))
03052 ast_log(LOG_WARNING, "failed to create persistent dynamic entry!\n");
03053 } else
03054
03055 ast_db_del(pm_family, pm_queue->name);
03056 }
03057
03058 static int remove_from_queue(const char *queuename, const char *interface)
03059 {
03060 struct call_queue *q;
03061 struct member *mem, tmpmem;
03062 int res = RES_NOSUCHQUEUE;
03063
03064 ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
03065
03066 AST_LIST_LOCK(&queues);
03067 AST_LIST_TRAVERSE(&queues, q, list) {
03068 ast_mutex_lock(&q->lock);
03069 if (strcmp(q->name, queuename)) {
03070 ast_mutex_unlock(&q->lock);
03071 continue;
03072 }
03073
03074 if ((mem = ao2_find(q->members, &tmpmem, OBJ_POINTER))) {
03075
03076 if(!mem->dynamic) {
03077 res = RES_NOT_DYNAMIC;
03078 ao2_ref(mem, -1);
03079 ast_mutex_unlock(&q->lock);
03080 break;
03081 }
03082 q->membercount--;
03083 manager_event(EVENT_FLAG_AGENT, "QueueMemberRemoved",
03084 "Queue: %s\r\n"
03085 "Location: %s\r\n"
03086 "MemberName: %s\r\n",
03087 q->name, mem->interface, mem->membername);
03088 ao2_unlink(q->members, mem);
03089 ao2_ref(mem, -1);
03090
03091 if (queue_persistent_members)
03092 dump_queue_members(q);
03093
03094 res = RES_OKAY;
03095 } else {
03096 res = RES_EXISTS;
03097 }
03098 ast_mutex_unlock(&q->lock);
03099 break;
03100 }
03101
03102 if (res == RES_OKAY)
03103 remove_from_interfaces(interface);
03104
03105 AST_LIST_UNLOCK(&queues);
03106
03107 return res;
03108 }
03109
03110
03111 static int add_to_queue(const char *queuename, const char *interface, const char *membername, int penalty, int paused, int dump)
03112 {
03113 struct call_queue *q;
03114 struct member *new_member, *old_member;
03115 int res = RES_NOSUCHQUEUE;
03116
03117
03118
03119 if (!(q = load_realtime_queue(queuename)))
03120 return res;
03121
03122 AST_LIST_LOCK(&queues);
03123
03124 ast_mutex_lock(&q->lock);
03125 if ((old_member = interface_exists(q, interface)) == NULL) {
03126 add_to_interfaces(interface);
03127 if ((new_member = create_queue_member(interface, membername, penalty, paused))) {
03128 new_member->dynamic = 1;
03129 ao2_link(q->members, new_member);
03130 q->membercount++;
03131 manager_event(EVENT_FLAG_AGENT, "QueueMemberAdded",
03132 "Queue: %s\r\n"
03133 "Location: %s\r\n"
03134 "MemberName: %s\r\n"
03135 "Membership: %s\r\n"
03136 "Penalty: %d\r\n"
03137 "CallsTaken: %d\r\n"
03138 "LastCall: %d\r\n"
03139 "Status: %d\r\n"
03140 "Paused: %d\r\n",
03141 q->name, new_member->interface, new_member->membername,
03142 "dynamic",
03143 new_member->penalty, new_member->calls, (int) new_member->lastcall,
03144 new_member->status, new_member->paused);
03145
03146 ao2_ref(new_member, -1);
03147 new_member = NULL;
03148
03149 if (dump)
03150 dump_queue_members(q);
03151
03152 res = RES_OKAY;
03153 } else {
03154 res = RES_OUTOFMEMORY;
03155 }
03156 } else {
03157 ao2_ref(old_member, -1);
03158 res = RES_EXISTS;
03159 }
03160 ast_mutex_unlock(&q->lock);
03161 AST_LIST_UNLOCK(&queues);
03162
03163 return res;
03164 }
03165
03166 static int set_member_paused(const char *queuename, const char *interface, int paused)
03167 {
03168 int found = 0;
03169 struct call_queue *q;
03170 struct member *mem;
03171
03172
03173
03174 if (ast_strlen_zero(queuename))
03175 ast_queue_log("NONE", "NONE", interface, (paused ? "PAUSEALL" : "UNPAUSEALL"), "%s", "");
03176
03177 AST_LIST_LOCK(&queues);
03178 AST_LIST_TRAVERSE(&queues, q, list) {
03179 ast_mutex_lock(&q->lock);
03180 if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
03181 if ((mem = interface_exists(q, interface))) {
03182 found++;
03183 if (mem->paused == paused)
03184 ast_log(LOG_DEBUG, "%spausing already-%spaused queue member %s:%s\n", (paused ? "" : "un"), (paused ? "" : "un"), q->name, interface);
03185 mem->paused = paused;
03186
03187 if (queue_persistent_members)
03188 dump_queue_members(q);
03189
03190 if(mem->realtime)
03191 update_realtime_member_field(mem, q->name, "paused", paused ? "1" : "0");
03192
03193 ast_queue_log(q->name, "NONE", mem->membername, (paused ? "PAUSE" : "UNPAUSE"), "%s", "");
03194
03195 manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
03196 "Queue: %s\r\n"
03197 "Location: %s\r\n"
03198 "MemberName: %s\r\n"
03199 "Paused: %d\r\n",
03200 q->name, mem->interface, mem->membername, paused);
03201 ao2_ref(mem, -1);
03202 }
03203 }
03204 ast_mutex_unlock(&q->lock);
03205 }
03206 AST_LIST_UNLOCK(&queues);
03207
03208 return found ? RESULT_SUCCESS : RESULT_FAILURE;
03209 }
03210
03211
03212 static void reload_queue_members(void)
03213 {
03214 char *cur_ptr;
03215 char *queue_name;
03216 char *member;
03217 char *interface;
03218 char *membername = NULL;
03219 char *penalty_tok;
03220 int penalty = 0;
03221 char *paused_tok;
03222 int paused = 0;
03223 struct ast_db_entry *db_tree;
03224 struct ast_db_entry *entry;
03225 struct call_queue *cur_queue;
03226 char queue_data[PM_MAX_LEN];
03227
03228 AST_LIST_LOCK(&queues);
03229
03230
03231 db_tree = ast_db_gettree(pm_family, NULL);
03232 for (entry = db_tree; entry; entry = entry->next) {
03233
03234 queue_name = entry->key + strlen(pm_family) + 2;
03235
03236 AST_LIST_TRAVERSE(&queues, cur_queue, list) {
03237 ast_mutex_lock(&cur_queue->lock);
03238 if (!strcmp(queue_name, cur_queue->name))
03239 break;
03240 ast_mutex_unlock(&cur_queue->lock);
03241 }
03242
03243 if (!cur_queue)
03244 cur_queue = load_realtime_queue(queue_name);
03245
03246 if (!cur_queue) {
03247
03248
03249 ast_log(LOG_WARNING, "Error loading persistent queue: '%s': it does not exist\n", queue_name);
03250 ast_db_del(pm_family, queue_name);
03251 continue;
03252 } else
03253 ast_mutex_unlock(&cur_queue->lock);
03254
03255 if (ast_db_get(pm_family, queue_name, queue_data, PM_MAX_LEN))
03256 continue;
03257
03258 cur_ptr = queue_data;
03259 while ((member = strsep(&cur_ptr, "|"))) {
03260 if (ast_strlen_zero(member))
03261 continue;
03262
03263 interface = strsep(&member, ";");
03264 penalty_tok = strsep(&member, ";");
03265 paused_tok = strsep(&member, ";");
03266 membername = strsep(&member, ";");
03267
03268 if (!penalty_tok) {
03269 ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (penalty)\n", queue_name);
03270 break;
03271 }
03272 penalty = strtol(penalty_tok, NULL, 10);
03273 if (errno == ERANGE) {
03274 ast_log(LOG_WARNING, "Error converting penalty: %s: Out of range.\n", penalty_tok);
03275 break;
03276 }
03277
03278 if (!paused_tok) {
03279 ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (paused)\n", queue_name);
03280 break;
03281 }
03282 paused = strtol(paused_tok, NULL, 10);
03283 if ((errno == ERANGE) || paused < 0 || paused > 1) {
03284 ast_log(LOG_WARNING, "Error converting paused: %s: Expected 0 or 1.\n", paused_tok);
03285 break;
03286 }
03287 if (ast_strlen_zero(membername))
03288 membername = interface;
03289
03290 if (option_debug)
03291 ast_log(LOG_DEBUG, "Reload Members: Queue: %s Member: %s Name: %s Penalty: %d Paused: %d\n", queue_name, interface, membername, penalty, paused);
03292
03293 if (add_to_queue(queue_name, interface, membername, penalty, paused, 0) == RES_OUTOFMEMORY) {
03294 ast_log(LOG_ERROR, "Out of Memory when reloading persistent queue member\n");
03295 break;
03296 }
03297 }
03298 }
03299
03300 AST_LIST_UNLOCK(&queues);
03301 if (db_tree) {
03302 ast_log(LOG_NOTICE, "Queue members successfully reloaded from database.\n");
03303 ast_db_freetree(db_tree);
03304 }
03305 }
03306
03307 static int pqm_exec(struct ast_channel *chan, void *data)
03308 {
03309 struct ast_module_user *lu;
03310 char *parse;
03311 int priority_jump = 0;
03312 AST_DECLARE_APP_ARGS(args,
03313 AST_APP_ARG(queuename);
03314 AST_APP_ARG(interface);
03315 AST_APP_ARG(options);
03316 );
03317
03318 if (ast_strlen_zero(data)) {
03319 ast_log(LOG_WARNING, "PauseQueueMember requires an argument ([queuename]|interface[|options])\n");
03320 return -1;
03321 }
03322
03323 parse = ast_strdupa(data);
03324
03325 AST_STANDARD_APP_ARGS(args, parse);
03326
03327 lu = ast_module_user_add(chan);
03328
03329 if (args.options) {
03330 if (strchr(args.options, 'j'))
03331 priority_jump = 1;
03332 }
03333
03334 if (ast_strlen_zero(args.interface)) {
03335 ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename]|interface[|options])\n");
03336 ast_module_user_remove(lu);
03337 return -1;
03338 }
03339
03340 if (set_member_paused(args.queuename, args.interface, 1)) {
03341 ast_log(LOG_WARNING, "Attempt to pause interface %s, not found\n", args.interface);
03342 if (priority_jump || ast_opt_priority_jumping) {
03343 if (ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101)) {
03344 pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND");
03345 ast_module_user_remove(lu);
03346 return 0;
03347 }
03348 }
03349 ast_module_user_remove(lu);
03350 pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND");
03351 return -1;
03352 }
03353
03354 ast_module_user_remove(lu);
03355 pbx_builtin_setvar_helper(chan, "PQMSTATUS", "PAUSED");
03356
03357 return 0;
03358 }
03359
03360 static int upqm_exec(struct ast_channel *chan, void *data)
03361 {
03362 struct ast_module_user *lu;
03363 char *parse;
03364 int priority_jump = 0;
03365 AST_DECLARE_APP_ARGS(args,
03366 AST_APP_ARG(queuename);
03367 AST_APP_ARG(interface);
03368 AST_APP_ARG(options);
03369 );
03370
03371 if (ast_strlen_zero(data)) {
03372 ast_log(LOG_WARNING, "UnpauseQueueMember requires an argument ([queuename]|interface[|options])\n");
03373 return -1;
03374 }
03375
03376 parse = ast_strdupa(data);
03377
03378 AST_STANDARD_APP_ARGS(args, parse);
03379
03380 lu = ast_module_user_add(chan);
03381
03382 if (args.options) {
03383 if (strchr(args.options, 'j'))
03384 priority_jump = 1;
03385 }
03386
03387 if (ast_strlen_zero(args.interface)) {
03388 ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename]|interface[|options])\n");
03389 ast_module_user_remove(lu);
03390 return -1;
03391 }
03392
03393 if (set_member_paused(args.queuename, args.interface, 0)) {
03394 ast_log(LOG_WARNING, "Attempt to unpause interface %s, not found\n", args.interface);
03395 if (priority_jump || ast_opt_priority_jumping) {
03396 if (ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101)) {
03397 pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND");
03398 ast_module_user_remove(lu);
03399 return 0;
03400 }
03401 }
03402 ast_module_user_remove(lu);
03403 pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND");
03404 return -1;
03405 }
03406
03407 ast_module_user_remove(lu);
03408 pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "UNPAUSED");
03409
03410 return 0;
03411 }
03412
03413 static int rqm_exec(struct ast_channel *chan, void *data)
03414 {
03415 int res=-1;
03416 struct ast_module_user *lu;
03417 char *parse, *temppos = NULL;
03418 int priority_jump = 0;
03419 AST_DECLARE_APP_ARGS(args,
03420 AST_APP_ARG(queuename);
03421 AST_APP_ARG(interface);
03422 AST_APP_ARG(options);
03423 );
03424
03425
03426 if (ast_strlen_zero(data)) {
03427 ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename[|interface[|options]])\n");
03428 return -1;
03429 }
03430
03431 parse = ast_strdupa(data);
03432
03433 AST_STANDARD_APP_ARGS(args, parse);
03434
03435 lu = ast_module_user_add(chan);
03436
03437 if (ast_strlen_zero(args.interface)) {
03438 args.interface = ast_strdupa(chan->name);
03439 temppos = strrchr(args.interface, '-');
03440 if (temppos)
03441 *temppos = '\0';
03442 }
03443
03444 if (args.options) {
03445 if (strchr(args.options, 'j'))
03446 priority_jump = 1;
03447 }
03448
03449 switch (remove_from_queue(args.queuename, args.interface)) {
03450 case RES_OKAY:
03451 ast_queue_log(args.queuename, chan->uniqueid, args.interface, "REMOVEMEMBER", "%s", "");
03452 ast_log(LOG_NOTICE, "Removed interface '%s' from queue '%s'\n", args.interface, args.queuename);
03453 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "REMOVED");
03454 res = 0;
03455 break;
03456 case RES_EXISTS:
03457 ast_log(LOG_DEBUG, "Unable to remove interface '%s' from queue '%s': Not there\n", args.interface, args.queuename);
03458 if (priority_jump || ast_opt_priority_jumping)
03459 ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
03460 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTINQUEUE");
03461 res = 0;
03462 break;
03463 case RES_NOSUCHQUEUE:
03464 ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", args.queuename);
03465 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOSUCHQUEUE");
03466 res = 0;
03467 break;
03468 case RES_NOT_DYNAMIC:
03469 ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': '%s' is not a dynamic member\n", args.queuename, args.interface);
03470 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTDYNAMIC");
03471 res = 0;
03472 break;
03473 }
03474
03475 ast_module_user_remove(lu);
03476
03477 return res;
03478 }
03479
03480 static int aqm_exec(struct ast_channel *chan, void *data)
03481 {
03482 int res=-1;
03483 struct ast_module_user *lu;
03484 char *parse, *temppos = NULL;
03485 int priority_jump = 0;
03486 AST_DECLARE_APP_ARGS(args,
03487 AST_APP_ARG(queuename);
03488 AST_APP_ARG(interface);
03489 AST_APP_ARG(penalty);
03490 AST_APP_ARG(options);
03491 AST_APP_ARG(membername);
03492 );
03493 int penalty = 0;
03494
03495 if (ast_strlen_zero(data)) {
03496 ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename[|[interface]|[penalty][|options][|membername]])\n");
03497 return -1;
03498 }
03499
03500 parse = ast_strdupa(data);
03501
03502 AST_STANDARD_APP_ARGS(args, parse);
03503
03504 lu = ast_module_user_add(chan);
03505
03506 if (ast_strlen_zero(args.interface)) {
03507 args.interface = ast_strdupa(chan->name);
03508 temppos = strrchr(args.interface, '-');
03509 if (temppos)
03510 *temppos = '\0';
03511 }
03512
03513 if (!ast_strlen_zero(args.penalty)) {
03514 if ((sscanf(args.penalty, "%d", &penalty) != 1) || penalty < 0) {
03515 ast_log(LOG_WARNING, "Penalty '%s' is invalid, must be an integer >= 0\n", args.penalty);
03516 penalty = 0;
03517 }
03518 }
03519
03520 if (args.options) {
03521 if (strchr(args.options, 'j'))
03522 priority_jump = 1;
03523 }
03524
03525 switch (add_to_queue(args.queuename, args.interface, args.membername, penalty, 0, queue_persistent_members)) {
03526 case RES_OKAY:
03527 ast_queue_log(args.queuename, chan->uniqueid, args.interface, "ADDMEMBER", "%s", "");
03528 ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", args.interface, args.queuename);
03529 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "ADDED");
03530 res = 0;
03531 break;
03532 case RES_EXISTS:
03533 ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': Already there\n", args.interface, args.queuename);
03534 if (priority_jump || ast_opt_priority_jumping)
03535 ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
03536 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "MEMBERALREADY");
03537 res = 0;
03538 break;
03539 case RES_NOSUCHQUEUE:
03540 ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", args.queuename);
03541 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "NOSUCHQUEUE");
03542 res = 0;
03543 break;
03544 case RES_OUTOFMEMORY:
03545 ast_log(LOG_ERROR, "Out of memory adding member %s to queue %s\n", args.interface, args.queuename);
03546 break;
03547 }
03548
03549 ast_module_user_remove(lu);
03550
03551 return res;
03552 }
03553
03554 static int ql_exec(struct ast_channel *chan, void *data)
03555 {
03556 struct ast_module_user *u;
03557 char *parse;
03558
03559 AST_DECLARE_APP_ARGS(args,
03560 AST_APP_ARG(queuename);
03561 AST_APP_ARG(uniqueid);
03562 AST_APP_ARG(membername);
03563 AST_APP_ARG(event);
03564 AST_APP_ARG(params);
03565 );
03566
03567 if (ast_strlen_zero(data)) {
03568 ast_log(LOG_WARNING, "QueueLog requires arguments (queuename|uniqueid|membername|event[|additionalinfo]\n");
03569 return -1;
03570 }
03571
03572 u = ast_module_user_add(chan);
03573
03574 parse = ast_strdupa(data);
03575
03576 AST_STANDARD_APP_ARGS(args, parse);
03577
03578 if (ast_strlen_zero(args.queuename) || ast_strlen_zero(args.uniqueid)
03579 || ast_strlen_zero(args.membername) || ast_strlen_zero(args.event)) {
03580 ast_log(LOG_WARNING, "QueueLog requires arguments (queuename|uniqueid|membername|event[|additionalinfo])\n");
03581 ast_module_user_remove(u);
03582 return -1;
03583 }
03584
03585 ast_queue_log(args.queuename, args.uniqueid, args.membername, args.event,
03586 "%s", args.params ? args.params : "");
03587
03588 ast_module_user_remove(u);
03589
03590 return 0;
03591 }
03592
03593 static int queue_exec(struct ast_channel *chan, void *data)
03594 {
03595 int res=-1;
03596 int ringing=0;
03597 struct ast_module_user *lu;
03598 const char *user_priority;
03599 const char *max_penalty_str;
03600 int prio;
03601 int max_penalty;
03602 enum queue_result reason = QUEUE_UNKNOWN;
03603
03604 int tries = 0;
03605 int noption = 0;
03606 char *parse;
03607 AST_DECLARE_APP_ARGS(args,
03608 AST_APP_ARG(queuename);
03609 AST_APP_ARG(options);
03610 AST_APP_ARG(url);
03611 AST_APP_ARG(announceoverride);
03612 AST_APP_ARG(queuetimeoutstr);
03613 AST_APP_ARG(agi);
03614 );
03615
03616 struct queue_ent qe;
03617
03618 if (ast_strlen_zero(data)) {
03619 ast_log(LOG_WARNING, "Queue requires an argument: queuename[|options[|URL[|announceoverride[|timeout[|agi]]]]]\n");
03620 return -1;
03621 }
03622
03623 parse = ast_strdupa(data);
03624 AST_STANDARD_APP_ARGS(args, parse);
03625
03626 lu = ast_module_user_add(chan);
03627
03628
03629 memset(&qe, 0, sizeof(qe));
03630 qe.start = time(NULL);
03631
03632
03633 if (args.queuetimeoutstr)
03634 qe.expire = qe.start + atoi(args.queuetimeoutstr);
03635 else
03636 qe.expire = 0;
03637
03638
03639 user_priority = pbx_builtin_getvar_helper(chan, "QUEUE_PRIO");
03640 if (user_priority) {
03641 if (sscanf(user_priority, "%d", &prio) == 1) {
03642 if (option_debug)
03643 ast_log(LOG_DEBUG, "%s: Got priority %d from ${QUEUE_PRIO}.\n",
03644 chan->name, prio);
03645 } else {
03646 ast_log(LOG_WARNING, "${QUEUE_PRIO}: Invalid value (%s), channel %s.\n",
03647 user_priority, chan->name);
03648 prio = 0;
03649 }
03650 } else {
03651 if (option_debug > 2)
03652 ast_log(LOG_DEBUG, "NO QUEUE_PRIO variable found. Using default.\n");
03653 prio = 0;
03654 }
03655
03656
03657 if ((max_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MAX_PENALTY"))) {
03658 if (sscanf(max_penalty_str, "%d", &max_penalty) == 1) {
03659 if (option_debug)
03660 ast_log(LOG_DEBUG, "%s: Got max penalty %d from ${QUEUE_MAX_PENALTY}.\n",
03661 chan->name, max_penalty);
03662 } else {
03663 ast_log(LOG_WARNING, "${QUEUE_MAX_PENALTY}: Invalid value (%s), channel %s.\n",
03664 max_penalty_str, chan->name);
03665 max_penalty = 0;
03666 }
03667 } else {
03668 max_penalty = 0;
03669 }
03670
03671 if (args.options && (strchr(args.options, 'r')))
03672 ringing = 1;
03673
03674 if (option_debug)
03675 ast_log(LOG_DEBUG, "queue: %s, options: %s, url: %s, announce: %s, expires: %ld, priority: %d\n",
03676 args.queuename, args.options, args.url, args.announceoverride, (long)qe.expire, prio);
03677
03678 qe.chan = chan;
03679 qe.prio = prio;
03680 qe.max_penalty = max_penalty;
03681 qe.last_pos_said = 0;
03682 qe.last_pos = 0;
03683 qe.last_periodic_announce_time = time(NULL);
03684 qe.last_periodic_announce_sound = 0;
03685 qe.valid_digits = 0;
03686 if (!join_queue(args.queuename, &qe, &reason)) {
03687 int makeannouncement = 0;
03688
03689 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ENTERQUEUE", "%s|%s", S_OR(args.url, ""),
03690 S_OR(chan->cid.cid_num, ""));
03691 check_turns:
03692 if (ringing) {
03693 ast_indicate(chan, AST_CONTROL_RINGING);
03694 } else {
03695 ast_moh_start(chan, qe.moh, NULL);
03696 }
03697
03698
03699 res = wait_our_turn(&qe, ringing, &reason);
03700 if (res)
03701 goto stop;
03702
03703 for (;;) {
03704
03705
03706
03707
03708
03709 enum queue_member_status stat;
03710
03711
03712 if (qe.expire && (time(NULL) > qe.expire)) {
03713 record_abandoned(&qe);
03714 reason = QUEUE_TIMEOUT;
03715 res = 0;
03716 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
03717 break;
03718 }
03719
03720 if (makeannouncement) {
03721
03722 if (qe.parent->announcefrequency && !ringing)
03723 if ((res = say_position(&qe)))
03724 goto stop;
03725
03726 }
03727 makeannouncement = 1;
03728
03729
03730 if (qe.parent->periodicannouncefrequency && !ringing)
03731 if ((res = say_periodic_announcement(&qe)))
03732 goto stop;
03733
03734
03735 res = try_calling(&qe, args.options, args.announceoverride, args.url, &tries, &noption, args.agi);
03736 if (res)
03737 goto stop;
03738
03739 stat = get_member_status(qe.parent, qe.max_penalty);
03740
03741
03742 if (noption && tries >= qe.parent->membercount) {
03743 if (option_verbose > 2)
03744 ast_verbose(VERBOSE_PREFIX_3 "Exiting on time-out cycle\n");
03745 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
03746 record_abandoned(&qe);
03747 reason = QUEUE_TIMEOUT;
03748 res = 0;
03749 break;
03750 }
03751
03752
03753 if (qe.parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) {
03754 record_abandoned(&qe);
03755 reason = QUEUE_LEAVEEMPTY;
03756 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
03757 res = 0;
03758 break;
03759 }
03760
03761
03762 if ((qe.parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) {
03763 record_abandoned(&qe);
03764 reason = QUEUE_LEAVEUNAVAIL;
03765 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
03766 res = 0;
03767 break;
03768 }
03769
03770
03771 if (qe.expire && (time(NULL) > qe.expire)) {
03772 record_abandoned(&qe);
03773 reason = QUEUE_TIMEOUT;
03774 res = 0;
03775 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
03776 break;
03777 }
03778
03779
03780 update_realtime_members(qe.parent);
03781
03782
03783 res = wait_a_bit(&qe);
03784 if (res)
03785 goto stop;
03786
03787
03788
03789
03790
03791
03792 if (!is_our_turn(&qe)) {
03793 if (option_debug)
03794 ast_log(LOG_DEBUG, "Darn priorities, going back in queue (%s)!\n",
03795 qe.chan->name);
03796 goto check_turns;
03797 }
03798 }
03799
03800 stop:
03801 if (res) {
03802 if (res < 0) {
03803 if (!qe.handled) {
03804 record_abandoned(&qe);
03805 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ABANDON",
03806 "%d|%d|%ld", qe.pos, qe.opos,
03807 (long) time(NULL) - qe.start);
03808 }
03809 res = -1;
03810 } else if (qe.valid_digits) {
03811 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHKEY",
03812 "%s|%d", qe.digits, qe.pos);
03813 }
03814 }
03815
03816
03817 if (res >= 0 && res != AST_PBX_KEEPALIVE) {
03818 res = 0;
03819 if (ringing) {
03820 ast_indicate(chan, -1);
03821 } else {
03822 ast_moh_stop(chan);
03823 }
03824 ast_stopstream(chan);
03825 }
03826 leave_queue(&qe);
03827 if (reason != QUEUE_UNKNOWN)
03828 set_queue_result(chan, reason);
03829 } else {
03830 ast_log(LOG_WARNING, "Unable to join queue '%s'\n", args.queuename);
03831 set_queue_result(chan, reason);
03832 res = 0;
03833 }
03834 ast_module_user_remove(lu);
03835
03836 return res;
03837 }
03838
03839 static int queue_function_qac(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
03840 {
03841 int count = 0;
03842 struct call_queue *q;
03843 struct ast_module_user *lu;
03844 struct member *m;
03845 struct ao2_iterator mem_iter;
03846
03847 buf[0] = '\0';
03848
03849 if (ast_strlen_zero(data)) {
03850 ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
03851 return -1;
03852 }
03853
03854 lu = ast_module_user_add(chan);
03855
03856 if((q = load_realtime_queue(data))) {
03857 ast_mutex_lock(&q->lock);
03858 mem_iter = ao2_iterator_init(q->members, 0);
03859 while ((m = ao2_iterator_next(&mem_iter))) {
03860
03861 if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
03862 count++;
03863 }
03864 ao2_ref(m, -1);
03865 }
03866 ast_mutex_unlock(&q->lock);
03867 } else
03868 ast_log(LOG_WARNING, "queue %s was not found\n", data);
03869
03870 snprintf(buf, len, "%d", count);
03871 ast_module_user_remove(lu);
03872
03873 return 0;
03874 }
03875
03876 static int queue_function_queuewaitingcount(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
03877 {
03878 int count = 0;
03879 struct call_queue *q;
03880 struct ast_module_user *lu;
03881
03882 buf[0] = '\0';
03883
03884 if (ast_strlen_zero(data)) {
03885 ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
03886 return -1;
03887 }
03888
03889 lu = ast_module_user_add(chan);
03890
03891 AST_LIST_LOCK(&queues);
03892 AST_LIST_TRAVERSE(&queues, q, list) {
03893 if (!strcasecmp(q->name, data)) {
03894 ast_mutex_lock(&q->lock);
03895 break;
03896 }
03897 }
03898 AST_LIST_UNLOCK(&queues);
03899
03900 if (q) {
03901 count = q->count;
03902 ast_mutex_unlock(&q->lock);
03903 } else
03904 ast_log(LOG_WARNING, "queue %s was not found\n", data);
03905
03906 snprintf(buf, len, "%d", count);
03907 ast_module_user_remove(lu);
03908 return 0;
03909 }
03910
03911 static int queue_function_queuememberlist(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
03912 {
03913 struct ast_module_user *u;
03914 struct call_queue *q;
03915 struct member *m;
03916
03917
03918 buf[0] = '\0';
03919
03920 if (ast_strlen_zero(data)) {
03921 ast_log(LOG_ERROR, "QUEUE_MEMBER_LIST requires an argument: queuename\n");
03922 return -1;
03923 }
03924
03925 u = ast_module_user_add(chan);
03926
03927 AST_LIST_LOCK(&queues);
03928 AST_LIST_TRAVERSE(&queues, q, list) {
03929 if (!strcasecmp(q->name, data)) {
03930 ast_mutex_lock(&q->lock);
03931 break;
03932 }
03933 }
03934 AST_LIST_UNLOCK(&queues);
03935
03936 if (q) {
03937 int buflen = 0, count = 0;
03938 struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
03939
03940 while ((m = ao2_iterator_next(&mem_iter))) {
03941
03942 if (count++) {
03943 strncat(buf + buflen, ",", len - buflen - 1);
03944 buflen++;
03945 }
03946 strncat(buf + buflen, m->membername, len - buflen - 1);
03947 buflen += strlen(m->membername);
03948
03949 if (buflen >= len - 2) {
03950 ao2_ref(m, -1);
03951 ast_log(LOG_WARNING, "Truncating list\n");
03952 break;
03953 }
03954 ao2_ref(m, -1);
03955 }
03956 ast_mutex_unlock(&q->lock);
03957 } else
03958 ast_log(LOG_WARNING, "queue %s was not found\n", data);
03959
03960
03961 buf[len - 1] = '\0';
03962 ast_module_user_remove(u);
03963
03964 return 0;
03965 }
03966
03967 static struct ast_custom_function queueagentcount_function = {
03968 .name = "QUEUEAGENTCOUNT",
03969 .synopsis = "Count number of agents answering a queue",
03970 .syntax = "QUEUEAGENTCOUNT(<queuename>)",
03971 .desc =
03972 "Returns the number of members currently associated with the specified queue.\n"
03973 "This function is deprecated. You should use QUEUE_MEMBER_COUNT() instead.\n",
03974 .read = queue_function_qac,
03975 };
03976
03977 static struct ast_custom_function queuemembercount_function = {
03978 .name = "QUEUE_MEMBER_COUNT",
03979 .synopsis = "Count number of members answering a queue",
03980 .syntax = "QUEUE_MEMBER_COUNT(<queuename>)",
03981 .desc =
03982 "Returns the number of members currently associated with the specified queue.\n",
03983 .read = queue_function_qac,
03984 };
03985
03986 static struct ast_custom_function queuewaitingcount_function = {
03987 .name = "QUEUE_WAITING_COUNT",
03988 .synopsis = "Count number of calls currently waiting in a queue",
03989 .syntax = "QUEUE_WAITING_COUNT(<queuename>)",
03990 .desc =
03991 "Returns the number of callers currently waiting in the specified queue.\n",
03992 .read = queue_function_queuewaitingcount,
03993 };
03994
03995 static struct ast_custom_function queuememberlist_function = {
03996 .name = "QUEUE_MEMBER_LIST",
03997 .synopsis = "Returns a list of interfaces on a queue",
03998 .syntax = "QUEUE_MEMBER_LIST(<queuename>)",
03999 .desc =
04000 "Returns a comma-separated list of members associated with the specified queue.\n",
04001 .read = queue_function_queuememberlist,
04002 };
04003
04004 static int reload_queues(void)
04005 {
04006 struct call_queue *q;
04007 struct ast_config *cfg;
04008 char *cat, *tmp;
04009 struct ast_variable *var;
04010 struct member *cur, *newm;
04011 struct ao2_iterator mem_iter;
04012 int new;
04013 const char *general_val = NULL;
04014 char parse[80];
04015 char *interface;
04016 char *membername = NULL;
04017 int penalty;
04018 AST_DECLARE_APP_ARGS(args,
04019 AST_APP_ARG(interface);
04020 AST_APP_ARG(penalty);
04021 AST_APP_ARG(membername);
04022 );
04023
04024 if (!(cfg = ast_config_load("queues.conf"))) {
04025 ast_log(LOG_NOTICE, "No call queueing config file (queues.conf), so no call queues\n");
04026 return 0;
04027 }
04028 AST_LIST_LOCK(&queues);
04029 use_weight=0;
04030
04031 AST_LIST_TRAVERSE(&queues, q, list) {
04032 if(!q->realtime) {
04033 q->dead = 1;
04034 q->found = 0;
04035 }
04036 }
04037
04038
04039 cat = NULL;
04040 while ((cat = ast_category_browse(cfg, cat)) ) {
04041 if (!strcasecmp(cat, "general")) {
04042
04043 queue_persistent_members = 0;
04044 if ((general_val = ast_variable_retrieve(cfg, "general", "persistentmembers")))
04045 queue_persistent_members = ast_true(general_val);
04046 autofill_default = 0;
04047 if ((general_val = ast_variable_retrieve(cfg, "general", "autofill")))
04048 autofill_default = ast_true(general_val);
04049 montype_default = 0;
04050 if ((general_val = ast_variable_retrieve(cfg, "general", "monitor-type")))
04051 if (!strcasecmp(general_val, "mixmonitor"))
04052 montype_default = 1;
04053 } else {
04054
04055 AST_LIST_TRAVERSE(&queues, q, list) {
04056 if (!strcmp(q->name, cat))
04057 break;
04058 }
04059 if (!q) {
04060
04061 if (!(q = alloc_queue(cat))) {
04062
04063 }
04064 new = 1;
04065 } else
04066 new = 0;
04067 if (q) {
04068 if (!new)
04069 ast_mutex_lock(&q->lock);
04070
04071 if (q->found) {
04072 ast_log(LOG_WARNING, "Queue '%s' already defined! Skipping!\n", cat);
04073 if(!new)
04074 ast_mutex_unlock(&q->lock);
04075 continue;
04076 }
04077
04078 init_queue(q);
04079 clear_queue(q);
04080 mem_iter = ao2_iterator_init(q->members, 0);
04081 while ((cur = ao2_iterator_next(&mem_iter))) {
04082 if (!cur->dynamic) {
04083 cur->delme = 1;
04084 }
04085 ao2_ref(cur, -1);
04086 }
04087 for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
04088 if (!strcasecmp(var->name, "member")) {
04089 struct member tmpmem;
04090
04091
04092 ast_copy_string(parse, var->value, sizeof(parse));
04093
04094 AST_NONSTANDARD_APP_ARGS(args, parse, ',');
04095
04096 interface = args.interface;
04097 if(!ast_strlen_zero(args.penalty)) {
04098 tmp = args.penalty;
04099 while (*tmp && *tmp < 33) tmp++;
04100 penalty = atoi(tmp);
04101 if (penalty < 0) {
04102 penalty = 0;
04103 }
04104 } else
04105 penalty = 0;
04106
04107 if (!ast_strlen_zero(args.membername)) {
04108 membername = args.membername;
04109 while (*membername && *membername < 33) membername++;
04110 }
04111
04112
04113 ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
04114 cur = ao2_find(q->members, &tmpmem, OBJ_POINTER | OBJ_UNLINK);
04115
04116 newm = create_queue_member(interface, membername, penalty, cur ? cur->paused : 0);
04117 ao2_link(q->members, newm);
04118 ao2_ref(newm, -1);
04119 newm = NULL;
04120
04121 if (cur)
04122 ao2_ref(cur, -1);
04123 else {
04124
04125 add_to_interfaces(interface);
04126 q->membercount++;
04127 }
04128 } else {
04129 queue_set_param(q, var->name, var->value, var->lineno, 1);
04130 }
04131 }
04132
04133
04134 mem_iter = ao2_iterator_init(q->members, 0);
04135 while ((cur = ao2_iterator_next(&mem_iter))) {
04136 if (! cur->delme) {
04137 ao2_ref(cur, -1);
04138 continue;
04139 }
04140
04141 remove_from_interfaces(cur->interface);
04142 q->membercount--;
04143 ao2_unlink(q->members, cur);
04144 ao2_ref(cur, -1);
04145 }
04146
04147 if (q->strategy == QUEUE_STRATEGY_ROUNDROBIN)
04148 rr_dep_warning();
04149
04150 if (new) {
04151 AST_LIST_INSERT_HEAD(&queues, q, list);
04152 } else
04153 ast_mutex_unlock(&q->lock);
04154 }
04155 }
04156 }
04157 ast_config_destroy(cfg);
04158 AST_LIST_TRAVERSE_SAFE_BEGIN(&queues, q, list) {
04159 if (q->dead) {
04160 AST_LIST_REMOVE_CURRENT(&queues, list);
04161 if (!q->count)
04162 destroy_queue(q);
04163 else
04164 ast_log(LOG_DEBUG, "XXX Leaking a little memory :( XXX\n");
04165 } else {
04166 ast_mutex_lock(&q->lock);
04167 mem_iter = ao2_iterator_init(q->members, 0);
04168 while ((cur = ao2_iterator_next(&mem_iter))) {
04169 if (cur->dynamic)
04170 q->membercount++;
04171 cur->status = ast_device_state(cur->interface);
04172 ao2_ref(cur, -1);
04173 }
04174 ast_mutex_unlock(&q->lock);
04175 }
04176 }
04177 AST_LIST_TRAVERSE_SAFE_END;
04178 AST_LIST_UNLOCK(&queues);
04179 return 1;
04180 }
04181
04182 static int __queues_show(struct mansession *s, int manager, int fd, int argc, char **argv)
04183 {
04184 struct call_queue *q;
04185 struct queue_ent *qe;
04186 struct member *mem;
04187 int pos, queue_show;
04188 time_t now;
04189 char max_buf[80];
04190 char *max;
04191 size_t max_left;
04192 float sl = 0;
04193 char *term = manager ? "\r\n" : "\n";
04194 struct ao2_iterator mem_iter;
04195
04196 time(&now);
04197 if (argc == 2)
04198 queue_show = 0;
04199 else if (argc == 3)
04200 queue_show = 1;
04201 else
04202 return RESULT_SHOWUSAGE;
04203
04204
04205 if (queue_show)
04206 load_realtime_queue(argv[2]);
04207
04208 AST_LIST_LOCK(&queues);
04209 if (AST_LIST_EMPTY(&queues)) {
04210 AST_LIST_UNLOCK(&queues);
04211 if (queue_show) {
04212 if (s)
04213 astman_append(s, "No such queue: %s.%s",argv[2], term);
04214 else
04215 ast_cli(fd, "No such queue: %s.%s",argv[2], term);
04216 } else {
04217 if (s)
04218 astman_append(s, "No queues.%s", term);
04219 else
04220 ast_cli(fd, "No queues.%s", term);
04221 }
04222 return RESULT_SUCCESS;
04223 }
04224 AST_LIST_TRAVERSE(&queues, q, list) {
04225 ast_mutex_lock(&q->lock);
04226 if (queue_show) {
04227 if (strcasecmp(q->name, argv[2]) != 0) {
04228 ast_mutex_unlock(&q->lock);
04229 if (!AST_LIST_NEXT(q, list)) {
04230 ast_cli(fd, "No such queue: %s.%s",argv[2], term);
04231 break;
04232 }
04233 continue;
04234 }
04235 }
04236 max_buf[0] = '\0';
04237 max = max_buf;
04238 max_left = sizeof(max_buf);
04239 if (q->maxlen)
04240 ast_build_string(&max, &max_left, "%d", q->maxlen);
04241 else
04242 ast_build_string(&max, &max_left, "unlimited");
04243 sl = 0;
04244 if (q->callscompleted > 0)
04245 sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
04246 if (s)
04247 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",
04248 q->name, q->count, max_buf, int2strat(q->strategy), q->holdtime, q->weight,
04249 q->callscompleted, q->callsabandoned,sl,q->servicelevel, term);
04250 else
04251 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",
04252 q->name, q->count, max_buf, int2strat(q->strategy), q->holdtime, q->weight, q->callscompleted, q->callsabandoned,sl,q->servicelevel, term);
04253 if (ao2_container_count(q->members)) {
04254 if (s)
04255 astman_append(s, " Members: %s", term);
04256 else
04257 ast_cli(fd, " Members: %s", term);
04258 mem_iter = ao2_iterator_init(q->members, 0);
04259 while ((mem = ao2_iterator_next(&mem_iter))) {
04260 max_buf[0] = '\0';
04261 max = max_buf;
04262 max_left = sizeof(max_buf);
04263 if (mem->penalty)
04264 ast_build_string(&max, &max_left, " with penalty %d", mem->penalty);
04265 if (mem->dynamic)
04266 ast_build_string(&max, &max_left, " (dynamic)");
04267 if (mem->realtime)
04268 ast_build_string(&max, &max_left, " (realtime)");
04269 if (mem->paused)
04270 ast_build_string(&max, &max_left, " (paused)");
04271 ast_build_string(&max, &max_left, " (%s)", devstate2str(mem->status));
04272 if (mem->calls) {
04273 ast_build_string(&max, &max_left, " has taken %d calls (last was %ld secs ago)",
04274 mem->calls, (long) (time(NULL) - mem->lastcall));
04275 } else
04276 ast_build_string(&max, &max_left, " has taken no calls yet");
04277 if (s)
04278 astman_append(s, " %s%s%s", mem->membername, max_buf, term);
04279 else
04280 ast_cli(fd, " %s%s%s", mem->membername, max_buf, term);
04281 ao2_ref(mem, -1);
04282 }
04283 } else if (s)
04284 astman_append(s, " No Members%s", term);
04285 else
04286 ast_cli(fd, " No Members%s", term);
04287 if (q->head) {
04288 pos = 1;
04289 if (s)
04290 astman_append(s, " Callers: %s", term);
04291 else
04292 ast_cli(fd, " Callers: %s", term);
04293 for (qe = q->head; qe; qe = qe->next) {
04294 if (s)
04295 astman_append(s, " %d. %s (wait: %ld:%2.2ld, prio: %d)%s",
04296 pos++, qe->chan->name, (long) (now - qe->start) / 60,
04297 (long) (now - qe->start) % 60, qe->prio, term);
04298 else
04299 ast_cli(fd, " %d. %s (wait: %ld:%2.2ld, prio: %d)%s", pos++,
04300 qe->chan->name, (long) (now - qe->start) / 60,
04301 (long) (now - qe->start) % 60, qe->prio, term);
04302 }
04303 } else if (s)
04304 astman_append(s, " No Callers%s", term);
04305 else
04306 ast_cli(fd, " No Callers%s", term);
04307 if (s)
04308 astman_append(s, "%s", term);
04309 else
04310 ast_cli(fd, "%s", term);
04311 ast_mutex_unlock(&q->lock);
04312 if (queue_show)
04313 break;
04314 }
04315 AST_LIST_UNLOCK(&queues);
04316 return RESULT_SUCCESS;
04317 }
04318
04319 static int queue_show(int fd, int argc, char **argv)
04320 {
04321 return __queues_show(NULL, 0, fd, argc, argv);
04322 }
04323
04324 static char *complete_queue(const char *line, const char *word, int pos, int state)
04325 {
04326 struct call_queue *q;
04327 char *ret = NULL;
04328 int which = 0;
04329 int wordlen = strlen(word);
04330
04331 AST_LIST_LOCK(&queues);
04332 AST_LIST_TRAVERSE(&queues, q, list) {
04333 if (!strncasecmp(word, q->name, wordlen) && ++which > state) {
04334 ret = ast_strdup(q->name);
04335 break;
04336 }
04337 }
04338 AST_LIST_UNLOCK(&queues);
04339
04340 return ret;
04341 }
04342
04343 static char *complete_queue_show(const char *line, const char *word, int pos, int state)
04344 {
04345 if (pos == 2)
04346 return complete_queue(line, word, pos, state);
04347 return NULL;
04348 }
04349
04350
04351
04352
04353 static int manager_queues_show(struct mansession *s, const struct message *m)
04354 {
04355 char *a[] = { "queue", "show" };
04356
04357 __queues_show(s, 1, -1, 2, a);
04358 astman_append(s, "\r\n\r\n");
04359
04360 return RESULT_SUCCESS;
04361 }
04362
04363
04364 static int manager_queues_status(struct mansession *s, const struct message *m)
04365 {
04366 time_t now;
04367 int pos;
04368 const char *id = astman_get_header(m,"ActionID");
04369 const char *queuefilter = astman_get_header(m,"Queue");
04370 const char *memberfilter = astman_get_header(m,"Member");
04371 char idText[256] = "";
04372 struct call_queue *q;
04373 struct queue_ent *qe;
04374 float sl = 0;
04375 struct member *mem;
04376 struct ao2_iterator mem_iter;
04377
04378 astman_send_ack(s, m, "Queue status will follow");
04379 time(&now);
04380 AST_LIST_LOCK(&queues);
04381 if (!ast_strlen_zero(id))
04382 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
04383
04384 AST_LIST_TRAVERSE(&queues, q, list) {
04385 ast_mutex_lock(&q->lock);
04386
04387
04388 if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) {
04389 sl = ((q->callscompleted > 0) ? 100 * ((float)q->callscompletedinsl / (float)q->callscompleted) : 0);
04390 astman_append(s, "Event: QueueParams\r\n"
04391 "Queue: %s\r\n"
04392 "Max: %d\r\n"
04393 "Calls: %d\r\n"
04394 "Holdtime: %d\r\n"
04395 "Completed: %d\r\n"
04396 "Abandoned: %d\r\n"
04397 "ServiceLevel: %d\r\n"
04398 "ServicelevelPerf: %2.1f\r\n"
04399 "Weight: %d\r\n"
04400 "%s"
04401 "\r\n",
04402 q->name, q->maxlen, q->count, q->holdtime, q->callscompleted,
04403 q->callsabandoned, q->servicelevel, sl, q->weight, idText);
04404
04405 mem_iter = ao2_iterator_init(q->members, 0);
04406 while ((mem = ao2_iterator_next(&mem_iter))) {
04407 if (ast_strlen_zero(memberfilter) || !strcmp(mem->interface, memberfilter)) {
04408 astman_append(s, "Event: QueueMember\r\n"
04409 "Queue: %s\r\n"
04410 "Name: %s\r\n"
04411 "Location: %s\r\n"
04412 "Membership: %s\r\n"
04413 "Penalty: %d\r\n"
04414 "CallsTaken: %d\r\n"
04415 "LastCall: %d\r\n"
04416 "Status: %d\r\n"
04417 "Paused: %d\r\n"
04418 "%s"
04419 "\r\n",
04420 q->name, mem->membername, mem->interface, mem->dynamic ? "dynamic" : "static",
04421 mem->penalty, mem->calls, (int)mem->lastcall, mem->status, mem->paused, idText);
04422 }
04423 ao2_ref(mem, -1);
04424 }
04425
04426 pos = 1;
04427 for (qe = q->head; qe; qe = qe->next) {
04428 astman_append(s, "Event: QueueEntry\r\n"
04429 "Queue: %s\r\n"
04430 "Position: %d\r\n"
04431 "Channel: %s\r\n"
04432 "CallerID: %s\r\n"
04433 "CallerIDName: %s\r\n"
04434 "Wait: %ld\r\n"
04435 "%s"
04436 "\r\n",
04437 q->name, pos++, qe->chan->name,
04438 S_OR(qe->chan->cid.cid_num, "unknown"),
04439 S_OR(qe->chan->cid.cid_name, "unknown"),
04440 (long) (now - qe->start), idText);
04441 }
04442 }
04443 ast_mutex_unlock(&q->lock);
04444 }
04445
04446 astman_append(s,
04447 "Event: QueueStatusComplete\r\n"
04448 "%s"
04449 "\r\n",idText);
04450
04451 AST_LIST_UNLOCK(&queues);
04452
04453
04454 return RESULT_SUCCESS;
04455 }
04456
04457 static int manager_add_queue_member(struct mansession *s, const struct message *m)
04458 {
04459 const char *queuename, *interface, *penalty_s, *paused_s, *membername;
04460 int paused, penalty = 0;
04461
04462 queuename = astman_get_header(m, "Queue");
04463 interface = astman_get_header(m, "Interface");
04464 penalty_s = astman_get_header(m, "Penalty");
04465 paused_s = astman_get_header(m, "Paused");
04466 membername = astman_get_header(m, "MemberName");
04467
04468 if (ast_strlen_zero(queuename)) {
04469 astman_send_error(s, m, "'Queue' not specified.");
04470 return 0;
04471 }
04472
04473 if (ast_strlen_zero(interface)) {
04474 astman_send_error(s, m, "'Interface' not specified.");
04475 return 0;
04476 }
04477
04478 if (ast_strlen_zero(penalty_s))
04479 penalty = 0;
04480 else if (sscanf(penalty_s, "%d", &penalty) != 1 || penalty < 0)
04481 penalty = 0;
04482
04483 if (ast_strlen_zero(paused_s))
04484 paused = 0;
04485 else
04486 paused = abs(ast_true(paused_s));
04487
04488 switch (add_to_queue(queuename, interface, membername, penalty, paused, queue_persistent_members)) {
04489 case RES_OKAY:
04490 ast_queue_log(queuename, "MANAGER", interface, "ADDMEMBER", "%s", "");
04491 astman_send_ack(s, m, "Added interface to queue");
04492 break;
04493 case RES_EXISTS:
04494 astman_send_error(s, m, "Unable to add interface: Already there");
04495 break;
04496 case RES_NOSUCHQUEUE:
04497 astman_send_error(s, m, "Unable to add interface to queue: No such queue");
04498 break;
04499 case RES_OUTOFMEMORY:
04500 astman_send_error(s, m, "Out of memory");
04501 break;
04502 }
04503
04504 return 0;
04505 }
04506
04507 static int manager_remove_queue_member(struct mansession *s, const struct message *m)
04508 {
04509 const char *queuename, *interface;
04510
04511 queuename = astman_get_header(m, "Queue");
04512 interface = astman_get_header(m, "Interface");
04513
04514 if (ast_strlen_zero(queuename) || ast_strlen_zero(interface)) {
04515 astman_send_error(s, m, "Need 'Queue' and 'Interface' parameters.");
04516 return 0;
04517 }
04518
04519 switch (remove_from_queue(queuename, interface)) {
04520 case RES_OKAY:
04521 ast_queue_log(queuename, "MANAGER", interface, "REMOVEMEMBER", "%s", "");
04522 astman_send_ack(s, m, "Removed interface from queue");
04523 break;
04524 case RES_EXISTS:
04525 astman_send_error(s, m, "Unable to remove interface: Not there");
04526 break;
04527 case RES_NOSUCHQUEUE:
04528 astman_send_error(s, m, "Unable to remove interface from queue: No such queue");
04529 break;
04530 case RES_OUTOFMEMORY:
04531 astman_send_error(s, m, "Out of memory");
04532 break;
04533 case RES_NOT_DYNAMIC:
04534 astman_send_error(s, m, "Member not dynamic");
04535 break;
04536 }
04537
04538 return 0;
04539 }
04540
04541 static int manager_pause_queue_member(struct mansession *s, const struct message *m)
04542 {
04543 const char *queuename, *interface, *paused_s;
04544 int paused;
04545
04546 interface = astman_get_header(m, "Interface");
04547 paused_s = astman_get_header(m, "Paused");
04548 queuename = astman_get_header(m, "Queue");
04549
04550 if (ast_strlen_zero(interface) || ast_strlen_zero(paused_s)) {
04551 astman_send_error(s, m, "Need 'Interface' and 'Paused' parameters.");
04552 return 0;
04553 }
04554
04555 paused = abs(ast_true(paused_s));
04556
04557 if (set_member_paused(queuename, interface, paused))
04558 astman_send_error(s, m, "Interface not found");
04559 else
04560 astman_send_ack(s, m, paused ? "Interface paused successfully" : "Interface unpaused successfully");
04561 return 0;
04562 }
04563
04564 static int handle_queue_add_member(int fd, int argc, char *argv[])
04565 {
04566 char *queuename, *interface, *membername = NULL;
04567 int penalty;
04568
04569 if ((argc != 6) && (argc != 8) && (argc != 10)) {
04570 return RESULT_SHOWUSAGE;
04571 } else if (strcmp(argv[4], "to")) {
04572 return RESULT_SHOWUSAGE;
04573 } else if ((argc == 8) && strcmp(argv[6], "penalty")) {
04574 return RESULT_SHOWUSAGE;
04575 } else if ((argc == 10) && strcmp(argv[8], "as")) {
04576 return RESULT_SHOWUSAGE;
04577 }
04578
04579 queuename = argv[5];
04580 interface = argv[3];
04581 if (argc >= 8) {
04582 if (sscanf(argv[7], "%d", &penalty) == 1) {
04583 if (penalty < 0) {
04584 ast_cli(fd, "Penalty must be >= 0\n");
04585 penalty = 0;
04586 }
04587 } else {
04588 ast_cli(fd, "Penalty must be an integer >= 0\n");
04589 penalty = 0;
04590 }
04591 } else {
04592 penalty = 0;
04593 }
04594
04595 if (argc >= 10) {
04596 membername = argv[9];
04597 }
04598
04599 switch (add_to_queue(queuename, interface, membername, penalty, 0, queue_persistent_members)) {
04600 case RES_OKAY:
04601 ast_queue_log(queuename, "CLI", interface, "ADDMEMBER", "%s", "");
04602 ast_cli(fd, "Added interface '%s' to queue '%s'\n", interface, queuename);
04603 return RESULT_SUCCESS;
04604 case RES_EXISTS:
04605 ast_cli(fd, "Unable to add interface '%s' to queue '%s': Already there\n", interface, queuename);
04606 return RESULT_FAILURE;
04607 case RES_NOSUCHQUEUE:
04608 ast_cli(fd, "Unable to add interface to queue '%s': No such queue\n", queuename);
04609 return RESULT_FAILURE;
04610 case RES_OUTOFMEMORY:
04611 ast_cli(fd, "Out of memory\n");
04612 return RESULT_FAILURE;
04613 default:
04614 return RESULT_FAILURE;
04615 }
04616 }
04617
04618 static char *complete_queue_add_member(const char *line, const char *word, int pos, int state)
04619 {
04620
04621 switch (pos) {
04622 case 3:
04623 return NULL;
04624 case 4:
04625 return state == 0 ? ast_strdup("to") : NULL;
04626 case 5:
04627 return complete_queue(line, word, pos, state);
04628 case 6:
04629 return state == 0 ? ast_strdup("penalty") : NULL;
04630 case 7:
04631 if (state < 100) {
04632 char *num;
04633 if ((num = ast_malloc(3))) {
04634 sprintf(num, "%d", state);
04635 }
04636 return num;
04637 } else {
04638 return NULL;
04639 }
04640 case 8:
04641 return state == 0 ? ast_strdup("as") : NULL;
04642 case 9:
04643 return NULL;
04644 default:
04645 return NULL;
04646 }
04647 }
04648
04649 static int handle_queue_remove_member(int fd, int argc, char *argv[])
04650 {
04651 char *queuename, *interface;
04652
04653 if (argc != 6) {
04654 return RESULT_SHOWUSAGE;
04655 } else if (strcmp(argv[4], "from")) {
04656 return RESULT_SHOWUSAGE;
04657 }
04658
04659 queuename = argv[5];
04660 interface = argv[3];
04661
04662 switch (remove_from_queue(queuename, interface)) {
04663 case RES_OKAY:
04664 ast_queue_log(queuename, "CLI", interface, "REMOVEMEMBER", "%s", "");
04665 ast_cli(fd, "Removed interface '%s' from queue '%s'\n", interface, queuename);
04666 return RESULT_SUCCESS;
04667 case RES_EXISTS:
04668 ast_cli(fd, "Unable to remove interface '%s' from queue '%s': Not there\n", interface, queuename);
04669 return RESULT_FAILURE;
04670 case RES_NOSUCHQUEUE:
04671 ast_cli(fd, "Unable to remove interface from queue '%s': No such queue\n", queuename);
04672 return RESULT_FAILURE;
04673 case RES_OUTOFMEMORY:
04674 ast_cli(fd, "Out of memory\n");
04675 return RESULT_FAILURE;
04676 case RES_NOT_DYNAMIC:
04677 ast_cli(fd, "Member not dynamic\n");
04678 return RESULT_FAILURE;
04679 default:
04680 return RESULT_FAILURE;
04681 }
04682 }
04683
04684 static char *complete_queue_remove_member(const char *line, const char *word, int pos, int state)
04685 {
04686 int which = 0;
04687 struct call_queue *q;
04688 struct member *m;
04689 struct ao2_iterator mem_iter;
04690
04691
04692 if (pos > 5 || pos < 3)
04693 return NULL;
04694 if (pos == 4)
04695 return state == 0 ? ast_strdup("from") : NULL;
04696
04697 if (pos == 5)
04698 return complete_queue(line, word, pos, state);
04699
04700
04701 if (!AST_LIST_EMPTY(&queues)) {
04702 AST_LIST_TRAVERSE(&queues, q, list) {
04703 ast_mutex_lock(&q->lock);
04704 mem_iter = ao2_iterator_init(q->members, 0);
04705 while ((m = ao2_iterator_next(&mem_iter))) {
04706 if (++which > state) {
04707 char *tmp;
04708 ast_mutex_unlock(&q->lock);
04709 tmp = m->membername;
04710 ao2_ref(m, -1);
04711 return ast_strdup(tmp);
04712 }
04713 ao2_ref(m, -1);
04714 }
04715 ast_mutex_unlock(&q->lock);
04716 }
04717 }
04718
04719 return NULL;
04720 }
04721
04722 static char queue_show_usage[] =
04723 "Usage: queue show\n"
04724 " Provides summary information on a specified queue.\n";
04725
04726 static char qam_cmd_usage[] =
04727 "Usage: queue add member <channel> to <queue> [penalty <penalty>]\n";
04728
04729 static char qrm_cmd_usage[] =
04730 "Usage: queue remove member <channel> from <queue>\n";
04731
04732 static struct ast_cli_entry cli_show_queue_deprecated = {
04733 { "show", "queue", NULL },
04734 queue_show, NULL,
04735 NULL, complete_queue_show };
04736
04737 static struct ast_cli_entry cli_add_queue_member_deprecated = {
04738 { "add", "queue", "member", NULL },
04739 handle_queue_add_member, NULL,
04740 NULL, complete_queue_add_member };
04741
04742 static struct ast_cli_entry cli_remove_queue_member_deprecated = {
04743 { "remove", "queue", "member", NULL },
04744 handle_queue_remove_member, NULL,
04745 NULL, complete_queue_remove_member };
04746
04747 static struct ast_cli_entry cli_queue[] = {
04748
04749 { { "show", "queues", NULL },
04750 queue_show, NULL,
04751 NULL, NULL },
04752
04753 { { "queue", "show", NULL },
04754 queue_show, "Show status of a specified queue",
04755 queue_show_usage, complete_queue_show, &cli_show_queue_deprecated },
04756
04757 { { "queue", "add", "member", NULL },
04758 handle_queue_add_member, "Add a channel to a specified queue",
04759 qam_cmd_usage, complete_queue_add_member, &cli_add_queue_member_deprecated },
04760
04761 { { "queue", "remove", "member", NULL },
04762 handle_queue_remove_member, "Removes a channel from a specified queue",
04763 qrm_cmd_usage, complete_queue_remove_member, &cli_remove_queue_member_deprecated },
04764 };
04765
04766 static int unload_module(void)
04767 {
04768 int res;
04769
04770 if (device_state.thread != AST_PTHREADT_NULL) {
04771 device_state.stop = 1;
04772 ast_mutex_lock(&device_state.lock);
04773 ast_cond_signal(&device_state.cond);
04774 ast_mutex_unlock(&device_state.lock);
04775 pthread_join(device_state.thread, NULL);
04776 }
04777
04778 ast_cli_unregister_multiple(cli_queue, sizeof(cli_queue) / sizeof(struct ast_cli_entry));
04779 res = ast_manager_unregister("QueueStatus");
04780 res |= ast_manager_unregister("Queues");
04781 res |= ast_manager_unregister("QueueStatus");
04782 res |= ast_manager_unregister("QueueAdd");
04783 res |= ast_manager_unregister("QueueRemove");
04784 res |= ast_manager_unregister("QueuePause");
04785 res |= ast_unregister_application(app_aqm);
04786 res |= ast_unregister_application(app_rqm);
04787 res |= ast_unregister_application(app_pqm);
04788 res |= ast_unregister_application(app_upqm);
04789 res |= ast_unregister_application(app_ql);
04790 res |= ast_unregister_application(app);
04791 res |= ast_custom_function_unregister(&queueagentcount_function);
04792 res |= ast_custom_function_unregister(&queuemembercount_function);
04793 res |= ast_custom_function_unregister(&queuememberlist_function);
04794 res |= ast_custom_function_unregister(&queuewaitingcount_function);
04795 ast_devstate_del(statechange_queue, NULL);
04796
04797 ast_module_user_hangup_all();
04798
04799 clear_and_free_interfaces();
04800
04801 return res;
04802 }
04803
04804 static int load_module(void)
04805 {
04806 int res;
04807
04808 if (!reload_queues())
04809 return AST_MODULE_LOAD_DECLINE;
04810
04811 if (queue_persistent_members)
04812 reload_queue_members();
04813
04814 ast_mutex_init(&device_state.lock);
04815 ast_cond_init(&device_state.cond, NULL);
04816 ast_pthread_create(&device_state.thread, NULL, device_state_thread, NULL);
04817
04818 ast_cli_register_multiple(cli_queue, sizeof(cli_queue) / sizeof(struct ast_cli_entry));
04819 res = ast_register_application(app, queue_exec, synopsis, descrip);
04820 res |= ast_register_application(app_aqm, aqm_exec, app_aqm_synopsis, app_aqm_descrip);
04821 res |= ast_register_application(app_rqm, rqm_exec, app_rqm_synopsis, app_rqm_descrip);
04822 res |= ast_register_application(app_pqm, pqm_exec, app_pqm_synopsis, app_pqm_descrip);
04823 res |= ast_register_application(app_upqm, upqm_exec, app_upqm_synopsis, app_upqm_descrip);
04824 res |= ast_register_application(app_ql, ql_exec, app_ql_synopsis, app_ql_descrip);
04825 res |= ast_manager_register("Queues", 0, manager_queues_show, "Queues");
04826 res |= ast_manager_register("QueueStatus", 0, manager_queues_status, "Queue Status");
04827 res |= ast_manager_register("QueueAdd", EVENT_FLAG_AGENT, manager_add_queue_member, "Add interface to queue.");
04828 res |= ast_manager_register("QueueRemove", EVENT_FLAG_AGENT, manager_remove_queue_member, "Remove interface from queue.");
04829 res |= ast_manager_register("QueuePause", EVENT_FLAG_AGENT, manager_pause_queue_member, "Makes a queue member temporarily unavailable");
04830 res |= ast_custom_function_register(&queueagentcount_function);
04831 res |= ast_custom_function_register(&queuemembercount_function);
04832 res |= ast_custom_function_register(&queuememberlist_function);
04833 res |= ast_custom_function_register(&queuewaitingcount_function);
04834 res |= ast_devstate_add(statechange_queue, NULL);
04835
04836 return res;
04837 }
04838
04839 static int reload(void)
04840 {
04841 reload_queues();
04842 return 0;
04843 }
04844
04845 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "True Call Queueing",
04846 .load = load_module,
04847 .unload = unload_module,
04848 .reload = reload,
04849 );
04850