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