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