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