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