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